Update Simulcrypt ECMg

This commit is contained in:
Lu Chen
2020-07-24 18:17:12 -07:00
parent ed5a1d5db1
commit 785df31261
97 changed files with 3671 additions and 987 deletions

View File

@@ -16,10 +16,30 @@ filegroup(
name = "binary_release_files", name = "binary_release_files",
srcs = [ srcs = [
"certificate_type.h", "certificate_type.h",
"default_device_security_profile_list.h",
"security_profile_list.h",
"status.h", "status.h",
], ],
) )
cc_library(
name = "playready_interface",
hdrs = ["playready_interface.h"],
deps = [
"//util:error_space",
"//protos/public:license_protocol_cc_proto",
],
)
cc_library(
name = "playready_sdk_impl",
hdrs = ["playready_sdk_impl.h"],
deps = [
":playready_interface",
"//protos/public:license_protocol_cc_proto",
],
)
cc_library( cc_library(
name = "content_id_util", name = "content_id_util",
srcs = ["content_id_util.cc"], srcs = ["content_id_util.cc"],
@@ -27,6 +47,7 @@ cc_library(
deps = [ deps = [
":error_space", ":error_space",
":status", ":status",
"//base",
"//license_server_sdk/internal:sdk", "//license_server_sdk/internal:sdk",
"//protos/public:errors_cc_proto", "//protos/public:errors_cc_proto",
"//protos/public:external_license_cc_proto", "//protos/public:external_license_cc_proto",
@@ -67,9 +88,14 @@ cc_library(
hdrs = ["security_profile_list.h"], hdrs = ["security_profile_list.h"],
deps = [ deps = [
":client_id_util", ":client_id_util",
":device_status_list",
"//base",
"//external:protobuf",
"@abseil_repo//absl/synchronization", "@abseil_repo//absl/synchronization",
"//protos/public:client_identification_cc_proto", "//protos/public:client_identification_cc_proto",
"//protos/public:device_certificate_status_cc_proto",
"//protos/public:device_common_cc_proto", "//protos/public:device_common_cc_proto",
"//protos/public:device_security_profile_data_cc_proto",
"//protos/public:provisioned_device_info_cc_proto", "//protos/public:provisioned_device_info_cc_proto",
"//protos/public:security_profile_cc_proto", "//protos/public:security_profile_cc_proto",
], ],
@@ -80,6 +106,7 @@ cc_test(
timeout = "short", timeout = "short",
srcs = ["security_profile_list_test.cc"], srcs = ["security_profile_list_test.cc"],
deps = [ deps = [
":client_id_util",
":security_profile_list", ":security_profile_list",
"//base", "//base",
"//external:protobuf", "//external:protobuf",
@@ -90,6 +117,40 @@ cc_test(
], ],
) )
cc_library(
name = "default_device_security_profile_list",
srcs = ["default_device_security_profile_list.cc"],
hdrs = ["default_device_security_profile_list.h"],
deps = [
":client_id_util",
":device_status_list",
":security_profile_list",
"//base",
"//external:protobuf",
"//protos/public:client_identification_cc_proto",
"//protos/public:device_certificate_status_cc_proto",
"//protos/public:device_common_cc_proto",
"//protos/public:provisioned_device_info_cc_proto",
"//protos/public:security_profile_cc_proto",
],
)
cc_test(
name = "default_device_security_profile_list_test",
timeout = "short",
srcs = ["default_device_security_profile_list_test.cc"],
deps = [
":client_id_util",
":default_device_security_profile_list",
"//base",
"//external:protobuf",
"//testing:gunit_main",
"@abseil_repo//absl/memory",
"//protos/public:device_common_cc_proto",
"//protos/public:security_profile_cc_proto",
],
)
cc_library( cc_library(
name = "status", name = "status",
srcs = ["status.cc"], srcs = ["status.cc"],
@@ -120,6 +181,8 @@ cc_library(
"certificate_client_cert.cc", "certificate_client_cert.cc",
"certificate_client_cert.h", "certificate_client_cert.h",
"client_cert.cc", "client_cert.cc",
"dual_certificate_client_cert.cc",
"dual_certificate_client_cert.h",
"keybox_client_cert.cc", "keybox_client_cert.cc",
], ],
hdrs = [ hdrs = [
@@ -132,6 +195,7 @@ cc_library(
":ec_key", ":ec_key",
":ec_util", ":ec_util",
":error_space", ":error_space",
":hash_algorithm",
":openssl_util", ":openssl_util",
":random_util", ":random_util",
":rsa_key", ":rsa_key",
@@ -155,8 +219,11 @@ cc_test(
srcs = ["client_cert_test.cc"], srcs = ["client_cert_test.cc"],
deps = [ deps = [
":client_cert", ":client_cert",
":ec_key",
":ec_test_keys", ":ec_test_keys",
":error_space", ":error_space",
":hash_algorithm",
":hash_algorithm_util",
":rsa_key", ":rsa_key",
":rsa_test_keys", ":rsa_test_keys",
":sha_util", ":sha_util",
@@ -179,6 +246,8 @@ cc_library(
":client_cert", ":client_cert",
":drm_service_certificate", ":drm_service_certificate",
":error_space", ":error_space",
":hash_algorithm",
":hash_algorithm_util",
":rsa_key", ":rsa_key",
":status", ":status",
"//base", "//base",
@@ -211,6 +280,8 @@ cc_test(
deps = [ deps = [
":client_cert", ":client_cert",
":device_status_list", ":device_status_list",
":hash_algorithm",
":hash_algorithm_util",
":rsa_key", ":rsa_key",
":rsa_test_keys", ":rsa_test_keys",
":status", ":status",
@@ -235,6 +306,8 @@ cc_library(
":certificate_type", ":certificate_type",
":ec_key", ":ec_key",
":error_space", ":error_space",
":hash_algorithm",
":hash_algorithm_util",
":rsa_key", ":rsa_key",
":sha_util", ":sha_util",
":signer_public_key", ":signer_public_key",
@@ -258,6 +331,8 @@ cc_test(
":ec_key", ":ec_key",
":ec_test_keys", ":ec_test_keys",
":error_space", ":error_space",
":hash_algorithm",
":hash_algorithm_util",
":rsa_key", ":rsa_key",
":rsa_test_keys", ":rsa_test_keys",
":test_drm_certificates", ":test_drm_certificates",
@@ -338,9 +413,12 @@ cc_library(
srcs = ["rsa_key.cc"], srcs = ["rsa_key.cc"],
hdrs = ["rsa_key.h"], hdrs = ["rsa_key.h"],
deps = [ deps = [
":hash_algorithm",
":rsa_util", ":rsa_util",
":sha_util", ":sha_util",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"//external:openssl", "//external:openssl",
], ],
) )
@@ -371,6 +449,7 @@ cc_library(
testonly = 1, testonly = 1,
hdrs = ["mock_rsa_key.h"], hdrs = ["mock_rsa_key.h"],
deps = [ deps = [
":hash_algorithm",
":rsa_key", ":rsa_key",
"//testing:gunit", "//testing:gunit",
], ],
@@ -384,9 +463,11 @@ cc_library(
"ec_util.h", "ec_util.h",
], ],
deps = [ deps = [
":hash_algorithm",
":openssl_util", ":openssl_util",
":private_key_util", ":private_key_util",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/memory", "@abseil_repo//absl/memory",
"//external:openssl", "//external:openssl",
], ],
@@ -415,9 +496,11 @@ cc_library(
deps = [ deps = [
":aes_cbc_util", ":aes_cbc_util",
":ec_util", ":ec_util",
":hash_algorithm",
":openssl_util", ":openssl_util",
":sha_util", ":sha_util",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/memory", "@abseil_repo//absl/memory",
"//external:openssl", "//external:openssl",
], ],
@@ -671,6 +754,7 @@ cc_library(
hdrs = ["signature_util.h"], hdrs = ["signature_util.h"],
deps = [ deps = [
":aes_cbc_util", ":aes_cbc_util",
":hash_algorithm",
":rsa_key", ":rsa_key",
":sha_util", ":sha_util",
":status", ":status",
@@ -774,6 +858,7 @@ cc_library(
":status", ":status",
":x509_cert", ":x509_cert",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization", "@abseil_repo//absl/synchronization",
"//protos/public:client_identification_cc_proto", "//protos/public:client_identification_cc_proto",
@@ -795,6 +880,7 @@ cc_library(
":rsa_util", ":rsa_util",
":status", ":status",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization", "@abseil_repo//absl/synchronization",
"//util/gtl:map_util", "//util/gtl:map_util",
@@ -813,6 +899,7 @@ cc_test(
":aes_cbc_util", ":aes_cbc_util",
":drm_root_certificate", ":drm_root_certificate",
":drm_service_certificate", ":drm_service_certificate",
":hash_algorithm_util",
":rsa_key", ":rsa_key",
":rsa_test_keys", ":rsa_test_keys",
":rsa_util", ":rsa_util",
@@ -849,6 +936,7 @@ cc_library(
":rsa_key", ":rsa_key",
":status", ":status",
"//base", "//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization", "@abseil_repo//absl/synchronization",
"//external:openssl", "//external:openssl",
@@ -887,6 +975,7 @@ cc_library(
deps = [ deps = [
":certificate_type", ":certificate_type",
":error_space", ":error_space",
":hash_algorithm_util",
":rsa_key", ":rsa_key",
":status", ":status",
":x509_cert", ":x509_cert",
@@ -901,6 +990,7 @@ cc_test(
timeout = "short", timeout = "short",
srcs = ["vmp_checker_test.cc"], srcs = ["vmp_checker_test.cc"],
deps = [ deps = [
":hash_algorithm_util",
":rsa_key", ":rsa_key",
":vmp_checker", ":vmp_checker",
"//base", "//base",
@@ -1012,6 +1102,7 @@ cc_library(
hdrs = ["signer_public_key.h"], hdrs = ["signer_public_key.h"],
deps = [ deps = [
":ec_key", ":ec_key",
":hash_algorithm",
":rsa_key", ":rsa_key",
"@abseil_repo//absl/memory", "@abseil_repo//absl/memory",
"//protos/public:drm_certificate_cc_proto", "//protos/public:drm_certificate_cc_proto",
@@ -1024,6 +1115,7 @@ cc_test(
deps = [ deps = [
":ec_key", ":ec_key",
":ec_test_keys", ":ec_test_keys",
":hash_algorithm",
":rsa_key", ":rsa_key",
":rsa_test_keys", ":rsa_test_keys",
":signer_public_key", ":signer_public_key",
@@ -1041,3 +1133,29 @@ cc_library(
"//common/oemcrypto_core_message/odk:kdo", "//common/oemcrypto_core_message/odk:kdo",
], ],
) )
cc_library(
name = "hash_algorithm",
hdrs = ["hash_algorithm.h"],
)
cc_library(
name = "hash_algorithm_util",
srcs = ["hash_algorithm_util.cc"],
hdrs = ["hash_algorithm_util.h"],
deps = [
":hash_algorithm",
"//base",
"//protos/public:hash_algorithm_cc_proto",
],
)
cc_test(
name = "hash_algorithm_util_test",
srcs = ["hash_algorithm_util_test.cc"],
deps = [
":hash_algorithm",
":hash_algorithm_util",
"//testing:gunit_main",
],
)

View File

@@ -7,6 +7,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/aes_cbc_util.h" #include "common/aes_cbc_util.h"
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"

View File

@@ -66,10 +66,11 @@ class ClientCertAlgorithmRSA : public ClientCertAlgorithm {
} }
Status VerifySignature(const std::string& message, Status VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature) const override { const std::string& signature) const override {
CHECK(rsa_public_key_); CHECK(rsa_public_key_);
if (!rsa_public_key_->VerifySignature(message, signature)) { if (!rsa_public_key_->VerifySignature(message, hash_algorithm, signature)) {
return Status(error_space, INVALID_SIGNATURE, ""); return Status(error_space, INVALID_SIGNATURE, "");
} }
return OkStatus(); return OkStatus();
@@ -143,10 +144,12 @@ class ClientCertAlgorithmECC : public ClientCertAlgorithm {
} }
Status VerifySignature(const std::string& message, Status VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature) const override { const std::string& signature) const override {
CHECK(client_ecc_public_key_); CHECK(client_ecc_public_key_);
if (!client_ecc_public_key_->VerifySignature(message, signature)) { if (!client_ecc_public_key_->VerifySignature(message, hash_algorithm,
signature)) {
return Status(error_space, INVALID_SIGNATURE, ""); return Status(error_space, INVALID_SIGNATURE, "");
} }
return OkStatus(); return OkStatus();
@@ -165,7 +168,7 @@ class ClientCertAlgorithmECC : public ClientCertAlgorithm {
} }
SignedMessage::SessionKeyType session_key_type() const override { SignedMessage::SessionKeyType session_key_type() const override {
return SignedMessage::EPHERMERAL_ECC_PUBLIC_KEY; return SignedMessage::EPHEMERAL_ECC_PUBLIC_KEY;
} }
private: private:
@@ -259,11 +262,11 @@ Status CertificateClientCert::Initialize(
} }
Status CertificateClientCert::VerifySignature( Status CertificateClientCert::VerifySignature(
const std::string& message, const std::string& signature, const std::string& message, HashAlgorithm hash_algorithm,
ProtocolVersion protocol_version) const { const std::string& signature, ProtocolVersion protocol_version) const {
return algorithm_->VerifySignature( return algorithm_->VerifySignature(
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message), protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
signature); hash_algorithm, signature);
} }
void CertificateClientCert::GenerateSigningKey( void CertificateClientCert::GenerateSigningKey(

View File

@@ -10,6 +10,7 @@
#define COMMON_CERTIFICATE_CLIENT_CERT_H_ #define COMMON_CERTIFICATE_CLIENT_CERT_H_
#include "common/client_cert.h" #include "common/client_cert.h"
#include "common/hash_algorithm.h"
#include "protos/public/drm_certificate.pb.h" #include "protos/public/drm_certificate.pb.h"
namespace widevine { namespace widevine {
@@ -30,6 +31,7 @@ class ClientCertAlgorithm {
// Verify the |signature| of an incoming request |message| using the public // Verify the |signature| of an incoming request |message| using the public
// key from the drm certificate. // key from the drm certificate.
virtual Status VerifySignature(const std::string& message, virtual Status VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature) const = 0; const std::string& signature) const = 0;
// Returns the key to be used in key derivation of the license // Returns the key to be used in key derivation of the license
@@ -57,6 +59,7 @@ class CertificateClientCert : public ClientCert {
const std::string& serialized_certificate); const std::string& serialized_certificate);
Status VerifySignature(const std::string& message, Status VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature, const std::string& signature,
ProtocolVersion protocol_version) const override; ProtocolVersion protocol_version) const override;
@@ -70,6 +73,7 @@ class CertificateClientCert : public ClientCert {
SignedMessage::SessionKeyType key_type() const override { SignedMessage::SessionKeyType key_type() const override {
return algorithm_->session_key_type(); return algorithm_->session_key_type();
} }
bool using_dual_certificate() const override { return false; }
const std::string& serial_number() const override { const std::string& serial_number() const override {
return device_cert_.serial_number(); return device_cert_.serial_number();
} }

View File

@@ -17,6 +17,7 @@
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "common/certificate_client_cert.h" #include "common/certificate_client_cert.h"
#include "common/crypto_util.h" #include "common/crypto_util.h"
#include "common/dual_certificate_client_cert.h"
#include "common/error_space.h" #include "common/error_space.h"
#include "common/keybox_client_cert.h" #include "common/keybox_client_cert.h"
#include "common/random_util.h" #include "common/random_util.h"
@@ -52,23 +53,34 @@ uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
return WvmTokenHandler::GetSystemId(keybox_bytes); return WvmTokenHandler::GetSystemId(keybox_bytes);
} }
Status ClientCert::Create( Status ClientCert::Create(const DrmRootCertificate* root_certificate,
const DrmRootCertificate* root_certificate, const widevine::ClientIdentification& client_id,
widevine::ClientIdentification::TokenType token_type, std::unique_ptr<ClientCert>* client_cert) {
const std::string& token, std::unique_ptr<ClientCert>* client_cert) {
CHECK(client_cert); CHECK(client_cert);
Status status;
switch (token_type) { switch (client_id.type()) {
case ClientIdentification::KEYBOX: case ClientIdentification::KEYBOX:
return CreateWithKeybox(token, client_cert); return CreateWithKeybox(client_id.token(), client_cert);
case ClientIdentification::DRM_DEVICE_CERTIFICATE: case ClientIdentification::DRM_DEVICE_CERTIFICATE:
return CreateWithDrmCertificate(root_certificate, token, client_cert); if (!client_id.has_device_credentials()) {
return CreateWithDrmCertificate(root_certificate, client_id.token(),
client_cert);
}
// Assumes |client_id.token| is the signing cert and
// |client_id.device_credentials().token| is the encryption cert.
if (client_id.device_credentials().type() !=
ClientIdentification::DRM_DEVICE_CERTIFICATE)
return Status(error_space, INVALID_DRM_CERTIFICATE,
"unsupported-encryption-certificate");
return CreateWithDualDrmCertificates(
root_certificate, client_id.token(),
client_id.device_credentials().token(), client_cert);
default: default:
return Status(error_space, error::UNIMPLEMENTED, return Status(error_space, error::UNIMPLEMENTED,
"client-type-not-implemented"); "client-type-not-implemented");
} }
return OkStatus(); return OkStatus();
} }
@@ -78,6 +90,7 @@ Status ClientCert::CreateWithDrmCertificate(
const DrmRootCertificate* root_certificate, const DrmRootCertificate* root_certificate,
const std::string& drm_certificate, const std::string& drm_certificate,
std::unique_ptr<ClientCert>* client_cert) { std::unique_ptr<ClientCert>* client_cert) {
CHECK(root_certificate);
CHECK(client_cert); CHECK(client_cert);
auto device_cert = absl::make_unique<CertificateClientCert>(); auto device_cert = absl::make_unique<CertificateClientCert>();
Status status = device_cert->Initialize(root_certificate, drm_certificate); Status status = device_cert->Initialize(root_certificate, drm_certificate);
@@ -87,6 +100,22 @@ Status ClientCert::CreateWithDrmCertificate(
return status; return status;
} }
Status ClientCert::CreateWithDualDrmCertificates(
const DrmRootCertificate* root_certificate,
const std::string& signing_drm_certificate,
const std::string& encryption_drm_certificate,
std::unique_ptr<ClientCert>* client_cert) {
CHECK(root_certificate);
CHECK(client_cert);
auto device_cert = absl::make_unique<DualCertificateClientCert>();
Status status = device_cert->Initialize(
root_certificate, signing_drm_certificate, encryption_drm_certificate);
if (status.ok()) {
*client_cert = std::move(device_cert);
}
return status;
}
Status ClientCert::CreateWithKeybox(const std::string& keybox_token, Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
std::unique_ptr<ClientCert>* client_cert) { std::unique_ptr<ClientCert>* client_cert) {
CHECK(client_cert); CHECK(client_cert);

View File

@@ -12,8 +12,11 @@
#include <memory> #include <memory>
#include "common/drm_root_certificate.h" #include "common/drm_root_certificate.h"
#include "common/error_space.h"
#include "common/hash_algorithm.h"
#include "common/status.h" #include "common/status.h"
#include "protos/public/client_identification.pb.h" #include "protos/public/client_identification.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/license_protocol.pb.h" #include "protos/public/license_protocol.pb.h"
namespace widevine { namespace widevine {
@@ -24,17 +27,10 @@ class ClientCert {
ClientCert() = default; ClientCert() = default;
public: public:
// Creates a ClientCert from the |token|. The type of ClientCert created is // Creates a Device Certificate from the supplied |client_id|.
// determined by the |token_type|. static Status Create(const DrmRootCertificate* root_certificate,
static Status Create( const widevine::ClientIdentification& client_id,
const DrmRootCertificate* root_certificate, std::unique_ptr<ClientCert>* client_cert);
widevine::ClientIdentification::TokenType token_type,
const std::string& token, std::unique_ptr<ClientCert>* client_cert);
// Creates a Keybox based ClientCert. The |client_cert| is a caller supplied
// unique_ptr to receive the new ClientCert.
static Status CreateWithKeybox(const std::string& keybox_token,
std::unique_ptr<ClientCert>* client_cert);
// Creates a Device Certificate based ClientCert. // Creates a Device Certificate based ClientCert.
static Status CreateWithDrmCertificate( static Status CreateWithDrmCertificate(
@@ -42,6 +38,21 @@ class ClientCert {
const std::string& drm_certificate, const std::string& drm_certificate,
std::unique_ptr<ClientCert>* client_cert); std::unique_ptr<ClientCert>* client_cert);
// Creates a Device Certificate using the supplied certificates.
// The|signing_drm_certificate| will be used to verify an incoming request.
// The |encryption_drm_certificate| will be used to define the session key
// used to protect a response message.
static Status CreateWithDualDrmCertificates(
const DrmRootCertificate* root_certificate,
const std::string& signing_drm_certificate,
const std::string& encryption_drm_certificate,
std::unique_ptr<ClientCert>* client_cert);
// Creates a Keybox based ClientCert. The |client_cert| is a caller supplied
// unique_ptr to receive the new ClientCert.
static Status CreateWithKeybox(const std::string& keybox_token,
std::unique_ptr<ClientCert>* client_cert);
virtual ~ClientCert() = default; virtual ~ClientCert() = default;
ClientCert(const ClientCert&) = delete; ClientCert(const ClientCert&) = delete;
ClientCert& operator=(const ClientCert&) = delete; ClientCert& operator=(const ClientCert&) = delete;
@@ -50,6 +61,7 @@ class ClientCert {
// classes information and the passed in message. Returns OK if signature // classes information and the passed in message. Returns OK if signature
// is valid. // is valid.
virtual Status VerifySignature(const std::string& message, virtual Status VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature, const std::string& signature,
ProtocolVersion protocol_version) const = 0; ProtocolVersion protocol_version) const = 0;
@@ -61,6 +73,7 @@ class ClientCert {
virtual const std::string& encrypted_key() const = 0; virtual const std::string& encrypted_key() const = 0;
virtual const std::string& key() const = 0; virtual const std::string& key() const = 0;
virtual SignedMessage::SessionKeyType key_type() const = 0; virtual SignedMessage::SessionKeyType key_type() const = 0;
virtual bool using_dual_certificate() const = 0;
virtual const std::string& serial_number() const = 0; virtual const std::string& serial_number() const = 0;
virtual const std::string& service_id() const = 0; virtual const std::string& service_id() const = 0;
virtual const std::string& signing_key() const = 0; virtual const std::string& signing_key() const = 0;
@@ -71,6 +84,14 @@ class ClientCert {
virtual widevine::ClientIdentification::TokenType type() const = 0; virtual widevine::ClientIdentification::TokenType type() const = 0;
virtual const std::string& encrypted_unique_id() const = 0; virtual const std::string& encrypted_unique_id() const = 0;
virtual const std::string& unique_id_hash() const = 0; virtual const std::string& unique_id_hash() const = 0;
virtual Status SystemIdUnknownError() const {
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown");
}
virtual Status SystemIdRevokedError() const {
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
"device-certificate-revoked");
}
}; };
} // namespace widevine } // namespace widevine

View File

@@ -9,12 +9,16 @@
#include "common/client_cert.h" #include "common/client_cert.h"
#include <memory> #include <memory>
#include <tuple>
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "common/ec_key.h"
#include "common/ec_test_keys.h" #include "common/ec_test_keys.h"
#include "common/error_space.h" #include "common/error_space.h"
#include "common/hash_algorithm.h"
#include "common/hash_algorithm_util.h"
#include "common/keybox_client_cert.h" #include "common/keybox_client_cert.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/rsa_test_keys.h" #include "common/rsa_test_keys.h"
@@ -35,13 +39,16 @@ const DrmCertificate::Type kNoSigner = DrmCertificate::ROOT;
const DrmCertificate::Type kDeviceModelSigner = DrmCertificate::DEVICE_MODEL; const DrmCertificate::Type kDeviceModelSigner = DrmCertificate::DEVICE_MODEL;
const DrmCertificate::Type kProvisionerSigner = DrmCertificate::PROVISIONER; const DrmCertificate::Type kProvisionerSigner = DrmCertificate::PROVISIONER;
const HashAlgorithm kSha256 = HashAlgorithm::kSha256;
// TODO(user): Change these tests to use on-the-fly generated intermediate // TODO(user): Change these tests to use on-the-fly generated intermediate
// and device certificates based on RsaTestKeys. // and device certificates based on RsaTestKeys.
// TODO(user): Add testcase(s) CreateSignature, // TODO(user): Add testcase(s) CreateSignature,
// and GenerateSigningKey. // and GenerateSigningKey.
class ClientCertTest class ClientCertTest
: public ::testing::TestWithParam<DrmCertificate::Algorithm> { : public ::testing::TestWithParam<
std::tuple<DrmCertificate::Algorithm, DrmCertificate::Algorithm>> {
public: public:
~ClientCertTest() override = default; ~ClientCertTest() override = default;
void SetUp() override { void SetUp() override {
@@ -74,16 +81,30 @@ class ClientCertTest
class TestCertificateAndData { class TestCertificateAndData {
public: public:
const std::string certificate_; const std::string certificate_;
const std::string encryption_certificate_;
const std::string expected_serial_number_; const std::string expected_serial_number_;
uint32_t expected_system_id_; uint32_t expected_system_id_;
Status expected_status_; Status expected_status_;
SignedMessage::SessionKeyType expected_key_type_;
TestCertificateAndData(const std::string& certificate, TestCertificateAndData(const std::string& certificate,
const std::string& expected_serial_number, const std::string& expected_serial_number,
uint32_t expected_system_id, Status expected_status) uint32_t expected_system_id, Status expected_status)
: certificate_(certificate), : certificate_(certificate),
expected_serial_number_(expected_serial_number), expected_serial_number_(expected_serial_number),
expected_system_id_(expected_system_id), expected_system_id_(expected_system_id),
expected_status_(expected_status) {} expected_status_(expected_status),
expected_key_type_(SignedMessage::WRAPPED_AES_KEY) {}
TestCertificateAndData(const std::string& certificate,
const std::string& encryption_certificate,
const std::string& expected_serial_number,
uint32_t expected_system_id, Status expected_status,
SignedMessage::SessionKeyType expected_key_type)
: certificate_(certificate),
encryption_certificate_(encryption_certificate),
expected_serial_number_(expected_serial_number),
expected_system_id_(expected_system_id),
expected_status_(expected_status),
expected_key_type_(expected_key_type) {}
}; };
void TestBasicValidation(const TestTokenAndKeys& expectation, void TestBasicValidation(const TestTokenAndKeys& expectation,
@@ -94,31 +115,32 @@ class ClientCertTest
void GenerateSignature(const std::string& message, void GenerateSignature(const std::string& message,
const std::string& private_key, const std::string& private_key,
std::string* signature); HashAlgorithm hash_algorithm, std::string* signature);
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate, std::unique_ptr<SignedDrmCertificate> SignCertificate(
SignedDrmCertificate* signer, const DrmCertificate& certificate, const SignedDrmCertificate* signer,
const std::string& private_key); const std::string& private_key);
DrmCertificate* GenerateProvisionerCertificate( std::unique_ptr<DrmCertificate> GenerateProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, uint32_t system_id, const std::string& serial_number,
const std::string& provider_id); const std::string& provider_id);
SignedDrmCertificate* GenerateSignedProvisionerCertificate( std::unique_ptr<SignedDrmCertificate> GenerateSignedProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, uint32_t system_id, const std::string& serial_number,
const std::string& service_id); const std::string& service_id);
DrmCertificate* GenerateIntermediateCertificate( std::unique_ptr<DrmCertificate> GenerateIntermediateCertificate(
uint32_t system_id, const std::string& serial_number); uint32_t system_id, const std::string& serial_number);
SignedDrmCertificate* GenerateSignedIntermediateCertificate( std::unique_ptr<SignedDrmCertificate> GenerateSignedIntermediateCertificate(
SignedDrmCertificate* signer, uint32_t system_id, SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number, DrmCertificate::Type signer_cert_type); const std::string& serial_number, DrmCertificate::Type signer_cert_type);
DrmCertificate* GenerateDrmCertificate( std::unique_ptr<DrmCertificate> GenerateDrmCertificate(
uint32_t system_id, const std::string& serial_number, uint32_t system_id, const std::string& serial_number,
DrmCertificate::Algorithm = DrmCertificate::RSA); DrmCertificate::Algorithm = DrmCertificate::RSA);
SignedDrmCertificate* GenerateSignedDrmCertificate( std::unique_ptr<SignedDrmCertificate> GenerateSignedDrmCertificate(
SignedDrmCertificate* signer, uint32_t system_id, SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number, const std::string& serial_number,
DrmCertificate::Algorithm = DrmCertificate::RSA); DrmCertificate::Algorithm = DrmCertificate::RSA);
std::string GetPublicKeyByCertType(DrmCertificate::Type cert_type); std::string GetPublicKeyByCertType(DrmCertificate::Type cert_type);
std::string GetPrivateKeyByCertType(DrmCertificate::Type cert_type); std::string GetPrivateKeyByCertType(DrmCertificate::Type cert_type);
std::string GetECCPrivateKey(DrmCertificate::Algorithm algorithm);
std::string GetECCPublicKey(DrmCertificate::Algorithm algorithm); std::string GetECCPublicKey(DrmCertificate::Algorithm algorithm);
RsaTestKeys test_rsa_keys_; RsaTestKeys test_rsa_keys_;
@@ -136,8 +158,11 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
Status status; Status status;
std::unique_ptr<ClientCert> keybox_cert; std::unique_ptr<ClientCert> keybox_cert;
status = ClientCert::Create(root_cert_.get(), ClientIdentification::KEYBOX, ClientIdentification client_id;
expectation.token_, &keybox_cert); client_id.set_type(ClientIdentification::KEYBOX);
client_id.set_token(expectation.token_);
status = ClientCert::Create(root_cert_.get(), client_id, &keybox_cert);
if (expect_success) { if (expect_success) {
ASSERT_EQ(OkStatus(), status); ASSERT_EQ(OkStatus(), status);
ASSERT_TRUE(keybox_cert.get()); ASSERT_TRUE(keybox_cert.get());
@@ -163,12 +188,25 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
// Test validation of a valid request. // Test validation of a valid request.
Status status; Status status;
std::unique_ptr<ClientCert> drm_certificate_cert; std::unique_ptr<ClientCert> drm_certificate_cert;
status = ClientCert::Create(root_cert_.get(), ClientIdentification client_id;
ClientIdentification::DRM_DEVICE_CERTIFICATE, client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
expectation.certificate_, &drm_certificate_cert); client_id.set_token(expectation.certificate_);
if (!expectation.encryption_certificate_.empty()) {
client_id.mutable_device_credentials()->set_token(
expectation.encryption_certificate_);
client_id.mutable_device_credentials()->set_type(
ClientIdentification::DRM_DEVICE_CERTIFICATE);
}
status =
ClientCert::Create(root_cert_.get(), client_id, &drm_certificate_cert);
ASSERT_EQ(expectation.expected_status_, status); ASSERT_EQ(expectation.expected_status_, status);
if (expectation.expected_status_.ok()) { if (expectation.expected_status_.ok()) {
ASSERT_TRUE(drm_certificate_cert.get()); ASSERT_TRUE(drm_certificate_cert.get());
if (!expectation.encryption_certificate_.empty()) {
ASSERT_TRUE(drm_certificate_cert->using_dual_certificate());
}
ASSERT_EQ(expectation.expected_key_type_, drm_certificate_cert->key_type());
if (compare_data) { if (compare_data) {
ASSERT_EQ(expectation.expected_serial_number_, ASSERT_EQ(expectation.expected_serial_number_,
drm_certificate_cert->signer_serial_number()); drm_certificate_cert->signer_serial_number());
@@ -182,26 +220,29 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
void ClientCertTest::GenerateSignature(const std::string& message, void ClientCertTest::GenerateSignature(const std::string& message,
const std::string& private_key, const std::string& private_key,
HashAlgorithm hash_algorithm,
std::string* signature) { std::string* signature) {
std::unique_ptr<RsaPrivateKey> rsa_private_key( std::unique_ptr<RsaPrivateKey> rsa_private_key(
RsaPrivateKey::Create(private_key)); RsaPrivateKey::Create(private_key));
ASSERT_TRUE(rsa_private_key != nullptr); ASSERT_TRUE(rsa_private_key != nullptr);
rsa_private_key->GenerateSignature(message, signature); rsa_private_key->GenerateSignature(message, hash_algorithm, signature);
} }
// The caller relinquishes ownership of |signer|, which may also be nullptr. // The caller retains ownership of |signer|, which may also be nullptr.
SignedDrmCertificate* ClientCertTest::SignCertificate( std::unique_ptr<SignedDrmCertificate> ClientCertTest::SignCertificate(
const DrmCertificate& certificate, SignedDrmCertificate* signer, const DrmCertificate& certificate, const SignedDrmCertificate* signer,
const std::string& private_key) { const std::string& private_key) {
std::unique_ptr<SignedDrmCertificate> signed_certificate( std::unique_ptr<SignedDrmCertificate> signed_certificate(
new SignedDrmCertificate); new SignedDrmCertificate);
signed_certificate->set_drm_certificate(certificate.SerializeAsString()); signed_certificate->set_drm_certificate(certificate.SerializeAsString());
GenerateSignature(signed_certificate->drm_certificate(), private_key, GenerateSignature(
signed_certificate->mutable_signature()); signed_certificate->drm_certificate(), private_key,
HashAlgorithmProtoToEnum(signed_certificate->hash_algorithm()),
signed_certificate->mutable_signature());
if (signer != nullptr) { if (signer != nullptr) {
signed_certificate->set_allocated_signer(signer); *(signed_certificate->mutable_signer()) = *signer;
} }
return signed_certificate.release(); return signed_certificate;
} }
std::string ClientCertTest::GetPublicKeyByCertType( std::string ClientCertTest::GetPublicKeyByCertType(
@@ -224,6 +265,21 @@ std::string ClientCertTest::GetPrivateKeyByCertType(
return test_rsa_keys_.private_test_key_1_3072_bits(); return test_rsa_keys_.private_test_key_1_3072_bits();
} }
std::string ClientCertTest::GetECCPrivateKey(
DrmCertificate::Algorithm algorithm) {
ECTestKeys keys;
switch (algorithm) {
case DrmCertificate::ECC_SECP256R1:
return keys.private_test_key_1_secp256r1();
case DrmCertificate::ECC_SECP384R1:
return keys.private_test_key_1_secp384r1();
case DrmCertificate::ECC_SECP521R1:
return keys.private_test_key_1_secp521r1();
default:
return "";
}
}
std::string ClientCertTest::GetECCPublicKey( std::string ClientCertTest::GetECCPublicKey(
DrmCertificate::Algorithm algorithm) { DrmCertificate::Algorithm algorithm) {
ECTestKeys keys; ECTestKeys keys;
@@ -239,7 +295,7 @@ std::string ClientCertTest::GetECCPublicKey(
} }
} }
DrmCertificate* ClientCertTest::GenerateIntermediateCertificate( std::unique_ptr<DrmCertificate> ClientCertTest::GenerateIntermediateCertificate(
uint32_t system_id, const std::string& serial_number) { uint32_t system_id, const std::string& serial_number) {
std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate); std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate);
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL); intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
@@ -248,10 +304,11 @@ DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
GetPublicKeyByCertType(DrmCertificate::DEVICE_MODEL)); GetPublicKeyByCertType(DrmCertificate::DEVICE_MODEL));
intermediate_certificate->set_system_id(system_id); intermediate_certificate->set_system_id(system_id);
intermediate_certificate->set_creation_time_seconds(1234); intermediate_certificate->set_creation_time_seconds(1234);
return intermediate_certificate.release(); return intermediate_certificate;
} }
SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate( std::unique_ptr<SignedDrmCertificate>
ClientCertTest::GenerateSignedIntermediateCertificate(
SignedDrmCertificate* signer, uint32_t system_id, SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number, DrmCertificate::Type signer_cert_type) { const std::string& serial_number, DrmCertificate::Type signer_cert_type) {
std::unique_ptr<DrmCertificate> intermediate_certificate( std::unique_ptr<DrmCertificate> intermediate_certificate(
@@ -261,7 +318,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
GetPrivateKeyByCertType(signer_cert_type)); GetPrivateKeyByCertType(signer_cert_type));
} }
DrmCertificate* ClientCertTest::GenerateDrmCertificate( std::unique_ptr<DrmCertificate> ClientCertTest::GenerateDrmCertificate(
uint32_t system_id, const std::string& serial_number, uint32_t system_id, const std::string& serial_number,
DrmCertificate::Algorithm algorithm) { DrmCertificate::Algorithm algorithm) {
std::unique_ptr<DrmCertificate> drm_certificate(new DrmCertificate); std::unique_ptr<DrmCertificate> drm_certificate(new DrmCertificate);
@@ -274,10 +331,11 @@ DrmCertificate* ClientCertTest::GenerateDrmCertificate(
: GetECCPublicKey(algorithm)); : GetECCPublicKey(algorithm));
drm_certificate->set_creation_time_seconds(4321); drm_certificate->set_creation_time_seconds(4321);
drm_certificate->set_algorithm(algorithm); drm_certificate->set_algorithm(algorithm);
return drm_certificate.release(); return drm_certificate;
} }
SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate( std::unique_ptr<SignedDrmCertificate>
ClientCertTest::GenerateSignedDrmCertificate(
SignedDrmCertificate* signer, uint32_t system_id, SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number, DrmCertificate::Algorithm algorithm) { const std::string& serial_number, DrmCertificate::Algorithm algorithm) {
std::unique_ptr<DrmCertificate> drm_certificate( std::unique_ptr<DrmCertificate> drm_certificate(
@@ -285,10 +343,10 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate( std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(
SignCertificate(*drm_certificate, signer, SignCertificate(*drm_certificate, signer,
GetPrivateKeyByCertType(DrmCertificate::DEVICE_MODEL))); GetPrivateKeyByCertType(DrmCertificate::DEVICE_MODEL)));
return signed_drm_certificate.release(); return signed_drm_certificate;
} }
DrmCertificate* ClientCertTest::GenerateProvisionerCertificate( std::unique_ptr<DrmCertificate> ClientCertTest::GenerateProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, uint32_t system_id, const std::string& serial_number,
const std::string& provider_id) { const std::string& provider_id) {
std::unique_ptr<DrmCertificate> provisioner_certificate(new DrmCertificate); std::unique_ptr<DrmCertificate> provisioner_certificate(new DrmCertificate);
@@ -299,10 +357,11 @@ DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
provisioner_certificate->set_system_id(system_id); provisioner_certificate->set_system_id(system_id);
provisioner_certificate->set_provider_id(provider_id); provisioner_certificate->set_provider_id(provider_id);
provisioner_certificate->set_creation_time_seconds(1234); provisioner_certificate->set_creation_time_seconds(1234);
return provisioner_certificate.release(); return provisioner_certificate;
} }
SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate( std::unique_ptr<SignedDrmCertificate>
ClientCertTest::GenerateSignedProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, uint32_t system_id, const std::string& serial_number,
const std::string& service_id) { const std::string& service_id) {
std::unique_ptr<DrmCertificate> provisioner_certificate( std::unique_ptr<DrmCertificate> provisioner_certificate(
@@ -341,22 +400,48 @@ TEST_F(ClientCertTest, BasicValidation) {
TEST_P(ClientCertTest, BasicCertValidation) { TEST_P(ClientCertTest, BasicCertValidation) {
const uint32_t system_id = 1234; const uint32_t system_id = 1234;
const std::string serial_number("serial_number"); const std::string serial_number("serial_number");
std::unique_ptr<SignedDrmCertificate> signed_cert( std::unique_ptr<SignedDrmCertificate> intermediate_certificate =
GenerateSignedDrmCertificate( GenerateSignedIntermediateCertificate(nullptr, system_id, serial_number,
GenerateSignedIntermediateCertificate(nullptr, system_id, kNoSigner);
serial_number, kNoSigner), std::unique_ptr<SignedDrmCertificate> signed_cert =
system_id, serial_number + "-device", GetParam())); GenerateSignedDrmCertificate(intermediate_certificate.get(), system_id,
serial_number + "-device1",
std::get<0>(GetParam()));
SignedMessage::SessionKeyType expected_key_type =
std::get<0>(GetParam()) != DrmCertificate::RSA
? SignedMessage::EPHEMERAL_ECC_PUBLIC_KEY
: SignedMessage::WRAPPED_AES_KEY;
std::unique_ptr<SignedDrmCertificate> encryption_certificate;
if (std::get<1>(GetParam()) != DrmCertificate::UNKNOWN_ALGORITHM) {
encryption_certificate = GenerateSignedDrmCertificate(
intermediate_certificate.get(), system_id, serial_number + "-device2",
std::get<1>(GetParam()));
expected_key_type = std::get<1>(GetParam()) != DrmCertificate::RSA
? SignedMessage::EPHEMERAL_ECC_PUBLIC_KEY
: SignedMessage::WRAPPED_AES_KEY;
}
const TestCertificateAndData kValidCertificateAndExpectedData( const TestCertificateAndData kValidCertificateAndExpectedData(
signed_cert->SerializeAsString(), serial_number, system_id, OkStatus()); signed_cert->SerializeAsString(),
encryption_certificate == nullptr
? std::string()
: encryption_certificate->SerializeAsString(),
serial_number, system_id, OkStatus(), expected_key_type);
const bool compare_data = true; const bool compare_data = true;
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData, TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
compare_data); compare_data);
} }
INSTANTIATE_TEST_SUITE_P(BasicCertValidation, ClientCertTest,
testing::Values(DrmCertificate::RSA, INSTANTIATE_TEST_SUITE_P(
DrmCertificate::ECC_SECP256R1, BasicCertValidation, ClientCertTest,
DrmCertificate::ECC_SECP384R1, testing::Combine(testing::Values(DrmCertificate::RSA,
DrmCertificate::ECC_SECP521R1)); DrmCertificate::ECC_SECP256R1,
DrmCertificate::ECC_SECP384R1,
DrmCertificate::ECC_SECP521R1),
testing::Values(DrmCertificate::UNKNOWN_ALGORITHM,
DrmCertificate::RSA,
DrmCertificate::ECC_SECP256R1,
DrmCertificate::ECC_SECP384R1,
DrmCertificate::ECC_SECP521R1)));
TEST_F(ClientCertTest, InvalidKeybox) { TEST_F(ClientCertTest, InvalidKeybox) {
const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = { const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = {
@@ -395,71 +480,80 @@ TEST_F(ClientCertTest, InvalidCertificate) {
std::unique_ptr<SignedDrmCertificate> invalid_drm_cert( std::unique_ptr<SignedDrmCertificate> invalid_drm_cert(
new SignedDrmCertificate); new SignedDrmCertificate);
invalid_drm_cert->set_drm_certificate("bad-serialized-cert"); invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
GenerateSignature(invalid_drm_cert->drm_certificate(), GenerateSignature(
test_rsa_keys_.private_test_key_2_2048_bits(), invalid_drm_cert->drm_certificate(),
invalid_drm_cert->mutable_signature()); test_rsa_keys_.private_test_key_2_2048_bits(),
invalid_drm_cert->set_allocated_signer(GenerateSignedIntermediateCertificate( HashAlgorithmProtoToEnum(invalid_drm_cert->hash_algorithm()),
nullptr, system_id, signer_sn, kNoSigner)); invalid_drm_cert->mutable_signature());
invalid_drm_cert->set_allocated_signer(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn,
kNoSigner)
.release());
// Invalid device public key. // Invalid device public key.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert = GenerateDrmCertificate(system_id, device_sn);
dev_cert->set_public_key("bad-device-public-key"); dev_cert->set_public_key("bad-device-public-key");
std::unique_ptr<SignedDrmCertificate> bad_device_public_key( std::unique_ptr<SignedDrmCertificate> bad_device_public_key =
SignCertificate(*dev_cert, SignCertificate(*dev_cert,
GenerateSignedIntermediateCertificate( GenerateSignedIntermediateCertificate(
nullptr, system_id, signer_sn, kNoSigner), nullptr, system_id, signer_sn, kNoSigner)
test_rsa_keys_.private_test_key_2_2048_bits())); .get(),
test_rsa_keys_.private_test_key_2_2048_bits());
// Invalid serialized intermediate certificate. // Invalid serialized intermediate certificate.
signed_signer.reset(GenerateSignedIntermediateCertificate( signed_signer = GenerateSignedIntermediateCertificate(nullptr, system_id,
nullptr, system_id, signer_sn, kNoSigner)); signer_sn, kNoSigner);
signed_signer->set_drm_certificate("bad-serialized-cert"); signed_signer->set_drm_certificate("bad-serialized-cert");
GenerateSignature(signed_signer->drm_certificate(), GenerateSignature(signed_signer->drm_certificate(),
test_rsa_keys_.private_test_key_1_3072_bits(), test_rsa_keys_.private_test_key_1_3072_bits(),
HashAlgorithmProtoToEnum(signed_signer->hash_algorithm()),
signed_signer->mutable_signature()); signed_signer->mutable_signature());
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert = GenerateDrmCertificate(system_id, device_sn);
std::unique_ptr<SignedDrmCertificate> invalid_signer( std::unique_ptr<SignedDrmCertificate> invalid_signer(
SignCertificate(*dev_cert, signed_signer.release(), SignCertificate(*dev_cert, signed_signer.get(),
test_rsa_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid signer public key. // Invalid signer public key.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert = GenerateDrmCertificate(system_id, device_sn);
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); signer_cert = GenerateIntermediateCertificate(system_id, signer_sn);
signer_cert->set_public_key("bad-signer-public-key"); signer_cert->set_public_key("bad-signer-public-key");
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate( std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
*dev_cert, *dev_cert,
SignCertificate(*signer_cert, nullptr, SignCertificate(*signer_cert, nullptr,
test_rsa_keys_.private_test_key_1_3072_bits()), test_rsa_keys_.private_test_key_1_3072_bits())
.get(),
test_rsa_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid device certificate signature. // Invalid device certificate signature.
std::unique_ptr<SignedDrmCertificate> bad_device_signature( std::unique_ptr<SignedDrmCertificate> bad_device_signature(
GenerateSignedDrmCertificate( GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn, nullptr, system_id, signer_sn, kNoSigner)
kNoSigner), .get(),
system_id, device_sn)); system_id, device_sn));
bad_device_signature->set_signature("bad-signature"); bad_device_signature->set_signature("bad-signature");
// Missing model system ID. // Missing model system ID.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert = GenerateDrmCertificate(system_id, device_sn);
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); signer_cert = GenerateIntermediateCertificate(system_id, signer_sn);
signer_cert->clear_system_id(); signer_cert->clear_system_id();
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate( std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
*dev_cert, *dev_cert,
SignCertificate(*signer_cert, nullptr, SignCertificate(*signer_cert, nullptr,
test_rsa_keys_.private_test_key_1_3072_bits()), test_rsa_keys_.private_test_key_1_3072_bits())
.get(),
test_rsa_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
// Missing signer serial number. // Missing signer serial number.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert = GenerateDrmCertificate(system_id, device_sn);
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); signer_cert = GenerateIntermediateCertificate(system_id, signer_sn);
signer_cert->clear_serial_number(); signer_cert->clear_serial_number();
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate( std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
*dev_cert, *dev_cert,
SignCertificate(*signer_cert, nullptr, SignCertificate(*signer_cert, nullptr,
test_rsa_keys_.private_test_key_1_3072_bits()), test_rsa_keys_.private_test_key_1_3072_bits())
.get(),
test_rsa_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid serialized intermediate certificate. // Invalid serialized intermediate certificate.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert = GenerateDrmCertificate(system_id, device_sn);
signed_signer.reset(GenerateSignedIntermediateCertificate( signed_signer = GenerateSignedIntermediateCertificate(nullptr, system_id,
nullptr, system_id, signer_sn, kNoSigner)); signer_sn, kNoSigner);
signed_signer->set_signature("bad-signature"); signed_signer->set_signature("bad-signature");
std::unique_ptr<SignedDrmCertificate> bad_signer_signature( std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
SignCertificate(*dev_cert, signed_signer.release(), SignCertificate(*dev_cert, signed_signer.get(),
test_rsa_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
const TestCertificateAndData kInvalidCertificate[] = { const TestCertificateAndData kInvalidCertificate[] = {
@@ -504,8 +598,11 @@ TEST_F(ClientCertTest, MissingPreProvKey) {
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e")); "2517a12f4922953e"));
std::unique_ptr<ClientCert> client_cert_ptr; std::unique_ptr<ClientCert> client_cert_ptr;
Status status = ClientCert::Create( ClientIdentification client_id;
root_cert_.get(), ClientIdentification::KEYBOX, token, &client_cert_ptr); client_id.set_type(ClientIdentification::KEYBOX);
client_id.set_token(token);
Status status =
ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr);
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code()); ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
} }
@@ -516,26 +613,27 @@ TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
const std::string intermediate_serial_number("intermediate-serial-number"); const std::string intermediate_serial_number("intermediate-serial-number");
const std::string provisioner_serial_number("provisioner-serial-number"); const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert( std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert =
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number, GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id)); service_id);
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert( std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert =
GenerateSignedIntermediateCertificate( GenerateSignedIntermediateCertificate(
signed_provisioner_cert.release(), system_id, signed_provisioner_cert.get(), system_id, intermediate_serial_number,
intermediate_serial_number, kProvisionerSigner)); kProvisionerSigner);
std::unique_ptr<SignedDrmCertificate> signed_device_cert( std::unique_ptr<SignedDrmCertificate> signed_device_cert =
GenerateSignedDrmCertificate(signed_intermediate_cert.release(), GenerateSignedDrmCertificate(signed_intermediate_cert.get(), system_id,
system_id, device_serial_number)); device_serial_number);
std::string serialized_cert; std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert); signed_device_cert->SerializeToString(&serialized_cert);
std::unique_ptr<ClientCert> drm_cert; std::unique_ptr<ClientCert> drm_cert;
ClientIdentification client_id;
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
client_id.set_token(serialized_cert);
EXPECT_OK(ClientCert::Create(root_cert_.get(), EXPECT_OK(ClientCert::Create(root_cert_.get(), client_id, &drm_cert));
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &drm_cert));
ASSERT_TRUE(drm_cert); ASSERT_TRUE(drm_cert);
EXPECT_EQ(service_id, drm_cert->service_id()); EXPECT_EQ(service_id, drm_cert->service_id());
@@ -551,27 +649,28 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertEmptyServiceId) {
const std::string intermediate_serial_number("intermediate-serial-number"); const std::string intermediate_serial_number("intermediate-serial-number");
const std::string provisioner_serial_number("provisioner-serial-number"); const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert( std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert =
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number, GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id)); service_id);
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert( std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate( GenerateSignedIntermediateCertificate(
signed_provisioner_cert.release(), system_id, signed_provisioner_cert.get(), system_id, intermediate_serial_number,
intermediate_serial_number, kProvisionerSigner)); kProvisionerSigner));
std::unique_ptr<SignedDrmCertificate> signed_device_cert( std::unique_ptr<SignedDrmCertificate> signed_device_cert =
GenerateSignedDrmCertificate(signed_intermediate_cert.release(), GenerateSignedDrmCertificate(signed_intermediate_cert.get(), system_id,
system_id, device_serial_number)); device_serial_number);
std::string serialized_cert; std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert); signed_device_cert->SerializeToString(&serialized_cert);
std::unique_ptr<ClientCert> client_cert_ptr; std::unique_ptr<ClientCert> client_cert_ptr;
ClientIdentification client_id;
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
client_id.set_token(serialized_cert);
EXPECT_EQ("missing-provisioning-service-id", EXPECT_EQ("missing-provisioning-service-id",
ClientCert::Create(root_cert_.get(), ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message()); .error_message());
EXPECT_FALSE(client_cert_ptr); EXPECT_FALSE(client_cert_ptr);
} }
@@ -584,29 +683,30 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
const std::string intermediate_serial_number("intermediate-serial-number"); const std::string intermediate_serial_number("intermediate-serial-number");
const std::string intermediate_serial_number2("intermediate-serial-number-2"); const std::string intermediate_serial_number2("intermediate-serial-number-2");
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2( std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2 =
GenerateSignedIntermediateCertificate( GenerateSignedIntermediateCertificate(
nullptr, system_id2, intermediate_serial_number2, kNoSigner)); nullptr, system_id2, intermediate_serial_number2, kNoSigner);
// Instead of using a provisioner certificate to sign this intermediate // Instead of using a provisioner certificate to sign this intermediate
// certificate, use another intermediate certificate. This is an invalid // certificate, use another intermediate certificate. This is an invalid
// chain and should generate an error when trying to create a client // chain and should generate an error when trying to create a client
// certificate. // certificate.
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert( std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert =
GenerateSignedIntermediateCertificate( GenerateSignedIntermediateCertificate(
signed_intermediate_cert2.release(), system_id, signed_intermediate_cert2.get(), system_id,
intermediate_serial_number, kDeviceModelSigner)); intermediate_serial_number, kDeviceModelSigner);
std::unique_ptr<SignedDrmCertificate> signed_device_cert( std::unique_ptr<SignedDrmCertificate> signed_device_cert =
GenerateSignedDrmCertificate(signed_intermediate_cert.release(), GenerateSignedDrmCertificate(signed_intermediate_cert.get(), system_id,
system_id, device_serial_number)); device_serial_number);
std::string serialized_cert; std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert); signed_device_cert->SerializeToString(&serialized_cert);
std::unique_ptr<ClientCert> client_cert_ptr; std::unique_ptr<ClientCert> client_cert_ptr;
ClientIdentification client_id;
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
client_id.set_token(serialized_cert);
ASSERT_EQ("expected-provisioning-provider-certificate-type", ASSERT_EQ("expected-provisioning-provider-certificate-type",
ClientCert::Create(root_cert_.get(), ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message()); .error_message());
EXPECT_FALSE(client_cert_ptr); EXPECT_FALSE(client_cert_ptr);
} }
@@ -619,32 +719,33 @@ TEST_F(ClientCertTest, InvalidDeviceCertChainSize_TooLong) {
const std::string intermediate_serial_number2("intermediate-serial-number-2"); const std::string intermediate_serial_number2("intermediate-serial-number-2");
const std::string provisioner_serial_number("provisioner-serial-number"); const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert( std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert =
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number, GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id)); service_id);
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert1( std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert1 =
GenerateSignedIntermediateCertificate( GenerateSignedIntermediateCertificate(
signed_provisioner_cert.release(), system_id, signed_provisioner_cert.get(), system_id, intermediate_serial_number1,
intermediate_serial_number1, kProvisionerSigner)); kProvisionerSigner);
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2( std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2 =
GenerateSignedIntermediateCertificate( GenerateSignedIntermediateCertificate(
signed_intermediate_cert1.release(), system_id, signed_intermediate_cert1.get(), system_id,
intermediate_serial_number2, kDeviceModelSigner)); intermediate_serial_number2, kDeviceModelSigner);
std::unique_ptr<SignedDrmCertificate> signed_device_cert( std::unique_ptr<SignedDrmCertificate> signed_device_cert =
GenerateSignedDrmCertificate(signed_intermediate_cert2.release(), GenerateSignedDrmCertificate(signed_intermediate_cert2.get(), system_id,
system_id, device_serial_number)); device_serial_number);
std::string serialized_cert; std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert); signed_device_cert->SerializeToString(&serialized_cert);
std::unique_ptr<ClientCert> client_cert_ptr = nullptr; std::unique_ptr<ClientCert> client_cert_ptr = nullptr;
ClientIdentification client_id;
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
client_id.set_token(serialized_cert);
ASSERT_EQ("certificate-chain-size-exceeded", ASSERT_EQ("certificate-chain-size-exceeded",
ClientCert::Create(root_cert_.get(), ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message()); .error_message());
EXPECT_FALSE(client_cert_ptr); EXPECT_FALSE(client_cert_ptr);
} }
@@ -656,26 +757,27 @@ TEST_F(ClientCertTest, DeviceCertTypeNotLeaf) {
const std::string provisioner_serial_number("provisioner-serial-number"); const std::string provisioner_serial_number("provisioner-serial-number");
const std::string drm_serial_number("drm-serial-number"); const std::string drm_serial_number("drm-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert( std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert =
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number, GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id)); service_id);
// Use a DEVICE certificate as the intermediate certificate. // Use a DEVICE certificate as the intermediate certificate.
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert( std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert =
GenerateSignedDrmCertificate(signed_provisioner_cert.release(), system_id, GenerateSignedDrmCertificate(signed_provisioner_cert.get(), system_id,
intermediate_serial_number)); intermediate_serial_number);
std::unique_ptr<SignedDrmCertificate> signed_drm_cert( std::unique_ptr<SignedDrmCertificate> signed_drm_cert =
GenerateSignedDrmCertificate(signed_intermediate_cert.release(), GenerateSignedDrmCertificate(signed_intermediate_cert.get(), system_id,
system_id, drm_serial_number)); drm_serial_number);
std::string serialized_cert; std::string serialized_cert;
signed_drm_cert->SerializeToString(&serialized_cert); signed_drm_cert->SerializeToString(&serialized_cert);
std::unique_ptr<ClientCert> client_cert_ptr; std::unique_ptr<ClientCert> client_cert_ptr;
ClientIdentification client_id;
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
client_id.set_token(serialized_cert);
EXPECT_EQ("device-cert-must-be-leaf", EXPECT_EQ("device-cert-must-be-leaf",
ClientCert::Create(root_cert_.get(), ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message()); .error_message());
EXPECT_FALSE(client_cert_ptr); EXPECT_FALSE(client_cert_ptr);
} }
@@ -686,21 +788,22 @@ TEST_F(ClientCertTest, InvalidLeafCertificateType) {
const std::string intermediate_serial_number("intermediate-serial-number"); const std::string intermediate_serial_number("intermediate-serial-number");
const std::string provisioner_serial_number("provisioner-serial-number"); const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert( std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert =
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number, GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id)); service_id);
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert( std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert =
GenerateSignedIntermediateCertificate( GenerateSignedIntermediateCertificate(
signed_provisioner_cert.release(), system_id, signed_provisioner_cert.get(), system_id, intermediate_serial_number,
intermediate_serial_number, kProvisionerSigner)); kProvisionerSigner);
std::string serialized_cert; std::string serialized_cert;
signed_intermediate_cert->SerializeToString(&serialized_cert); signed_intermediate_cert->SerializeToString(&serialized_cert);
std::unique_ptr<ClientCert> client_cert_ptr; std::unique_ptr<ClientCert> client_cert_ptr;
ClientIdentification client_id;
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
client_id.set_token(serialized_cert);
// Leaf certificate must be a device certificate. // Leaf certificate must be a device certificate.
EXPECT_EQ("expected-device-certificate-type", EXPECT_EQ("expected-device-certificate-type",
ClientCert::Create(root_cert_.get(), ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message()); .error_message());
EXPECT_FALSE(client_cert_ptr); EXPECT_FALSE(client_cert_ptr);
} }
@@ -709,9 +812,10 @@ TEST_F(ClientCertTest, Protocol21WithDrmCert) {
const char message[] = "A weekend wasted is a weekend well spent."; const char message[] = "A weekend wasted is a weekend well spent.";
std::unique_ptr<ClientCert> client_cert; std::unique_ptr<ClientCert> client_cert;
ASSERT_OK(ClientCert::Create( ClientIdentification client_id;
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE, client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
test_drm_certs_.test_user_device_certificate(), &client_cert)); client_id.set_token(test_drm_certs_.test_user_device_certificate());
ASSERT_OK(ClientCert::Create(root_cert_.get(), client_id, &client_cert));
std::unique_ptr<RsaPrivateKey> private_key( std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits())); RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
@@ -719,14 +823,16 @@ TEST_F(ClientCertTest, Protocol21WithDrmCert) {
// Success // Success
std::string signature; std::string signature;
ASSERT_TRUE(private_key->GenerateSignature(message, &signature)); ASSERT_TRUE(private_key->GenerateSignature(message, kSha256, &signature));
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_1)); EXPECT_OK(
client_cert->VerifySignature(message, kSha256, signature, VERSION_2_1));
// Failure // Failure
ASSERT_EQ(256, signature.size()); ASSERT_EQ(256, signature.size());
++signature[127]; ++signature[127];
EXPECT_FALSE( EXPECT_FALSE(
client_cert->VerifySignature(message, signature, VERSION_2_1).ok()); client_cert->VerifySignature(message, kSha256, signature, VERSION_2_1)
.ok());
} }
TEST_F(ClientCertTest, Protocol22WithDrmCert) { TEST_F(ClientCertTest, Protocol22WithDrmCert) {
@@ -734,9 +840,10 @@ TEST_F(ClientCertTest, Protocol22WithDrmCert) {
const std::string message_hash(Sha512_Hash(message)); const std::string message_hash(Sha512_Hash(message));
std::unique_ptr<ClientCert> client_cert; std::unique_ptr<ClientCert> client_cert;
ASSERT_OK(ClientCert::Create( ClientIdentification client_id;
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE, client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
test_drm_certs_.test_user_device_certificate(), &client_cert)); client_id.set_token(test_drm_certs_.test_user_device_certificate());
ASSERT_OK(ClientCert::Create(root_cert_.get(), client_id, &client_cert));
std::unique_ptr<RsaPrivateKey> private_key( std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits())); RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
@@ -744,14 +851,17 @@ TEST_F(ClientCertTest, Protocol22WithDrmCert) {
// Success // Success
std::string signature; std::string signature;
ASSERT_TRUE(private_key->GenerateSignature(message_hash, &signature)); ASSERT_TRUE(
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_2)); private_key->GenerateSignature(message_hash, kSha256, &signature));
EXPECT_OK(
client_cert->VerifySignature(message, kSha256, signature, VERSION_2_2));
// Failure // Failure
ASSERT_EQ(256, signature.size()); ASSERT_EQ(256, signature.size());
++signature[127]; ++signature[127];
EXPECT_FALSE( EXPECT_FALSE(
client_cert->VerifySignature(message, signature, VERSION_2_2).ok()); client_cert->VerifySignature(message, kSha256, signature, VERSION_2_2)
.ok());
} }
} // namespace widevine } // namespace widevine

View File

@@ -22,6 +22,11 @@ namespace widevine {
const char kModDrmMake[] = "company_name"; const char kModDrmMake[] = "company_name";
const char kModDrmModel[] = "model_name"; const char kModDrmModel[] = "model_name";
const char kModDrmDeviceName[] = "device_name";
const char kModDrmProductName[] = "product_name";
const char kModDrmBuildInfo[] = "build_info";
const char kModDrmOemCryptoSecurityPatchLevel[] =
"oem_crypto_security_patch_level";
void AddClientInfo(ClientIdentification* client_id, absl::string_view name, void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
absl::string_view value) { absl::string_view value) {

View File

@@ -21,6 +21,10 @@ namespace widevine {
extern const char kModDrmMake[]; extern const char kModDrmMake[];
extern const char kModDrmModel[]; extern const char kModDrmModel[];
extern const char kModDrmDeviceName[];
extern const char kModDrmProductName[];
extern const char kModDrmBuildInfo[];
extern const char kModDrmOemCryptoSecurityPatchLevel[];
// Append the given name/value pair to client_id->client_info(). Does not // Append the given name/value pair to client_id->client_info(). Does not
// check for duplicates. // check for duplicates.

View File

@@ -7,6 +7,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/content_id_util.h" #include "common/content_id_util.h"
#include "glog/logging.h"
#include "common/error_space.h" #include "common/error_space.h"
#include "common/status.h" #include "common/status.h"
#include "license_server_sdk/internal/parse_content_id.h" #include "license_server_sdk/internal/parse_content_id.h"
@@ -18,33 +19,14 @@
namespace widevine { namespace widevine {
// TODO(user): Move the util methods from
// //license_server_sdk/internal/parse_content_id.h
// into this file.
Status GetContentIdFromExternalLicenseRequest( Status GetContentIdFromExternalLicenseRequest(
const ExternalLicenseRequest& external_license_request, const ExternalLicenseRequest& external_license_request,
std::string* content_id) { std::string* content_id) {
LicenseRequest::ContentIdentification content_identification = WidevinePsshData pssh_data;
external_license_request.content_id(); Status status = ParsePsshData(external_license_request, &pssh_data);
WidevinePsshData widevine_pssh_data; *content_id = pssh_data.content_id();
if (content_identification.has_widevine_pssh_data()) {
widevine_pssh_data.ParseFromString(
content_identification.widevine_pssh_data().pssh_data(0));
} else if (content_identification.has_webm_key_id()) {
widevine_pssh_data.ParseFromString(
content_identification.webm_key_id().header());
} else if (content_identification.has_init_data()) {
ContentInfo content_info;
if (ParseContentId(content_identification, &content_info).ok()) {
widevine_pssh_data =
content_info.content_info_entry(0).pssh().widevine_data();
}
}
*content_id = widevine_pssh_data.content_id();
return OkStatus(); return OkStatus();
} }
Status GetContentIdFromSignedExternalLicenseRequest( Status GetContentIdFromSignedExternalLicenseRequest(
const SignedMessage& signed_message, std::string* content_id) { const SignedMessage& signed_message, std::string* content_id) {
if (signed_message.type() != SignedMessage::EXTERNAL_LICENSE_REQUEST) { if (signed_message.type() != SignedMessage::EXTERNAL_LICENSE_REQUEST) {
@@ -61,4 +43,41 @@ Status GetContentIdFromSignedExternalLicenseRequest(
content_id); content_id);
} }
Status ParsePsshData(ExternalLicenseRequest external_license_request,
WidevinePsshData* widevine_pssh_data) {
if (!external_license_request.has_content_id()) {
std::string error = "ExternalLicenseRequest does not include ContentId";
LOG(ERROR) << error
<< ", request = " << external_license_request.ShortDebugString();
return Status(error_space, MISSING_CONTENT_ID, error);
}
ContentInfo content_info;
Status status =
ParseContentId(external_license_request.content_id(), &content_info);
if (!status.ok()) {
std::string error =
"Unable to retrieve ContentId from ExternalLicenseRequest";
LOG(ERROR) << error << ", status = " << status
<< ", request = " << external_license_request.ShortDebugString();
return Status(error_space, MISSING_CONTENT_ID, error);
}
switch (external_license_request.content_id().init_data().init_data_type()) {
case LicenseRequest::ContentIdentification::InitData::WEBM:
widevine_pssh_data->ParseFromString(
content_info.content_info_entry(0).key_ids(0));
break;
default:
*widevine_pssh_data =
content_info.content_info_entry(0).pssh().widevine_data();
break;
}
if (widevine_pssh_data->content_id().empty()) {
std::string error =
"Missing ContentId within Pssh data for ExternalLicenseRequest";
LOG(ERROR) << error
<< ", request = " << external_license_request.ShortDebugString();
return Status(error_space, MISSING_CONTENT_ID, error);
}
return OkStatus();
}
} // namespace widevine } // namespace widevine

View File

@@ -12,6 +12,7 @@
#include "common/status.h" #include "common/status.h"
#include "protos/public/external_license.pb.h" #include "protos/public/external_license.pb.h"
#include "protos/public/license_protocol.pb.h" #include "protos/public/license_protocol.pb.h"
#include "protos/public/widevine_pssh.pb.h"
namespace widevine { namespace widevine {
@@ -25,6 +26,12 @@ Status GetContentIdFromExternalLicenseRequest(
const ExternalLicenseRequest& external_license_request, const ExternalLicenseRequest& external_license_request,
std::string* content_id); std::string* content_id);
// Returns OK if successful and |widevine_pssh_data| will be populated by
// parsing |external_license_request|. Else, error and |widevine_pssh_data|
// will not be set within this method.
Status ParsePsshData(ExternalLicenseRequest external_license_request,
WidevinePsshData* widevine_pssh_data);
} // namespace widevine } // namespace widevine
#endif // COMMON_CONTENT_ID_UTIL_H_ #endif // COMMON_CONTENT_ID_UTIL_H_

View File

@@ -8,6 +8,10 @@
#include "common/content_id_util.h" #include "common/content_id_util.h"
#include <stddef.h>
#include <string>
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "protos/public/errors.pb.h" #include "protos/public/errors.pb.h"
@@ -23,9 +27,9 @@ const char kPlayReadyChallenge[] = "<TestPRChallenge></TestPRChallenge>";
namespace widevine { namespace widevine {
// Builds a SignedMessage that includes an ExternalLicenseRequest. // Builds a SignedMessage that includes an ExternalLicenseRequest.
SignedMessage BuildSignedExternalLicenseRequest( SignedMessage BuildSignedExternalLicenseRequest(const ExternalLicenseType type,
const ExternalLicenseRequest::RequestType type, const std::string& request, const std::string& request,
const std::string& content_id) { const std::string& content_id) {
ExternalLicenseRequest external_license_request; ExternalLicenseRequest external_license_request;
external_license_request.set_request_type(type); external_license_request.set_request_type(type);
external_license_request.set_request(request); external_license_request.set_request(request);
@@ -47,9 +51,8 @@ SignedMessage BuildSignedExternalLicenseRequest(
TEST(ContentIdUtil, GetContentId) { TEST(ContentIdUtil, GetContentId) {
std::string content_id; std::string content_id;
EXPECT_OK(GetContentIdFromSignedExternalLicenseRequest( EXPECT_OK(GetContentIdFromSignedExternalLicenseRequest(
BuildSignedExternalLicenseRequest( BuildSignedExternalLicenseRequest(PLAYREADY_LICENSE_NEW,
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST, kPlayReadyChallenge, kContentId),
kPlayReadyChallenge, kContentId),
&content_id)); &content_id));
EXPECT_EQ(kContentId, content_id); EXPECT_EQ(kContentId, content_id);
} }
@@ -57,8 +60,7 @@ TEST(ContentIdUtil, GetContentId) {
TEST(ContentIdUtil, GetContentIdFailureWithIncorrectType) { TEST(ContentIdUtil, GetContentIdFailureWithIncorrectType) {
std::string content_id; std::string content_id;
SignedMessage signed_message = BuildSignedExternalLicenseRequest( SignedMessage signed_message = BuildSignedExternalLicenseRequest(
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST, kPlayReadyChallenge, PLAYREADY_LICENSE_NEW, kPlayReadyChallenge, kContentId);
kContentId);
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST); signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
Status status = Status status =
GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id); GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id);
@@ -69,8 +71,7 @@ TEST(ContentIdUtil, GetContentIdFailureWithIncorrectType) {
TEST(ContentIdUtil, GetContentIdFailureWithInvalidExternalLicenseRequest) { TEST(ContentIdUtil, GetContentIdFailureWithInvalidExternalLicenseRequest) {
std::string content_id; std::string content_id;
SignedMessage signed_message = BuildSignedExternalLicenseRequest( SignedMessage signed_message = BuildSignedExternalLicenseRequest(
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST, kPlayReadyChallenge, PLAYREADY_LICENSE_NEW, kPlayReadyChallenge, kContentId);
kContentId);
signed_message.set_msg("Invalid payload"); signed_message.set_msg("Invalid payload");
Status status = Status status =
GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id); GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id);

View File

@@ -42,7 +42,7 @@ bool GetCoreProvisioningResponse(
} }
bool GetCoreRenewalOrReleaseLicenseResponse( bool GetCoreRenewalOrReleaseLicenseResponse(
const std::string& request_core_message, uint64_t renewal_duration_seconds, const std::string& request_core_message,
std::string* response_core_message) { std::string* response_core_message) {
oemcrypto_core_message::ODK_RenewalRequest odk_renewal_request; oemcrypto_core_message::ODK_RenewalRequest odk_renewal_request;
if (request_core_message.empty()) { if (request_core_message.empty()) {
@@ -52,11 +52,6 @@ bool GetCoreRenewalOrReleaseLicenseResponse(
&odk_renewal_request)) { &odk_renewal_request)) {
return false; return false;
} }
// TODO(b/141762043): This function is going to need to know what the
// renewal license is, and extract the renewal duration. This should be the
// sum of renewal_delay_seconds + 2 * renewal_recovery_duration_seconds.
uint64_t renewal_duration_seconds =
3600; // PTAL when addressing b/141762043.
return CreateCoreRenewalResponse( return CreateCoreRenewalResponse(
odk_renewal_request, renewal_duration_seconds, response_core_message); odk_renewal_request, renewal_duration_seconds, response_core_message);
} }

View File

@@ -24,7 +24,7 @@ bool GetCoreProvisioningResponse(
// Gets the |response_core_message| by parsing |request_core_message| for // Gets the |response_core_message| by parsing |request_core_message| for
// release and renewal response. The output is held in |response_core_message|. // release and renewal response. The output is held in |response_core_message|.
bool GetCoreRenewalOrReleaseLicenseResponse( bool GetCoreRenewalOrReleaseLicenseResponse(
const std::string& request_core_message, uint64_t renewal_duration_seconds, const std::string& request_core_message,
std::string* response_core_message); std::string* response_core_message);
// Gets the |response_core_message| by parsing |request_core_message| and // Gets the |response_core_message| by parsing |request_core_message| and

View File

@@ -11,6 +11,7 @@
#include "common/crypto_util.h" #include "common/crypto_util.h"
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "openssl/aes.h" #include "openssl/aes.h"
#include "openssl/cmac.h" #include "openssl/cmac.h"
@@ -37,6 +38,8 @@ const char kGroupKeyLabel[] = "GROUP_ENCRYPTION";
// a real group master key in keystore. // a real group master key in keystore.
// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy // TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy
// like VerifySignatureHmacSha1. // like VerifySignatureHmacSha1.
// TODO(user): Revert logging signature in VerifySignatureHmacSha256.
// function.
const char kPhonyGroupMasterKey[] = "fedcba9876543210"; const char kPhonyGroupMasterKey[] = "fedcba9876543210";
const int kAes128KeySizeBits = 128; const int kAes128KeySizeBits = 128;
const int kAes128KeySizeBytes = 16; const int kAes128KeySizeBytes = 16;

View File

@@ -0,0 +1,135 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
// Implementation of the DefaultDeviceSecurityProfileList class.
#include "common/default_device_security_profile_list.h"
#include <vector>
#include "glog/logging.h"
#include "google/protobuf/text_format.h"
#include "common/client_id_util.h"
#include "common/device_status_list.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/device_certificate_status.pb.h"
#include "protos/public/device_common.pb.h"
#include "protos/public/provisioned_device_info.pb.h"
#include "protos/public/security_profile.pb.h"
namespace widevine {
using ClientCapabilities = ClientIdentification::ClientCapabilities;
const char kWidevine[] = "widevine";
// Definition of Widevine default device security profiles.
// TODO(user): Add an OWNER file with per-file access to restrict changes to the
// profile definition.
const char kWidevineProfileMin[] =
(" name: \"minimum\""
" min_output_requirements {"
" hdcp_version: HDCP_NONE"
" analog_output_capabilities: ANALOG_OUTPUT_UNKNOWN"
" }"
" min_security_requirements {"
" oemcrypto_api_version: 0"
" security_level: LEVEL_3"
" resource_rating_tier: 0"
" vulnerability_level: VULNERABILITY_HIGH"
" }"
" owner: \"Widevine\"");
const char kWidevineProfileLow[] =
(" name: \"low\""
" min_output_requirements {"
" hdcp_version: HDCP_NONE"
" analog_output_capabilities: ANALOG_OUTPUT_UNKNOWN"
" }"
" min_security_requirements {"
" oemcrypto_api_version: 8"
" security_level: LEVEL_3"
" resource_rating_tier: 1"
" vulnerability_level: VULNERABILITY_MEDIUM"
" }"
" owner: \"Widevine\"");
const char kWidevineProfileMed[] =
(" name: \"medium\""
" min_output_requirements {"
" hdcp_version: HDCP_V1"
" analog_output_capabilities: ANALOG_OUTPUT_UNKNOWN"
" }"
" min_security_requirements {"
" oemcrypto_api_version: 12"
" security_level: LEVEL_3"
" resource_rating_tier: 1"
" vulnerability_level: VULNERABILITY_LOW"
" }"
" owner: \"Widevine\"");
const char kWidevineProfileHigh[] =
(" name: \"high\""
" min_output_requirements {"
" hdcp_version: HDCP_V1"
" analog_output_capabilities: ANALOG_OUTPUT_SUPPORTS_CGMS_A"
" }"
" min_security_requirements {"
" oemcrypto_api_version: 12"
" security_level: LEVEL_1"
" resource_rating_tier: 2"
" vulnerability_level: VULNERABILITY_NONE"
" }"
" owner: \"Widevine\"");
const char kWidevineProfileStrict[] =
(" name: \"strict\""
" min_output_requirements {"
" hdcp_version: HDCP_V2_2"
" analog_output_capabilities: ANALOG_OUTPUT_SUPPORTS_CGMS_A"
" }"
" min_security_requirements {"
" oemcrypto_api_version: 12"
" security_level: LEVEL_1"
" resource_rating_tier: 3"
" vulnerability_level: VULNERABILITY_NONE"
" }"
" owner: \"Widevine\"");
DefaultDeviceSecurityProfileList::DefaultDeviceSecurityProfileList()
: SecurityProfileList(kWidevine) {}
int DefaultDeviceSecurityProfileList::Init() { return AddDefaultProfiles(); }
int DefaultDeviceSecurityProfileList::AddDefaultProfiles() {
std::vector<std::string> default_profile_strings;
GetDefaultProfileStrings(&default_profile_strings);
for (auto& profile_string : default_profile_strings) {
SecurityProfile profile;
if (!google::protobuf::TextFormat::ParseFromString(profile_string, &profile)) {
LOG(ERROR) << "Unable to load default profile: " << profile.name();
ClearAllProfiles();
return 0;
}
InsertProfile(profile);
}
return NumProfiles();
}
int DefaultDeviceSecurityProfileList::GetDefaultProfileStrings(
std::vector<std::string>* default_profile_strings) const {
if (default_profile_strings == nullptr) {
return 0;
}
default_profile_strings->push_back(kWidevineProfileMin);
default_profile_strings->push_back(kWidevineProfileLow);
default_profile_strings->push_back(kWidevineProfileMed);
default_profile_strings->push_back(kWidevineProfileHigh);
default_profile_strings->push_back(kWidevineProfileStrict);
return default_profile_strings->size();
}
} // namespace widevine

View File

@@ -0,0 +1,39 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Container of Widevine default security profiless.
#ifndef COMMON_DEFAULT_DEVICE_SECURITY_PROFILE_LIST_H_
#define COMMON_DEFAULT_DEVICE_SECURITY_PROFILE_LIST_H_
#include "common/security_profile_list.h"
namespace widevine {
class DefaultDeviceSecurityProfileList : public SecurityProfileList {
public:
DefaultDeviceSecurityProfileList();
~DefaultDeviceSecurityProfileList() override {}
// Initialize the security profile list. The list is initially empty, this
// function will populate the list with default profiles. The size of the
// list is returned.
int Init() override;
private:
// Initialize the list with Widevine default profiles. The size of the
// profile list after the additions is returned.
virtual int AddDefaultProfiles();
virtual int GetDefaultProfileStrings(
std::vector<std::string>* default_profile_strings) const;
};
} // namespace widevine
#endif // COMMON_DEFAULT_DEVICE_SECURITY_PROFILE_LIST_H_

View File

@@ -0,0 +1,186 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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/default_device_security_profile_list.h"
#include "glog/logging.h"
#include "google/protobuf/util/message_differencer.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/memory/memory.h"
#include "common/client_id_util.h"
#include "protos/public/device_common.pb.h"
#include "protos/public/security_profile.pb.h"
namespace widevine {
namespace security_profile {
const uint32_t kResourceTierLow = 1;
const uint32_t kResourceTierMed = 2;
const uint32_t kResourceTierHigh = 3;
const char kMinProfileName[] = "minimum";
const char kLowProfileName[] = "low";
const char kMedProfileName[] = "medium";
const char kHighProfileName[] = "high";
const char kStrictProfileName[] = "strict";
class DefaultDeviceSecurityProfileListTest : public ::testing::Test {
public:
DefaultDeviceSecurityProfileListTest() {}
~DefaultDeviceSecurityProfileListTest() override {}
void SetUp() override {
SecurityProfile profile;
std::string profile_namespace = "widevine";
profile_list_ = absl::make_unique<DefaultDeviceSecurityProfileList>();
const int kNumWidevineProfiles = 5;
ASSERT_EQ(kNumWidevineProfiles, profile_list_->Init());
}
// Configure |client_id| and |device_info| with minimum settings.
void SetupMinDrmParams(ClientIdentification* client_id,
ProvisionedDeviceInfo* device_info) {
client_id->mutable_client_capabilities()->set_max_hdcp_version(
ClientCapabilities::HDCP_NONE);
client_id->mutable_client_capabilities()->set_analog_output_capabilities(
ClientCapabilities::ANALOG_OUTPUT_UNKNOWN);
client_id->mutable_client_capabilities()->set_oem_crypto_api_version(0);
client_id->mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierLow);
device_info->set_security_level(ProvisionedDeviceInfo::LEVEL_3);
}
// Configure |client_id| and |device_info| with maximum settings.
void SetupMaxDrmParams(ClientIdentification* client_id,
ProvisionedDeviceInfo* device_info) {
client_id->mutable_client_capabilities()->set_max_hdcp_version(
ClientCapabilities::HDCP_V2_3);
client_id->mutable_client_capabilities()->set_analog_output_capabilities(
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
client_id->mutable_client_capabilities()->set_oem_crypto_api_version(16);
client_id->mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierHigh);
device_info->set_security_level(ProvisionedDeviceInfo::LEVEL_1);
}
std::unique_ptr<SecurityProfileList> profile_list_;
};
TEST_F(DefaultDeviceSecurityProfileListTest, QualifiedProfiles) {
ClientIdentification client_id;
ProvisionedDeviceInfo device_info;
SetupMinDrmParams(&client_id, &device_info);
std::vector<std::string> qualified_profiles;
// Should only return the minimum profile.
ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id, device_info,
&qualified_profiles));
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kMinProfileName) != qualified_profiles.end());
// Increase the device capabilities to include the low profile.
client_id.mutable_client_capabilities()->set_oem_crypto_api_version(8);
ASSERT_EQ(2, profile_list_->GetQualifiedProfiles(client_id, device_info,
&qualified_profiles));
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kMinProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kLowProfileName) != qualified_profiles.end());
// Increase the device capabilities to include the med profile.
client_id.mutable_client_capabilities()->set_max_hdcp_version(
ClientCapabilities::HDCP_V1);
client_id.mutable_client_capabilities()->set_oem_crypto_api_version(12);
ASSERT_EQ(3, profile_list_->GetQualifiedProfiles(client_id, device_info,
&qualified_profiles));
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kMinProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kLowProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kMedProfileName) != qualified_profiles.end());
// Increase the device capabilities to include the high profile.
device_info.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
client_id.mutable_client_capabilities()->set_analog_output_capabilities(
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
client_id.mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierMed);
ASSERT_EQ(4, profile_list_->GetQualifiedProfiles(client_id, device_info,
&qualified_profiles));
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kMinProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kLowProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kMedProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kHighProfileName) != qualified_profiles.end());
// Increase the device capabilities to include the strict profile.
client_id.mutable_client_capabilities()->set_max_hdcp_version(
ClientCapabilities::HDCP_V2_2);
client_id.mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierHigh);
ASSERT_EQ(5, profile_list_->GetQualifiedProfiles(client_id, device_info,
&qualified_profiles));
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kMinProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kLowProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kMedProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kHighProfileName) != qualified_profiles.end());
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kStrictProfileName) != qualified_profiles.end());
}
TEST_F(DefaultDeviceSecurityProfileListTest,
DeviceQualifiedProfilesForLowEndDevice) {
ClientIdentification client_id;
ProvisionedDeviceInfo device_info;
SetupMinDrmParams(&client_id, &device_info);
// Only 1 profile should qualify for this device.
std::vector<std::string> qualified_profiles;
ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id, device_info,
&qualified_profiles));
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
kMinProfileName) != qualified_profiles.end());
}
TEST_F(DefaultDeviceSecurityProfileListTest,
QualifiedProfilesForHighEndDevice) {
ClientIdentification client_id;
ProvisionedDeviceInfo device_info;
SetupMaxDrmParams(&client_id, &device_info);
// All 5 default profiles should qualify for this device.
std::vector<std::string> qualified_profiles;
ASSERT_EQ(5, profile_list_->GetQualifiedProfiles(client_id, device_info,
&qualified_profiles));
}
// TODO(b/160019477): Add test once provisioned device info supports known
// vulnerability.
TEST_F(DefaultDeviceSecurityProfileListTest,
DISABLED_QualifiedProfilesByVunerabilityLevel) {
ClientIdentification client_id;
ProvisionedDeviceInfo device_info;
SetupMaxDrmParams(&client_id, &device_info);
std::vector<std::string> qualified_profiles;
ASSERT_EQ(0, profile_list_->GetQualifiedProfiles(client_id, device_info,
&qualified_profiles));
}
} // namespace security_profile
} // namespace widevine

View File

@@ -30,7 +30,7 @@ bool VerifyMakeModel(const ProvisionedDeviceInfo& device_info,
make_from_client, model_from_client)) { make_from_client, model_from_client)) {
return true; return true;
} }
for (DeviceModel product_info : device_info.model_info()) { for (const DeviceModel& product_info : device_info.model_info()) {
if (IsMatchedMakeModel(product_info.manufacturer(), if (IsMatchedMakeModel(product_info.manufacturer(),
product_info.model_name(), make_from_client, product_info.model_name(), make_from_client,
model_from_client)) { model_from_client)) {

View File

@@ -7,6 +7,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef COMMON_DEVICE_INFO_UTIL_H_ #ifndef COMMON_DEVICE_INFO_UTIL_H_
#define COMMON_DEVICE_INFO_UTIL_H_ #define COMMON_DEVICE_INFO_UTIL_H_
#include <string> #include <string>
#include "protos/public/provisioned_device_info.pb.h" #include "protos/public/provisioned_device_info.pb.h"

View File

@@ -27,6 +27,7 @@
#include "common/client_cert.h" #include "common/client_cert.h"
#include "common/drm_service_certificate.h" #include "common/drm_service_certificate.h"
#include "common/error_space.h" #include "common/error_space.h"
#include "common/hash_algorithm_util.h"
#include "common/keybox_client_cert.h" #include "common/keybox_client_cert.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/status.h" #include "common/status.h"
@@ -63,7 +64,8 @@ DeviceStatusList::~DeviceStatusList() {}
Status DeviceStatusList::UpdateStatusList( Status DeviceStatusList::UpdateStatusList(
const std::string& root_certificate_public_key, const std::string& root_certificate_public_key,
const std::string& serialized_device_certificate_status_list, const std::string& serialized_device_certificate_status_list,
const std::string& signature, uint32_t expiration_period_seconds) { HashAlgorithm hash_algorithm, const std::string& signature,
uint32_t expiration_period_seconds) {
if (serialized_device_certificate_status_list.empty()) { if (serialized_device_certificate_status_list.empty()) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list"); "missing-status-list");
@@ -79,7 +81,7 @@ Status DeviceStatusList::UpdateStatusList(
"invalid-root-public-key"); "invalid-root-public-key");
} }
if (!root_key->VerifySignature(serialized_device_certificate_status_list, if (!root_key->VerifySignature(serialized_device_certificate_status_list,
signature)) { hash_algorithm, signature)) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"invalid-status-list-signature"); "invalid-status-list-signature");
} }
@@ -117,29 +119,11 @@ Status DeviceStatusList::UpdateStatusList(
return OkStatus(); return OkStatus();
} }
Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert, Status DeviceStatusList::GetCertStatus(
const std::string& device_manufacturer, const ClientCert& client_cert, const std::string& make,
ProvisionedDeviceInfo* device_info) { const std::string& provider, bool allow_revoked_system_id,
CHECK(device_info); DeviceCertificateStatus* device_certificate_status) {
CHECK(device_certificate_status);
// Keybox checks.
if (client_cert.type() == ClientIdentification::KEYBOX) {
if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) {
return Status(error_space, UNSUPPORTED_SYSTEM_ID,
"keybox-unsupported-system-id");
}
// Get device information from certificate status list if available.
if (!GetDeviceInfo(client_cert, device_info)) {
device_info->Clear();
}
return OkStatus();
}
// DRM certificate checks.
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-unsupported-token-type");
}
absl::ReaderMutexLock lock(&status_map_lock_); absl::ReaderMutexLock lock(&status_map_lock_);
if (expiration_period_seconds_ && if (expiration_period_seconds_ &&
(GetCurrentTime() > (GetCurrentTime() >
@@ -149,59 +133,67 @@ Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
} }
DeviceCertificateStatus* device_cert_status = DeviceCertificateStatus* device_cert_status =
gtl::FindOrNull(device_status_map_, client_cert.system_id()); gtl::FindOrNull(device_status_map_, client_cert.system_id());
if (device_cert_status) {
*device_info = device_cert_status->device_info(); if (device_cert_status == nullptr) {
if (device_cert_status->status() == if (allow_unknown_devices_) {
DeviceCertificateStatus::STATUS_REVOKED) { return OkStatus();
if (IsRevokedSystemIdAllowed(client_cert.system_id())) {
LOG(WARNING) << "Allowing REVOKED device: "
<< device_info->ShortDebugString();
} else {
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
"device-certificate-revoked");
}
} }
if ((device_cert_status->status() == return client_cert.SystemIdUnknownError();
DeviceCertificateStatus::STATUS_TEST_ONLY) && }
!allow_test_only_devices_) { *device_certificate_status = *device_cert_status;
if (IsTestOnlyDeviceAllowed(client_cert.system_id(),
device_manufacturer)) { if (device_cert_status->status() == DeviceCertificateStatus::STATUS_REVOKED) {
LOG(WARNING) << "Allowing TEST_ONLY device with systemId = " if (IsRevokedSystemIdAllowed(client_cert.system_id()) ||
<< client_cert.system_id() allow_revoked_system_id) {
<< "make = " << device_manufacturer LOG(WARNING) << "Allowing REVOKED device: "
<< ", device info = " << device_info->ShortDebugString(); << device_cert_status->device_info().ShortDebugString();
} else { } else {
VLOG(2) << "Not allowing TEST ONLY device with systemId = " return client_cert.SystemIdRevokedError();
<< client_cert.system_id() << "make = " << device_manufacturer
<< ", device info = " << device_info->ShortDebugString();
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
"test-only-drm-certificate-not-allowed");
}
} }
if (!client_cert.signed_by_provisioner() &&
(client_cert.signer_serial_number() !=
device_cert_status->drm_serial_number())) {
// Widevine-provisioned device, and the intermediate certificate serial
// number does not match that in the status list. If the status list is
// newer than the certificate, indicate an invalid certificate, so that
// the device re-provisions. If, on the other hand, the certificate status
// list is older than the certificate, the certificate is for all purposes
// unknown.
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"intermediate-certificate-serial-number-mismatch");
}
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown");
}
} else {
if (!allow_unknown_devices_) {
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown");
}
device_info->Clear();
} }
// The remainder of this function is for DRM certificates.
if (client_cert.type() == ClientIdentification::KEYBOX) {
return OkStatus();
}
// DRM certificate checks.
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-unsupported-token-type");
}
if ((device_cert_status->status() ==
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
!allow_test_only_devices_) {
if (IsTestOnlyDeviceAllowedByMake(client_cert.system_id(), make) &&
IsTestOnlyDeviceAllowedByProvider(client_cert.system_id(), provider)) {
LOG(WARNING) << "Allowing TEST_ONLY device with systemId = "
<< client_cert.system_id() << ", make = " << make
<< ", provider = " << provider << ", device info = "
<< device_cert_status->device_info().ShortDebugString();
} else {
VLOG(2) << "Not allowing TEST ONLY device with systemId = "
<< client_cert.system_id() << ", provider = " << provider
<< ", device info = "
<< device_cert_status->device_info().ShortDebugString();
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
"test-only-drm-certificate-not-allowed");
}
}
if (!client_cert.signed_by_provisioner() &&
(client_cert.signer_serial_number() !=
device_cert_status->drm_serial_number())) {
// Widevine-provisioned device, and the intermediate certificate serial
// number does not match that in the status list. If the status list is
// newer than the certificate, indicate an invalid certificate, so that
// the device re-provisions. If, on the other hand, the certificate status
// list is older than the certificate, the certificate is for all purposes
// unknown.
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"intermediate-certificate-serial-number-mismatch");
}
return client_cert.SystemIdUnknownError();
}
return OkStatus(); return OkStatus();
} }
@@ -268,29 +260,55 @@ void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) {
std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end()); std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end());
} }
void DeviceStatusList::AllowTestOnlyDevices(const std::string& device_list) { void DeviceStatusList::AllowTestOnlyDevicesByMake(
const std::string& device_list_by_make) {
absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_); absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_);
if (device_list.empty()) { if (device_list_by_make.empty()) {
allowed_test_only_devices_.clear(); allowed_test_only_devices_by_make_.clear();
return; return;
} }
for (absl::string_view device : absl::StrSplit(device_list, ',')) { for (absl::string_view device : absl::StrSplit(device_list_by_make, ',')) {
const std::pair<absl::string_view, absl::string_view> device_split = const std::pair<absl::string_view, absl::string_view> device_split =
absl::StrSplit(device, ':'); absl::StrSplit(device, ':');
if (device_split.second.empty() || device_split.second == "*") { if (device_split.second.empty() || device_split.second == "*") {
allowed_test_only_devices_.emplace( allowed_test_only_devices_by_make_.emplace(
std::stoi(std::string(device_split.first)), "*"); std::stoi(std::string(device_split.first)), "*");
VLOG(2) << "Whitelisting TEST_ONLY device: systemId = " VLOG(2) << "Allowing TEST_ONLY device: systemId = "
<< std::stoi(std::string(device_split.first)) << std::stoi(std::string(device_split.first)) << ", make *";
<< ", manufacturer = *";
} else { } else {
allowed_test_only_devices_.emplace( allowed_test_only_devices_by_make_.emplace(
std::stoi(std::string(device_split.first)), std::stoi(std::string(device_split.first)),
absl::AsciiStrToUpper(device_split.second)); absl::AsciiStrToUpper(device_split.second));
VLOG(2) << "Whitelisting TEST_ONLY device: systemId = " VLOG(2) << "Allowing TEST_ONLY device: systemId = "
<< std::stoi(std::string(device_split.first)) << std::stoi(std::string(device_split.first))
<< ", manufacturer = " << ", make = " << absl::AsciiStrToUpper(device_split.second);
<< absl::AsciiStrToUpper(device_split.second); }
}
}
void DeviceStatusList::AllowTestOnlyDevicesByProvider(
const std::string& device_list_by_provider) {
absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_);
if (device_list_by_provider.empty()) {
allowed_test_only_devices_by_provider_.clear();
return;
}
for (absl::string_view device :
absl::StrSplit(device_list_by_provider, ',')) {
const std::pair<absl::string_view, absl::string_view> device_split =
absl::StrSplit(device, ':');
if (device_split.second.empty() || device_split.second == "*") {
allowed_test_only_devices_by_provider_.emplace(
std::stoi(std::string(device_split.first)), "*");
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
<< std::stoi(std::string(device_split.first)) << ", provider *";
} else {
allowed_test_only_devices_by_provider_.emplace(
std::stoi(std::string(device_split.first)),
absl::AsciiStrToUpper(device_split.second));
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
<< std::stoi(std::string(device_split.first))
<< ", provider = " << absl::AsciiStrToUpper(device_split.second);
} }
} }
} }
@@ -301,17 +319,34 @@ bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
return it; return it;
} }
bool DeviceStatusList::IsTestOnlyDeviceAllowed(uint32_t system_id, bool DeviceStatusList::IsTestOnlyDeviceAllowedByMake(
const std::string manufacturer) { uint32_t system_id, const std::string& manufacturer) {
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_); absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
std::pair<std::multimap<uint32_t, std::string>::iterator, std::pair<std::multimap<uint32_t, std::string>::iterator,
std::multimap<uint32_t, std::string>::iterator> std::multimap<uint32_t, std::string>::iterator>
allowed_manufacturers = allowed_test_only_devices_.equal_range(system_id); allowed_makes = allowed_test_only_devices_by_make_.equal_range(system_id);
for (auto it = allowed_manufacturers.first; for (auto it = allowed_makes.first; it != allowed_makes.second; ++it) {
it != allowed_manufacturers.second; ++it) { std::string allowed_makes = (*it).second;
std::string allowed_manufacturer = (*it).second; if (allowed_makes == "*" ||
if (allowed_manufacturer == "*" || allowed_makes == absl::AsciiStrToUpper(manufacturer)) {
allowed_manufacturer == absl::AsciiStrToUpper(manufacturer)) { return true;
}
}
return false;
}
bool DeviceStatusList::IsTestOnlyDeviceAllowedByProvider(
uint32_t system_id, const std::string& provider) {
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
std::pair<std::multimap<uint32_t, std::string>::iterator,
std::multimap<uint32_t, std::string>::iterator>
allowed_providers =
allowed_test_only_devices_by_provider_.equal_range(system_id);
for (auto it = allowed_providers.first; it != allowed_providers.second;
++it) {
std::string allowed_provider = (*it).second;
if (allowed_provider == "*" ||
allowed_provider == absl::AsciiStrToUpper(provider)) {
return true; return true;
} }
} }
@@ -321,7 +356,8 @@ bool DeviceStatusList::IsTestOnlyDeviceAllowed(uint32_t system_id,
Status DeviceStatusList::DetermineAndDeserializeServiceResponse( Status DeviceStatusList::DetermineAndDeserializeServiceResponse(
const std::string& service_response, const std::string& service_response,
DeviceCertificateStatusList* certificate_status_list, DeviceCertificateStatusList* certificate_status_list,
std::string* serialized_certificate_status_list, std::string* signature) { std::string* serialized_certificate_status_list,
HashAlgorithm* hash_algorithm, std::string* signature) {
if (certificate_status_list == nullptr) { if (certificate_status_list == nullptr) {
return Status(error_space, error::INVALID_ARGUMENT, return Status(error_space, error::INVALID_ARGUMENT,
"certificate_status_list is empty"); "certificate_status_list is empty");
@@ -337,13 +373,15 @@ Status DeviceStatusList::DetermineAndDeserializeServiceResponse(
// payload. If that doesn't match, then the method will try to parse the // payload. If that doesn't match, then the method will try to parse the
// serialized PublishedDeviceInfo proto. // serialized PublishedDeviceInfo proto.
Status status = ExtractPublishedDevicesInfo( Status status = ExtractPublishedDevicesInfo(
service_response, serialized_certificate_status_list, signature); service_response, serialized_certificate_status_list, hash_algorithm,
signature);
// If the payload was not correctly parsed as a PublishedDevices proto. // If the payload was not correctly parsed as a PublishedDevices proto.
// then attempt to parse it as a legacy payload. // then attempt to parse it as a legacy payload.
if (!status.ok()) { if (!status.ok()) {
status = ExtractLegacyDeviceList( status = ExtractLegacyDeviceList(service_response,
service_response, serialized_certificate_status_list, signature); serialized_certificate_status_list,
hash_algorithm, signature);
// The payload could not be parsed in either format, return the failure // The payload could not be parsed in either format, return the failure
// information. // information.
if (!status.ok()) { if (!status.ok()) {
@@ -361,7 +399,8 @@ Status DeviceStatusList::DetermineAndDeserializeServiceResponse(
Status DeviceStatusList::ExtractLegacyDeviceList( Status DeviceStatusList::ExtractLegacyDeviceList(
const std::string& raw_certificate_provisioning_service_response, const std::string& raw_certificate_provisioning_service_response,
std::string* serialized_certificate_status_list, std::string* signature) { std::string* serialized_certificate_status_list,
HashAlgorithm* hash_algorithm, std::string* signature) {
// First, attempt to extract the legacy JSON response. Example legacy format. // First, attempt to extract the legacy JSON response. Example legacy format.
// "signedList":"<b64 encoded data>" // "signedList":"<b64 encoded data>"
// where the b64 encoded data is a DeviceCertificateStatusListResponse. // where the b64 encoded data is a DeviceCertificateStatusListResponse.
@@ -424,12 +463,13 @@ Status DeviceStatusList::ExtractLegacyDeviceList(
// and extract the serialized status list and signature. // and extract the serialized status list and signature.
return ParseLegacySignedDeviceCertificateStatusList( return ParseLegacySignedDeviceCertificateStatusList(
serialized_signed_certificate_status_list, serialized_signed_certificate_status_list,
serialized_certificate_status_list, signature); serialized_certificate_status_list, hash_algorithm, signature);
} }
Status DeviceStatusList::ExtractPublishedDevicesInfo( Status DeviceStatusList::ExtractPublishedDevicesInfo(
const std::string& serialized_published_devices, const std::string& serialized_published_devices,
std::string* serialized_certificate_status_list, std::string* signature) { std::string* serialized_certificate_status_list,
HashAlgorithm* hash_algorithm, std::string* signature) {
// TODO(b/139067045): Change from using the SignedDeviceInfo proto // TODO(b/139067045): Change from using the SignedDeviceInfo proto
// to using the correct proto from the API. This duplicate, wire-compatible // to using the correct proto from the API. This duplicate, wire-compatible
// proto was a temporary way to workaround Proto2/Proto3 compatibility issues. // proto was a temporary way to workaround Proto2/Proto3 compatibility issues.
@@ -440,6 +480,7 @@ Status DeviceStatusList::ExtractPublishedDevicesInfo(
} }
*serialized_certificate_status_list = *serialized_certificate_status_list =
devices_info.device_certificate_status_list(); devices_info.device_certificate_status_list();
*hash_algorithm = HashAlgorithmProtoToEnum(devices_info.hash_algorithm());
*signature = devices_info.signature(); *signature = devices_info.signature();
return OkStatus(); return OkStatus();
} }
@@ -470,12 +511,12 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
DrmServiceCertificate::GetDefaultDrmServiceCertificate(); DrmServiceCertificate::GetDefaultDrmServiceCertificate();
if (sc == nullptr) { if (sc == nullptr) {
signed_device_certificate_status_list_request->clear(); signed_device_certificate_status_list_request->clear();
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE, return Status(error_space, widevine::SERVICE_CERTIFICATE_NOT_FOUND,
"Drm service certificate is not loaded."); "Drm service certificate is not loaded.");
} }
const RsaPrivateKey* private_key = sc->private_key(); const RsaPrivateKey* private_key = sc->private_key();
if (private_key == nullptr) { if (private_key == nullptr) {
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE, return Status(error_space, widevine::INVALID_SERVICE_PRIVATE_KEY,
"Private key in the service certificate is null."); "Private key in the service certificate is null.");
} }
std::string signature; std::string signature;
@@ -490,7 +531,7 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
Status DeviceStatusList::ParseLegacySignedDeviceCertificateStatusList( Status DeviceStatusList::ParseLegacySignedDeviceCertificateStatusList(
const std::string& serialized_signed_device_certificate_status_list, const std::string& serialized_signed_device_certificate_status_list,
std::string* serialized_device_certificate_status_list, std::string* serialized_device_certificate_status_list,
std::string* signature) { HashAlgorithm* hash_algorithm, std::string* signature) {
// Parse the serialized_signed_device_certificate_status_list to extract the // Parse the serialized_signed_device_certificate_status_list to extract the
// serialized_device_certificate_status_list // serialized_device_certificate_status_list
SignedDeviceCertificateStatusList signed_device_list; SignedDeviceCertificateStatusList signed_device_list;
@@ -509,6 +550,8 @@ Status DeviceStatusList::ParseLegacySignedDeviceCertificateStatusList(
} }
*serialized_device_certificate_status_list = *serialized_device_certificate_status_list =
signed_device_list.certificate_status_list(); signed_device_list.certificate_status_list();
*hash_algorithm =
HashAlgorithmProtoToEnum(signed_device_list.hash_algorithm());
*signature = signed_device_list.signature(); *signature = signed_device_list.signature();
return OkStatus(); return OkStatus();
} }
@@ -532,4 +575,24 @@ bool DeviceStatusList::IsDrmCertificateRevoked(
return false; return false;
} }
Status DeviceStatusList::GetDeviceCertificateStatusBySystemId(
uint32_t system_id, DeviceCertificateStatus* device_certificate_status) {
absl::ReaderMutexLock lock(&status_map_lock_);
if (expiration_period_seconds_ &&
(GetCurrentTime() >
(creation_time_seconds_ + expiration_period_seconds_))) {
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
"certificate-status-list-expired");
}
DeviceCertificateStatus* device_cert_status =
gtl::FindOrNull(device_status_map_, system_id);
if (device_cert_status == nullptr) {
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown");
} else {
*device_certificate_status = *device_cert_status;
}
return OkStatus();
}
} // namespace widevine } // namespace widevine

View File

@@ -16,6 +16,7 @@
#include <string> #include <string>
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "common/hash_algorithm.h"
#include "common/status.h" #include "common/status.h"
#include "protos/public/device_certificate_status.pb.h" #include "protos/public/device_certificate_status.pb.h"
#include "protos/public/provisioned_device_info.pb.h" #include "protos/public/provisioned_device_info.pb.h"
@@ -48,7 +49,8 @@ class DeviceStatusList {
Status UpdateStatusList( Status UpdateStatusList(
const std::string& root_certificate_public_key, const std::string& root_certificate_public_key,
const std::string& serialized_device_certificate_status_list, const std::string& serialized_device_certificate_status_list,
const std::string& signature, uint32_t expiration_period_seconds); HashAlgorithm hash_algorithm, const std::string& signature,
uint32_t expiration_period_seconds);
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; } void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
bool allow_unknown_devices() const { return allow_unknown_devices_; } bool allow_unknown_devices() const { return allow_unknown_devices_; }
void set_allow_test_only_devices(bool allow) { void set_allow_test_only_devices(bool allow) {
@@ -63,14 +65,15 @@ class DeviceStatusList {
// INVALID_DRM_CERTIFICATE // INVALID_DRM_CERTIFICATE
// DRM_DEVICE_CERTIFICATE_REVOKED // DRM_DEVICE_CERTIFICATE_REVOKED
// DRM_DEVICE_CERTIFICATE_UNKNOWN // DRM_DEVICE_CERTIFICATE_UNKNOWN
// If a TEST_ONLY device using "make" as identified by |device_manufacturer|,
// was not whitelisted, then will return
// DEVELOPMENT_CERTIFICATE_NOT_ALLOWED // DEVELOPMENT_CERTIFICATE_NOT_ALLOWED
// If status is OK, a copy of the provisioned device info is copied // |provider| is the service provider making the license request.
// into |device_info|. Caller owns |device_info| and it must not be null. // If status is OK, a copy of the device certificate status is copied
Status GetCertStatus(const ClientCert& client_cert, // into |device_certificate_status|. Caller owns |device_certificate_status|
const std::string& device_manufacturer, // and it must not be null.
widevine::ProvisionedDeviceInfo* device_info); Status GetCertStatus(
const ClientCert& client_cert, const std::string& make,
const std::string& provider, bool allow_revoked_system_id,
widevine::DeviceCertificateStatus* device_certificate_status);
// Returns true if the pre-provisioning key or certificate for the specified // Returns true if the pre-provisioning key or certificate for the specified
// system ID are active (not disallowed or revoked). // system ID are active (not disallowed or revoked).
bool IsSystemIdActive(uint32_t system_id); bool IsSystemIdActive(uint32_t system_id);
@@ -107,8 +110,16 @@ class DeviceStatusList {
// of the format <system_id>: // of the format <system_id>:
// Example usage: // Example usage:
// const std::string device_list = "4121:LG,7912:*" // const std::string device_list = "4121:LG,7912:*"
// AllowTestOnlyDevices(device_list); // AllowTestOnlyDevicesByMake(device_list_by_make);
virtual void AllowTestOnlyDevices(const std::string& device_list); virtual void AllowTestOnlyDevicesByMake(
const std::string& device_list_by_make);
// Same as above, except by providers instead of by manufacturers.
// Example usage:
// const std::string device_list = "4121:YouTube,4121:AndroidVideo"
// AllowTestOnlyDevicesByProvider(device_list);
virtual void AllowTestOnlyDevicesByProvider(
const std::string& device_list_by_provider);
// A comma separated list of DRM Certificate Serial Numbers that are revoked. // A comma separated list of DRM Certificate Serial Numbers that are revoked.
virtual void RevokedDrmCertificateSerialNumbers( virtual void RevokedDrmCertificateSerialNumbers(
@@ -119,6 +130,12 @@ class DeviceStatusList {
bool IsDrmCertificateRevoked( bool IsDrmCertificateRevoked(
const std::string& device_certificate_serial_number); const std::string& device_certificate_serial_number);
// Returns OK if |system_id| was found in the device certificate status list
// and |device_certificate_status| is populated. If |system_id| is not found,
// this call returns an error.
virtual Status GetDeviceCertificateStatusBySystemId(
uint32_t system_id, DeviceCertificateStatus* device_certificate_status);
// Parses the serialized certificate status list and the signature from the // Parses the serialized certificate status list and the signature from the
// service_response. The service_response is the JSON payload that comes // service_response. The service_response is the JSON payload that comes
// in the response to a certificate status list request. Both the legacy // in the response to a certificate status list request. Both the legacy
@@ -139,11 +156,13 @@ class DeviceStatusList {
// serialized proto against the |signature|. // serialized proto against the |signature|.
// The |signature| is the signature of the serialized_certificate_status_list // The |signature| is the signature of the serialized_certificate_status_list
// using RSASSA-PSS signed with the root certificate private key. // using RSASSA-PSS signed with the root certificate private key.
// The |hash_algorithm| is the hash algorithm used in signature.
// Returns WvPLStatus - Status::OK if success, else error. // Returns WvPLStatus - Status::OK if success, else error.
static Status DetermineAndDeserializeServiceResponse( static Status DetermineAndDeserializeServiceResponse(
const std::string& service_response, const std::string& service_response,
DeviceCertificateStatusList* certificate_status_list, DeviceCertificateStatusList* certificate_status_list,
std::string* serialized_certificate_status_list, std::string* signature); std::string* serialized_certificate_status_list,
HashAlgorithm* hash_algorithm, std::string* signature);
/** /**
* Constructs signed device certificate status list request string. * Constructs signed device certificate status list request string.
@@ -157,6 +176,20 @@ class DeviceStatusList {
const std::string& serialized_service_certificate, const std::string& serialized_service_certificate,
std::string* signed_device_certificate_status_list_request); std::string* signed_device_certificate_status_list_request);
// Returns true if the system ID is allowed to be revoked.
// Caller owns |system_id|. They must not be null.
bool IsRevokedSystemIdAllowed(uint32_t system_id);
// Returns true if the device, which is identified by system_id and
// device_manufacturer, is present in |allowed_test_only_devices_by_make_|.
bool IsTestOnlyDeviceAllowedByMake(uint32_t system_id,
const std::string& device_manufacturer);
// Returns true if the device, which is identified by system_id and
// provider, is present in |allowed_test_only_devices_by_provider_|.
bool IsTestOnlyDeviceAllowedByProvider(uint32_t system_id,
const std::string& provider);
private: private:
friend class DeviceStatusListTest; friend class DeviceStatusListTest;
@@ -167,12 +200,14 @@ class DeviceStatusList {
* *
* @param legacy_certificate_provisioning_service_response * @param legacy_certificate_provisioning_service_response
* @param serialized_certificate_status_list * @param serialized_certificate_status_list
* @param hash_algorithm
* @param signature * @param signature
* @return WvPLStatus - Status::OK if success, else error. * @return WvPLStatus - Status::OK if success, else error.
*/ */
static Status ExtractLegacyDeviceList( static Status ExtractLegacyDeviceList(
const std::string& raw_certificate_provisioning_service_response, const std::string& raw_certificate_provisioning_service_response,
std::string* serialized_certificate_status_list, std::string* signature); std::string* serialized_certificate_status_list,
HashAlgorithm* hash_algorithm, std::string* signature);
/** /**
* Parses the serialized published devices response. * Parses the serialized published devices response.
@@ -182,12 +217,14 @@ class DeviceStatusList {
* @param published_devices_response the serialized PublishedDevices proto * @param published_devices_response the serialized PublishedDevices proto
* containing the certificate status list. * containing the certificate status list.
* @param serialized_certificate_status_list * @param serialized_certificate_status_list
* @param hash_algorithm
* @param signature * @param signature
* @return WvPLStatus - Status::OK if success, else error. * @return WvPLStatus - Status::OK if success, else error.
*/ */
static Status ExtractPublishedDevicesInfo( static Status ExtractPublishedDevicesInfo(
const std::string& serialized_published_devices, const std::string& serialized_published_devices,
std::string* serialized_certificate_status_list, std::string* signature); std::string* serialized_certificate_status_list,
HashAlgorithm* hash_algorithm, std::string* signature);
/** /**
* Returns a |serialized_device_certificate_status_list| in its output * Returns a |serialized_device_certificate_status_list| in its output
@@ -196,25 +233,28 @@ class DeviceStatusList {
* *
* @param serialized_signed_device_certificate_status_list * @param serialized_signed_device_certificate_status_list
* @param serialized_device_certificate_status_list * @param serialized_device_certificate_status_list
* * @param hash_algorithm
* @return Status - Status::OK if success, else error. * @return Status - Status::OK if success, else error.
*/ */
static Status ParseLegacySignedDeviceCertificateStatusList( static Status ParseLegacySignedDeviceCertificateStatusList(
const std::string& serialized_signed_device_certificate_status_list, const std::string& serialized_signed_device_certificate_status_list,
std::string* serialized_device_certificate_status_list, std::string* serialized_device_certificate_status_list,
std::string* signature); HashAlgorithm* hash_algorithm, std::string* signature);
// Returns true if the system ID is allowed to be revoked. virtual size_t allowed_test_only_devices_by_make_size() {
// Caller owns |system_id|. They must not be null. absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
bool IsRevokedSystemIdAllowed(uint32_t system_id); return allowed_test_only_devices_by_make_.size();
// Returns true if the device, which is identified by system_id and }
// device_manufacturer, is present in |allowed_test_only_devices_|.
bool IsTestOnlyDeviceAllowed(uint32_t system_id,
const std::string device_manufacturer);
absl::Mutex status_map_lock_; virtual size_t allowed_test_only_devices_by_provider_size() {
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
return allowed_test_only_devices_by_provider_.size();
}
mutable absl::Mutex status_map_lock_;
// Key is the system id for the device. // Key is the system id for the device.
std::map<uint32_t, widevine::DeviceCertificateStatus> device_status_map_; std::map<uint32_t, widevine::DeviceCertificateStatus> device_status_map_
ABSL_GUARDED_BY(status_map_lock_);
uint32_t creation_time_seconds_ = 0; uint32_t creation_time_seconds_ = 0;
uint32_t expiration_period_seconds_ = 0; uint32_t expiration_period_seconds_ = 0;
bool allow_unknown_devices_ = false; bool allow_unknown_devices_ = false;
@@ -222,10 +262,15 @@ class DeviceStatusList {
// Contains the list of system_id values that are allowed to succeed even if // Contains the list of system_id values that are allowed to succeed even if
// revoked. // revoked.
std::vector<uint32_t> allowed_revoked_devices_; std::vector<uint32_t> allowed_revoked_devices_;
absl::Mutex allowed_test_only_devices_mutex_; mutable absl::Mutex allowed_test_only_devices_mutex_;
// Contains a map of 'system_id' to 'make'. If 'make' value is "*", any // Contains a map of 'system_id' to 'manufacturer'. If manufacturer value is
// 'make' for that 'system_id' is allowed. // "*", any manufacturer using that system_id is allowed.
std::multimap<uint32_t, std::string> allowed_test_only_devices_; std::multimap<uint32_t, std::string> allowed_test_only_devices_by_make_
ABSL_GUARDED_BY(allowed_test_only_devices_mutex_);
// Contains a map of 'system_id' to 'provider'. If provider value is "*", any
// provider using that system_id is allowed.
std::multimap<uint32_t, std::string> allowed_test_only_devices_by_provider_
ABSL_GUARDED_BY(allowed_test_only_devices_mutex_);
// Revoked DRM certificate serial numbers. // Revoked DRM certificate serial numbers.
std::set<std::string> revoked_drm_certificate_serial_numbers_; std::set<std::string> revoked_drm_certificate_serial_numbers_;
}; };

View File

@@ -21,6 +21,8 @@
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "common/client_cert.h" #include "common/client_cert.h"
#include "common/hash_algorithm.h"
#include "common/hash_algorithm_util.h"
#include "common/keybox_client_cert.h" #include "common/keybox_client_cert.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/rsa_test_keys.h" #include "common/rsa_test_keys.h"
@@ -34,20 +36,19 @@
namespace { namespace {
const char kTestSystemId_1[] = "4121"; const char kTestSystemId_1[] = "4121";
const char kTestManufacturer_LG[] = "LG";
const char kTestManufacturer_LGE[] = "LGE";
const char kTestSystemId_2[] = "8242"; const char kTestSystemId_2[] = "8242";
const char kTestManufacturer_Samsung[] = "Samsung";
const char kTestSystemId_3[] = "6556"; const char kTestSystemId_3[] = "6556";
const char kTestManufacturer[] = "TestManufacturer"; const char kTestManufacturer[] = "TestManufacturer";
const char kTestProvider[] = "TestProvider";
const char kRevokedManufacturer[] = "RevokedManufacturer";
const widevine::HashAlgorithm kHashAlgorithm =
widevine::HashAlgorithm::kSha256;
} // namespace } // namespace
namespace widevine { namespace widevine {
using ::testing::_;
using ::testing::Return; using ::testing::Return;
using ::testing::ReturnRef; using ::testing::ReturnRef;
using ::testing::ReturnRefOfCopy;
const uint32_t kValidCertSystemId = 100; const uint32_t kValidCertSystemId = 100;
const uint32_t kRevokedCertSystemId = 101; const uint32_t kRevokedCertSystemId = 101;
@@ -66,32 +67,40 @@ const char kRevokedUniqueIdentifiers[] = "revoked-unique-identifiers";
const char kTestOnlySerialNumber[] = "test_only-serial-number"; const char kTestOnlySerialNumber[] = "test_only-serial-number";
const char kMismatchSerialNumber[] = "mismatch-serial-number"; const char kMismatchSerialNumber[] = "mismatch-serial-number";
const char kDeviceModel[] = "device-model-x"; const char kDeviceModel[] = "device-model-x";
const char kRevokedDeviceModel[] = "device-model-revoked";
const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff"; const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
const uint32_t kStatusListCreationTime = 17798001; const uint32_t kStatusListCreationTime = 17798001;
const uint32_t kDefaultExpirePeriod = 0; const uint32_t kDefaultExpirePeriod = 0;
const bool kDenyRevokedDevice = false;
const bool kAllowRevokedDevice = true;
class MockClientCert : public ClientCert { class MockClientCert : public ClientCert {
public: public:
MockClientCert() {} MockClientCert() {}
~MockClientCert() override {} ~MockClientCert() override {}
MOCK_CONST_METHOD0(system_id, uint32_t()); MOCK_METHOD(uint32_t, system_id, (), (const, override));
MOCK_CONST_METHOD0(signer_serial_number, std::string &()); MOCK_METHOD(std::string &, signer_serial_number, (), (const, override));
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t()); MOCK_METHOD(uint32_t, signer_creation_time_seconds, (), (const, override));
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType()); MOCK_METHOD(ClientIdentification::TokenType, type, (), (const, override));
MOCK_CONST_METHOD0(encrypted_unique_id, const std::string &()); MOCK_METHOD(const std::string &, encrypted_unique_id, (), (const, override));
MOCK_CONST_METHOD0(unique_id_hash, const std::string &()); MOCK_METHOD(const std::string &, unique_id_hash, (), (const, override));
MOCK_CONST_METHOD0(signed_by_provisioner, bool()); MOCK_METHOD(bool, signed_by_provisioner, (), (const, override));
MOCK_CONST_METHOD3(VerifySignature, Status(const std::string &message, MOCK_METHOD(Status, VerifySignature,
const std::string &signature, (const std::string &message, HashAlgorithm hash_algorithm,
ProtocolVersion protocol_version)); const std::string &signature, ProtocolVersion protocol_version),
MOCK_METHOD2(GenerateSigningKey, void(const std::string &message, (const, override));
ProtocolVersion protocol_version)); MOCK_METHOD(void, GenerateSigningKey,
MOCK_CONST_METHOD0(serial_number, const std::string &()); (const std::string &message, ProtocolVersion protocol_version),
MOCK_CONST_METHOD0(key, const std::string &()); (override));
MOCK_CONST_METHOD0(key_type, SignedMessage::SessionKeyType()); MOCK_METHOD(const std::string &, serial_number, (), (const, override));
MOCK_CONST_METHOD0(service_id, const std::string &()); MOCK_METHOD(const std::string &, key, (), (const, override));
MOCK_CONST_METHOD0(encrypted_key, const std::string &()); MOCK_METHOD(SignedMessage::SessionKeyType, key_type, (), (const, override));
MOCK_CONST_METHOD0(signing_key, const std::string &()); MOCK_METHOD(bool, using_dual_certificate, (), (const override));
MOCK_METHOD(const std::string &, service_id, (), (const, override));
MOCK_METHOD(const std::string &, encrypted_key, (), (const, override));
MOCK_METHOD(const std::string &, signing_key, (), (const, override));
MOCK_METHOD(Status, SystemIdUnknownError, (), (const, override));
MOCK_METHOD(Status, SystemIdRevokedError, (), (const, override));
}; };
class DeviceStatusListTest : public ::testing::Test { class DeviceStatusListTest : public ::testing::Test {
@@ -116,6 +125,7 @@ class DeviceStatusListTest : public ::testing::Test {
cert_status = cert_status_list_.add_certificate_status(); cert_status = cert_status_list_.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId); cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId);
cert_status->set_drm_serial_number(kRevokedSerialNumber); cert_status->set_drm_serial_number(kRevokedSerialNumber);
cert_status->mutable_device_info()->set_model(kRevokedDeviceModel);
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED); cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
// Device cert with status REVOKED ALLOWED DEVICE. // Device cert with status REVOKED ALLOWED DEVICE.
@@ -141,6 +151,7 @@ class DeviceStatusListTest : public ::testing::Test {
ASSERT_TRUE(root_key); ASSERT_TRUE(root_key);
cert_status_list_.SerializeToString(&serialized_cert_status_list_); cert_status_list_.SerializeToString(&serialized_cert_status_list_);
ASSERT_TRUE(root_key->GenerateSignature(serialized_cert_status_list_, ASSERT_TRUE(root_key->GenerateSignature(serialized_cert_status_list_,
kHashAlgorithm,
&cert_status_list_signature_)); &cert_status_list_signature_));
// Update the device_status_list_ with the serialized status list // Update the device_status_list_ with the serialized status list
@@ -148,11 +159,12 @@ class DeviceStatusListTest : public ::testing::Test {
ASSERT_EQ(OkStatus(), ASSERT_EQ(OkStatus(),
device_status_list_.UpdateStatusList( device_status_list_.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(), test_keys_.public_test_key_1_3072_bits(),
serialized_cert_status_list_, cert_status_list_signature_, serialized_cert_status_list_, kHashAlgorithm,
kDefaultExpirePeriod)); cert_status_list_signature_, kDefaultExpirePeriod));
} }
void GenerateTrivialValidStatusList(std::string *serialized_cert_status_list, void GenerateTrivialValidStatusList(std::string *serialized_cert_status_list,
HashAlgorithm hash_algorithm,
std::string *signature) { std::string *signature) {
DeviceCertificateStatusList cert_status_list; DeviceCertificateStatusList cert_status_list;
DeviceCertificateStatus *cert_status; DeviceCertificateStatus *cert_status;
@@ -171,17 +183,27 @@ class DeviceStatusListTest : public ::testing::Test {
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
ASSERT_TRUE(root_key); ASSERT_TRUE(root_key);
cert_status_list.SerializeToString(serialized_cert_status_list); cert_status_list.SerializeToString(serialized_cert_status_list);
ASSERT_TRUE( ASSERT_TRUE(root_key->GenerateSignature(*serialized_cert_status_list,
root_key->GenerateSignature(*serialized_cert_status_list, signature)); hash_algorithm, signature));
} }
int VerifyAllowedTestOnlyDevicesAdded() { int AllowedTestOnlyDevicesByMakeSize() {
return device_status_list_.allowed_test_only_devices_.size(); return device_status_list_.allowed_test_only_devices_by_make_size();
} }
bool VerifyIsTestOnlyDeviceAllowed(uint32_t system_id, int AllowedTestOnlyDevicesByProviderSize() {
std::string manufacturer) { return device_status_list_.allowed_test_only_devices_by_provider_size();
return device_status_list_.IsTestOnlyDeviceAllowed(system_id, manufacturer); }
bool IsTestOnlyDeviceAllowedByMake(uint32_t system_id,
const std::string &make) {
return device_status_list_.IsTestOnlyDeviceAllowedByMake(system_id, make);
}
bool IsTestOnlyDeviceAllowedByProvider(uint32_t system_id,
const std::string &provider) {
return device_status_list_.IsTestOnlyDeviceAllowedByProvider(system_id,
provider);
} }
int VerifyRevokedDeviceCertificatesCount() { int VerifyRevokedDeviceCertificatesCount() {
@@ -203,7 +225,7 @@ class DeviceStatusListTest : public ::testing::Test {
TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) { TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
// Test case where the Certificate status is set to Valid. // Test case where the Certificate status is set to Valid.
ProvisionedDeviceInfo device_info; DeviceCertificateStatus device_certificate_status;
MockClientCert valid_client_cert; MockClientCert valid_client_cert;
std::string valid_drm_serial_number(kValidSerialNumber); std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_cert, type()) EXPECT_CALL(valid_client_cert, type())
@@ -212,9 +234,10 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
.WillRepeatedly(Return(kValidCertSystemId)); .WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(valid_client_cert, signer_serial_number()) EXPECT_CALL(valid_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(valid_drm_serial_number)); .WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_EQ(OkStatus(), EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(
device_status_list_.GetCertStatus(valid_client_cert, valid_client_cert, kTestManufacturer, kTestProvider,
kTestManufacturer, &device_info)); kDenyRevokedDevice, &device_certificate_status));
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
EXPECT_TRUE(device_info.has_model()); EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model()); EXPECT_EQ(kDeviceModel, device_info.model());
@@ -227,20 +250,25 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
.WillRepeatedly(Return(kRevokedCertSystemId)); .WillRepeatedly(Return(kRevokedCertSystemId));
EXPECT_CALL(revoked_client_cert, signer_serial_number()) EXPECT_CALL(revoked_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(revoked_drm_serial_number)); .WillRepeatedly(ReturnRef(revoked_drm_serial_number));
EXPECT_CALL(revoked_client_cert, SystemIdRevokedError())
.WillRepeatedly(
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED, "")));
EXPECT_EQ( EXPECT_EQ(
DRM_DEVICE_CERTIFICATE_REVOKED, DRM_DEVICE_CERTIFICATE_REVOKED,
device_status_list_ device_status_list_
.GetCertStatus(revoked_client_cert, kTestManufacturer, &device_info) .GetCertStatus(revoked_client_cert, kTestManufacturer, kTestProvider,
kDenyRevokedDevice, &device_certificate_status)
.error_code()); .error_code());
// Test case where the revoked cert is allowed. // Test case where the revoked cert is allowed.
device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId)); device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId));
EXPECT_OK(device_status_list_.GetCertStatus(revoked_client_cert, EXPECT_OK(device_status_list_.GetCertStatus(
kTestManufacturer, &device_info)); revoked_client_cert, kTestManufacturer, kTestProvider, kDenyRevokedDevice,
&device_certificate_status));
} }
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) { TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
ProvisionedDeviceInfo device_info; DeviceCertificateStatus device_certificate_status;
MockClientCert test_only_client_cert; MockClientCert test_only_client_cert;
std::string test_only_drm_serial_number(kTestOnlySerialNumber); std::string test_only_drm_serial_number(kTestOnlySerialNumber);
EXPECT_CALL(test_only_client_cert, type()) EXPECT_CALL(test_only_client_cert, type())
@@ -249,11 +277,12 @@ TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
.WillRepeatedly(Return(kTestOnlyCertSystemId)); .WillRepeatedly(Return(kTestOnlyCertSystemId));
EXPECT_CALL(test_only_client_cert, signer_serial_number()) EXPECT_CALL(test_only_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(test_only_drm_serial_number)); .WillRepeatedly(ReturnRef(test_only_drm_serial_number));
EXPECT_EQ( EXPECT_EQ(DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, device_status_list_
device_status_list_ .GetCertStatus(test_only_client_cert, kTestManufacturer,
.GetCertStatus(test_only_client_cert, kTestManufacturer, &device_info) kTestProvider, kDenyRevokedDevice,
.error_code()); &device_certificate_status)
.error_code());
} }
TEST_F(DeviceStatusListTest, GetRevokedIfentifiers) { TEST_F(DeviceStatusListTest, GetRevokedIfentifiers) {
@@ -269,7 +298,7 @@ TEST_F(DeviceStatusListTest, GetRevokedIfentifiers) {
} }
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) { TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
ProvisionedDeviceInfo device_info; DeviceCertificateStatus device_certificate_status;
MockClientCert test_only_client_cert; MockClientCert test_only_client_cert;
std::string test_only_drm_serial_number(kTestOnlySerialNumber); std::string test_only_drm_serial_number(kTestOnlySerialNumber);
device_status_list_.set_allow_test_only_devices(true); device_status_list_.set_allow_test_only_devices(true);
@@ -280,41 +309,98 @@ TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
EXPECT_CALL(test_only_client_cert, signer_serial_number()) EXPECT_CALL(test_only_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(test_only_drm_serial_number)); .WillRepeatedly(ReturnRef(test_only_drm_serial_number));
EXPECT_EQ(OkStatus(), EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(test_only_client_cert, device_status_list_.GetCertStatus(
kTestManufacturer, &device_info)); test_only_client_cert, kTestManufacturer, kTestProvider,
kDenyRevokedDevice, &device_certificate_status));
} }
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) { TEST_F(DeviceStatusListTest, RevokedSystemIdAllowed) {
DeviceCertificateStatus device_certificate_status;
MockClientCert revoked_client_cert;
std::string revoked_drm_serial_number(kRevokedSerialNumber);
EXPECT_CALL(revoked_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(revoked_client_cert, system_id())
.WillRepeatedly(Return(kRevokedCertSystemId));
EXPECT_CALL(revoked_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(revoked_drm_serial_number));
EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(
revoked_client_cert, kRevokedManufacturer, kTestProvider,
kAllowRevokedDevice, &device_certificate_status));
}
// Test case where the Certificate status is set to Valid.
TEST_F(DeviceStatusListTest, ValidKeybox) {
std::multimap<uint32_t, std::string> preprov_keys; std::multimap<uint32_t, std::string> preprov_keys;
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey)); preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys); KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
DeviceCertificateStatus device_certificate_status;
// Test case where the Certificate status is set to Valid.
ProvisionedDeviceInfo device_info;
MockClientCert valid_client_keybox; MockClientCert valid_client_keybox;
std::string valid_drm_serial_number(kValidSerialNumber); std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_keybox, type()) EXPECT_CALL(valid_client_keybox, type())
.WillRepeatedly(Return(ClientIdentification::KEYBOX)); .WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(valid_client_keybox, system_id()) EXPECT_CALL(valid_client_keybox, system_id())
.WillRepeatedly(Return(kValidCertSystemId)); .WillRepeatedly(Return(kValidCertSystemId));
EXPECT_EQ(OkStatus(), EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(valid_client_keybox, device_status_list_.GetCertStatus(
kTestManufacturer, &device_info)); valid_client_keybox, kTestManufacturer, kTestProvider,
EXPECT_TRUE(device_info.has_model()); kDenyRevokedDevice, &device_certificate_status));
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
ASSERT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model()); EXPECT_EQ(kDeviceModel, device_info.model());
}
// Test case where the keybox was not loaded into the pre-prov list.
TEST_F(DeviceStatusListTest, UnknownKeybox) {
std::multimap<uint32_t, std::string> preprov_keys;
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
DeviceCertificateStatus device_certificate_status;
MockClientCert unknown_client_keybox; MockClientCert unknown_client_keybox;
EXPECT_CALL(unknown_client_keybox, type()) EXPECT_CALL(unknown_client_keybox, type())
.WillRepeatedly(Return(ClientIdentification::KEYBOX)); .WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(unknown_client_keybox, system_id()) EXPECT_CALL(unknown_client_keybox, system_id())
.WillRepeatedly(Return(kUnknownSystemId)); .WillRepeatedly(Return(kUnknownSystemId));
EXPECT_EQ( EXPECT_CALL(unknown_client_keybox, SystemIdUnknownError())
UNSUPPORTED_SYSTEM_ID, .WillRepeatedly(Return(Status(error_space, UNSUPPORTED_SYSTEM_ID, "")));
device_status_list_ EXPECT_EQ(UNSUPPORTED_SYSTEM_ID,
.GetCertStatus(unknown_client_keybox, kTestManufacturer, &device_info) device_status_list_
.error_code()); .GetCertStatus(unknown_client_keybox, kTestManufacturer,
EXPECT_TRUE(device_info.has_model()); kTestProvider, kDenyRevokedDevice,
EXPECT_EQ(kDeviceModel, device_info.model()); &device_certificate_status)
.error_code());
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
ASSERT_FALSE(device_info.has_model());
}
// Test case where the keybox was loaded into the pre-prov list but it's
// certificate status is REVOKED.
TEST_F(DeviceStatusListTest, RevokedKeybox) {
std::multimap<uint32_t, std::string> preprov_keys;
preprov_keys.insert(std::make_pair(kRevokedCertSystemId, kTestPreprovKey));
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
DeviceCertificateStatus device_certificate_status;
MockClientCert revoked_client_keybox;
EXPECT_CALL(revoked_client_keybox, type())
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(revoked_client_keybox, system_id())
.WillRepeatedly(Return(kRevokedCertSystemId));
EXPECT_CALL(revoked_client_keybox, SystemIdRevokedError())
.WillRepeatedly(
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED, "")));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED,
device_status_list_
.GetCertStatus(revoked_client_keybox, kTestManufacturer,
kTestProvider, kDenyRevokedDevice,
&device_certificate_status)
.error_code());
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
ASSERT_TRUE(device_info.has_model());
EXPECT_EQ(kRevokedDeviceModel, device_info.model());
} }
TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) { TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
@@ -323,7 +409,7 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
// Test case where the signer certificate is older than the current status // Test case where the signer certificate is older than the current status
// list. // list.
MockClientCert older_client_cert; MockClientCert older_client_cert;
ProvisionedDeviceInfo device_info; DeviceCertificateStatus device_certificate_status;
std::string mismatch_drm_serial_number(kMismatchSerialNumber); std::string mismatch_drm_serial_number(kMismatchSerialNumber);
EXPECT_CALL(older_client_cert, type()) EXPECT_CALL(older_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
@@ -336,20 +422,23 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
EXPECT_EQ( EXPECT_EQ(
INVALID_DRM_CERTIFICATE, INVALID_DRM_CERTIFICATE,
device_status_list_ device_status_list_
.GetCertStatus(older_client_cert, kTestManufacturer, &device_info) .GetCertStatus(older_client_cert, kTestManufacturer, kTestProvider,
kDenyRevokedDevice, &device_certificate_status)
.error_code()); .error_code());
// We allow this case only for certs signed by a provisioner cert. // We allow this case only for certs signed by a provisioner cert.
EXPECT_CALL(older_client_cert, signed_by_provisioner()) EXPECT_CALL(older_client_cert, signed_by_provisioner())
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_EQ(OkStatus(), EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(
device_status_list_.GetCertStatus(older_client_cert, older_client_cert, kTestManufacturer, kTestProvider,
kTestManufacturer, &device_info)); kDenyRevokedDevice, &device_certificate_status));
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
EXPECT_TRUE(device_info.has_system_id()); EXPECT_TRUE(device_info.has_system_id());
EXPECT_EQ(kValidCertSystemId, device_info.system_id()); EXPECT_EQ(kValidCertSystemId, device_info.system_id());
// Test case where the signer certificate is newer than the current status // Test case where the signer certificate is newer than the current status
// list, and unknown devices are allowed. // list, and unknown devices are allowed.
device_certificate_status.Clear();
MockClientCert newer_client_cert1; MockClientCert newer_client_cert1;
EXPECT_CALL(newer_client_cert1, type()) EXPECT_CALL(newer_client_cert1, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
@@ -359,14 +448,19 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number)); .WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds()) EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime)); .WillRepeatedly(Return(kStatusListCreationTime));
EXPECT_CALL(newer_client_cert1, SystemIdUnknownError())
.WillRepeatedly(
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, "")));
EXPECT_EQ( EXPECT_EQ(
DRM_DEVICE_CERTIFICATE_UNKNOWN, DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_ device_status_list_
.GetCertStatus(newer_client_cert1, kTestManufacturer, &device_info) .GetCertStatus(newer_client_cert1, kTestManufacturer, kTestProvider,
kDenyRevokedDevice, &device_certificate_status)
.error_code()); .error_code());
// Test case where the signer certificate is newer than the current status // Test case where the signer certificate is newer than the current status
// list, and unknown devices are not allowed. // list, and unknown devices are not allowed.
device_certificate_status.Clear();
device_status_list_.set_allow_unknown_devices(false); device_status_list_.set_allow_unknown_devices(false);
MockClientCert newer_client_cert2; MockClientCert newer_client_cert2;
EXPECT_CALL(newer_client_cert2, type()) EXPECT_CALL(newer_client_cert2, type())
@@ -377,10 +471,14 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number)); .WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds()) EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime + 1)); .WillRepeatedly(Return(kStatusListCreationTime + 1));
EXPECT_CALL(newer_client_cert2, SystemIdUnknownError())
.WillRepeatedly(
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, "")));
EXPECT_EQ( EXPECT_EQ(
DRM_DEVICE_CERTIFICATE_UNKNOWN, DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_ device_status_list_
.GetCertStatus(newer_client_cert2, kTestManufacturer, &device_info) .GetCertStatus(newer_client_cert2, kTestManufacturer, kTestProvider,
kDenyRevokedDevice, &device_certificate_status)
.error_code()); .error_code());
} }
@@ -388,7 +486,7 @@ TEST_F(DeviceStatusListTest, InvalidStatusList) {
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
device_status_list_ device_status_list_
.UpdateStatusList(test_keys_.public_test_key_2_2048_bits(), .UpdateStatusList(test_keys_.public_test_key_2_2048_bits(),
serialized_cert_status_list_, serialized_cert_status_list_, kHashAlgorithm,
cert_status_list_signature_, 0) cert_status_list_signature_, 0)
.error_code()); .error_code());
@@ -396,14 +494,14 @@ TEST_F(DeviceStatusListTest, InvalidStatusList) {
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
device_status_list_ device_status_list_
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(), .UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
serialized_cert_status_list_, serialized_cert_status_list_, kHashAlgorithm,
cert_status_list_signature_, 0) cert_status_list_signature_, 0)
.error_code()); .error_code());
} }
class MockDeviceStatusList : public DeviceStatusList { class MockDeviceStatusList : public DeviceStatusList {
public: public:
MOCK_CONST_METHOD0(GetCurrentTime, uint32_t()); MOCK_METHOD(uint32_t, GetCurrentTime, (), (const, override));
}; };
TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) { TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
@@ -414,12 +512,12 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
.WillOnce(Return(kStatusListCreationTime + 101)); .WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList( EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(), test_keys_.public_test_key_1_3072_bits(),
serialized_cert_status_list_, serialized_cert_status_list_, kHashAlgorithm,
cert_status_list_signature_, 100)); cert_status_list_signature_, 100));
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST, EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list mock_device_status_list
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(), .UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
serialized_cert_status_list_, serialized_cert_status_list_, kHashAlgorithm,
cert_status_list_signature_, 100) cert_status_list_signature_, 100)
.error_code()); .error_code());
} }
@@ -433,10 +531,10 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
.WillOnce(Return(kStatusListCreationTime + 101)); .WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList( EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(), test_keys_.public_test_key_1_3072_bits(),
serialized_cert_status_list_, serialized_cert_status_list_, kHashAlgorithm,
cert_status_list_signature_, 100)); cert_status_list_signature_, 100));
ProvisionedDeviceInfo device_info; DeviceCertificateStatus device_certificate_status;
MockClientCert valid_client_cert; MockClientCert valid_client_cert;
std::string valid_drm_serial_number(kValidSerialNumber); std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_cert, type()) EXPECT_CALL(valid_client_cert, type())
@@ -447,14 +545,15 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
.WillRepeatedly(ReturnRef(valid_drm_serial_number)); .WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds()) EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime - 1)); .WillRepeatedly(Return(kStatusListCreationTime - 1));
EXPECT_EQ(OkStatus(), EXPECT_EQ(OkStatus(), mock_device_status_list.GetCertStatus(
mock_device_status_list.GetCertStatus( valid_client_cert, kTestManufacturer, kTestProvider,
valid_client_cert, kTestManufacturer, &device_info)); kDenyRevokedDevice, &device_certificate_status));
EXPECT_EQ( EXPECT_EQ(
EXPIRED_CERTIFICATE_STATUS_LIST, EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list mock_device_status_list
.GetCertStatus(valid_client_cert, kTestManufacturer, &device_info) .GetCertStatus(valid_client_cert, kTestManufacturer, kTestProvider,
kDenyRevokedDevice, &device_certificate_status)
.error_code()); .error_code());
} }
@@ -477,49 +576,109 @@ TEST_F(DeviceStatusListTest, IsSystemIdActive) {
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId)); device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
} }
TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowed) { TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowedByMake) {
std::string whitelisted_device_list = const char kTestManufacturer_AA[] = "AA";
std::string(kTestSystemId_1) + ":" + std::string(kTestManufacturer_LG); const char kTestManufacturer_AAA[] = "AAA";
whitelisted_device_list += "," + std::string(kTestSystemId_2) + ":" + const char kTestManufacturer_BBB[] = "BBB";
std::string(kTestManufacturer_Samsung); const char kTestManufacturer_BbB[] = "BbB";
whitelisted_device_list += "," + std::string(kTestSystemId_3) + ":"; const char kTestManufacturer_bbb[] = "bbb";
whitelisted_device_list += ", " + std::string(kTestSystemId_1) + ":" + const char kTestManufacturer_CCC[] = "CCC";
std::string(kTestManufacturer_LGE); const char kTestManufacturer_DDD[] = "AAA";
device_status_list_.AllowTestOnlyDevices(whitelisted_device_list); std::string allowed_device_list =
EXPECT_EQ(4, VerifyAllowedTestOnlyDevicesAdded()); std::string(kTestSystemId_1) + ":" + std::string(kTestManufacturer_AA);
allowed_device_list += "," + std::string(kTestSystemId_2) + ":" +
std::string(kTestManufacturer_BBB);
allowed_device_list += "," + std::string(kTestSystemId_3) + ":";
allowed_device_list += ", " + std::string(kTestSystemId_1) + ":" +
std::string(kTestManufacturer_AAA);
device_status_list_.AllowTestOnlyDevicesByMake(allowed_device_list);
EXPECT_EQ(4, AllowedTestOnlyDevicesByMakeSize());
// Verify that device with system_id = kTestSystemId_1 and // Verify that device with system_id = kTestSystemId_1 and
// manufacturer = kTestManufacturer_LG is allowed. // manufacturer AA is allowed.
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_1), EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_1),
kTestManufacturer_LG)); kTestManufacturer_AA));
// Verify that device with system_id = kTestSystemId_1 and // Verify that device with system_id = kTestSystemId_1 and
// manufacturer = kTestManufacturer_LGE is allowed. // manufacturer AAA is allowed.
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_1), EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_1),
kTestManufacturer_LGE)); kTestManufacturer_AAA));
// Verify that device with system_id = kTestSystemId_2 and // Verify that device with system_id = kTestSystemId_2 and
// manufacturer = kTestManufacturer_LGE is not allowed. // manufacturer AAA is not allowed.
// This is because this combination is not 'whitelisted'. // This is because this combination is not in the allowed list.
EXPECT_FALSE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), EXPECT_FALSE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
kTestManufacturer_LGE)); kTestManufacturer_AAA));
// Verify that device with system_id = kTestSystemId_2 and // Verify that device with system_id = kTestSystemId_2 and
// manufacturer = kTestManufacturer_Samsung is allowed. // manufacturer BBB is allowed.
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
kTestManufacturer_Samsung)); kTestManufacturer_BBB));
// Verifes that device with mixed case succeeds. // Verifes that device with mixed case succeeds.
EXPECT_TRUE( EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), "samSung")); kTestManufacturer_BbB));
EXPECT_TRUE( EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), "SAMsung")); kTestManufacturer_bbb));
// Verify that device with system_id = kTestSystemId_3 and // Verify that device with system_id = kTestSystemId_3 and
// any manufacturer is allowed. This checks that any manufacturer is // any manufacturer is allowed. This checks that any manufacturer is
// allowed for this system_id. // allowed for this system_id.
EXPECT_TRUE( EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_3),
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3), "Cisco")); kTestManufacturer_CCC));
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3), EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_3),
"ScientificAtlanta")); kTestManufacturer_DDD));
uint32_t unknown_system_id = 7890; uint32_t unknown_system_id = 7890;
// Verify that device with system_id = unknown_system_id and // Verify that device with system_id = unknown_system_id and
// manufacturer = "Cisco" is not allowed. // manufacturer CCC is not allowed.
EXPECT_FALSE(VerifyIsTestOnlyDeviceAllowed(unknown_system_id, "Cisco")); EXPECT_FALSE(
IsTestOnlyDeviceAllowedByMake(unknown_system_id, kTestManufacturer_CCC));
}
TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowedByProvider) {
const char kTestProvider_AA[] = "AA";
const char kTestProvider_AAA[] = "AAA";
const char kTestProvider_BBB[] = "BBB";
const char kTestProvider_BbB[] = "BbB";
const char kTestProvider_bbb[] = "bbb";
const char kTestProvider_CCC[] = "CCC";
std::string allowed_device_list =
std::string(kTestSystemId_1) + ":" + std::string(kTestProvider_AA);
allowed_device_list +=
"," + std::string(kTestSystemId_2) + ":" + std::string(kTestProvider_BBB);
allowed_device_list += "," + std::string(kTestSystemId_3) + ":";
allowed_device_list += ", " + std::string(kTestSystemId_1) + ":" +
std::string(kTestProvider_AAA);
device_status_list_.AllowTestOnlyDevicesByProvider(allowed_device_list);
EXPECT_EQ(4, AllowedTestOnlyDevicesByProviderSize());
// Verify that device with system_id = kTestSystemId_1 and
// provider AA is allowed.
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_1),
kTestProvider_AA));
// Verify that device with system_id = kTestSystemId_1 and
// provider AAA is allowed.
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_1),
kTestProvider_AAA));
// Verify that device with system_id = kTestSystemId_2 and
// provider AAA is not allowed.
// This is because this combination is not 'whitelisted'.
EXPECT_FALSE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_2),
kTestProvider_AAA));
// Verify that device with system_id = kTestSystemId_2 and
// provider BBB is allowed.
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_2),
kTestProvider_BBB));
// Verifes that device with mixed case succeeds.
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_2),
kTestProvider_BbB));
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_2),
kTestProvider_bbb));
// Verify that device with system_id = kTestSystemId_3 and
// any provider is allowed. This checks that any provider is
// allowed for this system_id.
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_3),
kTestProvider_CCC));
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_3),
kTestProvider_AAA));
uint32_t unknown_system_id = 7890;
// Verify that device with system_id = unknown_system_id and
// provider CCC is not allowed.
EXPECT_FALSE(
IsTestOnlyDeviceAllowedByProvider(unknown_system_id, kTestProvider_CCC));
} }
TEST_F(DeviceStatusListTest, IsDrmDeviceCertificateRevoked) { TEST_F(DeviceStatusListTest, IsDrmDeviceCertificateRevoked) {
@@ -552,6 +711,7 @@ TEST_F(DeviceStatusListTest, DetermineAndDeserializeServiceResponseSuccess) {
SignedDeviceInfo published_devices; SignedDeviceInfo published_devices;
GenerateTrivialValidStatusList( GenerateTrivialValidStatusList(
published_devices.mutable_device_certificate_status_list(), published_devices.mutable_device_certificate_status_list(),
HashAlgorithmProtoToEnum(published_devices.hash_algorithm()),
published_devices.mutable_signature()); published_devices.mutable_signature());
std::string serialized_published_devices; std::string serialized_published_devices;
@@ -561,13 +721,17 @@ TEST_F(DeviceStatusListTest, DetermineAndDeserializeServiceResponseSuccess) {
DeviceCertificateStatusList actual_cert_status_list; DeviceCertificateStatusList actual_cert_status_list;
std::string actual_serialized_cert_status_list; std::string actual_serialized_cert_status_list;
std::string actual_signature; std::string actual_signature;
HashAlgorithm hash_algorithm;
ASSERT_EQ(OkStatus(), ASSERT_EQ(OkStatus(),
DeviceStatusList::DetermineAndDeserializeServiceResponse( DeviceStatusList::DetermineAndDeserializeServiceResponse(
serialized_published_devices, &actual_cert_status_list, serialized_published_devices, &actual_cert_status_list,
&actual_serialized_cert_status_list, &actual_signature)); &actual_serialized_cert_status_list, &hash_algorithm,
&actual_signature));
EXPECT_EQ(published_devices.device_certificate_status_list(), EXPECT_EQ(published_devices.device_certificate_status_list(),
actual_serialized_cert_status_list); actual_serialized_cert_status_list);
EXPECT_EQ(published_devices.signature(), actual_signature); EXPECT_EQ(published_devices.signature(), actual_signature);
EXPECT_EQ(HashAlgorithmProtoToEnum(published_devices.hash_algorithm()),
hash_algorithm);
DeviceCertificateStatusList expected_cert_status_list; DeviceCertificateStatusList expected_cert_status_list;
ASSERT_TRUE(expected_cert_status_list.ParseFromString( ASSERT_TRUE(expected_cert_status_list.ParseFromString(
@@ -580,9 +744,11 @@ TEST_F(DeviceStatusListTest,
DetermineAndDeserializeServiceResponseLegacySuccess) { DetermineAndDeserializeServiceResponseLegacySuccess) {
std::string serialized_cert_status_list; std::string serialized_cert_status_list;
std::string signature; std::string signature;
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
SignedDeviceCertificateStatusList legacy_signed_cert_status_list; SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
GenerateTrivialValidStatusList(
&serialized_cert_status_list,
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
&signature);
*(legacy_signed_cert_status_list.mutable_certificate_status_list()) = *(legacy_signed_cert_status_list.mutable_certificate_status_list()) =
serialized_cert_status_list; serialized_cert_status_list;
*(legacy_signed_cert_status_list.mutable_signature()) = signature; *(legacy_signed_cert_status_list.mutable_signature()) = signature;
@@ -604,12 +770,17 @@ TEST_F(DeviceStatusListTest,
std::string actual_serialized_cert_status_list; std::string actual_serialized_cert_status_list;
std::string actual_signature; std::string actual_signature;
DeviceCertificateStatusList actual_cert_status_list; DeviceCertificateStatusList actual_cert_status_list;
HashAlgorithm hash_algorithm;
ASSERT_EQ(OkStatus(), ASSERT_EQ(OkStatus(),
DeviceStatusList::DetermineAndDeserializeServiceResponse( DeviceStatusList::DetermineAndDeserializeServiceResponse(
server_response, &actual_cert_status_list, server_response, &actual_cert_status_list,
&actual_serialized_cert_status_list, &actual_signature)); &actual_serialized_cert_status_list, &hash_algorithm,
&actual_signature));
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list); EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
EXPECT_EQ(signature, actual_signature); EXPECT_EQ(signature, actual_signature);
EXPECT_EQ(
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
hash_algorithm);
DeviceCertificateStatusList expected_cert_status_list; DeviceCertificateStatusList expected_cert_status_list;
ASSERT_TRUE( ASSERT_TRUE(
@@ -622,9 +793,11 @@ TEST_F(DeviceStatusListTest,
DetermineAndDeserializeServiceResponseLegacyWebSafeBase64Success) { DetermineAndDeserializeServiceResponseLegacyWebSafeBase64Success) {
std::string serialized_cert_status_list; std::string serialized_cert_status_list;
std::string signature; std::string signature;
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
SignedDeviceCertificateStatusList legacy_signed_cert_status_list; SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
GenerateTrivialValidStatusList(
&serialized_cert_status_list,
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
&signature);
*(legacy_signed_cert_status_list.mutable_certificate_status_list()) = *(legacy_signed_cert_status_list.mutable_certificate_status_list()) =
serialized_cert_status_list; serialized_cert_status_list;
*(legacy_signed_cert_status_list.mutable_signature()) = signature; *(legacy_signed_cert_status_list.mutable_signature()) = signature;
@@ -639,14 +812,18 @@ TEST_F(DeviceStatusListTest,
std::string actual_serialized_cert_status_list; std::string actual_serialized_cert_status_list;
std::string actual_signature; std::string actual_signature;
HashAlgorithm hash_algorithm;
DeviceCertificateStatusList actual_cert_status_list; DeviceCertificateStatusList actual_cert_status_list;
ASSERT_EQ(OkStatus(), ASSERT_EQ(OkStatus(),
DeviceStatusList::DetermineAndDeserializeServiceResponse( DeviceStatusList::DetermineAndDeserializeServiceResponse(
websafe_b64_serialized_signed_cert_status_list, websafe_b64_serialized_signed_cert_status_list,
&actual_cert_status_list, &actual_serialized_cert_status_list, &actual_cert_status_list, &actual_serialized_cert_status_list,
&actual_signature)); &hash_algorithm, &actual_signature));
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list); EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
EXPECT_EQ(signature, actual_signature); EXPECT_EQ(signature, actual_signature);
EXPECT_EQ(
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
hash_algorithm);
DeviceCertificateStatusList expected_cert_status_list; DeviceCertificateStatusList expected_cert_status_list;
ASSERT_TRUE( ASSERT_TRUE(
@@ -659,9 +836,11 @@ TEST_F(DeviceStatusListTest,
DetermineAndDeserializeServiceResponseLegacyBase64Success) { DetermineAndDeserializeServiceResponseLegacyBase64Success) {
std::string serialized_cert_status_list; std::string serialized_cert_status_list;
std::string signature; std::string signature;
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
SignedDeviceCertificateStatusList legacy_signed_cert_status_list; SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
GenerateTrivialValidStatusList(
&serialized_cert_status_list,
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
&signature);
*(legacy_signed_cert_status_list.mutable_certificate_status_list()) = *(legacy_signed_cert_status_list.mutable_certificate_status_list()) =
serialized_cert_status_list; serialized_cert_status_list;
*(legacy_signed_cert_status_list.mutable_signature()) = signature; *(legacy_signed_cert_status_list.mutable_signature()) = signature;
@@ -676,14 +855,18 @@ TEST_F(DeviceStatusListTest,
std::string actual_serialized_cert_status_list; std::string actual_serialized_cert_status_list;
std::string actual_signature; std::string actual_signature;
HashAlgorithm hash_algorithm;
DeviceCertificateStatusList actual_cert_status_list; DeviceCertificateStatusList actual_cert_status_list;
ASSERT_EQ(OkStatus(), ASSERT_EQ(OkStatus(),
DeviceStatusList::DetermineAndDeserializeServiceResponse( DeviceStatusList::DetermineAndDeserializeServiceResponse(
websafe_b64_serialized_signed_cert_status_list, websafe_b64_serialized_signed_cert_status_list,
&actual_cert_status_list, &actual_serialized_cert_status_list, &actual_cert_status_list, &actual_serialized_cert_status_list,
&actual_signature)); &hash_algorithm, &actual_signature));
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list); EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
EXPECT_EQ(signature, actual_signature); EXPECT_EQ(signature, actual_signature);
EXPECT_EQ(
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
hash_algorithm);
DeviceCertificateStatusList expected_cert_status_list; DeviceCertificateStatusList expected_cert_status_list;
ASSERT_TRUE( ASSERT_TRUE(
@@ -692,4 +875,44 @@ TEST_F(DeviceStatusListTest,
expected_cert_status_list, actual_cert_status_list)); expected_cert_status_list, actual_cert_status_list));
} }
TEST_F(DeviceStatusListTest,
GetDeviceCertificateStatusBySystemIdExpiredDeviceCertificateStatusList) {
MockDeviceStatusList mock_device_status_list;
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
.Times(3)
.WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_cert_status_list_, kHashAlgorithm,
cert_status_list_signature_, 100));
DeviceCertificateStatus device_certificate_status;
EXPECT_EQ(OkStatus(),
mock_device_status_list.GetDeviceCertificateStatusBySystemId(
kValidCertSystemId, &device_certificate_status));
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list
.GetDeviceCertificateStatusBySystemId(
kValidCertSystemId, &device_certificate_status)
.error_code());
}
TEST_F(DeviceStatusListTest,
GetDeviceCertificateStatusBySystemIdUnknownDevice) {
DeviceCertificateStatus device_certificate_status;
uint32_t unknown_system_id = 2000;
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_
.GetDeviceCertificateStatusBySystemId(
unknown_system_id, &device_certificate_status)
.error_code());
}
TEST_F(DeviceStatusListTest, GetDeviceCertificateStatusBySystemIdOk) {
DeviceCertificateStatus device_certificate_status;
EXPECT_OK(device_status_list_.GetDeviceCertificateStatusBySystemId(
kValidCertSystemId, &device_certificate_status));
}
} // namespace widevine } // namespace widevine

View File

@@ -18,6 +18,8 @@
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "common/ec_key.h" #include "common/ec_key.h"
#include "common/error_space.h" #include "common/error_space.h"
#include "common/hash_algorithm.h"
#include "common/hash_algorithm_util.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/sha_util.h" #include "common/sha_util.h"
#include "common/signer_public_key.h" #include "common/signer_public_key.h"
@@ -281,6 +283,7 @@ class VerifiedCertSignatureCache {
// cache. // cache.
Status VerifySignature(const std::string& cert, Status VerifySignature(const std::string& cert,
const std::string& serial_number, const std::string& serial_number,
HashAlgorithm hash_algorithm,
const std::string& signature, const std::string& signature,
const DrmCertificate& signer) { const DrmCertificate& signer) {
{ {
@@ -314,7 +317,7 @@ class VerifiedCertSignatureCache {
return Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-public-key"); "invalid-signer-public-key");
} }
if (!signer_public_key->VerifySignature(cert, signature)) { if (!signer_public_key->VerifySignature(cert, hash_algorithm, signature)) {
return Status(error_space, INVALID_SIGNATURE, return Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature"); "cache-miss-invalid-signature");
} }
@@ -428,8 +431,10 @@ Status DrmRootCertificate::Create(CertificateType cert_type,
return Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-public-key"); "invalid-root-public-key");
} }
if (!public_key->VerifySignature(signed_root_cert.drm_certificate(), if (!public_key->VerifySignature(
signed_root_cert.signature())) { signed_root_cert.drm_certificate(),
HashAlgorithmProtoToEnum(signed_root_cert.hash_algorithm()),
signed_root_cert.signature())) {
return Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-certificate-signature"); "invalid-root-certificate-signature");
} }
@@ -519,6 +524,7 @@ Status DrmRootCertificate::VerifySignatures(
// Always use cache for root-signed certificates. // Always use cache for root-signed certificates.
return signature_cache_->VerifySignature( return signature_cache_->VerifySignature(
signed_cert.drm_certificate(), cert_serial_number, signed_cert.drm_certificate(), cert_serial_number,
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
signed_cert.signature(), root_cert_); signed_cert.signature(), root_cert_);
} }
DrmCertificate signer; DrmCertificate signer;
@@ -539,9 +545,10 @@ Status DrmRootCertificate::VerifySignatures(
} }
if (use_cache) { if (use_cache) {
status = signature_cache_->VerifySignature(signed_cert.drm_certificate(), status = signature_cache_->VerifySignature(
cert_serial_number, signed_cert.drm_certificate(), cert_serial_number,
signed_cert.signature(), signer); HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
signed_cert.signature(), signer);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
@@ -552,8 +559,10 @@ Status DrmRootCertificate::VerifySignatures(
return Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-leaf-signer-public-key"); "invalid-leaf-signer-public-key");
} }
if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(), if (!signer_public_key->VerifySignature(
signed_cert.signature())) { signed_cert.drm_certificate(),
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
signed_cert.signature())) {
return Status(error_space, INVALID_SIGNATURE, return Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature"); "cache-miss-invalid-signature");
} }

View File

@@ -21,6 +21,8 @@
#include "common/ec_key.h" #include "common/ec_key.h"
#include "common/ec_test_keys.h" #include "common/ec_test_keys.h"
#include "common/error_space.h" #include "common/error_space.h"
#include "common/hash_algorithm.h"
#include "common/hash_algorithm_util.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/rsa_test_keys.h" #include "common/rsa_test_keys.h"
#include "common/test_drm_certificates.h" #include "common/test_drm_certificates.h"
@@ -101,6 +103,7 @@ class SignerPrivateKey {
public: public:
virtual ~SignerPrivateKey() {} virtual ~SignerPrivateKey() {}
virtual bool GenerateSignature(const std::string& message, virtual bool GenerateSignature(const std::string& message,
HashAlgorithm hash_algorithm,
std::string* signature) const = 0; std::string* signature) const = 0;
virtual DrmCertificate::Algorithm algorithm() const = 0; virtual DrmCertificate::Algorithm algorithm() const = 0;
static std::unique_ptr<SignerPrivateKey> Create( static std::unique_ptr<SignerPrivateKey> Create(
@@ -119,8 +122,9 @@ class SignerPrivateKeyImpl : public SignerPrivateKey {
: private_key_(std::move(private_key)), algorithm_(algorithm) {} : private_key_(std::move(private_key)), algorithm_(algorithm) {}
~SignerPrivateKeyImpl() override {} ~SignerPrivateKeyImpl() override {}
bool GenerateSignature(const std::string& message, bool GenerateSignature(const std::string& message,
HashAlgorithm hash_algorithm,
std::string* signature) const override { std::string* signature) const override {
return private_key_->GenerateSignature(message, signature); return private_key_->GenerateSignature(message, hash_algorithm, signature);
} }
DrmCertificate::Algorithm algorithm() const override { return algorithm_; } DrmCertificate::Algorithm algorithm() const override { return algorithm_; }
@@ -255,7 +259,9 @@ class DrmRootCertificateTest : public testing::TestWithParam<const char*> {
ASSERT_TRUE(drm_certificates_[kClientKey].SerializeToString( ASSERT_TRUE(drm_certificates_[kClientKey].SerializeToString(
current_sc->mutable_drm_certificate())); current_sc->mutable_drm_certificate()));
ASSERT_TRUE(private_keys_[kInterMediateKey]->GenerateSignature( ASSERT_TRUE(private_keys_[kInterMediateKey]->GenerateSignature(
current_sc->drm_certificate(), current_sc->mutable_signature())); current_sc->drm_certificate(),
HashAlgorithmProtoToEnum(current_sc->hash_algorithm()),
current_sc->mutable_signature()));
current_sc = current_sc->mutable_signer(); current_sc = current_sc->mutable_signer();
drm_certificates_[kInterMediateKey].set_algorithm( drm_certificates_[kInterMediateKey].set_algorithm(
@@ -263,7 +269,9 @@ class DrmRootCertificateTest : public testing::TestWithParam<const char*> {
ASSERT_TRUE(drm_certificates_[kInterMediateKey].SerializeToString( ASSERT_TRUE(drm_certificates_[kInterMediateKey].SerializeToString(
current_sc->mutable_drm_certificate())); current_sc->mutable_drm_certificate()));
ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature( ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature(
current_sc->drm_certificate(), current_sc->mutable_signature())); current_sc->drm_certificate(),
HashAlgorithmProtoToEnum(current_sc->hash_algorithm()),
current_sc->mutable_signature()));
current_sc = current_sc->mutable_signer(); current_sc = current_sc->mutable_signer();
drm_certificates_[kDrmRootKey].set_algorithm( drm_certificates_[kDrmRootKey].set_algorithm(
@@ -271,7 +279,9 @@ class DrmRootCertificateTest : public testing::TestWithParam<const char*> {
ASSERT_TRUE(drm_certificates_[kDrmRootKey].SerializeToString( ASSERT_TRUE(drm_certificates_[kDrmRootKey].SerializeToString(
current_sc->mutable_drm_certificate())); current_sc->mutable_drm_certificate()));
ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature( ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature(
current_sc->drm_certificate(), current_sc->mutable_signature())); current_sc->drm_certificate(),
HashAlgorithmProtoToEnum(current_sc->hash_algorithm()),
current_sc->mutable_signature()));
} }
RsaTestKeys rsa_test_keys_; RsaTestKeys rsa_test_keys_;

View File

@@ -13,7 +13,7 @@
#include <utility> #include <utility>
#include "glog/logging.h" #include "glog/logging.h"
#include "base/thread_annotations.h" #include "absl/base/thread_annotations.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "util/gtl/map_util.h" #include "util/gtl/map_util.h"

View File

@@ -17,6 +17,7 @@
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "common/aes_cbc_util.h" #include "common/aes_cbc_util.h"
#include "common/drm_root_certificate.h" #include "common/drm_root_certificate.h"
#include "common/hash_algorithm_util.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/rsa_test_keys.h" #include "common/rsa_test_keys.h"
#include "common/rsa_util.h" #include "common/rsa_util.h"
@@ -62,8 +63,10 @@ class DrmServiceCertificateTest : public ::testing::Test {
cert.set_creation_time_seconds(creation_time_seconds); cert.set_creation_time_seconds(creation_time_seconds);
SignedDrmCertificate signed_cert; SignedDrmCertificate signed_cert;
cert.SerializeToString(signed_cert.mutable_drm_certificate()); cert.SerializeToString(signed_cert.mutable_drm_certificate());
root_private_key_->GenerateSignature(signed_cert.drm_certificate(), root_private_key_->GenerateSignature(
signed_cert.mutable_signature()); signed_cert.drm_certificate(),
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
signed_cert.mutable_signature());
std::string serialized_cert; std::string serialized_cert;
signed_cert.SerializeToString(&serialized_cert); signed_cert.SerializeToString(&serialized_cert);
return serialized_cert; return serialized_cert;

View File

@@ -0,0 +1,113 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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/dual_certificate_client_cert.h"
#include "common/error_space.h"
#include "common/status.h"
#include "protos/public/errors.pb.h"
namespace widevine {
Status DualCertificateClientCert::Initialize(
const DrmRootCertificate* root_certificate,
const std::string& serialized_signing_certificate,
const std::string& serialized_encryption_certificate) {
Status status = signing_certificate_.Initialize(
root_certificate, serialized_signing_certificate);
if (!status.ok()) {
return status;
}
status = encryption_certificate_.Initialize(
root_certificate, serialized_encryption_certificate);
if (!status.ok()) {
return status;
}
if (encryption_certificate_.signer_serial_number() !=
signing_certificate_.signer_serial_number()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"certificate_signer_mismatch");
}
if ((encryption_certificate_.system_id() !=
signing_certificate_.system_id()) ||
(encryption_certificate_.service_id() !=
signing_certificate_.service_id()) ||
(encryption_certificate_.signer_creation_time_seconds() !=
signing_certificate_.signer_creation_time_seconds()) ||
(encryption_certificate_.signed_by_provisioner() !=
signing_certificate_.signed_by_provisioner())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid_certificate_pair");
}
return OkStatus();
}
Status DualCertificateClientCert::VerifySignature(
const std::string& message, HashAlgorithm hash_algorithm,
const std::string& signature, ProtocolVersion protocol_version) const {
return signing_certificate_.VerifySignature(message, hash_algorithm,
signature, protocol_version);
}
void DualCertificateClientCert::GenerateSigningKey(
const std::string& message, ProtocolVersion protocol_version) {
encryption_certificate_.GenerateSigningKey(message, protocol_version);
}
const std::string& DualCertificateClientCert::encrypted_key() const {
return encryption_certificate_.encrypted_key();
}
const std::string& DualCertificateClientCert::key() const {
return encryption_certificate_.key();
}
SignedMessage::SessionKeyType DualCertificateClientCert::key_type() const {
return encryption_certificate_.key_type();
}
// TODO(b/155979840): Support revocation check for the encryption certificate.
const std::string& DualCertificateClientCert::serial_number() const {
return signing_certificate_.serial_number();
}
const std::string& DualCertificateClientCert::service_id() const {
return signing_certificate_.service_id();
}
const std::string& DualCertificateClientCert::signing_key() const {
return encryption_certificate_.signing_key();
}
const std::string& DualCertificateClientCert::signer_serial_number() const {
return signing_certificate_.signer_serial_number();
}
uint32_t DualCertificateClientCert::signer_creation_time_seconds() const {
return signing_certificate_.signer_creation_time_seconds();
}
bool DualCertificateClientCert::signed_by_provisioner() const {
return signing_certificate_.signed_by_provisioner();
}
uint32_t DualCertificateClientCert::system_id() const {
return signing_certificate_.system_id();
}
// TODO(b/155979840): Support revocation check for the encryption certificate.
const std::string& DualCertificateClientCert::encrypted_unique_id() const {
return signing_certificate_.encrypted_unique_id();
}
// TODO(b/155979840): Support revocation check for the encryption certificate.
const std::string& DualCertificateClientCert::unique_id_hash() const {
return signing_certificate_.unique_id_hash();
}
} // namespace widevine

View File

@@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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_DUAL_CERTIFICATE_CLIENT_CERT_H_
#define COMMON_DUAL_CERTIFICATE_CLIENT_CERT_H_
#include "common/certificate_client_cert.h"
namespace widevine {
class DualCertificateClientCert : public ClientCert {
public:
DualCertificateClientCert() = default;
~DualCertificateClientCert() override = default;
DualCertificateClientCert(const DualCertificateClientCert&) = delete;
DualCertificateClientCert& operator=(const DualCertificateClientCert&) =
delete;
Status Initialize(const DrmRootCertificate* root_certificate,
const std::string& serialized_signing_certificate,
const std::string& serialized_encryption_certificate);
Status VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature,
ProtocolVersion protocol_version) const override;
void GenerateSigningKey(const std::string& message,
ProtocolVersion protocol_version) override;
const std::string& encrypted_key() const override;
const std::string& key() const override;
SignedMessage::SessionKeyType key_type() const override;
bool using_dual_certificate() const override { return true; }
const std::string& serial_number() const override;
const std::string& service_id() const override;
const std::string& signing_key() const override;
const std::string& signer_serial_number() const override;
uint32_t signer_creation_time_seconds() const override;
bool signed_by_provisioner() const override;
uint32_t system_id() const override;
widevine::ClientIdentification::TokenType type() const override {
return ClientIdentification::DRM_DEVICE_CERTIFICATE;
}
const std::string& encrypted_unique_id() const override;
const std::string& unique_id_hash() const override;
private:
CertificateClientCert signing_certificate_;
CertificateClientCert encryption_certificate_;
};
} // namespace widevine
#endif // COMMON_DUAL_CERTIFICATE_CLIENT_CERT_H_

View File

@@ -23,6 +23,7 @@
#include "openssl/sha.h" #include "openssl/sha.h"
#include "common/aes_cbc_util.h" #include "common/aes_cbc_util.h"
#include "common/ec_util.h" #include "common/ec_util.h"
#include "common/hash_algorithm.h"
#include "common/openssl_util.h" #include "common/openssl_util.h"
#include "common/sha_util.h" #include "common/sha_util.h"
@@ -53,6 +54,22 @@ std::string OpenSSLErrorString(uint32_t error) {
return buf; return buf;
} }
std::string GetMessageDigest(const std::string& message,
widevine::HashAlgorithm hash_algorithm) {
switch (hash_algorithm) {
case widevine::HashAlgorithm::kUnspecified:
case widevine::HashAlgorithm::kSha256:
return widevine::Sha256_Hash(message);
case widevine::HashAlgorithm::kSha1:
LOG(ERROR) << "Unexpected hash algorithm: "
<< static_cast<int>(hash_algorithm);
return "";
}
LOG(FATAL) << "Unexpected hash algorithm: "
<< static_cast<int>(hash_algorithm);
return "";
}
} // namespace } // namespace
ECPrivateKey::ECPrivateKey(EC_KEY* ec_key) : key_(ec_key) { ECPrivateKey::ECPrivateKey(EC_KEY* ec_key) : key_(ec_key) {
@@ -159,6 +176,47 @@ bool ECPrivateKey::GenerateSignature(const std::string& message,
return true; return true;
} }
bool ECPrivateKey::GenerateSignature(const std::string& message,
HashAlgorithm hash_algorithm,
std::string* signature) const {
if (message.empty()) {
LOG(ERROR) << "|message| cannot be empty";
return false;
}
if (signature == nullptr) {
LOG(ERROR) << "|signature| cannot be nullptr";
return false;
}
// Hash the message using corresponding hash algorithm.
std::string message_digest = GetMessageDigest(message, hash_algorithm);
if (message_digest.empty()) {
LOG(ERROR) << "Empty message digest";
return false;
}
size_t max_signature_size = ECDSA_size(key());
if (max_signature_size == 0) {
LOG(ERROR) << "key_ does not have a group set";
return false;
}
signature->resize(max_signature_size);
unsigned int bytes_written = 0;
int result = ECDSA_sign(
0 /* unused type */,
reinterpret_cast<const uint8_t*>(message_digest.data()),
message_digest.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&bytes_written, key());
if (result != 1) {
LOG(ERROR) << "Could not calculate signature: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
signature->resize(bytes_written);
return true;
}
bool ECPrivateKey::MatchesPrivateKey(const ECPrivateKey& private_key) const { bool ECPrivateKey::MatchesPrivateKey(const ECPrivateKey& private_key) const {
return BN_cmp(EC_KEY_get0_private_key(key()), return BN_cmp(EC_KEY_get0_private_key(key()),
EC_KEY_get0_private_key(private_key.key())) == 0; EC_KEY_get0_private_key(private_key.key())) == 0;
@@ -254,6 +312,39 @@ bool ECPublicKey::VerifySignature(const std::string& message,
return true; return true;
} }
bool ECPublicKey::VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature) const {
if (message.empty()) {
LOG(ERROR) << "|message| cannot be empty";
return false;
}
if (signature.empty()) {
LOG(ERROR) << "|signature| cannot be empty";
return false;
}
// Hash the message using corresponding hash algorithm.
std::string message_digest = GetMessageDigest(message, hash_algorithm);
if (message_digest.empty()) {
LOG(ERROR) << "Empty message digest";
return false;
}
int result = ECDSA_verify(
0 /* unused type */,
reinterpret_cast<const uint8_t*>(message_digest.data()),
message_digest.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature.data())),
signature.size(), key());
if (result != 1) {
LOG(ERROR) << "Could not verify signature: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
}
bool ECPublicKey::MatchesPrivateKey(const ECPrivateKey& private_key) const { bool ECPublicKey::MatchesPrivateKey(const ECPrivateKey& private_key) const {
return private_key.MatchesPublicKey(*this); return private_key.MatchesPublicKey(*this);
} }

View File

@@ -16,7 +16,9 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "absl/base/macros.h"
#include "openssl/ec.h" #include "openssl/ec.h"
#include "common/hash_algorithm.h"
#include "common/openssl_util.h" #include "common/openssl_util.h"
namespace widevine { namespace widevine {
@@ -65,9 +67,24 @@ class ECPrivateKey {
// DER-encoded signature. // DER-encoded signature.
// Caller retains ownership of all pointers. // Caller retains ownership of all pointers.
// Returns true on success and false on error. // Returns true on success and false on error.
// TODO(b/155438325): remove this function after the below function is fully
// propagated.
ABSL_DEPRECATED(
"Use the below function with |hash_algorithm| argument instead.")
virtual bool GenerateSignature(const std::string& message, virtual bool GenerateSignature(const std::string& message,
std::string* signature) const; std::string* signature) const;
// Given a message, calculates a signature using ECDSA with the key_.
// |message| is the message to be signed.
// |hash_algorithm| specifies the hash algorithm.
// |signature| will contain the resulting signature. This will be an ASN.1
// DER-encoded signature.
// Caller retains ownership of all pointers.
// Returns true on success and false on error.
virtual bool GenerateSignature(const std::string& message,
HashAlgorithm hash_algorithm,
std::string* signature) const;
// Returns whether the given private key is the same as key_. // Returns whether the given private key is the same as key_.
virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const; virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const;
@@ -110,9 +127,23 @@ class ECPublicKey {
// |message| is the message that was signed. // |message| is the message that was signed.
// |signature| is an ASN.1 DER-encoded signature. // |signature| is an ASN.1 DER-encoded signature.
// Returns true on success and false on error. // Returns true on success and false on error.
// TODO(b/155438325): remove this function after the below function is fully
// propagated.
ABSL_DEPRECATED(
"Use the below function with |hash_algorithm| argument instead.")
virtual bool VerifySignature(const std::string& message, virtual bool VerifySignature(const std::string& message,
const std::string& signature) const; const std::string& signature) const;
// Given a message and a signature, verifies that the signature was created
// using the private key associated with key_.
// |message| is the message that was signed.
// |hash_algorithm| specifies the hash algorithm.
// |signature| is an ASN.1 DER-encoded signature.
// Returns true on success and false on error.
virtual bool VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature) const;
// Returns whether the given private key is part of the same key pair as key_. // Returns whether the given private key is part of the same key pair as key_.
virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const; virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const;

View File

@@ -111,6 +111,10 @@ class ECKeyTestKeyPairs : public ECKeyTest,
std::unique_ptr<ECPublicKey> public_key_; std::unique_ptr<ECPublicKey> public_key_;
}; };
// Death test naming convention. See below link for details:
// go/gunitadvanced#death-test-naming
using ECKeyTestKeyPairsDeathTest = ECKeyTestKeyPairs;
TEST_P(ECKeyTestKeyPairs, CreateWrongKey) { TEST_P(ECKeyTestKeyPairs, CreateWrongKey) {
EXPECT_EQ(ECPrivateKey::Create(test_public_key_), nullptr); EXPECT_EQ(ECPrivateKey::Create(test_public_key_), nullptr);
EXPECT_EQ(ECPublicKey::Create(test_private_key_), nullptr); EXPECT_EQ(ECPublicKey::Create(test_private_key_), nullptr);
@@ -165,6 +169,40 @@ TEST_P(ECKeyTestKeyPairs, SignVerify) {
EXPECT_TRUE(public_key_->VerifySignature(plaintext_message_, signature)); EXPECT_TRUE(public_key_->VerifySignature(plaintext_message_, signature));
} }
TEST_P(ECKeyTestKeyPairs, SignVerifySha1) {
std::string signature;
EXPECT_FALSE(private_key_->GenerateSignature(
plaintext_message_, HashAlgorithm::kSha1, &signature));
EXPECT_FALSE(public_key_->VerifySignature(plaintext_message_,
HashAlgorithm::kSha1, signature));
}
TEST_P(ECKeyTestKeyPairs, SignVerifySha256) {
std::string signature;
ASSERT_TRUE(private_key_->GenerateSignature(
plaintext_message_, HashAlgorithm::kSha256, &signature));
ASSERT_TRUE(public_key_->VerifySignature(plaintext_message_,
HashAlgorithm::kSha256, signature));
}
TEST_P(ECKeyTestKeyPairs, SignVerifyUnspecified) {
std::string signature;
ASSERT_TRUE(private_key_->GenerateSignature(
plaintext_message_, HashAlgorithm::kUnspecified, &signature));
ASSERT_TRUE(public_key_->VerifySignature(
plaintext_message_, HashAlgorithm::kUnspecified, signature));
}
TEST_P(ECKeyTestKeyPairsDeathTest, SignVerifyUnexpected) {
std::string signature;
HashAlgorithm unexpected_hash_algorithm = static_cast<HashAlgorithm>(1234);
EXPECT_DEATH(private_key_->GenerateSignature(
plaintext_message_, unexpected_hash_algorithm, &signature),
"Unexpected hash algorithm: 1234");
EXPECT_FALSE(public_key_->VerifySignature(
plaintext_message_, unexpected_hash_algorithm, signature));
}
TEST_P(ECKeyTestKeyPairs, InvalidSignVerifyParameters) { TEST_P(ECKeyTestKeyPairs, InvalidSignVerifyParameters) {
std::string signature; std::string signature;
EXPECT_FALSE(private_key_->GenerateSignature("", &signature)); EXPECT_FALSE(private_key_->GenerateSignature("", &signature));
@@ -220,6 +258,9 @@ TEST_P(ECKeyTestKeyPairs, KeyPointEncodingSuccess) {
INSTANTIATE_TEST_SUITE_P(ECKeyTestKeyPairs, ECKeyTestKeyPairs, INSTANTIATE_TEST_SUITE_P(ECKeyTestKeyPairs, ECKeyTestKeyPairs,
::testing::ValuesIn(ECKeyTest::GetTestKeyList())); ::testing::ValuesIn(ECKeyTest::GetTestKeyList()));
INSTANTIATE_TEST_SUITE_P(ECKeyTestKeyPairsDeathTest, ECKeyTestKeyPairsDeathTest,
::testing::ValuesIn(ECKeyTest::GetTestKeyList()));
class ECKeyTestCurveMismatch class ECKeyTestCurveMismatch
: public ECKeyTest, : public ECKeyTest,
public ::testing::WithParamInterface< public ::testing::WithParamInterface<

View File

@@ -200,9 +200,10 @@ TEST(EciesEncryptorTest, EciesEncryptNullKeySource) {
class MockEcKeySource : public ECKeySource { class MockEcKeySource : public ECKeySource {
public: public:
MockEcKeySource() = default; MockEcKeySource() = default;
MOCK_METHOD3(GetECKey, MOCK_METHOD(bool, GetECKey,
bool(ECPrivateKey::EllipticCurve curve, std::string* private_key, (ECPrivateKey::EllipticCurve curve, std::string* private_key,
std::string* public_key)); std::string* public_key),
(override));
}; };
TEST(EciesEncryptorTest, EciesEncryptKeysourceFail) { TEST(EciesEncryptorTest, EciesEncryptKeysourceFail) {

View File

@@ -7,6 +7,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/file_util.h" #include "common/file_util.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"

18
common/hash_algorithm.h Normal file
View File

@@ -0,0 +1,18 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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_HASH_ALGORITHM_H_
#define COMMON_HASH_ALGORITHM_H_
namespace widevine {
enum class HashAlgorithm { kUnspecified, kSha1, kSha256 };
} // namespace widevine
#endif // COMMON_HASH_ALGORITHM_H_

View File

@@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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/hash_algorithm_util.h"
#include "glog/logging.h"
#include "protos/public/hash_algorithm.pb.h"
namespace widevine {
HashAlgorithm HashAlgorithmProtoToEnum(
HashAlgorithmProto hash_algorithm_proto) {
switch (hash_algorithm_proto) {
case HASH_ALGORITHM_UNSPECIFIED:
return HashAlgorithm::kUnspecified;
case HASH_ALGORITHM_SHA_1:
return HashAlgorithm::kSha1;
case HASH_ALGORITHM_SHA_256:
return HashAlgorithm::kSha256;
default:
// See below link for using proto3 enum in switch statement:
// http://shortn/_ma9MY7V9wh
if (HashAlgorithmProto_IsValid(hash_algorithm_proto)) {
LOG(ERROR) << "Unsupported value " << hash_algorithm_proto;
} else {
LOG(WARNING) << "Unexpected value " << hash_algorithm_proto;
}
return HashAlgorithm::kUnspecified;
}
}
HashAlgorithmProto HashAlgorithmEnumToProto(HashAlgorithm hash_algorithm) {
switch (hash_algorithm) {
case HashAlgorithm::kUnspecified:
return HASH_ALGORITHM_UNSPECIFIED;
case HashAlgorithm::kSha1:
return HASH_ALGORITHM_SHA_1;
case HashAlgorithm::kSha256:
return HASH_ALGORITHM_SHA_256;
}
LOG(WARNING) << "Unexpected hash algorithm "
<< static_cast<int>(hash_algorithm);
return HASH_ALGORITHM_UNSPECIFIED;
}
} // namespace widevine

View File

@@ -0,0 +1,23 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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_HASH_ALGORITHM_UTIL_H_
#define COMMON_HASH_ALGORITHM_UTIL_H_
#include "common/hash_algorithm.h"
#include "protos/public/hash_algorithm.pb.h"
namespace widevine {
HashAlgorithm HashAlgorithmProtoToEnum(HashAlgorithmProto hash_algorithm_proto);
HashAlgorithmProto HashAlgorithmEnumToProto(HashAlgorithm hash_algorithm);
} // namespace widevine
#endif // COMMON_HASH_ALGORITHM_UTIL_H_

View File

@@ -0,0 +1,58 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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/hash_algorithm_util.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "common/hash_algorithm.h"
namespace widevine {
TEST(HashAlgorithmTest, ProtoToEnumUnspecified) {
EXPECT_EQ(HashAlgorithm::kUnspecified,
HashAlgorithmProtoToEnum(HASH_ALGORITHM_UNSPECIFIED));
}
TEST(HashAlgorithmTest, ProtoToEnumSha1) {
EXPECT_EQ(HashAlgorithm::kSha1,
HashAlgorithmProtoToEnum(HASH_ALGORITHM_SHA_1));
}
TEST(HashAlgorithmTest, ProtoToEnumSha256) {
EXPECT_EQ(HashAlgorithm::kSha256,
HashAlgorithmProtoToEnum(HASH_ALGORITHM_SHA_256));
}
TEST(HashAlgorithmTest, ProtoToEnumUnsupported) {
EXPECT_EQ(HashAlgorithm::kUnspecified,
HashAlgorithmProtoToEnum(static_cast<HashAlgorithmProto>(1234)));
}
TEST(HashAlgorithmTest, EnumToProtoUnspecified) {
EXPECT_EQ(HASH_ALGORITHM_UNSPECIFIED,
HashAlgorithmEnumToProto(HashAlgorithm::kUnspecified));
}
TEST(HashAlgorithmTest, EnumToProtoSha1) {
EXPECT_EQ(HASH_ALGORITHM_SHA_1,
HashAlgorithmEnumToProto(HashAlgorithm::kSha1));
}
TEST(HashAlgorithmTest, EnumToProtoSha256) {
EXPECT_EQ(HASH_ALGORITHM_SHA_256,
HashAlgorithmEnumToProto(HashAlgorithm::kSha256));
}
TEST(HashAlgorithmTest, EnumToProtoUnexpected) {
int some_value = 1234;
EXPECT_EQ(HASH_ALGORITHM_UNSPECIFIED,
HashAlgorithmEnumToProto(static_cast<HashAlgorithm>(some_value)));
}
} // namespace widevine

View File

@@ -33,9 +33,11 @@ Status KeyboxClientCert::Initialize(const std::string& keybox_token) {
return OkStatus(); return OkStatus();
} }
// |hash_algorithm| is needed for function inheritance.
// For KeyBoxClientCert, we always use HMAC-SHA256 in signature verification.
Status KeyboxClientCert::VerifySignature( Status KeyboxClientCert::VerifySignature(
const std::string& message, const std::string& signature, const std::string& message, HashAlgorithm hash_algorithm,
ProtocolVersion protocol_version) const { const std::string& signature, ProtocolVersion protocol_version) const {
DCHECK(!signing_key_.empty()); DCHECK(!signing_key_.empty());
using crypto_util::VerifySignatureHmacSha256; using crypto_util::VerifySignatureHmacSha256;
if (!VerifySignatureHmacSha256( if (!VerifySignatureHmacSha256(

View File

@@ -10,10 +10,12 @@
#define COMMON_KEYBOX_CLIENT_CERT_H_ #define COMMON_KEYBOX_CLIENT_CERT_H_
#include "common/client_cert.h" #include "common/client_cert.h"
#include "common/error_space.h"
#include "common/hash_algorithm.h"
#include "protos/public/errors.pb.h"
namespace widevine { namespace widevine {
//
class KeyboxClientCert : public ClientCert { class KeyboxClientCert : public ClientCert {
public: public:
KeyboxClientCert() {} KeyboxClientCert() {}
@@ -23,6 +25,7 @@ class KeyboxClientCert : public ClientCert {
Status Initialize(const std::string& keybox_token); Status Initialize(const std::string& keybox_token);
Status VerifySignature(const std::string& message, Status VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature, const std::string& signature,
ProtocolVersion protocol_version) const override; ProtocolVersion protocol_version) const override;
@@ -34,6 +37,7 @@ class KeyboxClientCert : public ClientCert {
SignedMessage::SessionKeyType key_type() const override { SignedMessage::SessionKeyType key_type() const override {
return SignedMessage::WRAPPED_AES_KEY; return SignedMessage::WRAPPED_AES_KEY;
} }
bool using_dual_certificate() const override { return false; }
const std::string& serial_number() const override { return serial_number_; } const std::string& serial_number() const override { return serial_number_; }
const std::string& service_id() const override { return unimplemented_; } const std::string& service_id() const override { return unimplemented_; }
const std::string& signing_key() const override { return signing_key_; } const std::string& signing_key() const override { return signing_key_; }
@@ -59,6 +63,14 @@ class KeyboxClientCert : public ClientCert {
const std::multimap<uint32_t, std::string>& keymap); const std::multimap<uint32_t, std::string>& keymap);
static bool IsSystemIdKnown(const uint32_t system_id); static bool IsSystemIdKnown(const uint32_t system_id);
static uint32_t GetSystemId(const std::string& keybox_bytes); static uint32_t GetSystemId(const std::string& keybox_bytes);
Status SystemIdUnknownError() const override {
return Status(error_space, UNSUPPORTED_SYSTEM_ID,
"keybox-unsupported-system-id");
}
Status SystemIdRevokedError() const override {
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
"keybox-system-id-revoked");
}
private: private:
std::string unimplemented_; std::string unimplemented_;

View File

@@ -10,7 +10,9 @@
#define COMMON_MOCK_RSA_KEY_H_ #define COMMON_MOCK_RSA_KEY_H_
#include <string> #include <string>
#include "testing/gmock.h" #include "testing/gmock.h"
#include "common/hash_algorithm.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
namespace widevine { namespace widevine {
@@ -20,12 +22,23 @@ class MockRsaPrivateKey : public RsaPrivateKey {
MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {} MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {}
~MockRsaPrivateKey() override {} ~MockRsaPrivateKey() override {}
MOCK_CONST_METHOD2(Decrypt, bool(const std::string& encrypted_message, MOCK_METHOD(bool, Decrypt,
std::string* decrypted_message)); (const std::string& encrypted_message,
MOCK_CONST_METHOD2(GenerateSignature, std::string* decrypted_message),
bool(const std::string& message, std::string* signature)); (const, override));
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key)); // TODO(b/155438325): remove this function after the below function is fully
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key)); // propagated.
MOCK_METHOD(bool, GenerateSignature,
(const std::string& message, std::string* signature),
(const, override));
MOCK_METHOD(bool, GenerateSignature,
(const std::string& message, HashAlgorithm hash_algorithm,
std::string* signature),
(const, override));
MOCK_METHOD(bool, MatchesPrivateKey, (const RsaPrivateKey& private_key),
(const, override));
MOCK_METHOD(bool, MatchesPublicKey, (const RsaPublicKey& public_key),
(const, override));
private: private:
MockRsaPrivateKey(const MockRsaPrivateKey&) = delete; MockRsaPrivateKey(const MockRsaPrivateKey&) = delete;
@@ -37,12 +50,23 @@ class MockRsaPublicKey : public RsaPublicKey {
MockRsaPublicKey() : RsaPublicKey(RSA_new()) {} MockRsaPublicKey() : RsaPublicKey(RSA_new()) {}
~MockRsaPublicKey() override {} ~MockRsaPublicKey() override {}
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message, MOCK_METHOD(bool, Encrypt,
std::string* encrypted_message)); (const std::string& clear_message,
MOCK_CONST_METHOD2(VerifySignature, bool(const std::string& message, std::string* encrypted_message),
const std::string& signature)); (const, override));
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key)); // TODO(b/155438325): remove this function after the below function is fully
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key)); // propagated.
MOCK_METHOD(bool, VerifySignature,
(const std::string& message, const std::string& signature),
(const, override));
MOCK_METHOD(bool, VerifySignature,
(const std::string& message, HashAlgorithm hash_algorithm,
const std::string& signature),
(const, override));
MOCK_METHOD(bool, MatchesPrivateKey, (const RsaPrivateKey& private_key),
(const, override));
MOCK_METHOD(bool, MatchesPublicKey, (const RsaPublicKey& public_key),
(const, override));
private: private:
MockRsaPublicKey(const MockRsaPublicKey&) = delete; MockRsaPublicKey(const MockRsaPublicKey&) = delete;
@@ -54,16 +78,14 @@ class MockRsaKeyFactory : public RsaKeyFactory {
MockRsaKeyFactory() {} MockRsaKeyFactory() {}
~MockRsaKeyFactory() override {} ~MockRsaKeyFactory() override {}
MOCK_CONST_METHOD1( MOCK_METHOD(std::unique_ptr<RsaPrivateKey>, CreateFromPkcs1PrivateKey,
CreateFromPkcs1PrivateKey, (const std::string& private_key), (const, override));
std::unique_ptr<RsaPrivateKey>(const std::string& private_key)); MOCK_METHOD(std::unique_ptr<RsaPrivateKey>, CreateFromPkcs8PrivateKey,
MOCK_CONST_METHOD2(CreateFromPkcs8PrivateKey, (const std::string& private_key,
std::unique_ptr<RsaPrivateKey>( const std::string& private_key_passphrase),
const std::string& private_key, (const, override));
const std::string& private_key_passphrase)); MOCK_METHOD(std::unique_ptr<RsaPublicKey>, CreateFromPkcs1PublicKey,
MOCK_CONST_METHOD1( (const std::string& public_key), (const, override));
CreateFromPkcs1PublicKey,
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
private: private:
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete; MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;

View File

@@ -0,0 +1,42 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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_PLAYREADY_INTERFACE_H_
#define COMMON_PLAYREADY_INTERFACE_H_
#include <string>
#include "util/error_space.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
class PlayReadyInterface {
public:
PlayReadyInterface() {}
virtual ~PlayReadyInterface() {}
// Sends to a PlayReady Service running the PlayReady license server on
// Windows .
// Args:
// - |challenge| is a std::string which contains PlayReadyLicenseRequest.
// - |policy| is a std::string which contains the PlayReady Policy Setting.
// - |license| is a std::string of PlayReadyLicenseResponse returned from PlayReady
// Service.
// Returns:
// - status code from downstream components.
virtual util::Status SendToPlayReady(
const std::string& playready_challenge, const std::string& provider,
const std::string& content_id,
const std::list<License::KeyContainer>& keys,
const License::Policy& policy, std::string* playready_license) = 0;
};
} // namespace widevine
#endif // COMMON_PLAYREADY_INTERFACE_H_

View File

@@ -0,0 +1,23 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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/playready_sdk_impl.h"
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "util/task/codes.pb.h"
namespace widevine {
// TODO(user): fill in SendToPlayReady function.
util::Status PlayReadySdkImpl::SendToPlayReady(
const std::string& playready_challenge, const std::string& provider,
const std::string& content_id, const std::list<License::KeyContainer>& keys,
const License::Policy& policy, std::string* playready_license) {
return OkStatus;
}
} // namespace widevine

View File

@@ -0,0 +1,28 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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_PLAYREADY_SDK_IMPL_H_
#define COMMON_PLAYREADY_SDK_IMPL_H_
#include "common/playready_interface.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
class PlayReadySdkImpl : public PlayReadyInterface {
public:
PlayReadySdkImpl() : PlayReadyInterface() {}
~PlayReadySdkImpl() override {}
util::Status SendToPlayReady(const std::string& playready_challenge,
const std::string& provider,
const std::string& content_id,
const std::list<License::KeyContainer>& keys,
const License::Policy& policy,
std::string* playready_license) override;
};
} // namespace widevine
#endif // COMMON_PLAYREADY_SDK_IMPL_H_

View File

@@ -16,7 +16,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "base/thread_annotations.h" #include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "common/status.h" #include "common/status.h"
#include "common/x509_cert.h" #include "common/x509_cert.h"

View File

@@ -15,6 +15,7 @@
#include <memory> #include <memory>
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "common/crypto_util.h" #include "common/crypto_util.h"
#include "common/ec_key.h" #include "common/ec_key.h"
@@ -77,11 +78,7 @@ Status RootOfTrustIdGenerator::Generate(uint32_t system_id,
std::string RootOfTrustIdGenerator::GenerateUniqueIdHash( std::string RootOfTrustIdGenerator::GenerateUniqueIdHash(
const std::string& unique_id) const { const std::string& unique_id) const {
if (unique_id.empty()) { return widevine::GenerateUniqueIdHash(unique_id, wv_shared_salt_);
LOG(WARNING) << "unique_id should not be empty.";
return "";
}
return Sha256_Hash(absl::StrCat(unique_id, wv_shared_salt_));
} }
Status RootOfTrustIdDecryptor::DecryptUniqueId( Status RootOfTrustIdDecryptor::DecryptUniqueId(
@@ -104,4 +101,30 @@ Status RootOfTrustIdDecryptor::DecryptUniqueId(
return OkStatus(); return OkStatus();
} }
Status RootOfTrustIdDecryptor::VerifyAndExtractAllValues(
uint32_t system_id, const RootOfTrustId& root_of_trust_id,
std::string* device_unique_id, std::string* device_unique_id_hash) const {
CHECK(device_unique_id != nullptr) << "device_unique_id was null.";
CHECK(device_unique_id_hash != nullptr) << "device_unique_id_hash was null.";
Status status = DecryptUniqueId(
system_id, root_of_trust_id.encrypted_unique_id(), device_unique_id);
if (!status.ok()) {
return status;
}
*device_unique_id_hash =
widevine::GenerateUniqueIdHash(*device_unique_id, wv_shared_salt_);
std::string revocation_hash =
GenerateRotIdHash(root_of_trust_id.encrypted_unique_id(), system_id,
*device_unique_id_hash);
// This should not happen unless there's a bug in the way we issue root of
// trust ids.
if (revocation_hash != root_of_trust_id.unique_id_hash()) {
return Status(error::INVALID_ARGUMENT,
"The generated revocation hash did not match the one in the "
"root_of_trust_id");
}
return OkStatus();
}
} // namespace widevine } // namespace widevine

View File

@@ -38,7 +38,7 @@ class RootOfTrustIdGenerator {
// values. The unique id hash values identify revoked devices and are // values. The unique id hash values identify revoked devices and are
// published in the DCSL and consumed by the License SDK. // published in the DCSL and consumed by the License SDK.
RootOfTrustIdGenerator(std::unique_ptr<EciesEncryptor> ecies_encryptor, RootOfTrustIdGenerator(std::unique_ptr<EciesEncryptor> ecies_encryptor,
std::string wv_shared_salt) const std::string& wv_shared_salt)
: ecies_encryptor_(std::move(ecies_encryptor)), : ecies_encryptor_(std::move(ecies_encryptor)),
wv_shared_salt_(std::move(wv_shared_salt)) {} wv_shared_salt_(std::move(wv_shared_salt)) {}
@@ -73,8 +73,10 @@ class RootOfTrustIdGenerator {
class RootOfTrustIdDecryptor { class RootOfTrustIdDecryptor {
public: public:
explicit RootOfTrustIdDecryptor( explicit RootOfTrustIdDecryptor(
std::unique_ptr<EciesDecryptor> ecies_decryptor) std::unique_ptr<EciesDecryptor> ecies_decryptor,
: ecies_decryptor_(std::move(ecies_decryptor)) {} const std::string& wv_shared_salt)
: ecies_decryptor_(std::move(ecies_decryptor)),
wv_shared_salt_(wv_shared_salt) {}
// Decrypts the |rot_encrypted_id| using the |system_id| as part of the // Decrypts the |rot_encrypted_id| using the |system_id| as part of the
// context. |unique_id| contains the decrypted value on success. // context. |unique_id| contains the decrypted value on success.
@@ -83,8 +85,18 @@ class RootOfTrustIdDecryptor {
Status DecryptUniqueId(uint32_t system_id, const std::string& rot_encrypted_id, Status DecryptUniqueId(uint32_t system_id, const std::string& rot_encrypted_id,
std::string* unique_id) const; std::string* unique_id) const;
// Decrypts the encrypted id within the |root_of_trust_id|, extacting the
// |device_unique_id|, and generating the |device_unique_id_hash|. It then
// generates the rot id revocation hash and verifies that it matches the
// unique_id_hash from the root_of_trust_id.
Status VerifyAndExtractAllValues(uint32_t system_id,
const RootOfTrustId& root_of_trust_id,
std::string* device_unique_id,
std::string* device_unique_id_hash) const;
private: private:
std::unique_ptr<EciesDecryptor> ecies_decryptor_; std::unique_ptr<EciesDecryptor> ecies_decryptor_;
std::string wv_shared_salt_;
}; };
} // namespace widevine } // namespace widevine

View File

@@ -80,8 +80,9 @@ class MockEciesEncryptor : public EciesEncryptor {
ECPublicKey::Create(test_keys.public_test_key_1_secp256r1()); ECPublicKey::Create(test_keys.public_test_key_1_secp256r1());
return new MockEciesEncryptor(std::move(ec_key)); return new MockEciesEncryptor(std::move(ec_key));
} }
MOCK_CONST_METHOD3(Encrypt, bool(const std::string&, const std::string&, MOCK_METHOD(bool, Encrypt,
std::string*)); (const std::string&, const std::string&, std::string*),
(const, override));
private: private:
explicit MockEciesEncryptor(std::unique_ptr<ECPublicKey> ec_key) explicit MockEciesEncryptor(std::unique_ptr<ECPublicKey> ec_key)
@@ -92,7 +93,7 @@ class MockEciesEncryptor : public EciesEncryptor {
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) { TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt); RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor()); RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
// Generate the root of trust id. // Generate the root of trust id.
RootOfTrustId root_of_trust_id; RootOfTrustId root_of_trust_id;
@@ -117,7 +118,7 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) {
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) { TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt); RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor()); RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
std::string rot_encrypted_id; std::string rot_encrypted_id;
std::string rot_id_hash; std::string rot_id_hash;
@@ -208,7 +209,7 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdNullRotIdFail) {
TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdMismatchFails) { TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdMismatchFails) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt); RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor()); RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
// Generate the root of trust id. // Generate the root of trust id.
RootOfTrustId root_of_trust_id; RootOfTrustId root_of_trust_id;
@@ -228,7 +229,7 @@ TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdMismatchFails) {
} }
TEST_F(RootOfTrustIdGeneratorTest, DecryptorBlankUniqueId) { TEST_F(RootOfTrustIdGeneratorTest, DecryptorBlankUniqueId) {
RootOfTrustIdDecryptor decryptor(CreateDecryptor()); RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
// Attempt to decrypt empty encrypted id. // Attempt to decrypt empty encrypted id.
std::string decrypted_unique_id; std::string decrypted_unique_id;
@@ -239,7 +240,7 @@ TEST_F(RootOfTrustIdGeneratorTest, DecryptorBlankUniqueId) {
TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdNullDecryptedIdFails) { TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdNullDecryptedIdFails) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt); RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor()); RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
// Generate the root of trust id. // Generate the root of trust id.
RootOfTrustId root_of_trust_id; RootOfTrustId root_of_trust_id;
@@ -256,4 +257,48 @@ TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdNullDecryptedIdFails) {
"unique_id"); "unique_id");
} }
TEST_F(RootOfTrustIdGeneratorTest, VerifyAndExtractAllValuesSuccess) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
// Generate the root of trust id.
RootOfTrustId root_of_trust_id;
ASSERT_OK(
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_root_of_trust_id_, root_of_trust_id));
// Verify decrypted unique id.
std::string decrypted_unique_id;
std::string decrypted_unique_id_hash;
EXPECT_OK(decryptor.VerifyAndExtractAllValues(kTestSystemId, root_of_trust_id,
&decrypted_unique_id,
&decrypted_unique_id_hash));
EXPECT_EQ(kTestUniqueId, decrypted_unique_id);
EXPECT_EQ(generator.GenerateUniqueIdHash(kTestUniqueId),
decrypted_unique_id_hash);
}
TEST_F(RootOfTrustIdGeneratorTest, VerifyAndExtractAllValuesSystemIdMismatch) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
// Generate the root of trust id.
RootOfTrustId root_of_trust_id;
ASSERT_OK(
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_root_of_trust_id_, root_of_trust_id));
// Verify decrypted unique id.
std::string decrypted_unique_id;
std::string decrypted_unique_id_hash;
EXPECT_EQ(error::INTERNAL,
decryptor
.VerifyAndExtractAllValues(kTestSystemId + 1, root_of_trust_id,
&decrypted_unique_id,
&decrypted_unique_id_hash)
.error_code());
}
} // namespace widevine } // namespace widevine

View File

@@ -29,4 +29,17 @@ std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
return Sha256_Hash(absl::StrCat(salt, system_id, unique_id_hash)); return Sha256_Hash(absl::StrCat(salt, system_id, unique_id_hash));
} }
std::string GenerateUniqueIdHash(const std::string& unique_id,
const std::string& salt) {
if (unique_id.empty()) {
LOG(WARNING) << "unique_id should not be empty.";
return "";
}
if (salt.empty()) {
LOG(WARNING) << "salt should not be empty.";
return "";
}
return widevine::Sha256_Hash(absl::StrCat(unique_id, salt));
}
} // namespace widevine } // namespace widevine

View File

@@ -21,6 +21,14 @@
namespace widevine { namespace widevine {
// Helper function that generates the unique id hash from the |unique_id| and
// the |salt|. |salt| is an internal secret.
//
// Returns the hash value on success.
// If |salt| or |unique_id| are empty, this will return an empty string.
std::string GenerateUniqueIdHash(const std::string& unique_id,
const std::string& salt);
// Helper function that generates the hash for the ROT id from the // Helper function that generates the hash for the ROT id from the
// |unique_id_hash|, the |system_id| and the |salt|. |salt| is typically an // |unique_id_hash|, the |system_id| and the |salt|. |salt| is typically an
// encrypted unique id. Since we use an ephemeral eliptic curve key as part of // encrypted unique id. Since we use an ephemeral eliptic curve key as part of

View File

@@ -19,9 +19,15 @@ namespace {
constexpr char kFakeEncryptedId[] = "fake encrypted id"; constexpr char kFakeEncryptedId[] = "fake encrypted id";
constexpr char kFakeUniqueIdHash[] = "fake unique_id hash"; constexpr char kFakeUniqueIdHash[] = "fake unique_id hash";
constexpr char kFakeUniqueId[] = "fake unique_id";
constexpr char kFakeSecretSalt[] = "fake secret salt";
// This is the ROT ID Hash generated from the fake values. // This is the ROT ID Hash generated from the fake values.
constexpr char kRotIdHashHex[] = constexpr char kRotIdHashHex[] =
"0a757dde0f1080b60f34bf8e46af573ce987b5ed1c831b44952e2feed5243a95"; "0a757dde0f1080b60f34bf8e46af573ce987b5ed1c831b44952e2feed5243a95";
// This is the unique id hash generated from the fake unique id value.
constexpr char kUniqueIdHashHex[] =
"da20922e84b48e52223496f44b07632a4db19d488cd71cf813de300b9d244e06";
constexpr uint32_t kFakeSystemId = 1234; constexpr uint32_t kFakeSystemId = 1234;
constexpr uint32_t kOtherFakeSystemId = 9876; constexpr uint32_t kOtherFakeSystemId = 9876;
@@ -63,4 +69,17 @@ TEST(RotIdUtilTest, GenerateRotIdHashSuccess) {
GenerateRotIdHash(kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash)); GenerateRotIdHash(kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash));
} }
// This test really only ensures the stability of the GenerateUniqueIdHash
// implementation. If the hash ever changes, then it will introduce problems
// into the ecosystem.
TEST(RotIdUtilTest, GenerateUniqueIdHashSuccess) {
ASSERT_EQ(absl::HexStringToBytes(kUniqueIdHashHex),
GenerateUniqueIdHash(kFakeUniqueId, kFakeSecretSalt));
}
TEST(RotIdUtilTest, GenerateUniqueIdHashEmptyValues) {
ASSERT_EQ("", GenerateUniqueIdHash(kFakeUniqueId, ""));
ASSERT_EQ("", GenerateUniqueIdHash("", kFakeSecretSalt));
}
} // namespace widevine } // namespace widevine

View File

@@ -13,7 +13,7 @@
// //
// RSA signature details: // RSA signature details:
// Algorithm: RSASSA-PSS // Algorithm: RSASSA-PSS
// Hash algorithm: SHA1 // Hash algorithm: |hash_algorithm|
// Mask generation function: mgf1SHA1 // Mask generation function: mgf1SHA1
// Salt length: 20 bytes // Salt length: 20 bytes
// Trailer field: 0xbc // Trailer field: 0xbc
@@ -21,7 +21,7 @@
// RSA encryption details: // RSA encryption details:
// Algorithm: RSA-OAEP // Algorithm: RSA-OAEP
// Mask generation function: mgf1SHA1 // Mask generation function: mgf1SHA1
// Label (encoding paramter): empty std::string // Label (encoding parameter): empty std::string
#include "common/rsa_key.h" #include "common/rsa_key.h"
@@ -31,6 +31,7 @@
#include "openssl/evp.h" #include "openssl/evp.h"
#include "openssl/rsa.h" #include "openssl/rsa.h"
#include "openssl/sha.h" #include "openssl/sha.h"
#include "common/hash_algorithm.h"
#include "common/rsa_util.h" #include "common/rsa_util.h"
#include "common/sha_util.h" #include "common/sha_util.h"
@@ -51,6 +52,21 @@ std::string OpenSSLErrorString(uint32_t error) {
return buf; return buf;
} }
std::string GetMessageDigest(const std::string& message,
widevine::HashAlgorithm hash_algorithm) {
switch (hash_algorithm) {
// The default hash algorithm of RSA signature is SHA1.
case widevine::HashAlgorithm::kUnspecified:
case widevine::HashAlgorithm::kSha1:
return widevine::Sha1_Hash(message);
case widevine::HashAlgorithm::kSha256:
return widevine::Sha256_Hash(message);
}
LOG(FATAL) << "Unexpected hash algorithm: "
<< static_cast<int>(hash_algorithm);
return "";
}
} // namespace } // namespace
namespace widevine { namespace widevine {
@@ -137,6 +153,47 @@ bool RsaPrivateKey::GenerateSignature(const std::string& message,
return true; return true;
} }
bool RsaPrivateKey::GenerateSignature(const std::string& message,
HashAlgorithm hash_algorithm,
std::string* signature) const {
DCHECK(signature);
if (message.empty()) {
LOG(ERROR) << "Message to be signed is empty";
return false;
}
// Hash the message using corresponding hash algorithm.
std::string message_digest = GetMessageDigest(message, hash_algorithm);
if (message_digest.empty()) {
LOG(ERROR) << "Empty message digest";
return false;
}
// Add PSS padding.
size_t rsa_size = RSA_size(key_);
std::string padded_digest(rsa_size, 0);
if (!RSA_padding_add_PKCS1_PSS_mgf1(
key_, reinterpret_cast<unsigned char*>(&padded_digest[0]),
reinterpret_cast<unsigned char*>(&message_digest[0]), EVP_sha1(),
EVP_sha1(), kPssSaltLength)) {
LOG(ERROR) << "RSA padding failure: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
// Encrypt PSS padded digest.
signature->assign(rsa_size, 0);
if (RSA_private_encrypt(padded_digest.size(),
reinterpret_cast<unsigned char*>(&padded_digest[0]),
reinterpret_cast<unsigned char*>(&(*signature)[0]),
key_, RSA_NO_PADDING) !=
static_cast<int>(signature->size())) {
LOG(ERROR) << "RSA private encrypt failure: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
}
bool RsaPrivateKey::GenerateSignatureSha256Pkcs7(const std::string& message, bool RsaPrivateKey::GenerateSignatureSha256Pkcs7(const std::string& message,
std::string* signature) const { std::string* signature) const {
DCHECK(signature); DCHECK(signature);
@@ -253,6 +310,52 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
return true; return true;
} }
bool RsaPublicKey::VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature) const {
if (message.empty()) {
LOG(ERROR) << "Signed message is empty";
return false;
}
size_t rsa_size = RSA_size(key_);
if (signature.size() != rsa_size) {
LOG(ERROR) << "Message signature is of the wrong size (expected "
<< rsa_size << ", actual " << signature.size() << ")";
return false;
}
// Decrypt the signature.
std::string padded_digest(signature.size(), 0);
if (RSA_public_decrypt(
signature.size(),
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(signature.data())),
reinterpret_cast<unsigned char*>(&padded_digest[0]), key_,
RSA_NO_PADDING) != static_cast<int>(rsa_size)) {
LOG(ERROR) << "RSA public decrypt failure: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
// Hash the message using SHA1 using corresponding hash algorithm.
std::string message_digest = GetMessageDigest(message, hash_algorithm);
if (message_digest.empty()) {
LOG(ERROR) << "Empty message digest";
return false;
}
// Verify PSS padding.
if (RSA_verify_PKCS1_PSS_mgf1(
key_, reinterpret_cast<unsigned char*>(&message_digest[0]),
EVP_sha1(), EVP_sha1(),
reinterpret_cast<unsigned char*>(&padded_digest[0]),
kPssSaltLength) == 0) {
LOG(ERROR) << "RSA Verify PSS padding failure: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
}
bool RsaPublicKey::VerifySignatureSha256Pkcs7( bool RsaPublicKey::VerifySignatureSha256Pkcs7(
const std::string& message, const std::string& signature) const { const std::string& message, const std::string& signature) const {
if (message.empty()) { if (message.empty()) {

View File

@@ -18,7 +18,9 @@
#include <string> #include <string>
#include <cstdint> #include <cstdint>
#include "absl/base/macros.h"
#include "openssl/rsa.h" #include "openssl/rsa.h"
#include "common/hash_algorithm.h"
namespace widevine { namespace widevine {
@@ -41,9 +43,20 @@ class RsaPrivateKey {
// Generate RSSASSA-PSS signature. Caller retains ownership of all parameters. // Generate RSSASSA-PSS signature. Caller retains ownership of all parameters.
// Returns true if successful, false otherwise. // Returns true if successful, false otherwise.
// TODO(b/155438325): remove this function after the below function is fully
// propagated.
ABSL_DEPRECATED(
"Use the below function with |hash_algorithm| argument instead.")
virtual bool GenerateSignature(const std::string& message, virtual bool GenerateSignature(const std::string& message,
std::string* signature) const; std::string* signature) const;
// Generate RSSASSA-PSS signature. Caller retains ownership of all parameters.
// |hash_algorithm| indicates the hash algorithm used. Returns true if
// successful, false otherwise.
virtual bool GenerateSignature(const std::string& message,
HashAlgorithm hash_algorithm,
std::string* signature) const;
// Generate SHA256 digest, PKCS#7 padded signature. Caller retains ownership // Generate SHA256 digest, PKCS#7 padded signature. Caller retains ownership
// of all parameters. Returns true if successful, false otherwise. // of all parameters. Returns true if successful, false otherwise.
virtual bool GenerateSignatureSha256Pkcs7(const std::string& message, virtual bool GenerateSignatureSha256Pkcs7(const std::string& message,
@@ -98,9 +111,20 @@ class RsaPublicKey {
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters. // Verify RSSASSA-PSS signature. Caller retains ownership of all parameters.
// Returns true if validation succeeds, false otherwise. // Returns true if validation succeeds, false otherwise.
// TODO(b/155438325): remove this function after the below function is fully
// propagated.
ABSL_DEPRECATED(
"Use the below function with |hash_algorithm| argument instead.")
virtual bool VerifySignature(const std::string& message, virtual bool VerifySignature(const std::string& message,
const std::string& signature) const; const std::string& signature) const;
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters.
// |hash_algorithm| indicates the hash algorithm used. Returns true if
// validation succeeds, false otherwise.
virtual bool VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature) const;
// Verify a signature. This method takes two parameters: |message| which is a // Verify a signature. This method takes two parameters: |message| which is a
// std::string containing the data which was signed, and |signature| which is a // std::string containing the data which was signed, and |signature| which is a
// std::string containing the message SHA256 digest signature with PKCS#7 // std::string containing the message SHA256 digest signature with PKCS#7

View File

@@ -10,10 +10,11 @@
// Description: // Description:
// Unit test for rsa_key RSA encryption and signing. // Unit test for rsa_key RSA encryption and signing.
#include "common/rsa_key.h"
#include <memory> #include <memory>
#include "testing/gunit.h" #include "testing/gunit.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h" #include "common/rsa_test_keys.h"
#include "common/rsa_util.h" #include "common/rsa_util.h"
@@ -46,8 +47,7 @@ TEST_F(RsaKeyTest, CopyConstructor) {
std::unique_ptr<RsaPrivateKey> private_key_copy( std::unique_ptr<RsaPrivateKey> private_key_copy(
new RsaPrivateKey(*private_key)); new RsaPrivateKey(*private_key));
std::unique_ptr<RsaPublicKey> public_key_copy( std::unique_ptr<RsaPublicKey> public_key_copy(new RsaPublicKey(*public_key));
new RsaPublicKey(*public_key));
EXPECT_TRUE(public_key_copy->MatchesPublicKey(*public_key)); EXPECT_TRUE(public_key_copy->MatchesPublicKey(*public_key));
EXPECT_TRUE(public_key_copy->MatchesPrivateKey(*private_key)); EXPECT_TRUE(public_key_copy->MatchesPrivateKey(*private_key));

View File

@@ -11,8 +11,12 @@
#include <vector> #include <vector>
#include "glog/logging.h"
#include "google/protobuf/text_format.h"
#include "common/client_id_util.h" #include "common/client_id_util.h"
#include "common/device_status_list.h"
#include "protos/public/client_identification.pb.h" #include "protos/public/client_identification.pb.h"
#include "protos/public/device_certificate_status.pb.h"
#include "protos/public/device_common.pb.h" #include "protos/public/device_common.pb.h"
#include "protos/public/provisioned_device_info.pb.h" #include "protos/public/provisioned_device_info.pb.h"
#include "protos/public/security_profile.pb.h" #include "protos/public/security_profile.pb.h"
@@ -23,98 +27,44 @@ using ClientCapabilities = ClientIdentification::ClientCapabilities;
SecurityProfileList::SecurityProfileList(const std::string& profile_namespace) SecurityProfileList::SecurityProfileList(const std::string& profile_namespace)
: profile_namespace_(profile_namespace) {} : profile_namespace_(profile_namespace) {}
int SecurityProfileList::Init() { return AddDefaultProfiles(); } int SecurityProfileList::Init() { return 0; }
int SecurityProfileList::AddDefaultProfiles() { int SecurityProfileList::GetQualifiedProfilesFromSpecifiedProfiles(
const uint32_t oemcrypto_8 = 8;
const uint32_t oemcrypto_12 = 12;
const bool make_model_not_verified = false;
SecurityProfile profile;
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_1, "WVSP1",
ClientCapabilities::HDCP_NONE,
ClientCapabilities::ANALOG_OUTPUT_UNKNOWN, oemcrypto_8,
make_model_not_verified, ProvisionedDeviceInfo::LEVEL_3,
kResourceTierLow, &profile);
InsertProfile(profile);
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_2, "WVSP2",
ClientCapabilities::HDCP_NONE,
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A,
oemcrypto_12, make_model_not_verified,
ProvisionedDeviceInfo::LEVEL_2, kResourceTierLow, &profile);
InsertProfile(profile);
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_3, "WVSP3",
ClientCapabilities::HDCP_V1,
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A,
oemcrypto_12, make_model_not_verified,
ProvisionedDeviceInfo::LEVEL_1, kResourceTierMed, &profile);
InsertProfile(profile);
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_4, "WVSP4",
ClientCapabilities::HDCP_V2_2,
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A,
oemcrypto_12, make_model_not_verified,
ProvisionedDeviceInfo::LEVEL_1, kResourceTierHigh, &profile);
InsertProfile(profile);
absl::ReaderMutexLock lock(&mutex_);
return security_profiles_.size();
}
SecurityProfile::Level SecurityProfileList::GetBestProfileLevel(
const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info,
SecurityProfile::DrmInfo* drm_info) const {
// Iterate through each SP starting from the strictest first.
absl::ReaderMutexLock lock(&mutex_);
// Profile list is assumed to be sorted.
for (auto& profile : security_profiles_) {
if (!IsProfileAllowed(profile, client_id, device_info)) {
continue;
}
if (drm_info != nullptr) {
GetDrmInfo(client_id, device_info, drm_info);
}
return profile.level();
}
return SecurityProfile::SECURITY_PROFILE_LEVEL_UNDEFINED;
}
int SecurityProfileList::GetAllowedProfilesFromList(
const std::vector<std::string>& profiles_to_check, const std::vector<std::string>& profiles_to_check,
const ClientIdentification& client_id, const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info, const ProvisionedDeviceInfo& device_info,
std::vector<std::string>* profiles_to_allow) const { std::vector<std::string>* qualified_profiles) const {
if (profiles_to_allow == nullptr) { if (qualified_profiles == nullptr) {
return 0; return 0;
} }
qualified_profiles->clear();
absl::ReaderMutexLock lock(&mutex_); absl::ReaderMutexLock lock(&mutex_);
for (auto& profile_name : profiles_to_check) { for (auto& profile_name : profiles_to_check) {
SecurityProfile profile; SecurityProfile profile;
if (GetProfileByName(profile_name, &profile)) { if (GetProfileByName(profile_name, &profile)) {
if (IsProfileAllowed(profile, client_id, device_info)) { if (DoesProfileQualify(profile, client_id, device_info)) {
profiles_to_allow->push_back(profile.name()); qualified_profiles->push_back(profile.name());
} }
} }
} }
return profiles_to_allow->size(); return qualified_profiles->size();
} }
int SecurityProfileList::GetAllowedProfiles( int SecurityProfileList::GetQualifiedProfiles(
const ClientIdentification& client_id, const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info, const ProvisionedDeviceInfo& device_info,
std::vector<std::string>* profiles_to_allow) const { std::vector<std::string>* qualified_profiles) const {
if (profiles_to_allow == nullptr) { if (qualified_profiles == nullptr) {
return 0; return 0;
} }
qualified_profiles->clear();
absl::ReaderMutexLock lock(&mutex_); absl::ReaderMutexLock lock(&mutex_);
for (auto& profile : security_profiles_) { for (auto& profile : security_profiles_) {
if (IsProfileAllowed(profile, client_id, device_info)) { if (DoesProfileQualify(profile, client_id, device_info)) {
profiles_to_allow->push_back(profile.name()); qualified_profiles->push_back(profile.name());
} }
} }
return profiles_to_allow->size(); return qualified_profiles->size();
} }
bool SecurityProfileList::GetDrmInfo(const ClientIdentification& client_id, bool SecurityProfileList::GetDrmInfo(const ClientIdentification& client_id,
@@ -127,62 +77,65 @@ bool SecurityProfileList::GetDrmInfo(const ClientIdentification& client_id,
client_id.client_capabilities().max_hdcp_version()); client_id.client_capabilities().max_hdcp_version());
drm_info->mutable_output()->set_analog_output_capabilities( drm_info->mutable_output()->set_analog_output_capabilities(
client_id.client_capabilities().analog_output_capabilities()); client_id.client_capabilities().analog_output_capabilities());
drm_info->mutable_security()->set_oemcrypto_version( drm_info->mutable_security()->set_oemcrypto_api_version(
client_id.client_capabilities().oem_crypto_api_version()); client_id.client_capabilities().oem_crypto_api_version());
drm_info->mutable_security()->set_resource_rating_tier( drm_info->mutable_security()->set_resource_rating_tier(
client_id.client_capabilities().resource_rating_tier()); client_id.client_capabilities().resource_rating_tier());
drm_info->mutable_security()->set_security_level( drm_info->mutable_security()->set_security_level(
device_info.security_level()); device_info.security_level());
drm_info->mutable_security()->set_request_model_info_status(false);
drm_info->mutable_request_model_info()->set_manufacturer( drm_info->mutable_request_model_info()->set_manufacturer(
GetClientInfo(client_id, kModDrmMake)); GetClientInfo(client_id, kModDrmMake));
drm_info->mutable_request_model_info()->set_model_name( drm_info->mutable_request_model_info()->set_model_name(
GetClientInfo(client_id, kModDrmModel)); GetClientInfo(client_id, kModDrmModel));
drm_info->set_system_id(device_info.system_id()); drm_info->mutable_request_model_info()->set_status(
return true; DeviceModel::MODEL_STATUS_UNVERIFIED);
} for (const auto& model_info : device_info.model_info()) {
if (model_info.manufacturer() ==
bool SecurityProfileList::PopulateProfile( drm_info->request_model_info().manufacturer() &&
const SecurityProfile::Level profile_level, const std::string& profile_name, model_info.model_name() ==
const ClientCapabilities::HdcpVersion min_hdcp_version, drm_info->request_model_info().model_name()) {
const ClientCapabilities::AnalogOutputCapabilities drm_info->mutable_request_model_info()->set_status(model_info.status());
analog_output_capabilities, drm_info->mutable_request_model_info()->set_model_year(
const uint32_t min_oemcrypto_version, const bool make_model_verified, model_info.model_year());
const ProvisionedDeviceInfo::WvSecurityLevel security_level, break;
const uint32_t resource_rating_tier,
SecurityProfile* profile_to_create) const {
if (profile_to_create == nullptr) {
return false;
}
profile_to_create->set_level(profile_level);
profile_to_create->set_name(profile_name);
profile_to_create->mutable_min_output_requirements()->set_hdcp_version(
min_hdcp_version);
profile_to_create->mutable_min_output_requirements()
->set_analog_output_capabilities(analog_output_capabilities);
profile_to_create->mutable_min_security_requirements()->set_oemcrypto_version(
min_oemcrypto_version);
profile_to_create->mutable_min_security_requirements()->set_security_level(
security_level);
profile_to_create->mutable_min_security_requirements()
->set_resource_rating_tier(resource_rating_tier);
profile_to_create->mutable_min_security_requirements()
->set_request_model_info_status(make_model_verified);
return true;
}
bool SecurityProfileList::GetProfileByLevel(
SecurityProfile::Level level, SecurityProfile* security_profile) const {
absl::ReaderMutexLock lock(&mutex_);
for (auto& profile : security_profiles_) {
if (profile.level() == level) {
if (security_profile != nullptr) {
*security_profile = profile;
}
return true;
} }
} }
return false; drm_info->set_system_id(device_info.system_id());
SecurityProfile::ClientInfo* client_info = drm_info->mutable_client_info();
client_info->set_device_name(GetClientInfo(client_id, kModDrmDeviceName));
SecurityProfile::ClientInfo::ProductInfo* product_info =
client_info->mutable_product_info();
product_info->set_product_name(GetClientInfo(client_id, kModDrmProductName));
product_info->set_build_info(GetClientInfo(client_id, kModDrmBuildInfo));
product_info->set_oem_crypto_security_patch_level(
GetClientInfo(client_id, kModDrmOemCryptoSecurityPatchLevel));
// TODO(user): Figure out how to get device platform pushed into SPL API.
DeviceCertificateStatus device_certificate_status;
SecurityProfile::DeviceState device_model_state =
SecurityProfile::DEVICE_STATE_UNKNOWN;
Status status =
DeviceStatusList::Instance()->GetDeviceCertificateStatusBySystemId(
device_info.system_id(), &device_certificate_status);
if (status.ok() && device_certificate_status.has_status()) {
switch (device_certificate_status.status()) {
case DeviceCertificateStatus::STATUS_IN_TESTING:
device_model_state = SecurityProfile::IN_TESTING;
break;
case DeviceCertificateStatus::STATUS_RELEASED:
device_model_state = SecurityProfile::RELEASED;
break;
case DeviceCertificateStatus::STATUS_TEST_ONLY:
device_model_state = SecurityProfile::TEST_ONLY;
break;
case DeviceCertificateStatus::STATUS_REVOKED:
device_model_state = SecurityProfile::REVOKED;
break;
default:
break;
}
}
drm_info->set_device_model_state(device_model_state);
return true;
} }
bool SecurityProfileList::GetProfileByName( bool SecurityProfileList::GetProfileByName(
@@ -202,49 +155,91 @@ bool SecurityProfileList::GetProfileByName(
bool SecurityProfileList::InsertProfile( bool SecurityProfileList::InsertProfile(
const SecurityProfile& profile_to_insert) { const SecurityProfile& profile_to_insert) {
// Check if profile already exist. // Check if profile already exist.
if (GetProfileByLevel(profile_to_insert.level(), nullptr)) {
return false;
}
if (GetProfileByName(profile_to_insert.name(), nullptr)) { if (GetProfileByName(profile_to_insert.name(), nullptr)) {
LOG(ERROR) << "Unable to insert profile: " << profile_to_insert.name()
<< ". Name already exist.";
return false; return false;
} }
if (profile_to_insert.min_security_requirements().security_level() ==
ProvisionedDeviceInfo::LEVEL_UNSPECIFIED) {
LOG(ERROR) << "Unable to insert profile: " << profile_to_insert.name()
<< ". Security level not specified.";
return false;
}
absl::WriterMutexLock lock(&mutex_); absl::WriterMutexLock lock(&mutex_);
security_profiles_.push_back(profile_to_insert); security_profiles_.push_back(profile_to_insert);
sort(security_profiles_.begin(), security_profiles_.end(),
CompareProfileLevel);
return true; return true;
} }
bool SecurityProfileList::CompareProfileLevel(const SecurityProfile& p1, int SecurityProfileList::NumProfiles() const {
const SecurityProfile& p2) { absl::ReaderMutexLock lock(&mutex_);
// Profiles are sorted from highest to lowest (strictest) level. return security_profiles_.size();
return (p1.level() > p2.level());
} }
bool SecurityProfileList::IsProfileAllowed( void SecurityProfileList::ClearAllProfiles() {
absl::WriterMutexLock lock(&mutex_);
security_profiles_.clear();
}
bool SecurityProfileList::DoesProfileQualify(
const SecurityProfile& profile, const ClientIdentification& client_id, const SecurityProfile& profile, const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info) const { const ProvisionedDeviceInfo& device_info) const {
if (profile.min_security_requirements().security_level() < if (profile.min_security_requirements().security_level() <
device_info.security_level()) { device_info.security_level()) {
VLOG(1) << "Profile does not qualify <" << profile.name()
<< "> security level: "
<< profile.min_security_requirements().security_level()
<< ", device: " << device_info.security_level();
return false; return false;
} }
if (profile.min_security_requirements().oemcrypto_version() > if (profile.min_security_requirements().oemcrypto_api_version() >
client_id.client_capabilities().oem_crypto_api_version()) { client_id.client_capabilities().oem_crypto_api_version()) {
VLOG(1) << "Profile does not qualify <" << profile.name()
<< "> oemcrypto version: "
<< profile.min_security_requirements().oemcrypto_api_version()
<< ", device: "
<< client_id.client_capabilities().oem_crypto_api_version();
return false; return false;
} }
if (profile.min_output_requirements().hdcp_version() > if (profile.min_output_requirements().hdcp_version() >
client_id.client_capabilities().max_hdcp_version()) { client_id.client_capabilities().max_hdcp_version()) {
VLOG(1) << "profile does not qualify <" << profile.name()
<< "> hdcp_version: "
<< profile.min_output_requirements().hdcp_version() << ", device: "
<< client_id.client_capabilities().max_hdcp_version();
return false; return false;
} }
if (profile.min_output_requirements().analog_output_capabilities() > if (profile.min_output_requirements().analog_output_capabilities() >
client_id.client_capabilities().analog_output_capabilities()) { client_id.client_capabilities().analog_output_capabilities()) {
VLOG(1) << "Profile idoes not qualify <" << profile.name()
<< "> analog output: "
<< profile.min_output_requirements().analog_output_capabilities()
<< ", device: "
<< client_id.client_capabilities().analog_output_capabilities();
return false; return false;
} }
if (profile.min_security_requirements().resource_rating_tier() > if (profile.min_security_requirements().resource_rating_tier() >
client_id.client_capabilities().resource_rating_tier()) { client_id.client_capabilities().resource_rating_tier()) {
VLOG(1) << "Profile does not qualify <" << profile.name()
<< "> resource rating tier: "
<< profile.min_security_requirements().resource_rating_tier()
<< ", device: "
<< client_id.client_capabilities().resource_rating_tier();
return false; return false;
} }
return true; return true;
} }
void SecurityProfileList::GetProfileNames(
std::vector<std::string>* profile_names) const {
if (profile_names == nullptr) {
return;
}
absl::ReaderMutexLock lock(&mutex_);
for (auto& profile : security_profiles_) {
profile_names->push_back(profile.name());
}
}
} // namespace widevine } // namespace widevine

View File

@@ -7,25 +7,22 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Description: // Description:
// Container of Widevine security profiles. Security profiles indicate the // Container of device security profiles. Security profiles indicate rules
// level of security of a device based on the device's output protections, // to allow using the profile. The rules are based on DRM capabilities of a
// version of OEMCrypto and security level. // device.
#ifndef COMMON_SECURITY_PROFILE_LIST_H_ #ifndef COMMON_SECURITY_PROFILE_LIST_H_
#define COMMON_SECURITY_PROFILE_LIST_H_ #define COMMON_SECURITY_PROFILE_LIST_H_
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "protos/public/client_identification.pb.h" #include "protos/public/client_identification.pb.h"
#include "protos/public/device_security_profile_data.pb.h"
#include "protos/public/provisioned_device_info.pb.h" #include "protos/public/provisioned_device_info.pb.h"
#include "protos/public/security_profile.pb.h" #include "protos/public/security_profile.pb.h"
namespace widevine { namespace widevine {
using ClientCapabilities = ClientIdentification::ClientCapabilities; using ClientCapabilities = ClientIdentification::ClientCapabilities;
const uint32_t kResourceTierLow = 1;
const uint32_t kResourceTierMed = 2;
const uint32_t kResourceTierHigh = 3;
// The SecurityProfileList will hold all security profiles. During license // The SecurityProfileList will hold all security profiles. During license
// acquisition, information from the client and information from the server are // acquisition, information from the client and information from the server are
// combined to deternmine the device's security profile level. // combined to deternmine the device's security profile level.
@@ -33,57 +30,31 @@ const uint32_t kResourceTierHigh = 3;
class SecurityProfileList { class SecurityProfileList {
public: public:
explicit SecurityProfileList(const std::string& profile_namespace); explicit SecurityProfileList(const std::string& profile_namespace);
~SecurityProfileList() {} virtual ~SecurityProfileList() {}
// Initialize the security profile list. The list is initially empty, this // Initialize the security profile list. The size of the profile list is
// function will populate the list with default profiles. The size of the // returned.
// list is returned. virtual int Init();
int Init();
// Add the specified profile to the existing list of profiles. Returns true // Add the specified profile to the existing list of profiles. Returns true
// if successfully inserted, false if unable to insert. // if successfully inserted, false if unable to insert.
bool InsertProfile(const SecurityProfile& profile_to_insert); bool InsertProfile(const SecurityProfile& profile_to_insert);
// Populate |profile_to_create| with the specified output protections and
// security parameters. All input parameters are used hence should be set.
bool PopulateProfile(
const SecurityProfile::Level profile_level,
const std::string& profile_name,
const ClientCapabilities::HdcpVersion min_hdcp_version,
const ClientCapabilities::AnalogOutputCapabilities
analog_output_capabilities,
const uint32_t min_oemcrypto_version, const bool make_model_verified,
const ProvisionedDeviceInfo::WvSecurityLevel security_level,
const uint32_t resource_rating_tier,
SecurityProfile* profile_to_create) const;
// Return the highest security level based on the device capabilities.
// If |drm_info| is not null, |drm_info| is populated with the device data.
SecurityProfile::Level GetBestProfileLevel(
const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info,
SecurityProfile::DrmInfo* drm_info) const;
// Populates |profiles_to_allow| with a list of profiles that meet the
// requirements for the this device. The number of profiles is returned.
int GetAllowedProfiles(const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info,
std::vector<std::string>* profiles_to_allow) const;
// Populates |profiles_allow| with a list of profiles from the specified // Populates |profiles_allow| with a list of profiles from the specified
// |profiles_to_check| list that meet the requirements for the this device. // |profiles_to_check| list that meet the requirements for the this device.
// The number of profiles is returned. // The number of profiles is returned.
int GetAllowedProfilesFromList( virtual int GetQualifiedProfilesFromSpecifiedProfiles(
const std::vector<std::string>& profiles_to_check, const std::vector<std::string>& profiles_to_check,
const ClientIdentification& client_id, const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info, const ProvisionedDeviceInfo& device_info,
std::vector<std::string>* profiles_to_allow) const; std::vector<std::string>* qualified_profiles) const;
// Return true if a profile exist matching the specified |level|. // Populates |profiles_to_allow| with a list of profiles that meet the
// |security_profile| is owned by the caller and is populated if a profile // requirements for the this device. The number of profiles is returned.
// exist. virtual int GetQualifiedProfiles(
bool GetProfileByLevel(SecurityProfile::Level level, const ClientIdentification& client_id,
SecurityProfile* security_profile) const; const ProvisionedDeviceInfo& device_info,
std::vector<std::string>* qualified_profiles) const;
// Return true if a profile exist matching the specified |name|. // Return true if a profile exist matching the specified |name|.
// |security_profile| is owned by the caller and is populated if a profile // |security_profile| is owned by the caller and is populated if a profile
@@ -97,17 +68,20 @@ class SecurityProfileList {
const ProvisionedDeviceInfo& device_info, const ProvisionedDeviceInfo& device_info,
SecurityProfile::DrmInfo* drm_info) const; SecurityProfile::DrmInfo* drm_info) const;
// Return the number of profiles in the list.
int NumProfiles() const;
// Return a list of profile names.
virtual void GetProfileNames(std::vector<std::string>* profile_names) const;
protected:
void ClearAllProfiles();
private: private:
// Initialize the list with Widevine default profiles. The size of the bool DoesProfileQualify(const SecurityProfile& profile,
// profile list after the additions is returned. const ClientIdentification& client_id,
int AddDefaultProfiles(); const ProvisionedDeviceInfo& device_info) const;
static bool CompareProfileLevel(const SecurityProfile& p1,
const SecurityProfile& p2);
bool IsProfileAllowed(const SecurityProfile& profile,
const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info) const;
mutable absl::Mutex mutex_; mutable absl::Mutex mutex_;
// Security profiles // Security profiles

View File

@@ -13,6 +13,7 @@
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "common/client_id_util.h"
#include "protos/public/device_common.pb.h" #include "protos/public/device_common.pb.h"
#include "protos/public/security_profile.pb.h" #include "protos/public/security_profile.pb.h"
@@ -23,7 +24,16 @@ const char kMakeName[] = "company_name";
const char kMakeValue[] = "Google"; const char kMakeValue[] = "Google";
const char kModelName[] = "model_name"; const char kModelName[] = "model_name";
const char kModelValue[] = "model1"; const char kModelValue[] = "model1";
const char kDeviceNameValue[] = "TestDeviceName";
const char kProductNameValue[] = "TestProductName";
const char kBuildInfoValue[] = "TestBuildInfo";
const char kOemCryptoSecurityPatchLevelValue[] =
"TestOemCryptoSecurityPatchLevel";
const char kDefaultContentOwnerName[] = "Widevine";
const uint32_t kSystemId = 1234; const uint32_t kSystemId = 1234;
const uint32_t kResourceTierLow = 1;
const uint32_t kResourceTierMed = 2;
const uint32_t kResourceTierHigh = 3;
class SecurityProfileListTest : public ::testing::Test { class SecurityProfileListTest : public ::testing::Test {
public: public:
@@ -32,75 +42,71 @@ class SecurityProfileListTest : public ::testing::Test {
void SetUp() override { void SetUp() override {
const uint32_t oemcrypto_12 = 12; const uint32_t oemcrypto_12 = 12;
const bool make_model_not_verified = false; SecurityProfile profile;
const ClientIdentification::ClientCapabilities::HdcpVersion hdcp_version = std::string profile_namespace = "widevine";
ClientCapabilities::HDCP_V2_2;
test_profile_1_.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_1);
test_profile_1_.mutable_min_output_requirements()->set_hdcp_version(
hdcp_version);
test_profile_1_.mutable_min_output_requirements()
->set_analog_output_capabilities(
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
test_profile_1_.mutable_min_security_requirements()->set_oemcrypto_version(
oemcrypto_12);
test_profile_1_.mutable_min_security_requirements()->set_security_level(
ProvisionedDeviceInfo::LEVEL_1);
test_profile_1_.mutable_min_security_requirements()
->set_resource_rating_tier(kResourceTierHigh);
test_profile_1_.mutable_min_security_requirements()
->set_request_model_info_status(make_model_not_verified);
std::string profile_namespace = "widevine_test";
profile_list_ = absl::make_unique<SecurityProfileList>(profile_namespace); profile_list_ = absl::make_unique<SecurityProfileList>(profile_namespace);
ClientIdentification_NameValue *nv = client_id_.add_client_info(); AddClientInfo(&client_id_, kMakeName, kMakeValue);
nv->set_name(kMakeName); AddClientInfo(&client_id_, kModelName, kModelValue);
nv->set_value(kMakeValue); AddClientInfo(&client_id_, kModDrmDeviceName, kDeviceNameValue);
nv = client_id_.add_client_info(); AddClientInfo(&client_id_, kModDrmProductName, kProductNameValue);
nv->set_name(kModelName); AddClientInfo(&client_id_, kModDrmBuildInfo, kBuildInfoValue);
nv->set_value(kModelValue); AddClientInfo(&client_id_, kModDrmOemCryptoSecurityPatchLevel,
kOemCryptoSecurityPatchLevelValue);
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version( client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(
oemcrypto_12); oemcrypto_12);
client_id_.mutable_client_capabilities()->set_max_hdcp_version( client_id_.mutable_client_capabilities()->set_max_hdcp_version(
hdcp_version); ClientCapabilities::HDCP_V2_2);
client_id_.mutable_client_capabilities()->set_resource_rating_tier( client_id_.mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierHigh); kResourceTierHigh);
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1); device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
device_info_.set_system_id(kSystemId); device_info_.set_system_id(kSystemId);
} }
SecurityProfile test_profile_1_;
std::unique_ptr<SecurityProfileList> profile_list_; std::unique_ptr<SecurityProfileList> profile_list_;
ClientIdentification client_id_; ClientIdentification client_id_;
ProvisionedDeviceInfo device_info_; ProvisionedDeviceInfo device_info_;
}; };
TEST_F(SecurityProfileListTest, InsertProfile) { TEST_F(SecurityProfileListTest, InsertProfile) {
// This test will not initialize the SecurityProfileList, hence it's empty. // Insert test profile1 into the list.
// Insert test profile 1 into the list. SecurityProfileList profile_list("widevine-test");
EXPECT_TRUE(profile_list_->InsertProfile(test_profile_1_)); SecurityProfile profile1;
// Should not allow insertion of an already existing level. profile1.set_name("profile1");
EXPECT_FALSE(profile_list_->InsertProfile(test_profile_1_)); profile1.mutable_min_security_requirements()->set_security_level(
SecurityProfile profile; ProvisionedDeviceInfo::LEVEL_3);
// Should not allow insertion of an already existing name. EXPECT_TRUE(profile_list.InsertProfile(profile1));
// Make sure the level is not the same as already inserted level_1. // Verify the list still has one profile.
profile.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_2); EXPECT_EQ(1, profile_list.NumProfiles());
profile.set_name(test_profile_1_.name()); // Should not allow insert if existing profile has the same name.
EXPECT_FALSE(profile_list_->InsertProfile(test_profile_1_)); SecurityProfile profile2;
ASSERT_TRUE(profile_list_->GetProfileByLevel( profile2.set_name(profile1.name());
SecurityProfile::SECURITY_PROFILE_LEVEL_1, &profile)); profile2.mutable_min_security_requirements()->set_security_level(
EXPECT_TRUE( ProvisionedDeviceInfo::LEVEL_3);
google::protobuf::util::MessageDifferencer::Equals(test_profile_1_, profile)); EXPECT_FALSE(profile_list.InsertProfile(profile2));
// Verify the list still has one profile.
EXPECT_EQ(1, profile_list.NumProfiles());
// Should allow insert since this profile has a different name.
profile2.set_name("profile2");
EXPECT_TRUE(profile_list.InsertProfile(profile2));
EXPECT_EQ(2, profile_list.NumProfiles());
} }
TEST_F(SecurityProfileListTest, GetDrmInfo) { TEST_F(SecurityProfileListTest, GetDrmInfo) {
SecurityProfile::DrmInfo drm_info; SecurityProfile::DrmInfo drm_info;
DeviceModel* device_model = device_info_.add_model_info();
device_model->set_manufacturer(GetClientInfo(client_id_, kModDrmMake));
device_model->set_model_name(GetClientInfo(client_id_, kModDrmModel));
device_model->set_status(DeviceModel::MODEL_STATUS_VERIFIED);
const uint32_t model_launch_year = 2015;
device_model->set_model_year(model_launch_year);
ASSERT_TRUE(profile_list_->GetDrmInfo(client_id_, device_info_, &drm_info)); ASSERT_TRUE(profile_list_->GetDrmInfo(client_id_, device_info_, &drm_info));
EXPECT_EQ(client_id_.client_capabilities().max_hdcp_version(), EXPECT_EQ(client_id_.client_capabilities().max_hdcp_version(),
drm_info.output().hdcp_version()); drm_info.output().hdcp_version());
EXPECT_EQ(client_id_.client_capabilities().analog_output_capabilities(), EXPECT_EQ(client_id_.client_capabilities().analog_output_capabilities(),
drm_info.output().analog_output_capabilities()); drm_info.output().analog_output_capabilities());
EXPECT_EQ(client_id_.client_capabilities().oem_crypto_api_version(), EXPECT_EQ(client_id_.client_capabilities().oem_crypto_api_version(),
drm_info.security().oemcrypto_version()); drm_info.security().oemcrypto_api_version());
EXPECT_EQ(client_id_.client_capabilities().resource_rating_tier(), EXPECT_EQ(client_id_.client_capabilities().resource_rating_tier(),
drm_info.security().resource_rating_tier()); drm_info.security().resource_rating_tier());
@@ -108,104 +114,92 @@ TEST_F(SecurityProfileListTest, GetDrmInfo) {
drm_info.security().security_level()); drm_info.security().security_level());
EXPECT_EQ(device_info_.system_id(), drm_info.system_id()); EXPECT_EQ(device_info_.system_id(), drm_info.system_id());
// make_mode status is currently hard-coded to false.
EXPECT_EQ(false, drm_info.security().request_model_info_status());
EXPECT_EQ(kMakeValue, drm_info.request_model_info().manufacturer()); EXPECT_EQ(kMakeValue, drm_info.request_model_info().manufacturer());
EXPECT_EQ(kModelValue, drm_info.request_model_info().model_name()); EXPECT_EQ(kModelValue, drm_info.request_model_info().model_name());
EXPECT_EQ(DeviceModel::MODEL_STATUS_VERIFIED,
drm_info.request_model_info().status());
EXPECT_EQ(model_launch_year, drm_info.request_model_info().model_year());
EXPECT_EQ(kDeviceNameValue, drm_info.client_info().device_name());
EXPECT_EQ(kProductNameValue,
drm_info.client_info().product_info().product_name());
EXPECT_EQ(kBuildInfoValue,
drm_info.client_info().product_info().build_info());
EXPECT_EQ(
kOemCryptoSecurityPatchLevelValue,
drm_info.client_info().product_info().oem_crypto_security_patch_level());
} }
TEST_F(SecurityProfileListTest, ProfileLevels) { TEST_F(SecurityProfileListTest, QualifiedProfiles) {
SecurityProfile::DrmInfo drm_info; SecurityProfile profile1;
profile_list_->Init(); profile1.set_name("profile1");
profile1.mutable_min_security_requirements()->set_security_level(
ProvisionedDeviceInfo::LEVEL_3);
profile1.mutable_min_output_requirements()->set_hdcp_version(
ClientCapabilities::HDCP_V1);
profile_list_->InsertProfile(profile1);
client_id_.mutable_client_capabilities()->set_max_hdcp_version( SecurityProfile profile2;
ClientCapabilities::HDCP_NONE); profile2.set_name("profile2");
client_id_.mutable_client_capabilities()->set_analog_output_capabilities( profile2.mutable_min_security_requirements()->set_security_level(
ClientCapabilities::ANALOG_OUTPUT_UNKNOWN); ProvisionedDeviceInfo::LEVEL_1);
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(7); profile2.mutable_min_output_requirements()->set_hdcp_version(
client_id_.mutable_client_capabilities()->set_resource_rating_tier( ClientCapabilities::HDCP_V2);
kResourceTierLow); profile_list_->InsertProfile(profile2);
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_3);
// Lowest profile level requires OEMCrypto version 8. // Both profiles should qualify based on client_info and device_info from the
ASSERT_EQ( // Setup function.
SecurityProfile::SECURITY_PROFILE_LEVEL_UNDEFINED, std::vector<std::string> qualified_profiles;
profile_list_->GetBestProfileLevel(client_id_, device_info_, &drm_info)); EXPECT_EQ(2, profile_list_->GetQualifiedProfiles(client_id_, device_info_,
&qualified_profiles));
EXPECT_NE(qualified_profiles.end(),
std::find(qualified_profiles.begin(), qualified_profiles.end(),
profile1.name()));
EXPECT_NE(qualified_profiles.end(),
std::find(qualified_profiles.begin(), qualified_profiles.end(),
profile2.name()));
// Move up to profile 1 // Reduce the DRM capabilities of the device so profile2 will not qualify.
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(8);
ASSERT_EQ(
SecurityProfile::SECURITY_PROFILE_LEVEL_1,
profile_list_->GetBestProfileLevel(client_id_, device_info_, &drm_info));
// Move up to profile 2
client_id_.mutable_client_capabilities()->set_analog_output_capabilities(
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(12);
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_2);
ASSERT_EQ(
SecurityProfile::SECURITY_PROFILE_LEVEL_2,
profile_list_->GetBestProfileLevel(client_id_, device_info_, &drm_info));
// Move up to profile 3
client_id_.mutable_client_capabilities()->set_max_hdcp_version( client_id_.mutable_client_capabilities()->set_max_hdcp_version(
ClientCapabilities::HDCP_V1); ClientCapabilities::HDCP_V1);
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1); ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id_, device_info_,
client_id_.mutable_client_capabilities()->set_resource_rating_tier( &qualified_profiles));
kResourceTierMed); EXPECT_NE(qualified_profiles.end(),
ASSERT_EQ( std::find(qualified_profiles.begin(), qualified_profiles.end(),
SecurityProfile::SECURITY_PROFILE_LEVEL_3, profile1.name()));
profile_list_->GetBestProfileLevel(client_id_, device_info_, &drm_info));
// Move up to profile 4
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
ClientCapabilities::HDCP_V2_2);
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierHigh);
ASSERT_EQ(
SecurityProfile::SECURITY_PROFILE_LEVEL_4,
profile_list_->GetBestProfileLevel(client_id_, device_info_, &drm_info));
} }
TEST_F(SecurityProfileListTest, FindProfile) { TEST_F(SecurityProfileListTest, FindProfile) {
// This test will not initialize the SecurityProfileList, hence it's empty. SecurityProfileList profile_list("widevine-test");
// Insert test profile 1 into the list.
SecurityProfile profile1; SecurityProfile profile1;
profile1.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_1);
profile1.set_name("profile1"); profile1.set_name("profile1");
profile1.mutable_min_security_requirements()->set_security_level(
ProvisionedDeviceInfo::LEVEL_3);
EXPECT_EQ(kDefaultContentOwnerName, profile1.owner());
SecurityProfile profile2; SecurityProfile profile2;
profile2.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_2);
profile2.set_name("profile2"); profile2.set_name("profile2");
SecurityProfile profile3; profile2.mutable_min_security_requirements()->set_security_level(
profile3.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_3); ProvisionedDeviceInfo::LEVEL_3);
profile3.set_name("profile3"); // Override the default owner name.
profile2.set_owner("owner2");
// Insert profiles 1 & 2, but not 3.. // Insert profiles 1 & 2.
EXPECT_TRUE(profile_list_->InsertProfile(profile1)); EXPECT_TRUE(profile_list.InsertProfile(profile1));
EXPECT_TRUE(profile_list_->InsertProfile(profile2)); EXPECT_TRUE(profile_list.InsertProfile(profile2));
EXPECT_EQ(2, profile_list.NumProfiles());
// Find the profile by its level.
SecurityProfile profile;
EXPECT_TRUE(profile_list_->GetProfileByLevel(profile1.level(), &profile));
EXPECT_EQ(profile1.name(), profile.name());
EXPECT_EQ(profile1.level(), profile.level());
EXPECT_TRUE(profile_list_->GetProfileByLevel(profile2.level(), &profile));
EXPECT_EQ(profile2.name(), profile.name());
EXPECT_EQ(profile2.level(), profile.level());
EXPECT_FALSE(profile_list_->GetProfileByName(profile3.name(), nullptr));
// Find the profile by its name. // Find the profile by its name.
EXPECT_TRUE(profile_list_->GetProfileByName(profile1.name(), &profile)); SecurityProfile profile;
EXPECT_TRUE(profile_list.GetProfileByName(profile1.name(), &profile));
EXPECT_EQ(profile1.name(), profile.name()); EXPECT_EQ(profile1.name(), profile.name());
EXPECT_EQ(profile1.level(), profile.level()); EXPECT_EQ(profile1.level(), profile.level());
EXPECT_EQ(profile1.owner(), profile.owner());
EXPECT_TRUE(profile_list_->GetProfileByName(profile2.name(), &profile)); EXPECT_TRUE(profile_list.GetProfileByName(profile2.name(), &profile));
EXPECT_EQ(profile2.name(), profile.name()); EXPECT_EQ(profile2.name(), profile.name());
EXPECT_EQ(profile2.level(), profile.level()); EXPECT_EQ(profile2.level(), profile.level());
EXPECT_EQ(profile2.owner(), profile.owner());
EXPECT_FALSE(profile_list_->GetProfileByName(profile3.name(), nullptr)); EXPECT_FALSE(
profile_list.GetProfileByName("you-should-not-find-me", &profile));
} }
} // namespace security_profile } // namespace security_profile

View File

@@ -37,7 +37,8 @@ std::string Sha512_Hash(const std::string& message) {
return digest; return digest;
} }
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name) { std::string GenerateSha1Uuid(const std::string& name_space,
const std::string& name) {
// X.667 14 Setting the fields of a name-based UUID. // X.667 14 Setting the fields of a name-based UUID.
// - Allocate a UUID to use as a "name space identifier" for all UUIDs // - Allocate a UUID to use as a "name space identifier" for all UUIDs
// generated from names in that name space. // generated from names in that name space.

View File

@@ -7,6 +7,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/sha_util.h" #include "common/sha_util.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"

View File

@@ -39,6 +39,7 @@ Status GenerateAesSignature(const std::string& message,
Status GenerateRsaSignature(const std::string& message, Status GenerateRsaSignature(const std::string& message,
const std::string& private_key, const std::string& private_key,
HashAlgorithm hash_algorithm,
std::string* signature) { std::string* signature) {
if (signature == nullptr) { if (signature == nullptr) {
return Status(error::INVALID_ARGUMENT, "signature is nullptr"); return Status(error::INVALID_ARGUMENT, "signature is nullptr");
@@ -49,7 +50,7 @@ Status GenerateRsaSignature(const std::string& message,
return Status(error::INTERNAL, "Failed to construct a RsaPrivateKey"); return Status(error::INTERNAL, "Failed to construct a RsaPrivateKey");
} }
std::string sig; std::string sig;
if (!rsa_private_key->GenerateSignature(message, &sig)) { if (!rsa_private_key->GenerateSignature(message, hash_algorithm, &sig)) {
return Status(error::INTERNAL, "Failed to generate a RSA signature"); return Status(error::INTERNAL, "Failed to generate a RSA signature");
} }
if (sig.empty()) { if (sig.empty()) {

View File

@@ -11,6 +11,7 @@
#include <string> #include <string>
#include "common/hash_algorithm.h"
#include "common/status.h" #include "common/status.h"
namespace widevine { namespace widevine {
@@ -23,11 +24,13 @@ Status GenerateAesSignature(const std::string& message,
const std::string& aes_key, const std::string& aes_key,
const std::string& aes_iv, std::string* signature); const std::string& aes_iv, std::string* signature);
// Generates a RSA signature of |message| using |private_key|. // Generates a RSA signature of |message| using |private_key| and
// Signature is returned via |sigature| if generation was successful. // |hash_algorithm|. Signature is returned via |sigature| if generation was
// Returns a Status that carries the details of error if generation failed. // successful. Returns a Status that carries the details of error if generation
// failed.
Status GenerateRsaSignature(const std::string& message, Status GenerateRsaSignature(const std::string& message,
const std::string& private_key, const std::string& private_key,
HashAlgorithm hash_algorithm,
std::string* signature); std::string* signature);
} // namespace signature_util } // namespace signature_util

View File

@@ -25,9 +25,10 @@ class SignerPublicKeyImpl : public SignerPublicKey {
SignerPublicKeyImpl(const SignerPublicKeyImpl&) = delete; SignerPublicKeyImpl(const SignerPublicKeyImpl&) = delete;
SignerPublicKeyImpl& operator=(const SignerPublicKeyImpl&) = delete; SignerPublicKeyImpl& operator=(const SignerPublicKeyImpl&) = delete;
bool VerifySignature(const std::string& message, bool VerifySignature(const std::string& message, HashAlgorithm hash_algorithm,
const std::string& signature) const override { const std::string& signature) const override {
if (!signer_public_key_->VerifySignature(message, signature)) { if (!signer_public_key_->VerifySignature(message, hash_algorithm,
signature)) {
return false; return false;
} }
return true; return true;

View File

@@ -11,6 +11,7 @@
#include <string> #include <string>
#include "common/hash_algorithm.h"
#include "protos/public/drm_certificate.pb.h" #include "protos/public/drm_certificate.pb.h"
namespace widevine { namespace widevine {
@@ -24,8 +25,9 @@ class SignerPublicKey {
SignerPublicKey(const SignerPublicKey&) = delete; SignerPublicKey(const SignerPublicKey&) = delete;
SignerPublicKey& operator=(const SignerPublicKey&) = delete; SignerPublicKey& operator=(const SignerPublicKey&) = delete;
// Verify message using |signer_public_key_|. // Verify message using |signer_public_key_| and |hash_algorithm|.
virtual bool VerifySignature(const std::string& message, virtual bool VerifySignature(const std::string& message,
HashAlgorithm hash_algorithm,
const std::string& signature) const = 0; const std::string& signature) const = 0;
// A factory method to create a SignerPublicKey. The |algorithm| is used to // A factory method to create a SignerPublicKey. The |algorithm| is used to

View File

@@ -13,6 +13,7 @@
#include "testing/gunit.h" #include "testing/gunit.h"
#include "common/ec_key.h" #include "common/ec_key.h"
#include "common/ec_test_keys.h" #include "common/ec_test_keys.h"
#include "common/hash_algorithm.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/rsa_test_keys.h" #include "common/rsa_test_keys.h"
#include "protos/public/drm_certificate.pb.h" #include "protos/public/drm_certificate.pb.h"
@@ -20,6 +21,7 @@
namespace widevine { namespace widevine {
static const char kMessage[] = "The rain in Spain falls mainly in the blank?"; static const char kMessage[] = "The rain in Spain falls mainly in the blank?";
const HashAlgorithm kHashAlgorithm = HashAlgorithm::kSha256;
class SignerPublicKeyTest : public ::testing::Test { class SignerPublicKeyTest : public ::testing::Test {
public: public:
@@ -32,12 +34,13 @@ TEST_F(SignerPublicKeyTest, RSA) {
RsaPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits())); RsaPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits()));
std::string signature; std::string signature;
ASSERT_TRUE(private_key->GenerateSignature(kMessage, &signature)); ASSERT_TRUE(
private_key->GenerateSignature(kMessage, kHashAlgorithm, &signature));
std::unique_ptr<SignerPublicKey> public_key = SignerPublicKey::Create( std::unique_ptr<SignerPublicKey> public_key = SignerPublicKey::Create(
rsa_test_keys_.public_test_key_1_3072_bits(), DrmCertificate::RSA); rsa_test_keys_.public_test_key_1_3072_bits(), DrmCertificate::RSA);
ASSERT_NE(public_key, nullptr); ASSERT_NE(public_key, nullptr);
EXPECT_TRUE(public_key->VerifySignature(kMessage, signature)); EXPECT_TRUE(public_key->VerifySignature(kMessage, kHashAlgorithm, signature));
} }
TEST_F(SignerPublicKeyTest, ECC) { TEST_F(SignerPublicKeyTest, ECC) {
@@ -45,13 +48,14 @@ TEST_F(SignerPublicKeyTest, ECC) {
ECPrivateKey::Create(ec_test_keys_.private_test_key_1_secp521r1()); ECPrivateKey::Create(ec_test_keys_.private_test_key_1_secp521r1());
std::string signature; std::string signature;
ASSERT_TRUE(private_key->GenerateSignature(kMessage, &signature)); ASSERT_TRUE(
private_key->GenerateSignature(kMessage, kHashAlgorithm, &signature));
std::unique_ptr<SignerPublicKey> public_key = std::unique_ptr<SignerPublicKey> public_key =
SignerPublicKey::Create(ec_test_keys_.public_test_key_1_secp521r1(), SignerPublicKey::Create(ec_test_keys_.public_test_key_1_secp521r1(),
DrmCertificate::ECC_SECP521R1); DrmCertificate::ECC_SECP521R1);
ASSERT_NE(public_key, nullptr); ASSERT_NE(public_key, nullptr);
EXPECT_TRUE(public_key->VerifySignature(kMessage, signature)); EXPECT_TRUE(public_key->VerifySignature(kMessage, kHashAlgorithm, signature));
} }
TEST_F(SignerPublicKeyTest, IncorrectAlgorithm) { TEST_F(SignerPublicKeyTest, IncorrectAlgorithm) {

View File

@@ -7,6 +7,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/signing_key_util.h" #include "common/signing_key_util.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "common/crypto_util.h" #include "common/crypto_util.h"

View File

@@ -7,6 +7,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/string_util.h" #include "common/string_util.h"
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"

View File

@@ -9,6 +9,7 @@
#include "common/test_utils.h" #include "common/test_utils.h"
#include <stddef.h> #include <stddef.h>
#include <memory> #include <memory>
#include "glog/logging.h" #include "glog/logging.h"

View File

@@ -22,7 +22,7 @@ namespace widevine {
// and PKCS#1 1.5 padding. |pem_private_key| is a PEM-encoded private RSA key, // and PKCS#1 1.5 padding. |pem_private_key| is a PEM-encoded private RSA key,
// |message| is the message to be signed, and |signature| is a pointer to a // |message| is the message to be signed, and |signature| is a pointer to a
// std::string where the signature will be stored. The caller returns ownership of // std::string where the signature will be stored. The caller returns ownership of
// all paramters. // all parameters.
Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key, Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
const std::string& message, const std::string& message,
std::string* signature); std::string* signature);

View File

@@ -13,6 +13,7 @@
#define COMMON_VERIFIED_MEDIA_PIPELINE_H_ #define COMMON_VERIFIED_MEDIA_PIPELINE_H_
#include <string> #include <string>
#include "common/status.h" #include "common/status.h"
#include "protos/public/license_protocol.pb.h" #include "protos/public/license_protocol.pb.h"

View File

@@ -13,12 +13,14 @@
#include "common/vmp_checker.h" #include "common/vmp_checker.h"
#include <stddef.h> #include <stddef.h>
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
#include "glog/logging.h" #include "glog/logging.h"
#include "common/certificate_type.h" #include "common/certificate_type.h"
#include "common/error_space.h" #include "common/error_space.h"
#include "common/hash_algorithm_util.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/x509_cert.h" #include "common/x509_cert.h"
#include "protos/public/errors.pb.h" #include "protos/public/errors.pb.h"
@@ -334,7 +336,9 @@ Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) {
std::unique_ptr<RsaPublicKey> key(cert->GetRsaPublicKey()); std::unique_ptr<RsaPublicKey> key(cert->GetRsaPublicKey());
std::string message(binary_info.binary_hash()); std::string message(binary_info.binary_hash());
message += binary_info.flags() & 0xff; message += binary_info.flags() & 0xff;
if (!key->VerifySignature(message, binary_info.signature())) { if (!key->VerifySignature(
message, HashAlgorithmProtoToEnum(binary_info.hash_algorithm()),
binary_info.signature())) {
LOG(INFO) << "Code signature verification failed for file \"" LOG(INFO) << "Code signature verification failed for file \""
<< binary_info.file_name() << "\"."; << binary_info.file_name() << "\".";
*result = kTampered; *result = kTampered;

View File

@@ -6,14 +6,16 @@
// widevine-licensing@google.com. // widevine-licensing@google.com.
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/vmp_checker.h"
#include <memory> #include <memory>
#include "glog/logging.h" #include "glog/logging.h"
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "common/hash_algorithm_util.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/vmp_checker.h"
#include "protos/public/errors.pb.h" #include "protos/public/errors.pb.h"
#include "protos/public/verified_media_pipeline.pb.h" #include "protos/public/verified_media_pipeline.pb.h"
@@ -184,7 +186,9 @@ class VmpCheckerTest : public ::testing::Test {
std::string message(binary_hash); std::string message(binary_hash);
message += flags & 0xff; message += flags & 0xff;
std::string signature; std::string signature;
ASSERT_TRUE(signing_key_->GenerateSignature(message, &signature)); ASSERT_TRUE(signing_key_->GenerateSignature(
message, HashAlgorithmProtoToEnum(new_binary->hash_algorithm()),
&signature));
new_binary->set_signature(signature); new_binary->set_signature(signature);
} }

View File

@@ -6,11 +6,12 @@
// widevine-licensing@google.com. // widevine-licensing@google.com.
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/wvm_test_keys.h"
#include <vector> #include <vector>
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "common/wvm_test_keys.h"
#include "common/wvm_token_handler.h" #include "common/wvm_token_handler.h"
namespace widevine { namespace widevine {

View File

@@ -7,22 +7,23 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/wvm_token_handler.h" #include "common/wvm_token_handler.h"
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "common/wvm_test_keys.h" #include "common/wvm_test_keys.h"
using widevine::wvm_test_keys::kTestSystemId; using widevine::wvm_test_keys::GetPreprovKeyVector;
using widevine::wvm_test_keys::kTestSystemId3Des;
using widevine::wvm_test_keys::kTestPreprovKeyHex;
using widevine::wvm_test_keys::kTestDeviceKey1Hex; using widevine::wvm_test_keys::kTestDeviceKey1Hex;
using widevine::wvm_test_keys::kTestDeviceKey2Hex; using widevine::wvm_test_keys::kTestDeviceKey2Hex;
using widevine::wvm_test_keys::kTestDeviceKey3DesHex; using widevine::wvm_test_keys::kTestDeviceKey3DesHex;
using widevine::wvm_test_keys::kTestPreprovKeyHex;
using widevine::wvm_test_keys::kTestSystemId;
using widevine::wvm_test_keys::kTestSystemId3Des;
using widevine::wvm_test_keys::kTestToken1Hex; using widevine::wvm_test_keys::kTestToken1Hex;
using widevine::wvm_test_keys::kTestToken2Hex; using widevine::wvm_test_keys::kTestToken2Hex;
using widevine::wvm_test_keys::kTestToken3DesHex; using widevine::wvm_test_keys::kTestToken3DesHex;
using widevine::wvm_test_keys::GetPreprovKeyVector;
namespace widevine { namespace widevine {

View File

@@ -13,12 +13,13 @@
#define COMMON_X509_CERT_H_ #define COMMON_X509_CERT_H_
#include <stddef.h> #include <stddef.h>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/thread_annotations.h" #include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "openssl/pem.h" #include "openssl/pem.h"
#include "openssl/x509.h" #include "openssl/x509.h"

View File

@@ -6,13 +6,14 @@
// widevine-licensing@google.com. // widevine-licensing@google.com.
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "common/x509_cert.h"
#include <memory> #include <memory>
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/test_utils.h" #include "common/test_utils.h"
#include "common/x509_cert.h"
namespace widevine { namespace widevine {
const char kTestRootCaDerCert[] = const char kTestRootCaDerCert[] =
@@ -352,7 +353,6 @@ const char kTestDevCodeSigningCert[] =
const char kDevCertFlagOid[] = "1.3.6.1.4.1.11129.4.1.2"; const char kDevCertFlagOid[] = "1.3.6.1.4.1.11129.4.1.2";
const bool kTestDevCodeSigningCertFlagValue = true; const bool kTestDevCodeSigningCertFlagValue = true;
TEST(X509CertTest, LoadCert) { TEST(X509CertTest, LoadCert) {
X509Cert test_cert; X509Cert test_cert;
EXPECT_EQ(OkStatus(), EXPECT_EQ(OkStatus(),

View File

@@ -130,11 +130,11 @@ const char kTestEmmgDataProvision[] = {
'\x47', '\x40', '\x00', '\x10', '\x0a', '\x0d', '\x77', '\x69', '\x64', '\x47', '\x40', '\x00', '\x10', '\x0a', '\x0d', '\x77', '\x69', '\x64',
'\x65', '\x76', '\x69', '\x6e', '\x65', '\x5f', '\x74', '\x65', '\x73', '\x65', '\x76', '\x69', '\x6e', '\x65', '\x5f', '\x74', '\x65', '\x73',
'\x74', '\x12', '\x09', '\x43', '\x61', '\x73', '\x54', '\x73', '\x46', '\x74', '\x12', '\x09', '\x43', '\x61', '\x73', '\x54', '\x73', '\x46',
'\x61', '\x6b', '\x65', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x61', '\x6b', '\x65', '\x1a', '\x10', '\x66', '\x61', '\x6b', '\x65',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x4b', '\x65', '\x79', '\x49', '\x64', '\x31', '\x4b', '\x65', '\x79',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x49', '\x64', '\x31', '\x1a', '\x10', '\x66', '\x61', '\x6b', '\x65',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x4b', '\x65', '\x79', '\x49', '\x64', '\x32', '\x4b', '\x65', '\x79',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x49', '\x64', '\x32', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
@@ -149,6 +149,26 @@ const char kTestEmmgDataProvision[] = {
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'}; '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'};
const char kTestEmptyEmmgDataProvision[] = {
'\x02', // protocol_version
'\x02', '\x11', // message_type - Data_provision
'\x00', '\x00', // message_length
'\x00', '\x01', // parameter_type - client_id
'\x00', '\x04', // parameter_length
'\x4a', '\xd4', '\x00', '\x00', // parameter_value
'\x00', '\x03', // parameter_type - data_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x04', // parameter_type - data_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x08', // parameter_type - data_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x00', // parameter_type - datagram
'\x00', '\x00', // parameter_length
};
const char kTestEmmgStreamCloseRequest[] = { const char kTestEmmgStreamCloseRequest[] = {
'\x02', // protocol_version '\x02', // protocol_version
'\x01', '\x14', // message_type - Stream_close_request '\x01', '\x14', // message_type - Stream_close_request

View File

@@ -90,6 +90,7 @@ cc_library(
"@abseil_repo//absl/container:node_hash_map", "@abseil_repo//absl/container:node_hash_map",
"@abseil_repo//absl/memory", "@abseil_repo//absl/memory",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@abseil_repo//absl/types:optional",
"//common:crypto_util", "//common:crypto_util",
"//common:random_util", "//common:random_util",
"//common:status", "//common:status",
@@ -242,3 +243,25 @@ cc_test(
"//common:status", "//common:status",
], ],
) )
cc_library(
name = "emm",
srcs = ["emm.cc"],
hdrs = ["emm.h"],
deps = [
"//base",
"@abseil_repo//absl/strings",
"//common:status",
"//common:string_util",
"//protos/public:media_cas_cc_proto",
],
)
cc_test(
name = "emm_test",
srcs = ["emm_test.cc"],
deps = [
":emm",
"//testing:gunit_main",
],
)

View File

@@ -95,10 +95,10 @@ class MockEcm : public Ecm {
MockEcm() = default; MockEcm() = default;
~MockEcm() override = default; ~MockEcm() override = default;
MOCK_CONST_METHOD0(age_restriction, uint8_t()); MOCK_METHOD(uint8_t, age_restriction, (), (const, override));
MOCK_CONST_METHOD0(crypto_mode, CryptoMode()); MOCK_METHOD(CryptoMode, crypto_mode, (), (const, override));
MOCK_CONST_METHOD0(paired_keys_required, bool()); MOCK_METHOD(bool, paired_keys_required, (), (const, override));
MOCK_CONST_METHOD0(content_iv_size, size_t()); MOCK_METHOD(size_t, content_iv_size, (), (const, override));
std::string CallSerializeEcm(const std::vector<EntitledKeyInfo*>& keys) { std::string CallSerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
return SerializeEcm(keys); return SerializeEcm(keys);

View File

@@ -8,6 +8,7 @@
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h" #include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
#include <cstddef>
#include <cstring> #include <cstring>
#include "glog/logging.h" #include "glog/logging.h"
@@ -873,32 +874,55 @@ Status EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params,
EcmgStreamInfo* stream_info = streams_info_.at(params.ecm_stream_id).get(); EcmgStreamInfo* stream_info = streams_info_.at(params.ecm_stream_id).get();
DCHECK(stream_info->ecm); DCHECK(stream_info->ecm);
size_t key_count = params.cp_cw_combinations.size();
// Number of keys can only be 1 or 2.
if (key_count != ecmg_config_->number_of_content_keys || key_count < 1 ||
key_count > 2) {
return {error::INVALID_ARGUMENT, "Unexpected cp_cw_combinations size."};
}
// Generate serialized ECM. // Generate serialized ECM.
CryptoMode crypto_mode = stream_info->crypto_mode == CryptoMode::kInvalid CryptoMode crypto_mode = stream_info->crypto_mode == CryptoMode::kInvalid
? ecmg_config_->crypto_mode ? ecmg_config_->crypto_mode
: stream_info->crypto_mode; : stream_info->crypto_mode;
// If two keys are present, the even key should be put first, followed by the
// odd key.
std::vector<EntitledKeyInfo> keys; std::vector<EntitledKeyInfo> keys;
keys.reserve(ecmg_config_->number_of_content_keys); keys.reserve(key_count);
for (size_t i = 0; i < ecmg_config_->number_of_content_keys; i++) { for (const auto& cp_cw : params.cp_cw_combinations) {
DCHECK(params.cp_cw_combinations[i].cp == params.cp_number + i); auto key_info = cp_cw.cp % 2 == 0 ? keys.emplace(keys.begin())
keys.emplace_back(); : keys.emplace(keys.end());
keys[i].key_value = params.cp_cw_combinations[i].cw; key_info->key_value = cp_cw.cw;
// Make content key to 16 bytes if crypto mode is Csa2. // Make content key to 16 bytes if crypto mode is Csa2.
if (crypto_mode == CryptoMode::kDvbCsa2 && keys[i].key_value.size() == 8) { if (crypto_mode == CryptoMode::kDvbCsa2 &&
keys[i].key_value = keys[i].key_value + keys[i].key_value; key_info->key_value.size() == 8) {
key_info->key_value =
absl::StrCat(key_info->key_value, key_info->key_value);
} }
keys[i].key_id = crypto_util::DeriveKeyId(keys[i].key_value); key_info->key_id = crypto_util::DeriveKeyId(key_info->key_value);
keys[i].content_iv = stream_info->content_ivs.empty() auto generated_key_iv = GenerateRandomWrappedKeyIv();
? content_ivs_[i] if (!generated_key_iv.has_value() ||
: stream_info->content_ivs[i]; generated_key_iv->size() != kWrappedKeyIvSizeBytes) {
if (!RandomBytes(kWrappedKeyIvSizeBytes, &keys[i].wrapped_key_iv)) {
return {error::INTERNAL, "Unable to generate random wrapped key iv."}; return {error::INTERNAL, "Unable to generate random wrapped key iv."};
} }
key_info->wrapped_key_iv = generated_key_iv.value();
}
if (content_ivs_.size() < key_count &&
stream_info->content_ivs.size() < key_count) {
return {error::INVALID_ARGUMENT, "Not enough content iv."};
}
// The first iv received is for even key and second is for odd key.
for (size_t i = 0; i < key_count; ++i) {
keys[i].content_iv = stream_info->content_ivs.size() >= key_count
? stream_info->content_ivs[i]
: content_ivs_[i];
} }
Status status; Status status;
std::string serialized_ecm; std::string serialized_ecm;
if (ecmg_config_->number_of_content_keys > 1) { if (key_count > 1) {
status = stream_info->ecm->GenerateEcm( status = stream_info->ecm->GenerateEcm(
&keys[0], &keys[1], stream_info->track_type, &serialized_ecm); &keys[0], &keys[1], stream_info->track_type, &serialized_ecm);
} else { } else {
@@ -926,5 +950,14 @@ Status EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params,
return OkStatus(); return OkStatus();
} }
absl::optional<std::string> EcmgClientHandler::GenerateRandomWrappedKeyIv()
const {
std::string output_iv;
if (RandomBytes(kWrappedKeyIvSizeBytes, &output_iv)) {
return output_iv;
}
return absl::nullopt;
}
} // namespace cas } // namespace cas
} // namespace widevine } // namespace widevine

View File

@@ -16,6 +16,7 @@
#include <cstdint> #include <cstdint>
#include "absl/container/node_hash_map.h" #include "absl/container/node_hash_map.h"
#include "absl/types/optional.h"
#include "common/status.h" #include "common/status.h"
#include "media_cas_packager_sdk/internal/ecm.h" #include "media_cas_packager_sdk/internal/ecm.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h" #include "media_cas_packager_sdk/public/wv_cas_types.h"
@@ -126,6 +127,10 @@ class EcmgClientHandler {
Status BuildEcmDatagram(const EcmgParameters& params, Status BuildEcmDatagram(const EcmgParameters& params,
uint8_t* ecm_datagram) const; uint8_t* ecm_datagram) const;
// Generates a random wrapped key iv string. Returns true on success, false
// otherwise. The main purpose for this function is easier testing.
virtual absl::optional<std::string> GenerateRandomWrappedKeyIv() const;
EcmgConfig* ecmg_config_; EcmgConfig* ecmg_config_;
// Per spec, "There is always one (and only one) channel per TCP connection". // Per spec, "There is always one (and only one) channel per TCP connection".
bool channel_id_set_; bool channel_id_set_;

View File

@@ -12,12 +12,14 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h" #include "absl/strings/str_format.h"
#include "example/test_ecmg_messages.h" #include "example/test_ecmg_messages.h"
#include "media_cas_packager_sdk/internal/ecmg_constants.h" #include "media_cas_packager_sdk/internal/ecmg_constants.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
#include "media_cas_packager_sdk/internal/simulcrypt_util.h" #include "media_cas_packager_sdk/internal/simulcrypt_util.h"
#include "media_cas_packager_sdk/internal/util.h" #include "media_cas_packager_sdk/internal/util.h"
@@ -31,28 +33,36 @@ using simulcrypt_util::AddUint32Param;
using simulcrypt_util::AddUint8Param; using simulcrypt_util::AddUint8Param;
using simulcrypt_util::BuildMessageHeader; using simulcrypt_util::BuildMessageHeader;
static constexpr size_t kBufferSize = 1024; constexpr size_t kBufferSize = 1024;
static constexpr size_t kSuperCasId = 0x4AD40000; constexpr size_t kSuperCasId = 0x4AD40000;
static constexpr size_t kChannelId = 1; constexpr size_t kChannelId = 1;
static constexpr size_t kStreamId = 1; constexpr size_t kStreamId = 1;
static constexpr size_t kEcmId = 2; constexpr size_t kEcmId = 2;
static constexpr size_t kNominalCpDuration = 0x64; constexpr size_t kNominalCpDuration = 0x64;
static constexpr size_t kCpNumber = 0; constexpr size_t kCpNumber = 0;
static constexpr char kContentKeyEven[] = "0123456701234567"; constexpr char kContentKeyEven[] = "0123456701234567";
static constexpr char kContentKeyEven8Bytes[] = "01234567"; constexpr char kContentKeyEven8Bytes[] = "01234567";
static constexpr char kContentKeyOdd[] = "abcdefghabcdefgh"; constexpr char kContentKeyOdd[] = "abcdefghabcdefgh";
static constexpr char kContentKeyOdd8Bytes[] = "abcdefgh"; constexpr char kContentKeyOdd8Bytes[] = "abcdefgh";
static constexpr char kEntitlementKeyIdEven[] = "0123456701234567"; constexpr char kEntitlementKeyIdEven[] = "0123456701234567";
static constexpr char kEntitlementKeyValueEven[] = constexpr char kEntitlementKeyValueEven[] = "01234567012345670123456701234567";
"01234567012345670123456701234567"; constexpr char kEntitlementKeyIdOdd[] = "abcdefghabcdefgh";
static constexpr char kEntitlementKeyIdOdd[] = "abcdefghabcdefgh"; constexpr char kEntitlementKeyValueOdd[] = "abcdefghabcdefghabcdefghabcdefgh";
static constexpr char kEntitlementKeyValueOdd[] = constexpr size_t kAgeRestriction = 3;
"abcdefghabcdefghabcdefghabcdefgh"; constexpr char kCryptoMode[] = "AesScte";
static constexpr size_t kAgeRestriction = 3; constexpr char kCryptoModeCsa2[] = "DvbCsa2";
static constexpr char kCryptoMode[] = "AesScte"; constexpr char kTrackTypesSD[] = "SD";
static constexpr char kCryptoModeCsa2[] = "DvbCsa2"; constexpr char kTrackTypesHD[] = "HD";
static constexpr char kTrackTypesSD[] = "SD"; constexpr absl::string_view kWrappedKeyIv = "0123456701234567";
static constexpr char kTrackTypesHD[] = "HD";
class MockEcmgClientHandler : public EcmgClientHandler {
public:
explicit MockEcmgClientHandler(EcmgConfig* ecmg_config)
: EcmgClientHandler(ecmg_config) {}
absl::optional<std::string> GenerateRandomWrappedKeyIv() const override {
return std::string(kWrappedKeyIv);
}
};
class EcmgClientHandlerTest : public ::testing::Test { class EcmgClientHandlerTest : public ::testing::Test {
protected: protected:
@@ -63,7 +73,7 @@ class EcmgClientHandlerTest : public ::testing::Test {
config_.max_comp_time = 100; config_.max_comp_time = 100;
config_.access_criteria_transfer_mode = 1; config_.access_criteria_transfer_mode = 1;
config_.number_of_content_keys = 2; config_.number_of_content_keys = 2;
handler_ = absl::make_unique<EcmgClientHandler>(&config_); handler_ = absl::make_unique<MockEcmgClientHandler>(&config_);
} }
protected: protected:
@@ -564,6 +574,37 @@ TEST_F(EcmgClientHandlerTest, WrongMessageLength) {
CheckChannelError(UNKNOWN_PARAMETER_TYPE_VALUE, response_, response_len_); CheckChannelError(UNKNOWN_PARAMETER_TYPE_VALUE, response_, response_len_);
} }
TEST_F(EcmgClientHandlerTest, BuildEcmDatagramSequenceOfEvenOdd) {
SetupValidChannelStream();
std::vector<EcmgCpCwCombination> cp_cw_combination = {
{kCpNumber, kContentKeyEven}, {kCpNumber + 1, kContentKeyOdd}};
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(response_len_, sizeof(kTestEcmgEcmResponse));
std::string first_response(response_, response_len_);
// Change the sequence of cp_cw_combination.
cp_cw_combination = {{kCpNumber + 1, kContentKeyOdd},
{kCpNumber, kContentKeyEven}};
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
// Sequence of cp_cw_combination does not matter as even/odd is based on cp
// number.
EXPECT_EQ(std::string(response_, response_len_), first_response);
// Swap the key value in cp_cw_combination.
cp_cw_combination = {{kCpNumber, kContentKeyOdd},
{kCpNumber + 1, kContentKeyEven}};
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
// Swapping key value changes generated ecm.
EXPECT_NE(std::string(response_, response_len_), first_response);
}
} // namespace } // namespace
} // namespace cas } // namespace cas
} // namespace widevine } // namespace widevine

View File

@@ -0,0 +1,136 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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 "media_cas_packager_sdk/internal/emm.h"
#include <bitset>
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "common/status.h"
#include "common/string_util.h"
namespace widevine {
namespace cas {
namespace {
constexpr int kNumBitsVersionField = 8;
constexpr int kNumBitsHeaderLengthField = 8;
constexpr int kNumBitsTimestampLengthField = 64;
constexpr int kNumBitsPayloadLengthField = 16;
// Version - this should be incremented if there are changes to the EMM.
constexpr uint8_t kEmmVersion = 1;
} // namespace
Status Emm::SetFingerprinting(
const std::vector<FingerprintingInitParameters>& fingerprintings) {
// First clear all current fingerprinting payload.
emm_payload_.clear_fingerprinting();
// TODO(b/161149665): validate passed in data.
Status status;
for (const auto& fingerprinting_param : fingerprintings) {
Fingerprinting* fingerprinting_payload = emm_payload_.add_fingerprinting();
for (const auto& channel : fingerprinting_param.channels) {
fingerprinting_payload->add_channels(channel);
}
fingerprinting_payload->set_control(fingerprinting_param.control);
}
return status;
}
Status Emm::SetServiceBlocking(
const std::vector<ServiceBlockingInitParameters>& service_blockings) {
// First clear all current service blocking payload.
emm_payload_.clear_service_blocking();
// TODO(b/161149665): validate passed in data.
Status status;
for (const auto& service_blocking_param : service_blockings) {
ServiceBlocking* service_blocking_payload =
emm_payload_.add_service_blocking();
for (const auto& channel : service_blocking_param.channels) {
service_blocking_payload->add_channels(channel);
}
for (const auto& device_group : service_blocking_param.device_groups) {
service_blocking_payload->add_device_groups(device_group);
}
if (service_blocking_param.start_time != 0) {
service_blocking_payload->set_start_time_sec(
service_blocking_param.start_time);
}
service_blocking_payload->set_end_time_sec(service_blocking_param.end_time);
}
return status;
}
Status Emm::GenerateEmm(std::string* serialized_emm) const {
if (serialized_emm == nullptr) {
return {error::INVALID_ARGUMENT, "No return emm std::string pointer."};
}
EmmSerializingParameters serializing_params;
serializing_params.payload = emm_payload_.SerializeAsString();
serializing_params.timestamp = GenerateTimestamp();
// Generate serialized emm (without signature yet).
Status status =
GenerateSerializedEmmNoSignature(serializing_params, serialized_emm);
if (!status.ok()) {
return status;
}
// Calculate and append signature.
absl::StrAppend(serialized_emm, GenerateSignature(*serialized_emm));
return OkStatus();
}
Status Emm::GenerateSerializedEmmNoSignature(
const EmmSerializingParameters& params, std::string* serialized_emm) const {
if (serialized_emm == nullptr) {
return {error::INVALID_ARGUMENT, "No return emm std::string pointer."};
}
std::bitset<kNumBitsVersionField> version(kEmmVersion);
std::bitset<kNumBitsHeaderLengthField> header_length(
sizeof(params.timestamp));
std::bitset<kNumBitsTimestampLengthField> timestamp(params.timestamp);
std::bitset<kNumBitsPayloadLengthField> payload_length(
params.payload.length());
std::string emm_bitset =
absl::StrCat(version.to_string(), header_length.to_string(),
timestamp.to_string(), payload_length.to_string());
Status status =
string_util::BitsetStringToBinaryString(emm_bitset, serialized_emm);
if (!status.ok() || serialized_emm->empty()) {
LOG(ERROR) << "Failed to convert EMM bitset to std::string";
return {error::INTERNAL, "Failed to convert EMM bitset to std::string"};
}
// Appends payload.
absl::StrAppend(serialized_emm, params.payload);
return OkStatus();
}
int64_t Emm::GenerateTimestamp() const {
// TODO(b/161252065): Generate timestamp.
return 0;
}
std::string Emm::GenerateSignature(const std::string& content) const {
// TODO(b/161252442): Calculate signature.
std::string signature(32, 'x'); // A fake 32 bytes signature.
return signature;
}
} // namespace cas
} // namespace widevine

View File

@@ -0,0 +1,74 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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 MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMM_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMM_H_
#include <string>
#include <vector>
#include <cstdint>
#include "common/status.h"
#include "protos/public/media_cas.pb.h"
namespace widevine {
namespace cas {
struct FingerprintingInitParameters {
std::vector<std::string> channels;
std::string control;
};
struct ServiceBlockingInitParameters {
std::vector<std::string> channels;
std::vector<std::string> device_groups;
// Value 0 in start_time means immediate.
int64_t start_time = 0;
int64_t end_time;
};
// Generator for producing Widevine CAS-compliant EMMs. Used to construct the
// Transport Stream packet payload of an EMM containing messages including
// fingerprinting and service blocking.
// Class Emm is not thread safe.
class Emm {
public:
Emm() = default;
Emm(const Emm&) = delete;
Emm& operator=(const Emm&) = delete;
virtual ~Emm() = default;
// Replaces current fingerprinting info with |fingerprintings|.
Status SetFingerprinting(
const std::vector<FingerprintingInitParameters>& fingerprintings);
// Replaces current service blocking info with |service_blockings|.
Status SetServiceBlocking(
const std::vector<ServiceBlockingInitParameters>& service_blockings);
// Generates serialized EMM to |serialized_emm|.
Status GenerateEmm(std::string* serialized_emm) const;
private:
struct EmmSerializingParameters {
int64_t timestamp;
std::string payload;
};
Status GenerateSerializedEmmNoSignature(
const EmmSerializingParameters& params,
std::string* serialized_emm) const;
int64_t GenerateTimestamp() const;
std::string GenerateSignature(const std::string& content) const;
EmmPayload emm_payload_;
};
} // namespace cas
} // namespace widevine
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMM_H_

View File

@@ -0,0 +1,236 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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 "media_cas_packager_sdk/internal/emm.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
namespace widevine {
namespace cas {
namespace {
constexpr char kChannelOne[] = "CH1";
constexpr char kChannelTwo[] = "CH2";
constexpr char kChannelThree[] = "CH3";
constexpr char kFingerprintingControl[] = "controls";
constexpr char kDeviceGroupOne[] = "Group1";
constexpr char kDeviceGroupTwo[] = "Group2";
constexpr int64_t kServiceBockingStartTime = 100;
constexpr int64_t kServiceBockingEndTime = 1000;
// Length in bytes when there is no payload.
constexpr uint8_t kExpectedNoPayloadLengthBytes = 44;
constexpr uint8_t kExpectedEmmVersion = 1;
constexpr uint8_t kExpectedHeaderLength = 8;
constexpr uint8_t kVersionStartIndex = 0;
constexpr uint8_t kHeaderLengthStartIndex = 1;
constexpr uint8_t kPayloadLengthStartIndex = 10;
constexpr uint8_t kPayloadStartIndex = 12;
constexpr uint8_t kSignatureLength = 32;
FingerprintingInitParameters GetValidFingerprintingParams() {
FingerprintingInitParameters fingerprinting_params;
fingerprinting_params.channels = {kChannelOne, kChannelTwo};
fingerprinting_params.control = kFingerprintingControl;
return fingerprinting_params;
}
void LoadExpectedFingerprintingProto(Fingerprinting* fingerprinting_payload) {
fingerprinting_payload->add_channels(kChannelOne);
fingerprinting_payload->add_channels(kChannelTwo);
fingerprinting_payload->set_control(kFingerprintingControl);
}
ServiceBlockingInitParameters GetValidServiceBlockingParams() {
ServiceBlockingInitParameters service_blocking_params;
service_blocking_params.channels = {kChannelOne, kChannelTwo};
service_blocking_params.device_groups = {kDeviceGroupOne, kDeviceGroupTwo};
service_blocking_params.start_time = kServiceBockingStartTime;
service_blocking_params.end_time = kServiceBockingEndTime;
return service_blocking_params;
}
void LoadExpectedServiceBlockingProto(
ServiceBlocking* service_blocking_payload) {
service_blocking_payload->add_channels(kChannelOne);
service_blocking_payload->add_channels(kChannelTwo);
service_blocking_payload->add_device_groups(kDeviceGroupOne);
service_blocking_payload->add_device_groups(kDeviceGroupTwo);
service_blocking_payload->set_start_time_sec(kServiceBockingStartTime);
service_blocking_payload->set_end_time_sec(kServiceBockingEndTime);
}
TEST(EmmTest, GenerateEmmSinglePayloadSuccess) {
Emm emm_gen;
FingerprintingInitParameters fingerprinting = GetValidFingerprintingParams();
ServiceBlockingInitParameters service_blocking =
GetValidServiceBlockingParams();
EXPECT_EQ(emm_gen.SetFingerprinting({fingerprinting}), OkStatus());
EXPECT_EQ(emm_gen.SetServiceBlocking({service_blocking}), OkStatus());
std::string result;
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
EXPECT_GT(result.length(), kExpectedNoPayloadLengthBytes);
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
kExpectedEmmVersion);
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
kExpectedHeaderLength);
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
<< 8 |
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
ASSERT_GT(payload_lengh, 0);
ASSERT_EQ(result.length(),
kPayloadStartIndex + payload_lengh + kSignatureLength);
std::string payload_section =
result.substr(kPayloadStartIndex, payload_lengh);
// Parse the payload and validate fields.
EmmPayload emm_payload;
ASSERT_TRUE(emm_payload.ParseFromString(payload_section));
EmmPayload expected_payload;
LoadExpectedFingerprintingProto(expected_payload.add_fingerprinting());
LoadExpectedServiceBlockingProto(expected_payload.add_service_blocking());
std::string serialized_expected_payload;
expected_payload.SerializeToString(&serialized_expected_payload);
EXPECT_EQ(payload_section, serialized_expected_payload);
}
TEST(EmmTest, GenerateEmmMultiplePayloadSuccess) {
Emm emm_gen;
FingerprintingInitParameters fingerprinting_params;
fingerprinting_params.channels = {kChannelThree};
fingerprinting_params.control = kFingerprintingControl;
EXPECT_EQ(emm_gen.SetFingerprinting(
{GetValidFingerprintingParams(), fingerprinting_params}),
OkStatus());
ServiceBlockingInitParameters service_blocking_params;
service_blocking_params.channels = {kChannelThree};
service_blocking_params.device_groups = {kDeviceGroupOne, kDeviceGroupTwo};
service_blocking_params.start_time = kServiceBockingStartTime;
service_blocking_params.end_time = kServiceBockingEndTime;
EXPECT_EQ(emm_gen.SetServiceBlocking(
{GetValidServiceBlockingParams(), service_blocking_params}),
OkStatus());
std::string result;
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
EXPECT_GT(result.length(), kExpectedNoPayloadLengthBytes);
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
kExpectedEmmVersion);
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
kExpectedHeaderLength);
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
<< 8 |
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
ASSERT_GT(payload_lengh, 0);
ASSERT_EQ(result.length(),
kPayloadStartIndex + payload_lengh + kSignatureLength);
std::string payload_section =
result.substr(kPayloadStartIndex, payload_lengh);
// Parse the payload and validate fields.
EmmPayload emm_payload;
ASSERT_TRUE(emm_payload.ParseFromString(payload_section));
EXPECT_EQ(emm_payload.fingerprinting_size(), 2);
EXPECT_EQ(emm_payload.service_blocking_size(), 2);
}
TEST(EmmTest, GenerateEmmFingerprintingOnlySuccess) {
Emm emm_gen;
FingerprintingInitParameters fingerprinting = GetValidFingerprintingParams();
EXPECT_EQ(emm_gen.SetFingerprinting({fingerprinting}), OkStatus());
// OK to be called again.
EXPECT_EQ(emm_gen.SetFingerprinting({fingerprinting}), OkStatus());
std::string result;
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
EXPECT_GT(result.length(), kExpectedNoPayloadLengthBytes);
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
kExpectedEmmVersion);
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
kExpectedHeaderLength);
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
<< 8 |
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
ASSERT_GT(payload_lengh, 0);
ASSERT_EQ(result.length(),
kPayloadStartIndex + payload_lengh + kSignatureLength);
std::string payload_section =
result.substr(kPayloadStartIndex, payload_lengh);
// Parse the payload and validate fields.
EmmPayload emm_payload;
ASSERT_TRUE(emm_payload.ParseFromString(payload_section));
EmmPayload expected_payload;
LoadExpectedFingerprintingProto(expected_payload.add_fingerprinting());
std::string serialized_expected_payload;
expected_payload.SerializeToString(&serialized_expected_payload);
EXPECT_EQ(payload_section, serialized_expected_payload);
}
TEST(EmmTest, GenerateEmmServiceBlockingOnlySuccess) {
Emm emm_gen;
ServiceBlockingInitParameters service_blocking =
GetValidServiceBlockingParams();
EXPECT_EQ(emm_gen.SetServiceBlocking({service_blocking}), OkStatus());
std::string result;
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
EXPECT_GT(result.length(), kExpectedNoPayloadLengthBytes);
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
kExpectedEmmVersion);
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
kExpectedHeaderLength);
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
<< 8 |
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
ASSERT_GT(payload_lengh, 0);
ASSERT_EQ(result.length(),
kPayloadStartIndex + payload_lengh + kSignatureLength);
std::string payload_section =
result.substr(kPayloadStartIndex, payload_lengh);
// Parse the payload and validate fields.
EmmPayload emm_payload;
ASSERT_TRUE(emm_payload.ParseFromString(payload_section));
EmmPayload expected_payload;
LoadExpectedServiceBlockingProto(expected_payload.add_service_blocking());
std::string serialized_expected_payload;
expected_payload.SerializeToString(&serialized_expected_payload);
EXPECT_EQ(payload_section, serialized_expected_payload);
}
TEST(EmmTest, GenerateEmmNoPayloadSuccess) {
Emm emm_gen;
std::string result;
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
EXPECT_EQ(result.length(), kExpectedNoPayloadLengthBytes);
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
kExpectedEmmVersion);
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
kExpectedHeaderLength);
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
<< 8 |
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
EXPECT_EQ(payload_lengh, 0);
EXPECT_EQ(result.length(), kPayloadStartIndex + kSignatureLength);
}
} // namespace
} // namespace cas
} // namespace widevine

View File

@@ -28,6 +28,10 @@
// Minimum sending interval in milliseconds. // Minimum sending interval in milliseconds.
static constexpr uint16_t KMinSendIntervalMs = 2; static constexpr uint16_t KMinSendIntervalMs = 2;
// Maximum number of entitlement key ids shown in private data.
static constexpr uint16_t kMaxNumOfEntitlementKeyIds = 2;
// Entitlement key id length is fixed to 16 bytes.
static constexpr uint16_t kEntitlementKeyIdLength = 16;
namespace widevine { namespace widevine {
namespace cas { namespace cas {
@@ -153,8 +157,8 @@ void Emmg::Start() {
for (size_t i = 0; i < emmg_config_->max_num_message; i++) { for (size_t i = 0; i < emmg_config_->max_num_message; i++) {
SendDataProvision(); SendDataProvision();
absl::SleepFor( absl::SleepFor(std::max(absl::Milliseconds(KMinSendIntervalMs),
absl::Milliseconds(std::max(KMinSendIntervalMs, send_interval_ms_))); absl::Milliseconds(send_interval_ms_)));
} }
SendStreamCloseRequest(); SendStreamCloseRequest();
@@ -220,13 +224,33 @@ void Emmg::BuildStreamBwRequest() {
Host16ToBigEndian(request_ + 3, &total_param_length); Host16ToBigEndian(request_ + 3, &total_param_length);
} }
Status Emmg::GeneratePrivateData(const std::string& content_provider, Status Emmg::GeneratePrivateData(
const std::string& content_id, uint8_t* buffer) { const std::string& content_provider, const std::string& content_id,
const std::vector<std::string>& entitlement_key_ids, uint8_t* buffer) {
DCHECK(buffer); DCHECK(buffer);
// Generate payload. // Generate payload.
CaDescriptorPrivateData private_data; CaDescriptorPrivateData private_data;
private_data.set_provider(content_provider); private_data.set_provider(content_provider);
private_data.set_content_id(content_id); private_data.set_content_id(content_id);
if (entitlement_key_ids.size() > kMaxNumOfEntitlementKeyIds) {
return Status(
error::INVALID_ARGUMENT,
absl::StrCat("Number of entitlement key ids shouldn't exceed ",
kMaxNumOfEntitlementKeyIds));
}
for (const auto& entitlement_key_id : entitlement_key_ids) {
if (entitlement_key_id.size() != kEntitlementKeyIdLength) {
return Status(
error::INVALID_ARGUMENT,
absl::StrCat("Entitlement key id length must be ",
kEntitlementKeyIdLength, ". The offending key id is ",
entitlement_key_id));
}
}
for (const auto& entitlement_key_id : entitlement_key_ids) {
private_data.add_entitlement_key_ids(entitlement_key_id);
}
std::string private_data_str = private_data.SerializeAsString(); std::string private_data_str = private_data.SerializeAsString();
std::string payload_filler(kMaxTsPayloadSize - private_data_str.size(), 0); std::string payload_filler(kMaxTsPayloadSize - private_data_str.size(), 0);
@@ -268,8 +292,13 @@ void Emmg::BuildDataProvision() {
&request_length_); &request_length_);
uint8_t datagram[kTsPacketSize]; uint8_t datagram[kTsPacketSize];
GeneratePrivateData(emmg_config_->content_provider, emmg_config_->content_id, Status status = GeneratePrivateData(
datagram); emmg_config_->content_provider, emmg_config_->content_id,
emmg_config_->entitlement_key_ids, datagram);
if (!status.ok()) {
LOG(ERROR) << "Fail to generate private data. " << status.ToString();
return;
}
simulcrypt_util::AddParam(EMMG_DATAGRAM, datagram, kTsPacketSize, request_, simulcrypt_util::AddParam(EMMG_DATAGRAM, datagram, kTsPacketSize, request_,
&request_length_); &request_length_);

View File

@@ -11,6 +11,7 @@
#include <cstddef> #include <cstddef>
#include <string> #include <string>
#include <vector>
#include <cstdint> #include <cstdint>
#include "common/status.h" #include "common/status.h"
@@ -30,6 +31,7 @@ struct EmmgConfig {
uint8_t data_type; uint8_t data_type;
std::string content_provider; std::string content_provider;
std::string content_id; std::string content_id;
std::vector<std::string> entitlement_key_ids;
uint16_t bandwidth; uint16_t bandwidth;
uint32_t max_num_message; uint32_t max_num_message;
}; };
@@ -82,8 +84,9 @@ class Emmg {
void UpdateSendInterval(uint16_t bandwidth_kbps); void UpdateSendInterval(uint16_t bandwidth_kbps);
Status GeneratePrivateData(const std::string& content_provider, Status GeneratePrivateData(
const std::string& content_id, uint8_t* buffer); const std::string& content_provider, const std::string& content_id,
const std::vector<std::string>& entitlement_key_ids, uint8_t* buffer);
void ReceiveResponseAndVerify(uint16_t expected_type); void ReceiveResponseAndVerify(uint16_t expected_type);
void Send(uint16_t message_type); void Send(uint16_t message_type);

View File

@@ -51,6 +51,7 @@ class EmmgTest : public ::testing::Test {
config_.data_type = 0x01; config_.data_type = 0x01;
config_.content_provider = "widevine_test"; config_.content_provider = "widevine_test";
config_.content_id = "CasTsFake"; config_.content_id = "CasTsFake";
config_.entitlement_key_ids = {"fakeKeyId1KeyId1", "fakeKeyId2KeyId2"};
config_.bandwidth = 100; config_.bandwidth = 100;
config_.max_num_message = 100; config_.max_num_message = 100;
emmg_ = absl::make_unique<TestableEmmg>(&config_); emmg_ = absl::make_unique<TestableEmmg>(&config_);
@@ -103,6 +104,25 @@ TEST_F(EmmgTest, BuildDataProvision) {
sizeof(kTestEmmgDataProvision))); sizeof(kTestEmmgDataProvision)));
} }
TEST_F(EmmgTest,
BuildDataProvisionFailedWhenNumOfEntitlementKeyIdsExceedLimit) {
config_.entitlement_key_ids = {"fakeKeyId1KeyId1", "fakeKeyId2KeyId2",
"fakeKeyId3KeyId3"};
emmg_ = absl::make_unique<TestableEmmg>(&config_);
emmg_->PublicBuildDataProvision();
EXPECT_EQ(0, memcmp(kTestEmptyEmmgDataProvision, emmg_->GetRequest(),
sizeof(kTestEmptyEmmgDataProvision)));
}
TEST_F(EmmgTest,
BuildDataProvisionFailedWhenEntitlementKeyIdLengthExceedLimit) {
config_.entitlement_key_ids = {"fakeKeyId1KeyId1", "fakeKeyId2KeyId2KeyId2"};
emmg_ = absl::make_unique<TestableEmmg>(&config_);
emmg_->PublicBuildDataProvision();
EXPECT_EQ(0, memcmp(kTestEmptyEmmgDataProvision, emmg_->GetRequest(),
sizeof(kTestEmptyEmmgDataProvision)));
}
TEST_F(EmmgTest, BuildStreamCloseRequest) { TEST_F(EmmgTest, BuildStreamCloseRequest) {
emmg_->PublicBuildStreamCloseRequest(); emmg_->PublicBuildStreamCloseRequest();
EXPECT_EQ(0, memcmp(kTestEmmgStreamCloseRequest, emmg_->GetRequest(), EXPECT_EQ(0, memcmp(kTestEmmgStreamCloseRequest, emmg_->GetRequest(),

View File

@@ -9,6 +9,7 @@
#include "media_cas_packager_sdk/public/wv_cas_ca_descriptor.h" #include "media_cas_packager_sdk/public/wv_cas_ca_descriptor.h"
#include <bitset> #include <bitset>
#include <vector>
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
@@ -21,39 +22,44 @@ namespace cas {
namespace { namespace {
// Size of fixed portion of CA descriptor (before any private bytes). // Size of fixed portion of CA descriptor (before any private bytes).
static constexpr uint32_t kCaDescriptorBaseSize = 6; constexpr uint32_t kCaDescriptorBaseSize = 6;
// Size of fixed portion of CA descriptor that follows the length field. // Size of fixed portion of CA descriptor that follows the length field.
// This and the size of any private bytes must be placed in the length field. // This and the size of any private bytes must be placed in the length field.
static constexpr uint32_t kCaDescriptorBasePostLengthSize = 4; constexpr uint32_t kCaDescriptorBasePostLengthSize = 4;
// Bitfield lengths for the CA descriptor fields // Bitfield lengths for the CA descriptor fields
static constexpr int kNumBitsCaDescriptorTagField = 8; constexpr int kNumBitsCaDescriptorTagField = 8;
static constexpr int kNumBitsCaDescriptorLengthField = 8; constexpr int kNumBitsCaDescriptorLengthField = 8;
static constexpr int kNumBitsCaSystemIdField = 16; constexpr int kNumBitsCaSystemIdField = 16;
static constexpr int kNumBitsCaDescriptorReservedField = 3; constexpr int kNumBitsCaDescriptorReservedField = 3;
static constexpr int kNumBitsCaDescriptorPidField = 13; constexpr int kNumBitsCaDescriptorPidField = 13;
// Bitfield constants for the CA descriptor fields. // Bitfield constants for the CA descriptor fields.
// CA descriptor tag value, from table 2-45. // CA descriptor tag value, from table 2-45.
static constexpr uint32_t kCaDescriptorTag = 9; constexpr uint32_t kCaDescriptorTag = 9;
// CA System ID for Widevine. From table in // CA System ID for Widevine. From table in
// https://en.wikipedia.org/wiki/Conditional_access // https://en.wikipedia.org/wiki/Conditional_access
static constexpr uint32_t kWidevineCaSystemId = 0x4AD4; constexpr uint32_t kWidevineCaSystemId = 0x4AD4;
// Value for CA descriptor reserved field should be set to 1. // Value for CA descriptor reserved field should be set to 1.
static constexpr uint32_t kReservedBit = 0x0007; constexpr uint32_t kReservedBit = 0x0007;
// The range of valid PIDs, from section 2.4.3.3, and table 2-3. // The range of valid PIDs, from section 2.4.3.3, and table 2-3.
static constexpr uint32_t kMinValidPID = 0x0010; constexpr uint32_t kMinValidPID = 0x0010;
static constexpr uint32_t kMaxValidPID = 0x1FFE; constexpr uint32_t kMaxValidPID = 0x1FFE;
// Maximum number of entitlement key ids shown in private data.
constexpr uint32_t kMaxNumOfEntitlementKeyIds = 2;
// Entitlement key id length is fixed to 16 bytes.
constexpr uint16_t kEntitlementKeyIdLength = 16;
} // namespace } // namespace
Status WvCasCaDescriptor::GenerateCaDescriptor( Status WvCasCaDescriptor::GenerateCaDescriptor(
uint16_t ca_pid, const std::string& provider, const std::string& content_id, uint16_t ca_pid, const std::string& provider, const std::string& content_id,
const std::vector<std::string>& entitlement_key_ids,
std::string* serialized_ca_desc) const { std::string* serialized_ca_desc) const {
if (serialized_ca_desc == nullptr) { if (serialized_ca_desc == nullptr) {
return {error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT,
@@ -62,10 +68,25 @@ Status WvCasCaDescriptor::GenerateCaDescriptor(
if (ca_pid < kMinValidPID || ca_pid > kMaxValidPID) { if (ca_pid < kMinValidPID || ca_pid > kMaxValidPID) {
return {error::INVALID_ARGUMENT, "PID value is out of the valid range."}; return {error::INVALID_ARGUMENT, "PID value is out of the valid range."};
} }
if (entitlement_key_ids.size() > kMaxNumOfEntitlementKeyIds) {
return {error::INVALID_ARGUMENT,
absl::StrCat("Number of entitlement key ids shouldn't exceed ",
kMaxNumOfEntitlementKeyIds)};
}
for (const auto& entitlement_key_id : entitlement_key_ids) {
if (entitlement_key_id.size() != kEntitlementKeyIdLength) {
return {error::INVALID_ARGUMENT,
absl::StrCat("Entitlement key id length must be ",
kEntitlementKeyIdLength,
". The offending key id is ", entitlement_key_id)};
}
}
std::string private_data = ""; std::string private_data = "";
// Field of Entitlement_key_ids could be empty.
if (!provider.empty() && !content_id.empty()) { if (!provider.empty() && !content_id.empty()) {
private_data = GeneratePrivateData(provider, content_id); private_data =
GeneratePrivateData(provider, content_id, entitlement_key_ids);
} }
const size_t descriptor_length = const size_t descriptor_length =
@@ -107,10 +128,14 @@ size_t WvCasCaDescriptor::CaDescriptorBaseSize() const {
} }
std::string WvCasCaDescriptor::GeneratePrivateData( std::string WvCasCaDescriptor::GeneratePrivateData(
const std::string& provider, const std::string& content_id) const { const std::string& provider, const std::string& content_id,
const std::vector<std::string>& entitlement_key_ids) const {
CaDescriptorPrivateData private_data; CaDescriptorPrivateData private_data;
private_data.set_provider(provider); private_data.set_provider(provider);
private_data.set_content_id(content_id); private_data.set_content_id(content_id);
for (const auto& entitlement_key_id : entitlement_key_ids) {
private_data.add_entitlement_key_ids(entitlement_key_id);
}
return private_data.SerializeAsString(); return private_data.SerializeAsString();
} }

View File

@@ -12,6 +12,7 @@
#include <stddef.h> #include <stddef.h>
#include <string> #include <string>
#include <vector>
#include <cstdint> #include <cstdint>
#include "common/status.h" #include "common/status.h"
@@ -48,6 +49,9 @@ class WvCasCaDescriptor {
// |ca_pid| the 13-bit PID of the ECMs // |ca_pid| the 13-bit PID of the ECMs
// |provider| provider name, put in private data for client to construct pssh // |provider| provider name, put in private data for client to construct pssh
// |content_id| content ID, put in private data for client to construct pssh // |content_id| content ID, put in private data for client to construct pssh
// |entitlement_key_ids| entitlement key ids, put in private data for client
// to select entitlement keys from single fat license. This field is only used
// when client uses single fat license.
// |serialized_ca_desc| a std::string object to receive the encoded descriptor. // |serialized_ca_desc| a std::string object to receive the encoded descriptor.
// //
// Notes: // Notes:
@@ -55,10 +59,10 @@ class WvCasCaDescriptor {
// section (for an EMM stream) or into a TS Program Map Table section (for an // section (for an EMM stream) or into a TS Program Map Table section (for an
// ECM stream). The descriptor will be 6 bytes plus any bytes added as // ECM stream). The descriptor will be 6 bytes plus any bytes added as
// (user-defined) private data. // (user-defined) private data.
virtual Status GenerateCaDescriptor(uint16_t ca_pid, virtual Status GenerateCaDescriptor(
const std::string& provider, uint16_t ca_pid, const std::string& provider, const std::string& content_id,
const std::string& content_id, const std::vector<std::string>& entitlement_key_ids,
std::string* serialized_ca_desc) const; std::string* serialized_ca_desc) const;
// Return the base size (before private data is added) of the CA // Return the base size (before private data is added) of the CA
// descriptor. The user can call this to plan the layout of the Table section // descriptor. The user can call this to plan the layout of the Table section
@@ -66,8 +70,9 @@ class WvCasCaDescriptor {
virtual size_t CaDescriptorBaseSize() const; virtual size_t CaDescriptorBaseSize() const;
// Return private data in the CA descriptor. // Return private data in the CA descriptor.
virtual std::string GeneratePrivateData(const std::string& provider, virtual std::string GeneratePrivateData(
const std::string& content_id) const; const std::string& provider, const std::string& content_id,
const std::vector<std::string>& entitlement_key_ids) const;
}; };
} // namespace cas } // namespace cas

View File

@@ -20,9 +20,11 @@ namespace cas {
namespace { namespace {
// Random value for PID // Random value for PID
static constexpr int kTestPid = 50; constexpr int kTestPid = 50;
static constexpr char kProvider[] = "widevine_test"; constexpr char kProvider[] = "widevine_test";
static constexpr char kContentId[] = "1234"; constexpr char kContentId[] = "1234";
const std::vector<std::string>* const kEntitlementKeyIds =
new std::vector<std::string>({"fakekey1fakekey1", "fakekey2fakekey2"});
} // namespace } // namespace
@@ -31,6 +33,7 @@ class WvCasCaDescriptorTest : public Test {
WvCasCaDescriptorTest() {} WvCasCaDescriptorTest() {}
WvCasCaDescriptor ca_descriptor_; WvCasCaDescriptor ca_descriptor_;
std::string actual_ca_descriptor_; std::string actual_ca_descriptor_;
std::vector<std::string> entitlement_key_ids_;
}; };
TEST_F(WvCasCaDescriptorTest, BaseSize) { TEST_F(WvCasCaDescriptorTest, BaseSize) {
@@ -38,38 +41,46 @@ TEST_F(WvCasCaDescriptorTest, BaseSize) {
} }
TEST_F(WvCasCaDescriptorTest, BasicGoodGen) { TEST_F(WvCasCaDescriptorTest, BasicGoodGen) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); kTestPid, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xE0\x32", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xE0\x32", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, NoReturnStringFail) { TEST_F(WvCasCaDescriptorTest, NoReturnStringFail) {
EXPECT_EQ(error::INVALID_ARGUMENT, EXPECT_EQ(error::INVALID_ARGUMENT,
ca_descriptor_.GenerateCaDescriptor(kTestPid, "", "", nullptr) ca_descriptor_
.GenerateCaDescriptor(
kTestPid, /*provider=*/"", /*content_id=*/"",
/*entitlement_key_ids=*/{}, /*serialized_ca_desc=*/nullptr)
.error_code()); .error_code());
} }
TEST_F(WvCasCaDescriptorTest, PidTooLowFail) { TEST_F(WvCasCaDescriptorTest, PidTooLowFail) {
const uint32_t bad_pid = 0x10 - 1; const uint32_t bad_pid = 0x10 - 1;
EXPECT_EQ(error::INVALID_ARGUMENT, EXPECT_EQ(
ca_descriptor_ error::INVALID_ARGUMENT,
.GenerateCaDescriptor(bad_pid, "", "", &actual_ca_descriptor_) ca_descriptor_
.error_code()); .GenerateCaDescriptor(bad_pid, /*provider=*/"", /*content_id=*/"",
entitlement_key_ids_, &actual_ca_descriptor_)
.error_code());
} }
TEST_F(WvCasCaDescriptorTest, PidMinOK) { TEST_F(WvCasCaDescriptorTest, PidMinOK) {
const uint32_t min_pid = 0x10; const uint32_t min_pid = 0x10;
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(min_pid, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); min_pid, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xE0\x10", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xE0\x10", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PidMaxOK) { TEST_F(WvCasCaDescriptorTest, PidMaxOK) {
const uint32_t max_pid = 0x1FFE; const uint32_t max_pid = 0x1FFE;
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(max_pid, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); max_pid, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xff\xfe"); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xff\xfe");
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
@@ -78,83 +89,93 @@ TEST_F(WvCasCaDescriptorTest, PidTooHighFail) {
const uint32_t bad_pid = 0x1FFF; const uint32_t bad_pid = 0x1FFF;
EXPECT_EQ(error::INVALID_ARGUMENT, EXPECT_EQ(error::INVALID_ARGUMENT,
ca_descriptor_ ca_descriptor_
.GenerateCaDescriptor(bad_pid, "", "", &actual_ca_descriptor_) .GenerateCaDescriptor(
bad_pid, /*provider=*/"", /*content_id=*/"",
/*entitlement_key_ids=*/{}, &actual_ca_descriptor_)
.error_code()); .error_code());
} }
TEST_F(WvCasCaDescriptorTest, PidOneByte) { TEST_F(WvCasCaDescriptorTest, PidOneByte) {
EXPECT_OK( EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
ca_descriptor_.GenerateCaDescriptor(255, "", "", &actual_ca_descriptor_)); 255, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe0\xff", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe0\xff", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PidSecondByte) { TEST_F(WvCasCaDescriptorTest, PidSecondByte) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x1F00, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); 0x1F00, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xff\x00", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xff\x00", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PidTwelveBits) { TEST_F(WvCasCaDescriptorTest, PidTwelveBits) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0xFFF, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); 0xFFF, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xef\xff"); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xef\xff");
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PidThirteenthBit) { TEST_F(WvCasCaDescriptorTest, PidThirteenthBit) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x1000, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); 0x1000, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xf0\x00", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xf0\x00", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PidTwelthBit) { TEST_F(WvCasCaDescriptorTest, PidTwelthBit) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x800, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); 0x800, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe8\x00", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe8\x00", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PidElevenththBit) { TEST_F(WvCasCaDescriptorTest, PidElevenththBit) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x400, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); 0x400, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe4\x00", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe4\x00", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PidTenthBit) { TEST_F(WvCasCaDescriptorTest, PidTenthBit) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x200, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); 0x200, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe2\x00", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe2\x00", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PidNinthBit) { TEST_F(WvCasCaDescriptorTest, PidNinthBit) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x100, "", "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); 0x100, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe1\x00", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe1\x00", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PrivateDataOnlyProviderIgnored) { TEST_F(WvCasCaDescriptorTest, PrivateDataWithNoContentIdIgnored) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, kProvider, "", EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); kTestPid, kProvider, "", entitlement_key_ids_, &actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe0\x32", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe0\x32", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PrivateDataOnlyContentIdIgnored) { TEST_F(WvCasCaDescriptorTest, PrivateDataWithNoProviderIgnored) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, "", kContentId, EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
&actual_ca_descriptor_)); kTestPid, "", kContentId, entitlement_key_ids_, &actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe0\x32", 6); const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe0\x32", 6);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest, PrivateData) { TEST_F(WvCasCaDescriptorTest, PrivateDataWithNoEntitlementKeyIds) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, kProvider, kContentId, EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, kProvider, kContentId,
&actual_ca_descriptor_)); {}, &actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x19\x4a\xd4\xe0\x32", 6); const std::string expected_ca_descriptor("\x09\x19\x4a\xd4\xe0\x32", 6);
CaDescriptorPrivateData private_data; CaDescriptorPrivateData private_data;
private_data.set_provider(kProvider); private_data.set_provider(kProvider);
@@ -163,6 +184,44 @@ TEST_F(WvCasCaDescriptorTest, PrivateData) {
actual_ca_descriptor_); actual_ca_descriptor_);
} }
TEST_F(WvCasCaDescriptorTest,
PrivateDataFailedWhenNumberOfEntitlementKeyIdsExceedLimit) {
const std::vector<std::string> entitlement_key_ids = {
"fakekey1fakekey1", "fakekey2fakekey2", "fakekey3fakekey3"};
Status status = {error::INVALID_ARGUMENT,
"Number of entitlement key ids shouldn't exceed 2"};
EXPECT_EQ(status, ca_descriptor_.GenerateCaDescriptor(
kTestPid, kProvider, kContentId, entitlement_key_ids,
&actual_ca_descriptor_));
}
TEST_F(WvCasCaDescriptorTest,
PrivateDataFailedWhenEntitlementKeyIdLengthExceedLimit) {
const std::vector<std::string> entitlement_key_ids = {
"fakekey1fakekey1", "fakekey2fakekey2fakekey2"};
Status status = {error::INVALID_ARGUMENT,
"Entitlement key id length must be 16. The offending key id "
"is fakekey2fakekey2fakekey2"};
EXPECT_EQ(status, ca_descriptor_.GenerateCaDescriptor(
kTestPid, kProvider, kContentId, entitlement_key_ids,
&actual_ca_descriptor_));
}
TEST_F(WvCasCaDescriptorTest, PrivateData) {
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, kProvider, kContentId,
*kEntitlementKeyIds,
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x3d\x4a\xd4\xe0\x32", 6);
CaDescriptorPrivateData private_data;
private_data.set_provider(kProvider);
private_data.set_content_id(kContentId);
for (const auto& entitlementKeyId : *kEntitlementKeyIds) {
private_data.add_entitlement_key_ids(entitlementKeyId);
}
EXPECT_EQ(expected_ca_descriptor + private_data.SerializeAsString(),
actual_ca_descriptor_);
}
class FakePrivateDataCaDescriptor : public WvCasCaDescriptor { class FakePrivateDataCaDescriptor : public WvCasCaDescriptor {
public: public:
void set_private_data(std::string private_data) { void set_private_data(std::string private_data) {
@@ -170,8 +229,8 @@ class FakePrivateDataCaDescriptor : public WvCasCaDescriptor {
} }
std::string GeneratePrivateData( std::string GeneratePrivateData(
const std::string& provider, const std::string& provider, const std::string& content_id,
const std::string& content_id) const override { const std::vector<std::string>& entitlement_key_ids) const override {
return private_data_; return private_data_;
} }
@@ -183,7 +242,8 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataOneByte) {
FakePrivateDataCaDescriptor fake_descriptor; FakePrivateDataCaDescriptor fake_descriptor;
fake_descriptor.set_private_data("X"); fake_descriptor.set_private_data("X");
EXPECT_OK(fake_descriptor.GenerateCaDescriptor( EXPECT_OK(fake_descriptor.GenerateCaDescriptor(
kTestPid, kProvider, kContentId, &actual_ca_descriptor_)); kTestPid, kProvider, kContentId, *kEntitlementKeyIds,
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x05\x4a\xd4\xe0\x32X", 7); const std::string expected_ca_descriptor("\x09\x05\x4a\xd4\xe0\x32X", 7);
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
} }
@@ -193,7 +253,8 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataMultipleBytes) {
FakePrivateDataCaDescriptor fake_descriptor; FakePrivateDataCaDescriptor fake_descriptor;
fake_descriptor.set_private_data(private_data_bytes); fake_descriptor.set_private_data(private_data_bytes);
EXPECT_OK(fake_descriptor.GenerateCaDescriptor( EXPECT_OK(fake_descriptor.GenerateCaDescriptor(
kTestPid, kProvider, kContentId, &actual_ca_descriptor_)); kTestPid, kProvider, kContentId, *kEntitlementKeyIds,
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\x0e\x4a\xd4\xe0\x32", 6); const std::string expected_ca_descriptor("\x09\x0e\x4a\xd4\xe0\x32", 6);
EXPECT_EQ(expected_ca_descriptor + private_data_bytes, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor + private_data_bytes, actual_ca_descriptor_);
} }
@@ -203,7 +264,8 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataMaxNumberBytes) {
FakePrivateDataCaDescriptor fake_descriptor; FakePrivateDataCaDescriptor fake_descriptor;
fake_descriptor.set_private_data(private_data_bytes); fake_descriptor.set_private_data(private_data_bytes);
EXPECT_OK(fake_descriptor.GenerateCaDescriptor( EXPECT_OK(fake_descriptor.GenerateCaDescriptor(
kTestPid, kProvider, kContentId, &actual_ca_descriptor_)); kTestPid, kProvider, kContentId, *kEntitlementKeyIds,
&actual_ca_descriptor_));
const std::string expected_ca_descriptor("\x09\xff\x4a\xd4\xe0\x32", 6); const std::string expected_ca_descriptor("\x09\xff\x4a\xd4\xe0\x32", 6);
EXPECT_EQ(expected_ca_descriptor + private_data_bytes, actual_ca_descriptor_); EXPECT_EQ(expected_ca_descriptor + private_data_bytes, actual_ca_descriptor_);
} }
@@ -212,11 +274,12 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataTooManyBytesFail) {
const std::string private_data_bytes(252, 'X'); const std::string private_data_bytes(252, 'X');
FakePrivateDataCaDescriptor fake_descriptor; FakePrivateDataCaDescriptor fake_descriptor;
fake_descriptor.set_private_data(private_data_bytes); fake_descriptor.set_private_data(private_data_bytes);
EXPECT_EQ(error::INVALID_ARGUMENT, EXPECT_EQ(
fake_descriptor error::INVALID_ARGUMENT,
.GenerateCaDescriptor(kTestPid, kProvider, kContentId, fake_descriptor
&actual_ca_descriptor_) .GenerateCaDescriptor(kTestPid, kProvider, kContentId,
.error_code()); *kEntitlementKeyIds, &actual_ca_descriptor_)
.error_code());
} }
} // namespace cas } // namespace cas

View File

@@ -18,7 +18,7 @@
using testing::_; using testing::_;
using testing::DoAll; using testing::DoAll;
using testing::Return; using testing::Return;
using testing::SetArgumentPointee; using testing::SetArgPointee;
namespace { namespace {
@@ -59,9 +59,10 @@ class HardcodedWvCasKeyFetcher : public WvCasKeyFetcher {
const std::string& signing_iv) const std::string& signing_iv)
: WvCasKeyFetcher(signing_provider, signing_key, signing_iv) {} : WvCasKeyFetcher(signing_provider, signing_key, signing_iv) {}
~HardcodedWvCasKeyFetcher() override {} ~HardcodedWvCasKeyFetcher() override {}
MOCK_CONST_METHOD2(MakeHttpRequest, MOCK_METHOD(Status, MakeHttpRequest,
Status(const std::string& signed_request_json, (const std::string& signed_request_json,
std::string* http_response_json)); std::string* http_response_json),
(const, override));
}; };
class MockWvCasKeyFetcher : public WvCasKeyFetcher { class MockWvCasKeyFetcher : public WvCasKeyFetcher {
@@ -168,7 +169,7 @@ TEST_F(WvCasKeyFetcherTest, TestRequestEntitlementKey) {
EXPECT_EQ(signed_request_json, kSignedCasEncryptionRequest); EXPECT_EQ(signed_request_json, kSignedCasEncryptionRequest);
EXPECT_CALL(mock_key_fetcher, MakeHttpRequest(kSignedCasEncryptionRequest, _)) EXPECT_CALL(mock_key_fetcher, MakeHttpRequest(kSignedCasEncryptionRequest, _))
.WillOnce(DoAll(SetArgumentPointee<1>(std::string(kHttpResponse)), .WillOnce(DoAll(SetArgPointee<1>(std::string(kHttpResponse)),
Return(OkStatus()))); Return(OkStatus())));
std::string actual_signed_response; std::string actual_signed_response;
EXPECT_OK(mock_key_fetcher.MakeHttpRequest(signed_request_json, EXPECT_OK(mock_key_fetcher.MakeHttpRequest(signed_request_json,

View File

@@ -43,6 +43,9 @@ ABSL_FLAG(int32_t, data_id, 0, "EMMG data_id.");
ABSL_FLAG(int32_t, data_type, 1, "EMMG data_type"); ABSL_FLAG(int32_t, data_type, 1, "EMMG data_type");
ABSL_FLAG(std::string, content_provider, "", "Content provider"); ABSL_FLAG(std::string, content_provider, "", "Content provider");
ABSL_FLAG(std::string, content_id, "", "Content id"); ABSL_FLAG(std::string, content_id, "", "Content id");
ABSL_FLAG(
std::vector<std::string>, entitlement_key_ids, {},
"Comma-separated list of entitlement_key_ids to put into private data");
ABSL_FLAG(int32_t, bandwidth, 100, "Requested bandwidth in kbps"); ABSL_FLAG(int32_t, bandwidth, 100, "Requested bandwidth in kbps");
ABSL_FLAG(int32_t, max_num_message, 100, ABSL_FLAG(int32_t, max_num_message, 100,
"Maximum number of messages that can be sent"); "Maximum number of messages that can be sent");
@@ -59,6 +62,7 @@ void BuildEmmgConfig(widevine::cas::EmmgConfig *config) {
config->data_type = absl::GetFlag(FLAGS_data_type); config->data_type = absl::GetFlag(FLAGS_data_type);
config->content_provider = absl::GetFlag(FLAGS_content_provider); config->content_provider = absl::GetFlag(FLAGS_content_provider);
config->content_id = absl::GetFlag(FLAGS_content_id); config->content_id = absl::GetFlag(FLAGS_content_id);
config->entitlement_key_ids = absl::GetFlag(FLAGS_entitlement_key_ids);
config->bandwidth = absl::GetFlag(FLAGS_bandwidth); config->bandwidth = absl::GetFlag(FLAGS_bandwidth);
config->max_num_message = absl::GetFlag(FLAGS_max_num_message); config->max_num_message = absl::GetFlag(FLAGS_max_num_message);
} }

View File

@@ -18,6 +18,7 @@ filegroup(
proto_library( proto_library(
name = "media_cas_encryption_proto", name = "media_cas_encryption_proto",
srcs = ["media_cas_encryption.proto"], srcs = ["media_cas_encryption.proto"],
deps = ["hash_algorithm_proto"],
) )
cc_proto_library( cc_proto_library(
@@ -34,3 +35,18 @@ cc_proto_library(
name = "media_cas_cc_proto", name = "media_cas_cc_proto",
deps = [":media_cas_proto"], deps = [":media_cas_proto"],
) )
proto_library(
name = "hash_algorithm_proto",
srcs = ["hash_algorithm.proto"],
)
cc_proto_library(
name = "hash_algorithm_cc_proto",
deps = [":hash_algorithm_proto"],
)
java_proto_library(
name = "hash_algorithm_java_proto",
deps = [":hash_algorithm_proto"],
)

View File

@@ -0,0 +1,20 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
syntax = "proto3";
package widevine;
// LINT.IfChange
enum HashAlgorithmProto {
// Unspecified hash algorithm: SHA_256 shall be used for ECC based algorithms
// and SHA_1 shall be used otherwise.
HASH_ALGORITHM_UNSPECIFIED = 0;
HASH_ALGORITHM_SHA_1 = 1;
HASH_ALGORITHM_SHA_256 = 2;
}

View File

@@ -18,4 +18,35 @@ message CaDescriptorPrivateData {
// Content ID. // Content ID.
optional bytes content_id = 2; optional bytes content_id = 2;
// Entitlement key IDs for current content per track. Each track will allow up
// to 2 entitlement key ids (odd and even entitlement keys).
repeated bytes entitlement_key_ids = 3;
}
// Widevine fingerprinting.
message Fingerprinting {
// Channels that will be applied with the controls.
repeated bytes channels = 1;
// Fingerprinting controls are opaque to Widevine.
optional bytes control = 2;
}
// Widevine service blocking.
message ServiceBlocking {
// Channels that will be blocked.
repeated bytes channels = 1;
// Device groups that will be blocked. Group definition is opaque to Widevine.
repeated bytes device_groups = 2;
// Blocking start time in seconds since epoch. Start time is "immediate" if
// this field is not set.
optional int64 start_time_sec = 3;
// Required. Blocking end time in seconds since epoch.
optional int64 end_time_sec = 4;
}
// The payload field for an EMM.
message EmmPayload {
repeated Fingerprinting fingerprinting = 1;
repeated ServiceBlocking service_blocking = 2;
} }

View File

@@ -12,8 +12,11 @@ syntax = "proto2";
package widevine; package widevine;
import "protos/public/hash_algorithm.proto";
option java_package = "com.google.video.widevine.mediacasencryption"; option java_package = "com.google.video.widevine.mediacasencryption";
message CasEncryptionRequest { message CasEncryptionRequest {
optional bytes content_id = 1; optional bytes content_id = 1;
optional string provider = 2; optional string provider = 2;
@@ -23,6 +26,10 @@ message CasEncryptionRequest {
// return one key for EVEN and one key for ODD, otherwise only a single key is // return one key for EVEN and one key for ODD, otherwise only a single key is
// returned. // returned.
optional bool key_rotation = 4; optional bool key_rotation = 4;
// Optional value which can be used to indicate a group.
// If present the CasEncryptionResponse will return key based on the group
// id.
optional bytes group_id = 5;
} }
message CasEncryptionResponse { message CasEncryptionResponse {
@@ -54,6 +61,8 @@ message CasEncryptionResponse {
optional string status_message = 2; optional string status_message = 2;
optional bytes content_id = 3; optional bytes content_id = 3;
repeated KeyInfo entitlement_keys = 4; repeated KeyInfo entitlement_keys = 4;
// If this is a group key license, this is the group identifier.
optional bytes group_id = 5;
} }
message SignedCasEncryptionRequest { message SignedCasEncryptionRequest {
@@ -61,6 +70,8 @@ message SignedCasEncryptionRequest {
optional bytes signature = 2; optional bytes signature = 2;
// Identifies the entity sending / signing the request. // Identifies the entity sending / signing the request.
optional string signer = 3; optional string signer = 3;
// Optional field that indicates the hash algorithm used in signature scheme.
optional HashAlgorithmProto hash_algorithm = 4;
} }
message SignedCasEncryptionResponse { message SignedCasEncryptionResponse {

View File

@@ -17,7 +17,9 @@ namespace util {
class ErrorSpace { class ErrorSpace {
public: public:
std::string SpaceName() const { return space_name_func_(this); } std::string SpaceName() const { return space_name_func_(this); }
std::string String(int code) const { return code_to_string_func_(this, code); } std::string String(int code) const {
return code_to_string_func_(this, code);
}
protected: protected:
// typedef instead of using statements for SWIG compatibility. // typedef instead of using statements for SWIG compatibility.