Update Simulcrypt ECMg
This commit is contained in:
118
common/BUILD
118
common/BUILD
@@ -16,10 +16,30 @@ filegroup(
|
||||
name = "binary_release_files",
|
||||
srcs = [
|
||||
"certificate_type.h",
|
||||
"default_device_security_profile_list.h",
|
||||
"security_profile_list.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(
|
||||
name = "content_id_util",
|
||||
srcs = ["content_id_util.cc"],
|
||||
@@ -27,6 +47,7 @@ cc_library(
|
||||
deps = [
|
||||
":error_space",
|
||||
":status",
|
||||
"//base",
|
||||
"//license_server_sdk/internal:sdk",
|
||||
"//protos/public:errors_cc_proto",
|
||||
"//protos/public:external_license_cc_proto",
|
||||
@@ -67,9 +88,14 @@ cc_library(
|
||||
hdrs = ["security_profile_list.h"],
|
||||
deps = [
|
||||
":client_id_util",
|
||||
":device_status_list",
|
||||
"//base",
|
||||
"//external:protobuf",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//protos/public:client_identification_cc_proto",
|
||||
"//protos/public:device_certificate_status_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:security_profile_cc_proto",
|
||||
],
|
||||
@@ -80,6 +106,7 @@ cc_test(
|
||||
timeout = "short",
|
||||
srcs = ["security_profile_list_test.cc"],
|
||||
deps = [
|
||||
":client_id_util",
|
||||
":security_profile_list",
|
||||
"//base",
|
||||
"//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(
|
||||
name = "status",
|
||||
srcs = ["status.cc"],
|
||||
@@ -120,6 +181,8 @@ cc_library(
|
||||
"certificate_client_cert.cc",
|
||||
"certificate_client_cert.h",
|
||||
"client_cert.cc",
|
||||
"dual_certificate_client_cert.cc",
|
||||
"dual_certificate_client_cert.h",
|
||||
"keybox_client_cert.cc",
|
||||
],
|
||||
hdrs = [
|
||||
@@ -132,6 +195,7 @@ cc_library(
|
||||
":ec_key",
|
||||
":ec_util",
|
||||
":error_space",
|
||||
":hash_algorithm",
|
||||
":openssl_util",
|
||||
":random_util",
|
||||
":rsa_key",
|
||||
@@ -155,8 +219,11 @@ cc_test(
|
||||
srcs = ["client_cert_test.cc"],
|
||||
deps = [
|
||||
":client_cert",
|
||||
":ec_key",
|
||||
":ec_test_keys",
|
||||
":error_space",
|
||||
":hash_algorithm",
|
||||
":hash_algorithm_util",
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
":sha_util",
|
||||
@@ -179,6 +246,8 @@ cc_library(
|
||||
":client_cert",
|
||||
":drm_service_certificate",
|
||||
":error_space",
|
||||
":hash_algorithm",
|
||||
":hash_algorithm_util",
|
||||
":rsa_key",
|
||||
":status",
|
||||
"//base",
|
||||
@@ -211,6 +280,8 @@ cc_test(
|
||||
deps = [
|
||||
":client_cert",
|
||||
":device_status_list",
|
||||
":hash_algorithm",
|
||||
":hash_algorithm_util",
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
":status",
|
||||
@@ -235,6 +306,8 @@ cc_library(
|
||||
":certificate_type",
|
||||
":ec_key",
|
||||
":error_space",
|
||||
":hash_algorithm",
|
||||
":hash_algorithm_util",
|
||||
":rsa_key",
|
||||
":sha_util",
|
||||
":signer_public_key",
|
||||
@@ -258,6 +331,8 @@ cc_test(
|
||||
":ec_key",
|
||||
":ec_test_keys",
|
||||
":error_space",
|
||||
":hash_algorithm",
|
||||
":hash_algorithm_util",
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
":test_drm_certificates",
|
||||
@@ -338,9 +413,12 @@ cc_library(
|
||||
srcs = ["rsa_key.cc"],
|
||||
hdrs = ["rsa_key.h"],
|
||||
deps = [
|
||||
":hash_algorithm",
|
||||
":rsa_util",
|
||||
":sha_util",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
@@ -371,6 +449,7 @@ cc_library(
|
||||
testonly = 1,
|
||||
hdrs = ["mock_rsa_key.h"],
|
||||
deps = [
|
||||
":hash_algorithm",
|
||||
":rsa_key",
|
||||
"//testing:gunit",
|
||||
],
|
||||
@@ -384,9 +463,11 @@ cc_library(
|
||||
"ec_util.h",
|
||||
],
|
||||
deps = [
|
||||
":hash_algorithm",
|
||||
":openssl_util",
|
||||
":private_key_util",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/memory",
|
||||
"//external:openssl",
|
||||
],
|
||||
@@ -415,9 +496,11 @@ cc_library(
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":ec_util",
|
||||
":hash_algorithm",
|
||||
":openssl_util",
|
||||
":sha_util",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/memory",
|
||||
"//external:openssl",
|
||||
],
|
||||
@@ -671,6 +754,7 @@ cc_library(
|
||||
hdrs = ["signature_util.h"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":hash_algorithm",
|
||||
":rsa_key",
|
||||
":sha_util",
|
||||
":status",
|
||||
@@ -774,6 +858,7 @@ cc_library(
|
||||
":status",
|
||||
":x509_cert",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//protos/public:client_identification_cc_proto",
|
||||
@@ -795,6 +880,7 @@ cc_library(
|
||||
":rsa_util",
|
||||
":status",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//util/gtl:map_util",
|
||||
@@ -813,6 +899,7 @@ cc_test(
|
||||
":aes_cbc_util",
|
||||
":drm_root_certificate",
|
||||
":drm_service_certificate",
|
||||
":hash_algorithm_util",
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
":rsa_util",
|
||||
@@ -849,6 +936,7 @@ cc_library(
|
||||
":rsa_key",
|
||||
":status",
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//external:openssl",
|
||||
@@ -887,6 +975,7 @@ cc_library(
|
||||
deps = [
|
||||
":certificate_type",
|
||||
":error_space",
|
||||
":hash_algorithm_util",
|
||||
":rsa_key",
|
||||
":status",
|
||||
":x509_cert",
|
||||
@@ -901,6 +990,7 @@ cc_test(
|
||||
timeout = "short",
|
||||
srcs = ["vmp_checker_test.cc"],
|
||||
deps = [
|
||||
":hash_algorithm_util",
|
||||
":rsa_key",
|
||||
":vmp_checker",
|
||||
"//base",
|
||||
@@ -1012,6 +1102,7 @@ cc_library(
|
||||
hdrs = ["signer_public_key.h"],
|
||||
deps = [
|
||||
":ec_key",
|
||||
":hash_algorithm",
|
||||
":rsa_key",
|
||||
"@abseil_repo//absl/memory",
|
||||
"//protos/public:drm_certificate_cc_proto",
|
||||
@@ -1024,6 +1115,7 @@ cc_test(
|
||||
deps = [
|
||||
":ec_key",
|
||||
":ec_test_keys",
|
||||
":hash_algorithm",
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
":signer_public_key",
|
||||
@@ -1041,3 +1133,29 @@ cc_library(
|
||||
"//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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/aes_cbc_util.h"
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
|
||||
@@ -66,10 +66,11 @@ class ClientCertAlgorithmRSA : public ClientCertAlgorithm {
|
||||
}
|
||||
|
||||
Status VerifySignature(const std::string& message,
|
||||
HashAlgorithm hash_algorithm,
|
||||
const std::string& signature) const override {
|
||||
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 OkStatus();
|
||||
@@ -143,10 +144,12 @@ class ClientCertAlgorithmECC : public ClientCertAlgorithm {
|
||||
}
|
||||
|
||||
Status VerifySignature(const std::string& message,
|
||||
HashAlgorithm hash_algorithm,
|
||||
const std::string& signature) const override {
|
||||
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 OkStatus();
|
||||
@@ -165,7 +168,7 @@ class ClientCertAlgorithmECC : public ClientCertAlgorithm {
|
||||
}
|
||||
|
||||
SignedMessage::SessionKeyType session_key_type() const override {
|
||||
return SignedMessage::EPHERMERAL_ECC_PUBLIC_KEY;
|
||||
return SignedMessage::EPHEMERAL_ECC_PUBLIC_KEY;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -259,11 +262,11 @@ Status CertificateClientCert::Initialize(
|
||||
}
|
||||
|
||||
Status CertificateClientCert::VerifySignature(
|
||||
const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) const {
|
||||
const std::string& message, HashAlgorithm hash_algorithm,
|
||||
const std::string& signature, ProtocolVersion protocol_version) const {
|
||||
return algorithm_->VerifySignature(
|
||||
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||
signature);
|
||||
hash_algorithm, signature);
|
||||
}
|
||||
|
||||
void CertificateClientCert::GenerateSigningKey(
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define COMMON_CERTIFICATE_CLIENT_CERT_H_
|
||||
|
||||
#include "common/client_cert.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
@@ -30,6 +31,7 @@ class ClientCertAlgorithm {
|
||||
// Verify the |signature| of an incoming request |message| using the public
|
||||
// key from the drm certificate.
|
||||
virtual Status VerifySignature(const std::string& message,
|
||||
HashAlgorithm hash_algorithm,
|
||||
const std::string& signature) const = 0;
|
||||
|
||||
// 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);
|
||||
|
||||
Status VerifySignature(const std::string& message,
|
||||
HashAlgorithm hash_algorithm,
|
||||
const std::string& signature,
|
||||
ProtocolVersion protocol_version) const override;
|
||||
|
||||
@@ -70,6 +73,7 @@ class CertificateClientCert : public ClientCert {
|
||||
SignedMessage::SessionKeyType key_type() const override {
|
||||
return algorithm_->session_key_type();
|
||||
}
|
||||
bool using_dual_certificate() const override { return false; }
|
||||
const std::string& serial_number() const override {
|
||||
return device_cert_.serial_number();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/certificate_client_cert.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "common/dual_certificate_client_cert.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/keybox_client_cert.h"
|
||||
#include "common/random_util.h"
|
||||
@@ -52,23 +53,34 @@ uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
|
||||
return WvmTokenHandler::GetSystemId(keybox_bytes);
|
||||
}
|
||||
|
||||
Status ClientCert::Create(
|
||||
const DrmRootCertificate* root_certificate,
|
||||
widevine::ClientIdentification::TokenType token_type,
|
||||
const std::string& token, std::unique_ptr<ClientCert>* client_cert) {
|
||||
Status ClientCert::Create(const DrmRootCertificate* root_certificate,
|
||||
const widevine::ClientIdentification& client_id,
|
||||
std::unique_ptr<ClientCert>* client_cert) {
|
||||
CHECK(client_cert);
|
||||
Status status;
|
||||
switch (token_type) {
|
||||
|
||||
switch (client_id.type()) {
|
||||
case ClientIdentification::KEYBOX:
|
||||
return CreateWithKeybox(token, client_cert);
|
||||
|
||||
return CreateWithKeybox(client_id.token(), client_cert);
|
||||
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:
|
||||
return Status(error_space, error::UNIMPLEMENTED,
|
||||
"client-type-not-implemented");
|
||||
}
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
@@ -78,6 +90,7 @@ Status ClientCert::CreateWithDrmCertificate(
|
||||
const DrmRootCertificate* root_certificate,
|
||||
const std::string& drm_certificate,
|
||||
std::unique_ptr<ClientCert>* client_cert) {
|
||||
CHECK(root_certificate);
|
||||
CHECK(client_cert);
|
||||
auto device_cert = absl::make_unique<CertificateClientCert>();
|
||||
Status status = device_cert->Initialize(root_certificate, drm_certificate);
|
||||
@@ -87,6 +100,22 @@ Status ClientCert::CreateWithDrmCertificate(
|
||||
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,
|
||||
std::unique_ptr<ClientCert>* client_cert) {
|
||||
CHECK(client_cert);
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
#include <memory>
|
||||
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "common/status.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
@@ -24,17 +27,10 @@ class ClientCert {
|
||||
ClientCert() = default;
|
||||
|
||||
public:
|
||||
// Creates a ClientCert from the |token|. The type of ClientCert created is
|
||||
// determined by the |token_type|.
|
||||
static Status Create(
|
||||
const DrmRootCertificate* root_certificate,
|
||||
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 from the supplied |client_id|.
|
||||
static Status Create(const DrmRootCertificate* root_certificate,
|
||||
const widevine::ClientIdentification& client_id,
|
||||
std::unique_ptr<ClientCert>* client_cert);
|
||||
|
||||
// Creates a Device Certificate based ClientCert.
|
||||
static Status CreateWithDrmCertificate(
|
||||
@@ -42,6 +38,21 @@ class ClientCert {
|
||||
const std::string& drm_certificate,
|
||||
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;
|
||||
ClientCert(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
|
||||
// is valid.
|
||||
virtual Status VerifySignature(const std::string& message,
|
||||
HashAlgorithm hash_algorithm,
|
||||
const std::string& signature,
|
||||
ProtocolVersion protocol_version) const = 0;
|
||||
|
||||
@@ -61,6 +73,7 @@ class ClientCert {
|
||||
virtual const std::string& encrypted_key() const = 0;
|
||||
virtual const std::string& key() 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& service_id() const = 0;
|
||||
virtual const std::string& signing_key() const = 0;
|
||||
@@ -71,6 +84,14 @@ class ClientCert {
|
||||
virtual widevine::ClientIdentification::TokenType type() const = 0;
|
||||
virtual const std::string& encrypted_unique_id() 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
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
#include "common/client_cert.h"
|
||||
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/ec_key.h"
|
||||
#include "common/ec_test_keys.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/rsa_key.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 kProvisionerSigner = DrmCertificate::PROVISIONER;
|
||||
|
||||
const HashAlgorithm kSha256 = HashAlgorithm::kSha256;
|
||||
|
||||
// TODO(user): Change these tests to use on-the-fly generated intermediate
|
||||
// and device certificates based on RsaTestKeys.
|
||||
// TODO(user): Add testcase(s) CreateSignature,
|
||||
// and GenerateSigningKey.
|
||||
|
||||
class ClientCertTest
|
||||
: public ::testing::TestWithParam<DrmCertificate::Algorithm> {
|
||||
: public ::testing::TestWithParam<
|
||||
std::tuple<DrmCertificate::Algorithm, DrmCertificate::Algorithm>> {
|
||||
public:
|
||||
~ClientCertTest() override = default;
|
||||
void SetUp() override {
|
||||
@@ -74,16 +81,30 @@ class ClientCertTest
|
||||
class TestCertificateAndData {
|
||||
public:
|
||||
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_;
|
||||
TestCertificateAndData(const std::string& certificate,
|
||||
const std::string& expected_serial_number,
|
||||
uint32_t expected_system_id, Status expected_status)
|
||||
: certificate_(certificate),
|
||||
expected_serial_number_(expected_serial_number),
|
||||
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,
|
||||
@@ -94,31 +115,32 @@ class ClientCertTest
|
||||
|
||||
void GenerateSignature(const std::string& message,
|
||||
const std::string& private_key,
|
||||
std::string* signature);
|
||||
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate,
|
||||
SignedDrmCertificate* signer,
|
||||
const std::string& private_key);
|
||||
DrmCertificate* GenerateProvisionerCertificate(
|
||||
HashAlgorithm hash_algorithm, std::string* signature);
|
||||
std::unique_ptr<SignedDrmCertificate> SignCertificate(
|
||||
const DrmCertificate& certificate, const SignedDrmCertificate* signer,
|
||||
const std::string& private_key);
|
||||
std::unique_ptr<DrmCertificate> GenerateProvisionerCertificate(
|
||||
uint32_t system_id, const std::string& serial_number,
|
||||
const std::string& provider_id);
|
||||
SignedDrmCertificate* GenerateSignedProvisionerCertificate(
|
||||
std::unique_ptr<SignedDrmCertificate> GenerateSignedProvisionerCertificate(
|
||||
uint32_t system_id, const std::string& serial_number,
|
||||
const std::string& service_id);
|
||||
DrmCertificate* GenerateIntermediateCertificate(
|
||||
std::unique_ptr<DrmCertificate> GenerateIntermediateCertificate(
|
||||
uint32_t system_id, const std::string& serial_number);
|
||||
SignedDrmCertificate* GenerateSignedIntermediateCertificate(
|
||||
std::unique_ptr<SignedDrmCertificate> GenerateSignedIntermediateCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
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,
|
||||
DrmCertificate::Algorithm = DrmCertificate::RSA);
|
||||
SignedDrmCertificate* GenerateSignedDrmCertificate(
|
||||
std::unique_ptr<SignedDrmCertificate> GenerateSignedDrmCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number,
|
||||
DrmCertificate::Algorithm = DrmCertificate::RSA);
|
||||
|
||||
std::string GetPublicKeyByCertType(DrmCertificate::Type cert_type);
|
||||
std::string GetPrivateKeyByCertType(DrmCertificate::Type cert_type);
|
||||
std::string GetECCPrivateKey(DrmCertificate::Algorithm algorithm);
|
||||
std::string GetECCPublicKey(DrmCertificate::Algorithm algorithm);
|
||||
|
||||
RsaTestKeys test_rsa_keys_;
|
||||
@@ -136,8 +158,11 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||
Status status;
|
||||
std::unique_ptr<ClientCert> keybox_cert;
|
||||
|
||||
status = ClientCert::Create(root_cert_.get(), ClientIdentification::KEYBOX,
|
||||
expectation.token_, &keybox_cert);
|
||||
ClientIdentification client_id;
|
||||
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) {
|
||||
ASSERT_EQ(OkStatus(), status);
|
||||
ASSERT_TRUE(keybox_cert.get());
|
||||
@@ -163,12 +188,25 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
|
||||
// Test validation of a valid request.
|
||||
Status status;
|
||||
std::unique_ptr<ClientCert> drm_certificate_cert;
|
||||
status = ClientCert::Create(root_cert_.get(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
expectation.certificate_, &drm_certificate_cert);
|
||||
ClientIdentification client_id;
|
||||
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||
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);
|
||||
if (expectation.expected_status_.ok()) {
|
||||
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) {
|
||||
ASSERT_EQ(expectation.expected_serial_number_,
|
||||
drm_certificate_cert->signer_serial_number());
|
||||
@@ -182,26 +220,29 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
|
||||
|
||||
void ClientCertTest::GenerateSignature(const std::string& message,
|
||||
const std::string& private_key,
|
||||
HashAlgorithm hash_algorithm,
|
||||
std::string* signature) {
|
||||
std::unique_ptr<RsaPrivateKey> rsa_private_key(
|
||||
RsaPrivateKey::Create(private_key));
|
||||
ASSERT_TRUE(rsa_private_key != nullptr);
|
||||
rsa_private_key->GenerateSignature(message, signature);
|
||||
rsa_private_key->GenerateSignature(message, hash_algorithm, signature);
|
||||
}
|
||||
|
||||
// The caller relinquishes ownership of |signer|, which may also be nullptr.
|
||||
SignedDrmCertificate* ClientCertTest::SignCertificate(
|
||||
const DrmCertificate& certificate, SignedDrmCertificate* signer,
|
||||
// The caller retains ownership of |signer|, which may also be nullptr.
|
||||
std::unique_ptr<SignedDrmCertificate> ClientCertTest::SignCertificate(
|
||||
const DrmCertificate& certificate, const SignedDrmCertificate* signer,
|
||||
const std::string& private_key) {
|
||||
std::unique_ptr<SignedDrmCertificate> signed_certificate(
|
||||
new SignedDrmCertificate);
|
||||
signed_certificate->set_drm_certificate(certificate.SerializeAsString());
|
||||
GenerateSignature(signed_certificate->drm_certificate(), private_key,
|
||||
signed_certificate->mutable_signature());
|
||||
GenerateSignature(
|
||||
signed_certificate->drm_certificate(), private_key,
|
||||
HashAlgorithmProtoToEnum(signed_certificate->hash_algorithm()),
|
||||
signed_certificate->mutable_signature());
|
||||
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(
|
||||
@@ -224,6 +265,21 @@ std::string ClientCertTest::GetPrivateKeyByCertType(
|
||||
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(
|
||||
DrmCertificate::Algorithm algorithm) {
|
||||
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) {
|
||||
std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate);
|
||||
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
|
||||
@@ -248,10 +304,11 @@ DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
|
||||
GetPublicKeyByCertType(DrmCertificate::DEVICE_MODEL));
|
||||
intermediate_certificate->set_system_id(system_id);
|
||||
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,
|
||||
const std::string& serial_number, DrmCertificate::Type signer_cert_type) {
|
||||
std::unique_ptr<DrmCertificate> intermediate_certificate(
|
||||
@@ -261,7 +318,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
|
||||
GetPrivateKeyByCertType(signer_cert_type));
|
||||
}
|
||||
|
||||
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
||||
std::unique_ptr<DrmCertificate> ClientCertTest::GenerateDrmCertificate(
|
||||
uint32_t system_id, const std::string& serial_number,
|
||||
DrmCertificate::Algorithm algorithm) {
|
||||
std::unique_ptr<DrmCertificate> drm_certificate(new DrmCertificate);
|
||||
@@ -274,10 +331,11 @@ DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
||||
: GetECCPublicKey(algorithm));
|
||||
drm_certificate->set_creation_time_seconds(4321);
|
||||
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,
|
||||
const std::string& serial_number, DrmCertificate::Algorithm algorithm) {
|
||||
std::unique_ptr<DrmCertificate> drm_certificate(
|
||||
@@ -285,10 +343,10 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
|
||||
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(
|
||||
SignCertificate(*drm_certificate, signer,
|
||||
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,
|
||||
const std::string& provider_id) {
|
||||
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_provider_id(provider_id);
|
||||
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,
|
||||
const std::string& service_id) {
|
||||
std::unique_ptr<DrmCertificate> provisioner_certificate(
|
||||
@@ -341,22 +400,48 @@ TEST_F(ClientCertTest, BasicValidation) {
|
||||
TEST_P(ClientCertTest, BasicCertValidation) {
|
||||
const uint32_t system_id = 1234;
|
||||
const std::string serial_number("serial_number");
|
||||
std::unique_ptr<SignedDrmCertificate> signed_cert(
|
||||
GenerateSignedDrmCertificate(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id,
|
||||
serial_number, kNoSigner),
|
||||
system_id, serial_number + "-device", GetParam()));
|
||||
std::unique_ptr<SignedDrmCertificate> intermediate_certificate =
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, serial_number,
|
||||
kNoSigner);
|
||||
std::unique_ptr<SignedDrmCertificate> signed_cert =
|
||||
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(
|
||||
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;
|
||||
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
|
||||
compare_data);
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(BasicCertValidation, ClientCertTest,
|
||||
testing::Values(DrmCertificate::RSA,
|
||||
DrmCertificate::ECC_SECP256R1,
|
||||
DrmCertificate::ECC_SECP384R1,
|
||||
DrmCertificate::ECC_SECP521R1));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
BasicCertValidation, ClientCertTest,
|
||||
testing::Combine(testing::Values(DrmCertificate::RSA,
|
||||
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) {
|
||||
const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = {
|
||||
@@ -395,71 +480,80 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
||||
std::unique_ptr<SignedDrmCertificate> invalid_drm_cert(
|
||||
new SignedDrmCertificate);
|
||||
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
|
||||
GenerateSignature(invalid_drm_cert->drm_certificate(),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits(),
|
||||
invalid_drm_cert->mutable_signature());
|
||||
invalid_drm_cert->set_allocated_signer(GenerateSignedIntermediateCertificate(
|
||||
nullptr, system_id, signer_sn, kNoSigner));
|
||||
GenerateSignature(
|
||||
invalid_drm_cert->drm_certificate(),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits(),
|
||||
HashAlgorithmProtoToEnum(invalid_drm_cert->hash_algorithm()),
|
||||
invalid_drm_cert->mutable_signature());
|
||||
invalid_drm_cert->set_allocated_signer(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn,
|
||||
kNoSigner)
|
||||
.release());
|
||||
// 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");
|
||||
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(
|
||||
std::unique_ptr<SignedDrmCertificate> bad_device_public_key =
|
||||
SignCertificate(*dev_cert,
|
||||
GenerateSignedIntermediateCertificate(
|
||||
nullptr, system_id, signer_sn, kNoSigner),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
nullptr, system_id, signer_sn, kNoSigner)
|
||||
.get(),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits());
|
||||
// Invalid serialized intermediate certificate.
|
||||
signed_signer.reset(GenerateSignedIntermediateCertificate(
|
||||
nullptr, system_id, signer_sn, kNoSigner));
|
||||
signed_signer = GenerateSignedIntermediateCertificate(nullptr, system_id,
|
||||
signer_sn, kNoSigner);
|
||||
signed_signer->set_drm_certificate("bad-serialized-cert");
|
||||
GenerateSignature(signed_signer->drm_certificate(),
|
||||
test_rsa_keys_.private_test_key_1_3072_bits(),
|
||||
HashAlgorithmProtoToEnum(signed_signer->hash_algorithm()),
|
||||
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(
|
||||
SignCertificate(*dev_cert, signed_signer.release(),
|
||||
SignCertificate(*dev_cert, signed_signer.get(),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
// Invalid signer public key.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||
dev_cert = GenerateDrmCertificate(system_id, device_sn);
|
||||
signer_cert = GenerateIntermediateCertificate(system_id, signer_sn);
|
||||
signer_cert->set_public_key("bad-signer-public-key");
|
||||
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_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()));
|
||||
// Invalid device certificate signature.
|
||||
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
|
||||
GenerateSignedDrmCertificate(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn,
|
||||
kNoSigner),
|
||||
system_id, device_sn));
|
||||
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
|
||||
nullptr, system_id, signer_sn, kNoSigner)
|
||||
.get(),
|
||||
system_id, device_sn));
|
||||
bad_device_signature->set_signature("bad-signature");
|
||||
// Missing model system ID.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||
dev_cert = GenerateDrmCertificate(system_id, device_sn);
|
||||
signer_cert = GenerateIntermediateCertificate(system_id, signer_sn);
|
||||
signer_cert->clear_system_id();
|
||||
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
|
||||
*dev_cert,
|
||||
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()));
|
||||
// Missing signer serial number.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||
dev_cert = GenerateDrmCertificate(system_id, device_sn);
|
||||
signer_cert = GenerateIntermediateCertificate(system_id, signer_sn);
|
||||
signer_cert->clear_serial_number();
|
||||
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_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()));
|
||||
// Invalid serialized intermediate certificate.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signed_signer.reset(GenerateSignedIntermediateCertificate(
|
||||
nullptr, system_id, signer_sn, kNoSigner));
|
||||
dev_cert = GenerateDrmCertificate(system_id, device_sn);
|
||||
signed_signer = GenerateSignedIntermediateCertificate(nullptr, system_id,
|
||||
signer_sn, kNoSigner);
|
||||
signed_signer->set_signature("bad-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()));
|
||||
|
||||
const TestCertificateAndData kInvalidCertificate[] = {
|
||||
@@ -504,8 +598,11 @@ TEST_F(ClientCertTest, MissingPreProvKey) {
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e"));
|
||||
std::unique_ptr<ClientCert> client_cert_ptr;
|
||||
Status status = ClientCert::Create(
|
||||
root_cert_.get(), ClientIdentification::KEYBOX, token, &client_cert_ptr);
|
||||
ClientIdentification client_id;
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -516,26 +613,27 @@ TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
|
||||
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||
const std::string provisioner_serial_number("provisioner-serial-number");
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
|
||||
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert =
|
||||
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(
|
||||
signed_provisioner_cert.release(), system_id,
|
||||
intermediate_serial_number, kProvisionerSigner));
|
||||
signed_provisioner_cert.get(), system_id, intermediate_serial_number,
|
||||
kProvisionerSigner);
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, device_serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert =
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.get(), system_id,
|
||||
device_serial_number);
|
||||
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_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(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &drm_cert));
|
||||
EXPECT_OK(ClientCert::Create(root_cert_.get(), client_id, &drm_cert));
|
||||
ASSERT_TRUE(drm_cert);
|
||||
|
||||
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 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,
|
||||
service_id));
|
||||
service_id);
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
GenerateSignedIntermediateCertificate(
|
||||
signed_provisioner_cert.release(), system_id,
|
||||
intermediate_serial_number, kProvisionerSigner));
|
||||
signed_provisioner_cert.get(), system_id, intermediate_serial_number,
|
||||
kProvisionerSigner));
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, device_serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert =
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.get(), system_id,
|
||||
device_serial_number);
|
||||
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
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",
|
||||
ClientCert::Create(root_cert_.get(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr)
|
||||
ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
|
||||
.error_message());
|
||||
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_number2("intermediate-serial-number-2");
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2 =
|
||||
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
|
||||
// certificate, use another intermediate certificate. This is an invalid
|
||||
// chain and should generate an error when trying to create a client
|
||||
// certificate.
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert =
|
||||
GenerateSignedIntermediateCertificate(
|
||||
signed_intermediate_cert2.release(), system_id,
|
||||
intermediate_serial_number, kDeviceModelSigner));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, device_serial_number));
|
||||
signed_intermediate_cert2.get(), system_id,
|
||||
intermediate_serial_number, kDeviceModelSigner);
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert =
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.get(), system_id,
|
||||
device_serial_number);
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
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",
|
||||
ClientCert::Create(root_cert_.get(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr)
|
||||
ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
|
||||
.error_message());
|
||||
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 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,
|
||||
service_id));
|
||||
service_id);
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert1(
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert1 =
|
||||
GenerateSignedIntermediateCertificate(
|
||||
signed_provisioner_cert.release(), system_id,
|
||||
intermediate_serial_number1, kProvisionerSigner));
|
||||
signed_provisioner_cert.get(), system_id, intermediate_serial_number1,
|
||||
kProvisionerSigner);
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2 =
|
||||
GenerateSignedIntermediateCertificate(
|
||||
signed_intermediate_cert1.release(), system_id,
|
||||
intermediate_serial_number2, kDeviceModelSigner));
|
||||
signed_intermediate_cert1.get(), system_id,
|
||||
intermediate_serial_number2, kDeviceModelSigner);
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert2.release(),
|
||||
system_id, device_serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert =
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert2.get(), system_id,
|
||||
device_serial_number);
|
||||
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
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",
|
||||
ClientCert::Create(root_cert_.get(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr)
|
||||
ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
|
||||
.error_message());
|
||||
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 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,
|
||||
service_id));
|
||||
service_id);
|
||||
|
||||
// Use a DEVICE certificate as the intermediate certificate.
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
GenerateSignedDrmCertificate(signed_provisioner_cert.release(), system_id,
|
||||
intermediate_serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert =
|
||||
GenerateSignedDrmCertificate(signed_provisioner_cert.get(), system_id,
|
||||
intermediate_serial_number);
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_drm_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, drm_serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_drm_cert =
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.get(), system_id,
|
||||
drm_serial_number);
|
||||
std::string serialized_cert;
|
||||
signed_drm_cert->SerializeToString(&serialized_cert);
|
||||
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",
|
||||
ClientCert::Create(root_cert_.get(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr)
|
||||
ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
|
||||
.error_message());
|
||||
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 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,
|
||||
service_id));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
service_id);
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert =
|
||||
GenerateSignedIntermediateCertificate(
|
||||
signed_provisioner_cert.release(), system_id,
|
||||
intermediate_serial_number, kProvisionerSigner));
|
||||
signed_provisioner_cert.get(), system_id, intermediate_serial_number,
|
||||
kProvisionerSigner);
|
||||
std::string serialized_cert;
|
||||
signed_intermediate_cert->SerializeToString(&serialized_cert);
|
||||
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.
|
||||
EXPECT_EQ("expected-device-certificate-type",
|
||||
ClientCert::Create(root_cert_.get(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr)
|
||||
ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
|
||||
.error_message());
|
||||
EXPECT_FALSE(client_cert_ptr);
|
||||
}
|
||||
@@ -709,9 +812,10 @@ TEST_F(ClientCertTest, Protocol21WithDrmCert) {
|
||||
const char message[] = "A weekend wasted is a weekend well spent.";
|
||||
|
||||
std::unique_ptr<ClientCert> client_cert;
|
||||
ASSERT_OK(ClientCert::Create(
|
||||
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
test_drm_certs_.test_user_device_certificate(), &client_cert));
|
||||
ClientIdentification client_id;
|
||||
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||
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(
|
||||
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
||||
@@ -719,14 +823,16 @@ TEST_F(ClientCertTest, Protocol21WithDrmCert) {
|
||||
|
||||
// Success
|
||||
std::string signature;
|
||||
ASSERT_TRUE(private_key->GenerateSignature(message, &signature));
|
||||
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_1));
|
||||
ASSERT_TRUE(private_key->GenerateSignature(message, kSha256, &signature));
|
||||
EXPECT_OK(
|
||||
client_cert->VerifySignature(message, kSha256, signature, VERSION_2_1));
|
||||
|
||||
// Failure
|
||||
ASSERT_EQ(256, signature.size());
|
||||
++signature[127];
|
||||
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) {
|
||||
@@ -734,9 +840,10 @@ TEST_F(ClientCertTest, Protocol22WithDrmCert) {
|
||||
const std::string message_hash(Sha512_Hash(message));
|
||||
|
||||
std::unique_ptr<ClientCert> client_cert;
|
||||
ASSERT_OK(ClientCert::Create(
|
||||
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
test_drm_certs_.test_user_device_certificate(), &client_cert));
|
||||
ClientIdentification client_id;
|
||||
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||
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(
|
||||
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
||||
@@ -744,14 +851,17 @@ TEST_F(ClientCertTest, Protocol22WithDrmCert) {
|
||||
|
||||
// Success
|
||||
std::string signature;
|
||||
ASSERT_TRUE(private_key->GenerateSignature(message_hash, &signature));
|
||||
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_2));
|
||||
ASSERT_TRUE(
|
||||
private_key->GenerateSignature(message_hash, kSha256, &signature));
|
||||
EXPECT_OK(
|
||||
client_cert->VerifySignature(message, kSha256, signature, VERSION_2_2));
|
||||
|
||||
// Failure
|
||||
ASSERT_EQ(256, signature.size());
|
||||
++signature[127];
|
||||
EXPECT_FALSE(
|
||||
client_cert->VerifySignature(message, signature, VERSION_2_2).ok());
|
||||
client_cert->VerifySignature(message, kSha256, signature, VERSION_2_2)
|
||||
.ok());
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -22,6 +22,11 @@ namespace widevine {
|
||||
|
||||
const char kModDrmMake[] = "company_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,
|
||||
absl::string_view value) {
|
||||
|
||||
@@ -21,6 +21,10 @@ namespace widevine {
|
||||
|
||||
extern const char kModDrmMake[];
|
||||
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
|
||||
// check for duplicates.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include "common/content_id_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/status.h"
|
||||
#include "license_server_sdk/internal/parse_content_id.h"
|
||||
@@ -18,33 +19,14 @@
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// TODO(user): Move the util methods from
|
||||
// //license_server_sdk/internal/parse_content_id.h
|
||||
// into this file.
|
||||
|
||||
Status GetContentIdFromExternalLicenseRequest(
|
||||
const ExternalLicenseRequest& external_license_request,
|
||||
std::string* content_id) {
|
||||
LicenseRequest::ContentIdentification content_identification =
|
||||
external_license_request.content_id();
|
||||
WidevinePsshData widevine_pssh_data;
|
||||
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();
|
||||
WidevinePsshData pssh_data;
|
||||
Status status = ParsePsshData(external_license_request, &pssh_data);
|
||||
*content_id = pssh_data.content_id();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status GetContentIdFromSignedExternalLicenseRequest(
|
||||
const SignedMessage& signed_message, std::string* content_id) {
|
||||
if (signed_message.type() != SignedMessage::EXTERNAL_LICENSE_REQUEST) {
|
||||
@@ -61,4 +43,41 @@ Status GetContentIdFromSignedExternalLicenseRequest(
|
||||
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
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/status.h"
|
||||
#include "protos/public/external_license.pb.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
#include "protos/public/widevine_pssh.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -25,6 +26,12 @@ Status GetContentIdFromExternalLicenseRequest(
|
||||
const ExternalLicenseRequest& external_license_request,
|
||||
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
|
||||
|
||||
#endif // COMMON_CONTENT_ID_UTIL_H_
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
#include "common/content_id_util.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
@@ -23,9 +27,9 @@ const char kPlayReadyChallenge[] = "<TestPRChallenge></TestPRChallenge>";
|
||||
namespace widevine {
|
||||
|
||||
// Builds a SignedMessage that includes an ExternalLicenseRequest.
|
||||
SignedMessage BuildSignedExternalLicenseRequest(
|
||||
const ExternalLicenseRequest::RequestType type, const std::string& request,
|
||||
const std::string& content_id) {
|
||||
SignedMessage BuildSignedExternalLicenseRequest(const ExternalLicenseType type,
|
||||
const std::string& request,
|
||||
const std::string& content_id) {
|
||||
ExternalLicenseRequest external_license_request;
|
||||
external_license_request.set_request_type(type);
|
||||
external_license_request.set_request(request);
|
||||
@@ -47,9 +51,8 @@ SignedMessage BuildSignedExternalLicenseRequest(
|
||||
TEST(ContentIdUtil, GetContentId) {
|
||||
std::string content_id;
|
||||
EXPECT_OK(GetContentIdFromSignedExternalLicenseRequest(
|
||||
BuildSignedExternalLicenseRequest(
|
||||
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST,
|
||||
kPlayReadyChallenge, kContentId),
|
||||
BuildSignedExternalLicenseRequest(PLAYREADY_LICENSE_NEW,
|
||||
kPlayReadyChallenge, kContentId),
|
||||
&content_id));
|
||||
EXPECT_EQ(kContentId, content_id);
|
||||
}
|
||||
@@ -57,8 +60,7 @@ TEST(ContentIdUtil, GetContentId) {
|
||||
TEST(ContentIdUtil, GetContentIdFailureWithIncorrectType) {
|
||||
std::string content_id;
|
||||
SignedMessage signed_message = BuildSignedExternalLicenseRequest(
|
||||
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST, kPlayReadyChallenge,
|
||||
kContentId);
|
||||
PLAYREADY_LICENSE_NEW, kPlayReadyChallenge, kContentId);
|
||||
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
|
||||
Status status =
|
||||
GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id);
|
||||
@@ -69,8 +71,7 @@ TEST(ContentIdUtil, GetContentIdFailureWithIncorrectType) {
|
||||
TEST(ContentIdUtil, GetContentIdFailureWithInvalidExternalLicenseRequest) {
|
||||
std::string content_id;
|
||||
SignedMessage signed_message = BuildSignedExternalLicenseRequest(
|
||||
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST, kPlayReadyChallenge,
|
||||
kContentId);
|
||||
PLAYREADY_LICENSE_NEW, kPlayReadyChallenge, kContentId);
|
||||
signed_message.set_msg("Invalid payload");
|
||||
Status status =
|
||||
GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id);
|
||||
|
||||
@@ -42,7 +42,7 @@ bool GetCoreProvisioningResponse(
|
||||
}
|
||||
|
||||
bool GetCoreRenewalOrReleaseLicenseResponse(
|
||||
const std::string& request_core_message,
|
||||
uint64_t renewal_duration_seconds, const std::string& request_core_message,
|
||||
std::string* response_core_message) {
|
||||
oemcrypto_core_message::ODK_RenewalRequest odk_renewal_request;
|
||||
if (request_core_message.empty()) {
|
||||
@@ -52,11 +52,6 @@ bool GetCoreRenewalOrReleaseLicenseResponse(
|
||||
&odk_renewal_request)) {
|
||||
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(
|
||||
odk_renewal_request, renewal_duration_seconds, response_core_message);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ bool GetCoreProvisioningResponse(
|
||||
// Gets the |response_core_message| by parsing |request_core_message| for
|
||||
// release and renewal response. The output is held in |response_core_message|.
|
||||
bool GetCoreRenewalOrReleaseLicenseResponse(
|
||||
const std::string& request_core_message,
|
||||
uint64_t renewal_duration_seconds, const std::string& request_core_message,
|
||||
std::string* response_core_message);
|
||||
|
||||
// Gets the |response_core_message| by parsing |request_core_message| and
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/crypto_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/cmac.h"
|
||||
@@ -37,6 +38,8 @@ const char kGroupKeyLabel[] = "GROUP_ENCRYPTION";
|
||||
// a real group master key in keystore.
|
||||
// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy
|
||||
// like VerifySignatureHmacSha1.
|
||||
// TODO(user): Revert logging signature in VerifySignatureHmacSha256.
|
||||
// function.
|
||||
const char kPhonyGroupMasterKey[] = "fedcba9876543210";
|
||||
const int kAes128KeySizeBits = 128;
|
||||
const int kAes128KeySizeBytes = 16;
|
||||
|
||||
135
common/default_device_security_profile_list.cc
Normal file
135
common/default_device_security_profile_list.cc
Normal 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
|
||||
39
common/default_device_security_profile_list.h
Normal file
39
common/default_device_security_profile_list.h
Normal 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_
|
||||
186
common/default_device_security_profile_list_test.cc
Normal file
186
common/default_device_security_profile_list_test.cc
Normal 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
|
||||
@@ -30,7 +30,7 @@ bool VerifyMakeModel(const ProvisionedDeviceInfo& device_info,
|
||||
make_from_client, model_from_client)) {
|
||||
return true;
|
||||
}
|
||||
for (DeviceModel product_info : device_info.model_info()) {
|
||||
for (const DeviceModel& product_info : device_info.model_info()) {
|
||||
if (IsMatchedMakeModel(product_info.manufacturer(),
|
||||
product_info.model_name(), make_from_client,
|
||||
model_from_client)) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef COMMON_DEVICE_INFO_UTIL_H_
|
||||
#define COMMON_DEVICE_INFO_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "protos/public/provisioned_device_info.pb.h"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "common/client_cert.h"
|
||||
#include "common/drm_service_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/hash_algorithm_util.h"
|
||||
#include "common/keybox_client_cert.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/status.h"
|
||||
@@ -63,7 +64,8 @@ DeviceStatusList::~DeviceStatusList() {}
|
||||
Status DeviceStatusList::UpdateStatusList(
|
||||
const std::string& root_certificate_public_key,
|
||||
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()) {
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-status-list");
|
||||
@@ -79,7 +81,7 @@ Status DeviceStatusList::UpdateStatusList(
|
||||
"invalid-root-public-key");
|
||||
}
|
||||
if (!root_key->VerifySignature(serialized_device_certificate_status_list,
|
||||
signature)) {
|
||||
hash_algorithm, signature)) {
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"invalid-status-list-signature");
|
||||
}
|
||||
@@ -117,29 +119,11 @@ Status DeviceStatusList::UpdateStatusList(
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
|
||||
const std::string& device_manufacturer,
|
||||
ProvisionedDeviceInfo* device_info) {
|
||||
CHECK(device_info);
|
||||
|
||||
// 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");
|
||||
}
|
||||
Status DeviceStatusList::GetCertStatus(
|
||||
const ClientCert& client_cert, const std::string& make,
|
||||
const std::string& provider, bool allow_revoked_system_id,
|
||||
DeviceCertificateStatus* device_certificate_status) {
|
||||
CHECK(device_certificate_status);
|
||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||
if (expiration_period_seconds_ &&
|
||||
(GetCurrentTime() >
|
||||
@@ -149,59 +133,67 @@ Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
|
||||
}
|
||||
DeviceCertificateStatus* device_cert_status =
|
||||
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
||||
if (device_cert_status) {
|
||||
*device_info = device_cert_status->device_info();
|
||||
if (device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_REVOKED) {
|
||||
if (IsRevokedSystemIdAllowed(client_cert.system_id())) {
|
||||
LOG(WARNING) << "Allowing REVOKED device: "
|
||||
<< device_info->ShortDebugString();
|
||||
} else {
|
||||
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||
"device-certificate-revoked");
|
||||
}
|
||||
|
||||
if (device_cert_status == nullptr) {
|
||||
if (allow_unknown_devices_) {
|
||||
return OkStatus();
|
||||
}
|
||||
if ((device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
||||
!allow_test_only_devices_) {
|
||||
if (IsTestOnlyDeviceAllowed(client_cert.system_id(),
|
||||
device_manufacturer)) {
|
||||
LOG(WARNING) << "Allowing TEST_ONLY device with systemId = "
|
||||
<< client_cert.system_id()
|
||||
<< "make = " << device_manufacturer
|
||||
<< ", device info = " << device_info->ShortDebugString();
|
||||
} else {
|
||||
VLOG(2) << "Not allowing TEST ONLY device with systemId = "
|
||||
<< 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");
|
||||
}
|
||||
return client_cert.SystemIdUnknownError();
|
||||
}
|
||||
*device_certificate_status = *device_cert_status;
|
||||
|
||||
if (device_cert_status->status() == DeviceCertificateStatus::STATUS_REVOKED) {
|
||||
if (IsRevokedSystemIdAllowed(client_cert.system_id()) ||
|
||||
allow_revoked_system_id) {
|
||||
LOG(WARNING) << "Allowing REVOKED device: "
|
||||
<< device_cert_status->device_info().ShortDebugString();
|
||||
} else {
|
||||
return client_cert.SystemIdRevokedError();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -268,29 +260,55 @@ void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) {
|
||||
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_);
|
||||
if (device_list.empty()) {
|
||||
allowed_test_only_devices_.clear();
|
||||
if (device_list_by_make.empty()) {
|
||||
allowed_test_only_devices_by_make_.clear();
|
||||
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 =
|
||||
absl::StrSplit(device, ':');
|
||||
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)), "*");
|
||||
VLOG(2) << "Whitelisting TEST_ONLY device: systemId = "
|
||||
<< std::stoi(std::string(device_split.first))
|
||||
<< ", manufacturer = *";
|
||||
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
|
||||
<< std::stoi(std::string(device_split.first)) << ", make *";
|
||||
} else {
|
||||
allowed_test_only_devices_.emplace(
|
||||
allowed_test_only_devices_by_make_.emplace(
|
||||
std::stoi(std::string(device_split.first)),
|
||||
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))
|
||||
<< ", manufacturer = "
|
||||
<< absl::AsciiStrToUpper(device_split.second);
|
||||
<< ", make = " << 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;
|
||||
}
|
||||
|
||||
bool DeviceStatusList::IsTestOnlyDeviceAllowed(uint32_t system_id,
|
||||
const std::string manufacturer) {
|
||||
bool DeviceStatusList::IsTestOnlyDeviceAllowedByMake(
|
||||
uint32_t system_id, const std::string& manufacturer) {
|
||||
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_manufacturers = allowed_test_only_devices_.equal_range(system_id);
|
||||
for (auto it = allowed_manufacturers.first;
|
||||
it != allowed_manufacturers.second; ++it) {
|
||||
std::string allowed_manufacturer = (*it).second;
|
||||
if (allowed_manufacturer == "*" ||
|
||||
allowed_manufacturer == absl::AsciiStrToUpper(manufacturer)) {
|
||||
allowed_makes = allowed_test_only_devices_by_make_.equal_range(system_id);
|
||||
for (auto it = allowed_makes.first; it != allowed_makes.second; ++it) {
|
||||
std::string allowed_makes = (*it).second;
|
||||
if (allowed_makes == "*" ||
|
||||
allowed_makes == 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;
|
||||
}
|
||||
}
|
||||
@@ -321,7 +356,8 @@ bool DeviceStatusList::IsTestOnlyDeviceAllowed(uint32_t system_id,
|
||||
Status DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||
const std::string& service_response,
|
||||
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) {
|
||||
return Status(error_space, error::INVALID_ARGUMENT,
|
||||
"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
|
||||
// serialized PublishedDeviceInfo proto.
|
||||
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.
|
||||
// then attempt to parse it as a legacy payload.
|
||||
if (!status.ok()) {
|
||||
status = ExtractLegacyDeviceList(
|
||||
service_response, serialized_certificate_status_list, signature);
|
||||
status = ExtractLegacyDeviceList(service_response,
|
||||
serialized_certificate_status_list,
|
||||
hash_algorithm, signature);
|
||||
// The payload could not be parsed in either format, return the failure
|
||||
// information.
|
||||
if (!status.ok()) {
|
||||
@@ -361,7 +399,8 @@ Status DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||
|
||||
Status DeviceStatusList::ExtractLegacyDeviceList(
|
||||
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.
|
||||
// "signedList":"<b64 encoded data>"
|
||||
// where the b64 encoded data is a DeviceCertificateStatusListResponse.
|
||||
@@ -424,12 +463,13 @@ Status DeviceStatusList::ExtractLegacyDeviceList(
|
||||
// and extract the serialized status list and signature.
|
||||
return ParseLegacySignedDeviceCertificateStatusList(
|
||||
serialized_signed_certificate_status_list,
|
||||
serialized_certificate_status_list, signature);
|
||||
serialized_certificate_status_list, hash_algorithm, signature);
|
||||
}
|
||||
|
||||
Status DeviceStatusList::ExtractPublishedDevicesInfo(
|
||||
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
|
||||
// to using the correct proto from the API. This duplicate, wire-compatible
|
||||
// proto was a temporary way to workaround Proto2/Proto3 compatibility issues.
|
||||
@@ -440,6 +480,7 @@ Status DeviceStatusList::ExtractPublishedDevicesInfo(
|
||||
}
|
||||
*serialized_certificate_status_list =
|
||||
devices_info.device_certificate_status_list();
|
||||
*hash_algorithm = HashAlgorithmProtoToEnum(devices_info.hash_algorithm());
|
||||
*signature = devices_info.signature();
|
||||
return OkStatus();
|
||||
}
|
||||
@@ -470,12 +511,12 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
||||
if (sc == nullptr) {
|
||||
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.");
|
||||
}
|
||||
const RsaPrivateKey* private_key = sc->private_key();
|
||||
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.");
|
||||
}
|
||||
std::string signature;
|
||||
@@ -490,7 +531,7 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
||||
Status DeviceStatusList::ParseLegacySignedDeviceCertificateStatusList(
|
||||
const std::string& serialized_signed_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
|
||||
// serialized_device_certificate_status_list
|
||||
SignedDeviceCertificateStatusList signed_device_list;
|
||||
@@ -509,6 +550,8 @@ Status DeviceStatusList::ParseLegacySignedDeviceCertificateStatusList(
|
||||
}
|
||||
*serialized_device_certificate_status_list =
|
||||
signed_device_list.certificate_status_list();
|
||||
*hash_algorithm =
|
||||
HashAlgorithmProtoToEnum(signed_device_list.hash_algorithm());
|
||||
*signature = signed_device_list.signature();
|
||||
return OkStatus();
|
||||
}
|
||||
@@ -532,4 +575,24 @@ bool DeviceStatusList::IsDrmCertificateRevoked(
|
||||
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
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "common/status.h"
|
||||
#include "protos/public/device_certificate_status.pb.h"
|
||||
#include "protos/public/provisioned_device_info.pb.h"
|
||||
@@ -48,7 +49,8 @@ class DeviceStatusList {
|
||||
Status UpdateStatusList(
|
||||
const std::string& root_certificate_public_key,
|
||||
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; }
|
||||
bool allow_unknown_devices() const { return allow_unknown_devices_; }
|
||||
void set_allow_test_only_devices(bool allow) {
|
||||
@@ -63,14 +65,15 @@ class DeviceStatusList {
|
||||
// INVALID_DRM_CERTIFICATE
|
||||
// DRM_DEVICE_CERTIFICATE_REVOKED
|
||||
// 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
|
||||
// If status is OK, a copy of the provisioned device info is copied
|
||||
// into |device_info|. Caller owns |device_info| and it must not be null.
|
||||
Status GetCertStatus(const ClientCert& client_cert,
|
||||
const std::string& device_manufacturer,
|
||||
widevine::ProvisionedDeviceInfo* device_info);
|
||||
// |provider| is the service provider making the license request.
|
||||
// If status is OK, a copy of the device certificate status is copied
|
||||
// into |device_certificate_status|. Caller owns |device_certificate_status|
|
||||
// and it must not be null.
|
||||
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
|
||||
// system ID are active (not disallowed or revoked).
|
||||
bool IsSystemIdActive(uint32_t system_id);
|
||||
@@ -107,8 +110,16 @@ class DeviceStatusList {
|
||||
// of the format <system_id>:
|
||||
// Example usage:
|
||||
// const std::string device_list = "4121:LG,7912:*"
|
||||
// AllowTestOnlyDevices(device_list);
|
||||
virtual void AllowTestOnlyDevices(const std::string& device_list);
|
||||
// AllowTestOnlyDevicesByMake(device_list_by_make);
|
||||
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.
|
||||
virtual void RevokedDrmCertificateSerialNumbers(
|
||||
@@ -119,6 +130,12 @@ class DeviceStatusList {
|
||||
bool IsDrmCertificateRevoked(
|
||||
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
|
||||
// service_response. The service_response is the JSON payload that comes
|
||||
// in the response to a certificate status list request. Both the legacy
|
||||
@@ -139,11 +156,13 @@ class DeviceStatusList {
|
||||
// serialized proto against the |signature|.
|
||||
// The |signature| is the signature of the serialized_certificate_status_list
|
||||
// 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.
|
||||
static Status DetermineAndDeserializeServiceResponse(
|
||||
const std::string& service_response,
|
||||
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.
|
||||
@@ -157,6 +176,20 @@ class DeviceStatusList {
|
||||
const std::string& serialized_service_certificate,
|
||||
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:
|
||||
friend class DeviceStatusListTest;
|
||||
|
||||
@@ -167,12 +200,14 @@ class DeviceStatusList {
|
||||
*
|
||||
* @param legacy_certificate_provisioning_service_response
|
||||
* @param serialized_certificate_status_list
|
||||
* @param hash_algorithm
|
||||
* @param signature
|
||||
* @return WvPLStatus - Status::OK if success, else error.
|
||||
*/
|
||||
static Status ExtractLegacyDeviceList(
|
||||
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.
|
||||
@@ -182,12 +217,14 @@ class DeviceStatusList {
|
||||
* @param published_devices_response the serialized PublishedDevices proto
|
||||
* containing the certificate status list.
|
||||
* @param serialized_certificate_status_list
|
||||
* @param hash_algorithm
|
||||
* @param signature
|
||||
* @return WvPLStatus - Status::OK if success, else error.
|
||||
*/
|
||||
static Status ExtractPublishedDevicesInfo(
|
||||
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
|
||||
@@ -196,25 +233,28 @@ class DeviceStatusList {
|
||||
*
|
||||
* @param serialized_signed_device_certificate_status_list
|
||||
* @param serialized_device_certificate_status_list
|
||||
*
|
||||
* @param hash_algorithm
|
||||
* @return Status - Status::OK if success, else error.
|
||||
*/
|
||||
static Status ParseLegacySignedDeviceCertificateStatusList(
|
||||
const std::string& serialized_signed_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.
|
||||
// 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_|.
|
||||
bool IsTestOnlyDeviceAllowed(uint32_t system_id,
|
||||
const std::string device_manufacturer);
|
||||
virtual size_t allowed_test_only_devices_by_make_size() {
|
||||
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
|
||||
return allowed_test_only_devices_by_make_.size();
|
||||
}
|
||||
|
||||
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.
|
||||
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 expiration_period_seconds_ = 0;
|
||||
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
|
||||
// revoked.
|
||||
std::vector<uint32_t> allowed_revoked_devices_;
|
||||
absl::Mutex allowed_test_only_devices_mutex_;
|
||||
// Contains a map of 'system_id' to 'make'. If 'make' value is "*", any
|
||||
// 'make' for that 'system_id' is allowed.
|
||||
std::multimap<uint32_t, std::string> allowed_test_only_devices_;
|
||||
mutable absl::Mutex allowed_test_only_devices_mutex_;
|
||||
// Contains a map of 'system_id' to 'manufacturer'. If manufacturer value is
|
||||
// "*", any manufacturer using that system_id is allowed.
|
||||
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.
|
||||
std::set<std::string> revoked_drm_certificate_serial_numbers_;
|
||||
};
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.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/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
@@ -34,20 +36,19 @@
|
||||
|
||||
namespace {
|
||||
const char kTestSystemId_1[] = "4121";
|
||||
const char kTestManufacturer_LG[] = "LG";
|
||||
const char kTestManufacturer_LGE[] = "LGE";
|
||||
const char kTestSystemId_2[] = "8242";
|
||||
const char kTestManufacturer_Samsung[] = "Samsung";
|
||||
const char kTestSystemId_3[] = "6556";
|
||||
const char kTestManufacturer[] = "TestManufacturer";
|
||||
const char kTestProvider[] = "TestProvider";
|
||||
const char kRevokedManufacturer[] = "RevokedManufacturer";
|
||||
const widevine::HashAlgorithm kHashAlgorithm =
|
||||
widevine::HashAlgorithm::kSha256;
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnRef;
|
||||
using ::testing::ReturnRefOfCopy;
|
||||
|
||||
const uint32_t kValidCertSystemId = 100;
|
||||
const uint32_t kRevokedCertSystemId = 101;
|
||||
@@ -66,32 +67,40 @@ const char kRevokedUniqueIdentifiers[] = "revoked-unique-identifiers";
|
||||
const char kTestOnlySerialNumber[] = "test_only-serial-number";
|
||||
const char kMismatchSerialNumber[] = "mismatch-serial-number";
|
||||
const char kDeviceModel[] = "device-model-x";
|
||||
const char kRevokedDeviceModel[] = "device-model-revoked";
|
||||
const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
|
||||
const uint32_t kStatusListCreationTime = 17798001;
|
||||
const uint32_t kDefaultExpirePeriod = 0;
|
||||
const bool kDenyRevokedDevice = false;
|
||||
const bool kAllowRevokedDevice = true;
|
||||
|
||||
class MockClientCert : public ClientCert {
|
||||
public:
|
||||
MockClientCert() {}
|
||||
~MockClientCert() override {}
|
||||
MOCK_CONST_METHOD0(system_id, uint32_t());
|
||||
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
|
||||
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
|
||||
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
|
||||
MOCK_CONST_METHOD0(encrypted_unique_id, const std::string &());
|
||||
MOCK_CONST_METHOD0(unique_id_hash, const std::string &());
|
||||
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
|
||||
MOCK_CONST_METHOD3(VerifySignature, Status(const std::string &message,
|
||||
const std::string &signature,
|
||||
ProtocolVersion protocol_version));
|
||||
MOCK_METHOD2(GenerateSigningKey, void(const std::string &message,
|
||||
ProtocolVersion protocol_version));
|
||||
MOCK_CONST_METHOD0(serial_number, const std::string &());
|
||||
MOCK_CONST_METHOD0(key, const std::string &());
|
||||
MOCK_CONST_METHOD0(key_type, SignedMessage::SessionKeyType());
|
||||
MOCK_CONST_METHOD0(service_id, const std::string &());
|
||||
MOCK_CONST_METHOD0(encrypted_key, const std::string &());
|
||||
MOCK_CONST_METHOD0(signing_key, const std::string &());
|
||||
MOCK_METHOD(uint32_t, system_id, (), (const, override));
|
||||
MOCK_METHOD(std::string &, signer_serial_number, (), (const, override));
|
||||
MOCK_METHOD(uint32_t, signer_creation_time_seconds, (), (const, override));
|
||||
MOCK_METHOD(ClientIdentification::TokenType, type, (), (const, override));
|
||||
MOCK_METHOD(const std::string &, encrypted_unique_id, (), (const, override));
|
||||
MOCK_METHOD(const std::string &, unique_id_hash, (), (const, override));
|
||||
MOCK_METHOD(bool, signed_by_provisioner, (), (const, override));
|
||||
MOCK_METHOD(Status, VerifySignature,
|
||||
(const std::string &message, HashAlgorithm hash_algorithm,
|
||||
const std::string &signature, ProtocolVersion protocol_version),
|
||||
(const, override));
|
||||
MOCK_METHOD(void, GenerateSigningKey,
|
||||
(const std::string &message, ProtocolVersion protocol_version),
|
||||
(override));
|
||||
MOCK_METHOD(const std::string &, serial_number, (), (const, override));
|
||||
MOCK_METHOD(const std::string &, key, (), (const, override));
|
||||
MOCK_METHOD(SignedMessage::SessionKeyType, key_type, (), (const, override));
|
||||
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 {
|
||||
@@ -116,6 +125,7 @@ class DeviceStatusListTest : public ::testing::Test {
|
||||
cert_status = cert_status_list_.add_certificate_status();
|
||||
cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId);
|
||||
cert_status->set_drm_serial_number(kRevokedSerialNumber);
|
||||
cert_status->mutable_device_info()->set_model(kRevokedDeviceModel);
|
||||
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
|
||||
|
||||
// Device cert with status REVOKED ALLOWED DEVICE.
|
||||
@@ -141,6 +151,7 @@ class DeviceStatusListTest : public ::testing::Test {
|
||||
ASSERT_TRUE(root_key);
|
||||
cert_status_list_.SerializeToString(&serialized_cert_status_list_);
|
||||
ASSERT_TRUE(root_key->GenerateSignature(serialized_cert_status_list_,
|
||||
kHashAlgorithm,
|
||||
&cert_status_list_signature_));
|
||||
|
||||
// Update the device_status_list_ with the serialized status list
|
||||
@@ -148,11 +159,12 @@ class DeviceStatusListTest : public ::testing::Test {
|
||||
ASSERT_EQ(OkStatus(),
|
||||
device_status_list_.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_cert_status_list_, cert_status_list_signature_,
|
||||
kDefaultExpirePeriod));
|
||||
serialized_cert_status_list_, kHashAlgorithm,
|
||||
cert_status_list_signature_, kDefaultExpirePeriod));
|
||||
}
|
||||
|
||||
void GenerateTrivialValidStatusList(std::string *serialized_cert_status_list,
|
||||
HashAlgorithm hash_algorithm,
|
||||
std::string *signature) {
|
||||
DeviceCertificateStatusList cert_status_list;
|
||||
DeviceCertificateStatus *cert_status;
|
||||
@@ -171,17 +183,27 @@ class DeviceStatusListTest : public ::testing::Test {
|
||||
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
|
||||
ASSERT_TRUE(root_key);
|
||||
cert_status_list.SerializeToString(serialized_cert_status_list);
|
||||
ASSERT_TRUE(
|
||||
root_key->GenerateSignature(*serialized_cert_status_list, signature));
|
||||
ASSERT_TRUE(root_key->GenerateSignature(*serialized_cert_status_list,
|
||||
hash_algorithm, signature));
|
||||
}
|
||||
|
||||
int VerifyAllowedTestOnlyDevicesAdded() {
|
||||
return device_status_list_.allowed_test_only_devices_.size();
|
||||
int AllowedTestOnlyDevicesByMakeSize() {
|
||||
return device_status_list_.allowed_test_only_devices_by_make_size();
|
||||
}
|
||||
|
||||
bool VerifyIsTestOnlyDeviceAllowed(uint32_t system_id,
|
||||
std::string manufacturer) {
|
||||
return device_status_list_.IsTestOnlyDeviceAllowed(system_id, manufacturer);
|
||||
int AllowedTestOnlyDevicesByProviderSize() {
|
||||
return device_status_list_.allowed_test_only_devices_by_provider_size();
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -203,7 +225,7 @@ class DeviceStatusListTest : public ::testing::Test {
|
||||
|
||||
TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
||||
// Test case where the Certificate status is set to Valid.
|
||||
ProvisionedDeviceInfo device_info;
|
||||
DeviceCertificateStatus device_certificate_status;
|
||||
MockClientCert valid_client_cert;
|
||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||
EXPECT_CALL(valid_client_cert, type())
|
||||
@@ -212,9 +234,10 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||
EXPECT_EQ(OkStatus(),
|
||||
device_status_list_.GetCertStatus(valid_client_cert,
|
||||
kTestManufacturer, &device_info));
|
||||
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(
|
||||
valid_client_cert, kTestManufacturer, kTestProvider,
|
||||
kDenyRevokedDevice, &device_certificate_status));
|
||||
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
|
||||
@@ -227,20 +250,25 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
||||
.WillRepeatedly(Return(kRevokedCertSystemId));
|
||||
EXPECT_CALL(revoked_client_cert, signer_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(
|
||||
DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||
device_status_list_
|
||||
.GetCertStatus(revoked_client_cert, kTestManufacturer, &device_info)
|
||||
.GetCertStatus(revoked_client_cert, kTestManufacturer, kTestProvider,
|
||||
kDenyRevokedDevice, &device_certificate_status)
|
||||
.error_code());
|
||||
|
||||
// Test case where the revoked cert is allowed.
|
||||
device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId));
|
||||
EXPECT_OK(device_status_list_.GetCertStatus(revoked_client_cert,
|
||||
kTestManufacturer, &device_info));
|
||||
EXPECT_OK(device_status_list_.GetCertStatus(
|
||||
revoked_client_cert, kTestManufacturer, kTestProvider, kDenyRevokedDevice,
|
||||
&device_certificate_status));
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
||||
ProvisionedDeviceInfo device_info;
|
||||
DeviceCertificateStatus device_certificate_status;
|
||||
MockClientCert test_only_client_cert;
|
||||
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
||||
EXPECT_CALL(test_only_client_cert, type())
|
||||
@@ -249,11 +277,12 @@ TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
||||
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
||||
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||
EXPECT_EQ(
|
||||
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
device_status_list_
|
||||
.GetCertStatus(test_only_client_cert, kTestManufacturer, &device_info)
|
||||
.error_code());
|
||||
EXPECT_EQ(DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
device_status_list_
|
||||
.GetCertStatus(test_only_client_cert, kTestManufacturer,
|
||||
kTestProvider, kDenyRevokedDevice,
|
||||
&device_certificate_status)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, GetRevokedIfentifiers) {
|
||||
@@ -269,7 +298,7 @@ TEST_F(DeviceStatusListTest, GetRevokedIfentifiers) {
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
|
||||
ProvisionedDeviceInfo device_info;
|
||||
DeviceCertificateStatus device_certificate_status;
|
||||
MockClientCert test_only_client_cert;
|
||||
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
||||
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())
|
||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||
EXPECT_EQ(OkStatus(),
|
||||
device_status_list_.GetCertStatus(test_only_client_cert,
|
||||
kTestManufacturer, &device_info));
|
||||
device_status_list_.GetCertStatus(
|
||||
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;
|
||||
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
|
||||
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
|
||||
|
||||
// Test case where the Certificate status is set to Valid.
|
||||
ProvisionedDeviceInfo device_info;
|
||||
DeviceCertificateStatus device_certificate_status;
|
||||
MockClientCert valid_client_keybox;
|
||||
|
||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||
EXPECT_CALL(valid_client_keybox, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||
EXPECT_CALL(valid_client_keybox, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_EQ(OkStatus(),
|
||||
device_status_list_.GetCertStatus(valid_client_keybox,
|
||||
kTestManufacturer, &device_info));
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
device_status_list_.GetCertStatus(
|
||||
valid_client_keybox, kTestManufacturer, kTestProvider,
|
||||
kDenyRevokedDevice, &device_certificate_status));
|
||||
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
|
||||
ASSERT_TRUE(device_info.has_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;
|
||||
|
||||
EXPECT_CALL(unknown_client_keybox, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||
EXPECT_CALL(unknown_client_keybox, system_id())
|
||||
.WillRepeatedly(Return(kUnknownSystemId));
|
||||
EXPECT_EQ(
|
||||
UNSUPPORTED_SYSTEM_ID,
|
||||
device_status_list_
|
||||
.GetCertStatus(unknown_client_keybox, kTestManufacturer, &device_info)
|
||||
.error_code());
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
EXPECT_CALL(unknown_client_keybox, SystemIdUnknownError())
|
||||
.WillRepeatedly(Return(Status(error_space, UNSUPPORTED_SYSTEM_ID, "")));
|
||||
EXPECT_EQ(UNSUPPORTED_SYSTEM_ID,
|
||||
device_status_list_
|
||||
.GetCertStatus(unknown_client_keybox, kTestManufacturer,
|
||||
kTestProvider, kDenyRevokedDevice,
|
||||
&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) {
|
||||
@@ -323,7 +409,7 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
||||
// Test case where the signer certificate is older than the current status
|
||||
// list.
|
||||
MockClientCert older_client_cert;
|
||||
ProvisionedDeviceInfo device_info;
|
||||
DeviceCertificateStatus device_certificate_status;
|
||||
std::string mismatch_drm_serial_number(kMismatchSerialNumber);
|
||||
EXPECT_CALL(older_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
@@ -336,20 +422,23 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
||||
EXPECT_EQ(
|
||||
INVALID_DRM_CERTIFICATE,
|
||||
device_status_list_
|
||||
.GetCertStatus(older_client_cert, kTestManufacturer, &device_info)
|
||||
.GetCertStatus(older_client_cert, kTestManufacturer, kTestProvider,
|
||||
kDenyRevokedDevice, &device_certificate_status)
|
||||
.error_code());
|
||||
|
||||
// We allow this case only for certs signed by a provisioner cert.
|
||||
EXPECT_CALL(older_client_cert, signed_by_provisioner())
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_EQ(OkStatus(),
|
||||
device_status_list_.GetCertStatus(older_client_cert,
|
||||
kTestManufacturer, &device_info));
|
||||
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(
|
||||
older_client_cert, kTestManufacturer, kTestProvider,
|
||||
kDenyRevokedDevice, &device_certificate_status));
|
||||
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
|
||||
EXPECT_TRUE(device_info.has_system_id());
|
||||
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
|
||||
|
||||
// Test case where the signer certificate is newer than the current status
|
||||
// list, and unknown devices are allowed.
|
||||
device_certificate_status.Clear();
|
||||
MockClientCert newer_client_cert1;
|
||||
EXPECT_CALL(newer_client_cert1, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
@@ -359,14 +448,19 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||
EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime));
|
||||
EXPECT_CALL(newer_client_cert1, SystemIdUnknownError())
|
||||
.WillRepeatedly(
|
||||
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, "")));
|
||||
EXPECT_EQ(
|
||||
DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
device_status_list_
|
||||
.GetCertStatus(newer_client_cert1, kTestManufacturer, &device_info)
|
||||
.GetCertStatus(newer_client_cert1, kTestManufacturer, kTestProvider,
|
||||
kDenyRevokedDevice, &device_certificate_status)
|
||||
.error_code());
|
||||
|
||||
// Test case where the signer certificate is newer than the current status
|
||||
// list, and unknown devices are not allowed.
|
||||
device_certificate_status.Clear();
|
||||
device_status_list_.set_allow_unknown_devices(false);
|
||||
MockClientCert newer_client_cert2;
|
||||
EXPECT_CALL(newer_client_cert2, type())
|
||||
@@ -377,10 +471,14 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||
EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime + 1));
|
||||
EXPECT_CALL(newer_client_cert2, SystemIdUnknownError())
|
||||
.WillRepeatedly(
|
||||
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, "")));
|
||||
EXPECT_EQ(
|
||||
DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
device_status_list_
|
||||
.GetCertStatus(newer_client_cert2, kTestManufacturer, &device_info)
|
||||
.GetCertStatus(newer_client_cert2, kTestManufacturer, kTestProvider,
|
||||
kDenyRevokedDevice, &device_certificate_status)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
@@ -388,7 +486,7 @@ TEST_F(DeviceStatusListTest, InvalidStatusList) {
|
||||
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
||||
device_status_list_
|
||||
.UpdateStatusList(test_keys_.public_test_key_2_2048_bits(),
|
||||
serialized_cert_status_list_,
|
||||
serialized_cert_status_list_, kHashAlgorithm,
|
||||
cert_status_list_signature_, 0)
|
||||
.error_code());
|
||||
|
||||
@@ -396,14 +494,14 @@ TEST_F(DeviceStatusListTest, InvalidStatusList) {
|
||||
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
||||
device_status_list_
|
||||
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_cert_status_list_,
|
||||
serialized_cert_status_list_, kHashAlgorithm,
|
||||
cert_status_list_signature_, 0)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
class MockDeviceStatusList : public DeviceStatusList {
|
||||
public:
|
||||
MOCK_CONST_METHOD0(GetCurrentTime, uint32_t());
|
||||
MOCK_METHOD(uint32_t, GetCurrentTime, (), (const, override));
|
||||
};
|
||||
|
||||
TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
|
||||
@@ -414,12 +512,12 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
|
||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_cert_status_list_,
|
||||
serialized_cert_status_list_, kHashAlgorithm,
|
||||
cert_status_list_signature_, 100));
|
||||
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
mock_device_status_list
|
||||
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_cert_status_list_,
|
||||
serialized_cert_status_list_, kHashAlgorithm,
|
||||
cert_status_list_signature_, 100)
|
||||
.error_code());
|
||||
}
|
||||
@@ -433,10 +531,10 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_cert_status_list_,
|
||||
serialized_cert_status_list_, kHashAlgorithm,
|
||||
cert_status_list_signature_, 100));
|
||||
|
||||
ProvisionedDeviceInfo device_info;
|
||||
DeviceCertificateStatus device_certificate_status;
|
||||
MockClientCert valid_client_cert;
|
||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||
EXPECT_CALL(valid_client_cert, type())
|
||||
@@ -447,14 +545,15 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
||||
EXPECT_EQ(OkStatus(),
|
||||
mock_device_status_list.GetCertStatus(
|
||||
valid_client_cert, kTestManufacturer, &device_info));
|
||||
EXPECT_EQ(OkStatus(), mock_device_status_list.GetCertStatus(
|
||||
valid_client_cert, kTestManufacturer, kTestProvider,
|
||||
kDenyRevokedDevice, &device_certificate_status));
|
||||
|
||||
EXPECT_EQ(
|
||||
EXPIRED_CERTIFICATE_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());
|
||||
}
|
||||
|
||||
@@ -477,49 +576,109 @@ TEST_F(DeviceStatusListTest, IsSystemIdActive) {
|
||||
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowed) {
|
||||
std::string whitelisted_device_list =
|
||||
std::string(kTestSystemId_1) + ":" + std::string(kTestManufacturer_LG);
|
||||
whitelisted_device_list += "," + std::string(kTestSystemId_2) + ":" +
|
||||
std::string(kTestManufacturer_Samsung);
|
||||
whitelisted_device_list += "," + std::string(kTestSystemId_3) + ":";
|
||||
whitelisted_device_list += ", " + std::string(kTestSystemId_1) + ":" +
|
||||
std::string(kTestManufacturer_LGE);
|
||||
device_status_list_.AllowTestOnlyDevices(whitelisted_device_list);
|
||||
EXPECT_EQ(4, VerifyAllowedTestOnlyDevicesAdded());
|
||||
TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowedByMake) {
|
||||
const char kTestManufacturer_AA[] = "AA";
|
||||
const char kTestManufacturer_AAA[] = "AAA";
|
||||
const char kTestManufacturer_BBB[] = "BBB";
|
||||
const char kTestManufacturer_BbB[] = "BbB";
|
||||
const char kTestManufacturer_bbb[] = "bbb";
|
||||
const char kTestManufacturer_CCC[] = "CCC";
|
||||
const char kTestManufacturer_DDD[] = "AAA";
|
||||
std::string allowed_device_list =
|
||||
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
|
||||
// manufacturer = kTestManufacturer_LG is allowed.
|
||||
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_1),
|
||||
kTestManufacturer_LG));
|
||||
// manufacturer AA is allowed.
|
||||
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_1),
|
||||
kTestManufacturer_AA));
|
||||
// Verify that device with system_id = kTestSystemId_1 and
|
||||
// manufacturer = kTestManufacturer_LGE is allowed.
|
||||
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_1),
|
||||
kTestManufacturer_LGE));
|
||||
// manufacturer AAA is allowed.
|
||||
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_1),
|
||||
kTestManufacturer_AAA));
|
||||
// Verify that device with system_id = kTestSystemId_2 and
|
||||
// manufacturer = kTestManufacturer_LGE is not allowed.
|
||||
// This is because this combination is not 'whitelisted'.
|
||||
EXPECT_FALSE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2),
|
||||
kTestManufacturer_LGE));
|
||||
// manufacturer AAA is not allowed.
|
||||
// This is because this combination is not in the allowed list.
|
||||
EXPECT_FALSE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
|
||||
kTestManufacturer_AAA));
|
||||
// Verify that device with system_id = kTestSystemId_2 and
|
||||
// manufacturer = kTestManufacturer_Samsung is allowed.
|
||||
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2),
|
||||
kTestManufacturer_Samsung));
|
||||
// manufacturer BBB is allowed.
|
||||
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
|
||||
kTestManufacturer_BBB));
|
||||
// Verifes that device with mixed case succeeds.
|
||||
EXPECT_TRUE(
|
||||
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), "samSung"));
|
||||
EXPECT_TRUE(
|
||||
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), "SAMsung"));
|
||||
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
|
||||
kTestManufacturer_BbB));
|
||||
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
|
||||
kTestManufacturer_bbb));
|
||||
// Verify that device with system_id = kTestSystemId_3 and
|
||||
// any manufacturer is allowed. This checks that any manufacturer is
|
||||
// allowed for this system_id.
|
||||
EXPECT_TRUE(
|
||||
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3), "Cisco"));
|
||||
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3),
|
||||
"ScientificAtlanta"));
|
||||
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_3),
|
||||
kTestManufacturer_CCC));
|
||||
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_3),
|
||||
kTestManufacturer_DDD));
|
||||
uint32_t unknown_system_id = 7890;
|
||||
// Verify that device with system_id = unknown_system_id and
|
||||
// manufacturer = "Cisco" is not allowed.
|
||||
EXPECT_FALSE(VerifyIsTestOnlyDeviceAllowed(unknown_system_id, "Cisco"));
|
||||
// manufacturer CCC is not allowed.
|
||||
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) {
|
||||
@@ -552,6 +711,7 @@ TEST_F(DeviceStatusListTest, DetermineAndDeserializeServiceResponseSuccess) {
|
||||
SignedDeviceInfo published_devices;
|
||||
GenerateTrivialValidStatusList(
|
||||
published_devices.mutable_device_certificate_status_list(),
|
||||
HashAlgorithmProtoToEnum(published_devices.hash_algorithm()),
|
||||
published_devices.mutable_signature());
|
||||
|
||||
std::string serialized_published_devices;
|
||||
@@ -561,13 +721,17 @@ TEST_F(DeviceStatusListTest, DetermineAndDeserializeServiceResponseSuccess) {
|
||||
DeviceCertificateStatusList actual_cert_status_list;
|
||||
std::string actual_serialized_cert_status_list;
|
||||
std::string actual_signature;
|
||||
HashAlgorithm hash_algorithm;
|
||||
ASSERT_EQ(OkStatus(),
|
||||
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||
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(),
|
||||
actual_serialized_cert_status_list);
|
||||
EXPECT_EQ(published_devices.signature(), actual_signature);
|
||||
EXPECT_EQ(HashAlgorithmProtoToEnum(published_devices.hash_algorithm()),
|
||||
hash_algorithm);
|
||||
|
||||
DeviceCertificateStatusList expected_cert_status_list;
|
||||
ASSERT_TRUE(expected_cert_status_list.ParseFromString(
|
||||
@@ -580,9 +744,11 @@ TEST_F(DeviceStatusListTest,
|
||||
DetermineAndDeserializeServiceResponseLegacySuccess) {
|
||||
std::string serialized_cert_status_list;
|
||||
std::string signature;
|
||||
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
|
||||
|
||||
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()) =
|
||||
serialized_cert_status_list;
|
||||
*(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_signature;
|
||||
DeviceCertificateStatusList actual_cert_status_list;
|
||||
HashAlgorithm hash_algorithm;
|
||||
ASSERT_EQ(OkStatus(),
|
||||
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||
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(signature, actual_signature);
|
||||
EXPECT_EQ(
|
||||
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
|
||||
hash_algorithm);
|
||||
|
||||
DeviceCertificateStatusList expected_cert_status_list;
|
||||
ASSERT_TRUE(
|
||||
@@ -622,9 +793,11 @@ TEST_F(DeviceStatusListTest,
|
||||
DetermineAndDeserializeServiceResponseLegacyWebSafeBase64Success) {
|
||||
std::string serialized_cert_status_list;
|
||||
std::string signature;
|
||||
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
|
||||
|
||||
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()) =
|
||||
serialized_cert_status_list;
|
||||
*(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_signature;
|
||||
HashAlgorithm hash_algorithm;
|
||||
DeviceCertificateStatusList actual_cert_status_list;
|
||||
ASSERT_EQ(OkStatus(),
|
||||
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||
websafe_b64_serialized_signed_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(signature, actual_signature);
|
||||
EXPECT_EQ(
|
||||
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
|
||||
hash_algorithm);
|
||||
|
||||
DeviceCertificateStatusList expected_cert_status_list;
|
||||
ASSERT_TRUE(
|
||||
@@ -659,9 +836,11 @@ TEST_F(DeviceStatusListTest,
|
||||
DetermineAndDeserializeServiceResponseLegacyBase64Success) {
|
||||
std::string serialized_cert_status_list;
|
||||
std::string signature;
|
||||
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
|
||||
|
||||
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()) =
|
||||
serialized_cert_status_list;
|
||||
*(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_signature;
|
||||
HashAlgorithm hash_algorithm;
|
||||
DeviceCertificateStatusList actual_cert_status_list;
|
||||
ASSERT_EQ(OkStatus(),
|
||||
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||
websafe_b64_serialized_signed_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(signature, actual_signature);
|
||||
EXPECT_EQ(
|
||||
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
|
||||
hash_algorithm);
|
||||
|
||||
DeviceCertificateStatusList expected_cert_status_list;
|
||||
ASSERT_TRUE(
|
||||
@@ -692,4 +875,44 @@ TEST_F(DeviceStatusListTest,
|
||||
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
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "common/ec_key.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "common/hash_algorithm_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/sha_util.h"
|
||||
#include "common/signer_public_key.h"
|
||||
@@ -281,6 +283,7 @@ class VerifiedCertSignatureCache {
|
||||
// cache.
|
||||
Status VerifySignature(const std::string& cert,
|
||||
const std::string& serial_number,
|
||||
HashAlgorithm hash_algorithm,
|
||||
const std::string& signature,
|
||||
const DrmCertificate& signer) {
|
||||
{
|
||||
@@ -314,7 +317,7 @@ class VerifiedCertSignatureCache {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"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,
|
||||
"cache-miss-invalid-signature");
|
||||
}
|
||||
@@ -428,8 +431,10 @@ Status DrmRootCertificate::Create(CertificateType cert_type,
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-public-key");
|
||||
}
|
||||
if (!public_key->VerifySignature(signed_root_cert.drm_certificate(),
|
||||
signed_root_cert.signature())) {
|
||||
if (!public_key->VerifySignature(
|
||||
signed_root_cert.drm_certificate(),
|
||||
HashAlgorithmProtoToEnum(signed_root_cert.hash_algorithm()),
|
||||
signed_root_cert.signature())) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-certificate-signature");
|
||||
}
|
||||
@@ -519,6 +524,7 @@ Status DrmRootCertificate::VerifySignatures(
|
||||
// Always use cache for root-signed certificates.
|
||||
return signature_cache_->VerifySignature(
|
||||
signed_cert.drm_certificate(), cert_serial_number,
|
||||
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
|
||||
signed_cert.signature(), root_cert_);
|
||||
}
|
||||
DrmCertificate signer;
|
||||
@@ -539,9 +545,10 @@ Status DrmRootCertificate::VerifySignatures(
|
||||
}
|
||||
|
||||
if (use_cache) {
|
||||
status = signature_cache_->VerifySignature(signed_cert.drm_certificate(),
|
||||
cert_serial_number,
|
||||
signed_cert.signature(), signer);
|
||||
status = signature_cache_->VerifySignature(
|
||||
signed_cert.drm_certificate(), cert_serial_number,
|
||||
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
|
||||
signed_cert.signature(), signer);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
@@ -552,8 +559,10 @@ Status DrmRootCertificate::VerifySignatures(
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-leaf-signer-public-key");
|
||||
}
|
||||
if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(),
|
||||
signed_cert.signature())) {
|
||||
if (!signer_public_key->VerifySignature(
|
||||
signed_cert.drm_certificate(),
|
||||
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
|
||||
signed_cert.signature())) {
|
||||
return Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature");
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "common/ec_key.h"
|
||||
#include "common/ec_test_keys.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_test_keys.h"
|
||||
#include "common/test_drm_certificates.h"
|
||||
@@ -101,6 +103,7 @@ class SignerPrivateKey {
|
||||
public:
|
||||
virtual ~SignerPrivateKey() {}
|
||||
virtual bool GenerateSignature(const std::string& message,
|
||||
HashAlgorithm hash_algorithm,
|
||||
std::string* signature) const = 0;
|
||||
virtual DrmCertificate::Algorithm algorithm() const = 0;
|
||||
static std::unique_ptr<SignerPrivateKey> Create(
|
||||
@@ -119,8 +122,9 @@ class SignerPrivateKeyImpl : public SignerPrivateKey {
|
||||
: private_key_(std::move(private_key)), algorithm_(algorithm) {}
|
||||
~SignerPrivateKeyImpl() override {}
|
||||
bool GenerateSignature(const std::string& message,
|
||||
HashAlgorithm hash_algorithm,
|
||||
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_; }
|
||||
|
||||
@@ -255,7 +259,9 @@ class DrmRootCertificateTest : public testing::TestWithParam<const char*> {
|
||||
ASSERT_TRUE(drm_certificates_[kClientKey].SerializeToString(
|
||||
current_sc->mutable_drm_certificate()));
|
||||
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();
|
||||
drm_certificates_[kInterMediateKey].set_algorithm(
|
||||
@@ -263,7 +269,9 @@ class DrmRootCertificateTest : public testing::TestWithParam<const char*> {
|
||||
ASSERT_TRUE(drm_certificates_[kInterMediateKey].SerializeToString(
|
||||
current_sc->mutable_drm_certificate()));
|
||||
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();
|
||||
drm_certificates_[kDrmRootKey].set_algorithm(
|
||||
@@ -271,7 +279,9 @@ class DrmRootCertificateTest : public testing::TestWithParam<const char*> {
|
||||
ASSERT_TRUE(drm_certificates_[kDrmRootKey].SerializeToString(
|
||||
current_sc->mutable_drm_certificate()));
|
||||
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_;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "base/thread_annotations.h"
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/hash_algorithm_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/rsa_util.h"
|
||||
@@ -62,8 +63,10 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
||||
cert.set_creation_time_seconds(creation_time_seconds);
|
||||
SignedDrmCertificate signed_cert;
|
||||
cert.SerializeToString(signed_cert.mutable_drm_certificate());
|
||||
root_private_key_->GenerateSignature(signed_cert.drm_certificate(),
|
||||
signed_cert.mutable_signature());
|
||||
root_private_key_->GenerateSignature(
|
||||
signed_cert.drm_certificate(),
|
||||
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
|
||||
signed_cert.mutable_signature());
|
||||
std::string serialized_cert;
|
||||
signed_cert.SerializeToString(&serialized_cert);
|
||||
return serialized_cert;
|
||||
|
||||
113
common/dual_certificate_client_cert.cc
Normal file
113
common/dual_certificate_client_cert.cc
Normal 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
|
||||
57
common/dual_certificate_client_cert.h
Normal file
57
common/dual_certificate_client_cert.h
Normal 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_
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "openssl/sha.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/ec_util.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "common/openssl_util.h"
|
||||
#include "common/sha_util.h"
|
||||
|
||||
@@ -53,6 +54,22 @@ std::string OpenSSLErrorString(uint32_t error) {
|
||||
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
|
||||
|
||||
ECPrivateKey::ECPrivateKey(EC_KEY* ec_key) : key_(ec_key) {
|
||||
@@ -159,6 +176,47 @@ bool ECPrivateKey::GenerateSignature(const std::string& message,
|
||||
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 {
|
||||
return BN_cmp(EC_KEY_get0_private_key(key()),
|
||||
EC_KEY_get0_private_key(private_key.key())) == 0;
|
||||
@@ -254,6 +312,39 @@ bool ECPublicKey::VerifySignature(const std::string& message,
|
||||
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 {
|
||||
return private_key.MatchesPublicKey(*this);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "openssl/ec.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "common/openssl_util.h"
|
||||
|
||||
namespace widevine {
|
||||
@@ -65,9 +67,24 @@ class ECPrivateKey {
|
||||
// DER-encoded signature.
|
||||
// Caller retains ownership of all pointers.
|
||||
// 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,
|
||||
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_.
|
||||
virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const;
|
||||
|
||||
@@ -110,9 +127,23 @@ class ECPublicKey {
|
||||
// |message| is the message that was signed.
|
||||
// |signature| is an ASN.1 DER-encoded signature.
|
||||
// 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,
|
||||
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_.
|
||||
virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const;
|
||||
|
||||
|
||||
@@ -111,6 +111,10 @@ class ECKeyTestKeyPairs : public ECKeyTest,
|
||||
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) {
|
||||
EXPECT_EQ(ECPrivateKey::Create(test_public_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));
|
||||
}
|
||||
|
||||
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) {
|
||||
std::string signature;
|
||||
EXPECT_FALSE(private_key_->GenerateSignature("", &signature));
|
||||
@@ -220,6 +258,9 @@ TEST_P(ECKeyTestKeyPairs, KeyPointEncodingSuccess) {
|
||||
INSTANTIATE_TEST_SUITE_P(ECKeyTestKeyPairs, ECKeyTestKeyPairs,
|
||||
::testing::ValuesIn(ECKeyTest::GetTestKeyList()));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ECKeyTestKeyPairsDeathTest, ECKeyTestKeyPairsDeathTest,
|
||||
::testing::ValuesIn(ECKeyTest::GetTestKeyList()));
|
||||
|
||||
class ECKeyTestCurveMismatch
|
||||
: public ECKeyTest,
|
||||
public ::testing::WithParamInterface<
|
||||
|
||||
@@ -200,9 +200,10 @@ TEST(EciesEncryptorTest, EciesEncryptNullKeySource) {
|
||||
class MockEcKeySource : public ECKeySource {
|
||||
public:
|
||||
MockEcKeySource() = default;
|
||||
MOCK_METHOD3(GetECKey,
|
||||
bool(ECPrivateKey::EllipticCurve curve, std::string* private_key,
|
||||
std::string* public_key));
|
||||
MOCK_METHOD(bool, GetECKey,
|
||||
(ECPrivateKey::EllipticCurve curve, std::string* private_key,
|
||||
std::string* public_key),
|
||||
(override));
|
||||
};
|
||||
|
||||
TEST(EciesEncryptorTest, EciesEncryptKeysourceFail) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/file_util.h"
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
|
||||
18
common/hash_algorithm.h
Normal file
18
common/hash_algorithm.h
Normal 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_
|
||||
51
common/hash_algorithm_util.cc
Normal file
51
common/hash_algorithm_util.cc
Normal 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
|
||||
23
common/hash_algorithm_util.h
Normal file
23
common/hash_algorithm_util.h
Normal 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_
|
||||
58
common/hash_algorithm_util_test.cc
Normal file
58
common/hash_algorithm_util_test.cc
Normal 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
|
||||
@@ -33,9 +33,11 @@ Status KeyboxClientCert::Initialize(const std::string& keybox_token) {
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
// |hash_algorithm| is needed for function inheritance.
|
||||
// For KeyBoxClientCert, we always use HMAC-SHA256 in signature verification.
|
||||
Status KeyboxClientCert::VerifySignature(
|
||||
const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) const {
|
||||
const std::string& message, HashAlgorithm hash_algorithm,
|
||||
const std::string& signature, ProtocolVersion protocol_version) const {
|
||||
DCHECK(!signing_key_.empty());
|
||||
using crypto_util::VerifySignatureHmacSha256;
|
||||
if (!VerifySignatureHmacSha256(
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
#define COMMON_KEYBOX_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 {
|
||||
|
||||
//
|
||||
class KeyboxClientCert : public ClientCert {
|
||||
public:
|
||||
KeyboxClientCert() {}
|
||||
@@ -23,6 +25,7 @@ class KeyboxClientCert : public ClientCert {
|
||||
Status Initialize(const std::string& keybox_token);
|
||||
|
||||
Status VerifySignature(const std::string& message,
|
||||
HashAlgorithm hash_algorithm,
|
||||
const std::string& signature,
|
||||
ProtocolVersion protocol_version) const override;
|
||||
|
||||
@@ -34,6 +37,7 @@ class KeyboxClientCert : public ClientCert {
|
||||
SignedMessage::SessionKeyType key_type() const override {
|
||||
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& service_id() const override { return unimplemented_; }
|
||||
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);
|
||||
static bool IsSystemIdKnown(const uint32_t system_id);
|
||||
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:
|
||||
std::string unimplemented_;
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#define COMMON_MOCK_RSA_KEY_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
namespace widevine {
|
||||
@@ -20,12 +22,23 @@ class MockRsaPrivateKey : public RsaPrivateKey {
|
||||
MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {}
|
||||
~MockRsaPrivateKey() override {}
|
||||
|
||||
MOCK_CONST_METHOD2(Decrypt, bool(const std::string& encrypted_message,
|
||||
std::string* decrypted_message));
|
||||
MOCK_CONST_METHOD2(GenerateSignature,
|
||||
bool(const std::string& message, std::string* signature));
|
||||
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
||||
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
||||
MOCK_METHOD(bool, Decrypt,
|
||||
(const std::string& encrypted_message,
|
||||
std::string* decrypted_message),
|
||||
(const, override));
|
||||
// TODO(b/155438325): remove this function after the below function is fully
|
||||
// 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:
|
||||
MockRsaPrivateKey(const MockRsaPrivateKey&) = delete;
|
||||
@@ -37,12 +50,23 @@ class MockRsaPublicKey : public RsaPublicKey {
|
||||
MockRsaPublicKey() : RsaPublicKey(RSA_new()) {}
|
||||
~MockRsaPublicKey() override {}
|
||||
|
||||
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
|
||||
std::string* encrypted_message));
|
||||
MOCK_CONST_METHOD2(VerifySignature, bool(const std::string& message,
|
||||
const std::string& signature));
|
||||
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
||||
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
||||
MOCK_METHOD(bool, Encrypt,
|
||||
(const std::string& clear_message,
|
||||
std::string* encrypted_message),
|
||||
(const, override));
|
||||
// TODO(b/155438325): remove this function after the below function is fully
|
||||
// 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:
|
||||
MockRsaPublicKey(const MockRsaPublicKey&) = delete;
|
||||
@@ -54,16 +78,14 @@ class MockRsaKeyFactory : public RsaKeyFactory {
|
||||
MockRsaKeyFactory() {}
|
||||
~MockRsaKeyFactory() override {}
|
||||
|
||||
MOCK_CONST_METHOD1(
|
||||
CreateFromPkcs1PrivateKey,
|
||||
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
|
||||
MOCK_CONST_METHOD2(CreateFromPkcs8PrivateKey,
|
||||
std::unique_ptr<RsaPrivateKey>(
|
||||
const std::string& private_key,
|
||||
const std::string& private_key_passphrase));
|
||||
MOCK_CONST_METHOD1(
|
||||
CreateFromPkcs1PublicKey,
|
||||
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
|
||||
MOCK_METHOD(std::unique_ptr<RsaPrivateKey>, CreateFromPkcs1PrivateKey,
|
||||
(const std::string& private_key), (const, override));
|
||||
MOCK_METHOD(std::unique_ptr<RsaPrivateKey>, CreateFromPkcs8PrivateKey,
|
||||
(const std::string& private_key,
|
||||
const std::string& private_key_passphrase),
|
||||
(const, override));
|
||||
MOCK_METHOD(std::unique_ptr<RsaPublicKey>, CreateFromPkcs1PublicKey,
|
||||
(const std::string& public_key), (const, override));
|
||||
|
||||
private:
|
||||
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;
|
||||
|
||||
42
common/playready_interface.h
Normal file
42
common/playready_interface.h
Normal 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_
|
||||
23
common/playready_sdk_impl.cc
Normal file
23
common/playready_sdk_impl.cc
Normal 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
|
||||
28
common/playready_sdk_impl.h
Normal file
28
common/playready_sdk_impl.h
Normal 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_
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/thread_annotations.h"
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "common/status.h"
|
||||
#include "common/x509_cert.h"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "common/ec_key.h"
|
||||
@@ -77,11 +78,7 @@ Status RootOfTrustIdGenerator::Generate(uint32_t system_id,
|
||||
|
||||
std::string RootOfTrustIdGenerator::GenerateUniqueIdHash(
|
||||
const std::string& unique_id) const {
|
||||
if (unique_id.empty()) {
|
||||
LOG(WARNING) << "unique_id should not be empty.";
|
||||
return "";
|
||||
}
|
||||
return Sha256_Hash(absl::StrCat(unique_id, wv_shared_salt_));
|
||||
return widevine::GenerateUniqueIdHash(unique_id, wv_shared_salt_);
|
||||
}
|
||||
|
||||
Status RootOfTrustIdDecryptor::DecryptUniqueId(
|
||||
@@ -104,4 +101,30 @@ Status RootOfTrustIdDecryptor::DecryptUniqueId(
|
||||
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
|
||||
|
||||
@@ -38,7 +38,7 @@ class RootOfTrustIdGenerator {
|
||||
// values. The unique id hash values identify revoked devices and are
|
||||
// published in the DCSL and consumed by the License SDK.
|
||||
RootOfTrustIdGenerator(std::unique_ptr<EciesEncryptor> ecies_encryptor,
|
||||
std::string wv_shared_salt)
|
||||
const std::string& wv_shared_salt)
|
||||
: ecies_encryptor_(std::move(ecies_encryptor)),
|
||||
wv_shared_salt_(std::move(wv_shared_salt)) {}
|
||||
|
||||
@@ -73,8 +73,10 @@ class RootOfTrustIdGenerator {
|
||||
class RootOfTrustIdDecryptor {
|
||||
public:
|
||||
explicit RootOfTrustIdDecryptor(
|
||||
std::unique_ptr<EciesDecryptor> ecies_decryptor)
|
||||
: ecies_decryptor_(std::move(ecies_decryptor)) {}
|
||||
std::unique_ptr<EciesDecryptor> 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
|
||||
// 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,
|
||||
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:
|
||||
std::unique_ptr<EciesDecryptor> ecies_decryptor_;
|
||||
std::string wv_shared_salt_;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -80,8 +80,9 @@ class MockEciesEncryptor : public EciesEncryptor {
|
||||
ECPublicKey::Create(test_keys.public_test_key_1_secp256r1());
|
||||
return new MockEciesEncryptor(std::move(ec_key));
|
||||
}
|
||||
MOCK_CONST_METHOD3(Encrypt, bool(const std::string&, const std::string&,
|
||||
std::string*));
|
||||
MOCK_METHOD(bool, Encrypt,
|
||||
(const std::string&, const std::string&, std::string*),
|
||||
(const, override));
|
||||
|
||||
private:
|
||||
explicit MockEciesEncryptor(std::unique_ptr<ECPublicKey> ec_key)
|
||||
@@ -92,7 +93,7 @@ class MockEciesEncryptor : public EciesEncryptor {
|
||||
|
||||
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) {
|
||||
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
|
||||
|
||||
// Generate the root of trust id.
|
||||
RootOfTrustId root_of_trust_id;
|
||||
@@ -117,7 +118,7 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) {
|
||||
|
||||
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
|
||||
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
|
||||
|
||||
std::string rot_encrypted_id;
|
||||
std::string rot_id_hash;
|
||||
@@ -208,7 +209,7 @@ TEST_F(RootOfTrustIdGeneratorTest, GenerateIdNullRotIdFail) {
|
||||
|
||||
TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdMismatchFails) {
|
||||
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
|
||||
|
||||
// Generate the root of trust id.
|
||||
RootOfTrustId root_of_trust_id;
|
||||
@@ -228,7 +229,7 @@ TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdMismatchFails) {
|
||||
}
|
||||
|
||||
TEST_F(RootOfTrustIdGeneratorTest, DecryptorBlankUniqueId) {
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
|
||||
|
||||
// Attempt to decrypt empty encrypted id.
|
||||
std::string decrypted_unique_id;
|
||||
@@ -239,7 +240,7 @@ TEST_F(RootOfTrustIdGeneratorTest, DecryptorBlankUniqueId) {
|
||||
|
||||
TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdNullDecryptedIdFails) {
|
||||
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||
RootOfTrustIdDecryptor decryptor(CreateDecryptor(), kTestSharedSalt);
|
||||
|
||||
// Generate the root of trust id.
|
||||
RootOfTrustId root_of_trust_id;
|
||||
@@ -256,4 +257,48 @@ TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdNullDecryptedIdFails) {
|
||||
"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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -21,6 +21,14 @@
|
||||
|
||||
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
|
||||
// |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
|
||||
|
||||
@@ -19,9 +19,15 @@ namespace {
|
||||
constexpr char kFakeEncryptedId[] = "fake encrypted id";
|
||||
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.
|
||||
constexpr char kRotIdHashHex[] =
|
||||
"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 kOtherFakeSystemId = 9876;
|
||||
@@ -63,4 +69,17 @@ TEST(RotIdUtilTest, GenerateRotIdHashSuccess) {
|
||||
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
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
//
|
||||
// RSA signature details:
|
||||
// Algorithm: RSASSA-PSS
|
||||
// Hash algorithm: SHA1
|
||||
// Hash algorithm: |hash_algorithm|
|
||||
// Mask generation function: mgf1SHA1
|
||||
// Salt length: 20 bytes
|
||||
// Trailer field: 0xbc
|
||||
@@ -21,7 +21,7 @@
|
||||
// RSA encryption details:
|
||||
// Algorithm: RSA-OAEP
|
||||
// Mask generation function: mgf1SHA1
|
||||
// Label (encoding paramter): empty std::string
|
||||
// Label (encoding parameter): empty std::string
|
||||
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "common/rsa_util.h"
|
||||
#include "common/sha_util.h"
|
||||
|
||||
@@ -51,6 +52,21 @@ std::string OpenSSLErrorString(uint32_t error) {
|
||||
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 widevine {
|
||||
@@ -137,6 +153,47 @@ bool RsaPrivateKey::GenerateSignature(const std::string& message,
|
||||
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,
|
||||
std::string* signature) const {
|
||||
DCHECK(signature);
|
||||
@@ -253,6 +310,52 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
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(
|
||||
const std::string& message, const std::string& signature) const {
|
||||
if (message.empty()) {
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "absl/base/macros.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -41,9 +43,20 @@ class RsaPrivateKey {
|
||||
|
||||
// Generate RSSASSA-PSS signature. Caller retains ownership of all parameters.
|
||||
// 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,
|
||||
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
|
||||
// of all parameters. Returns true if successful, false otherwise.
|
||||
virtual bool GenerateSignatureSha256Pkcs7(const std::string& message,
|
||||
@@ -98,9 +111,20 @@ class RsaPublicKey {
|
||||
|
||||
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters.
|
||||
// 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,
|
||||
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
|
||||
// std::string containing the data which was signed, and |signature| which is a
|
||||
// std::string containing the message SHA256 digest signature with PKCS#7
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
// Description:
|
||||
// Unit test for rsa_key RSA encryption and signing.
|
||||
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/rsa_util.h"
|
||||
|
||||
@@ -46,8 +47,7 @@ TEST_F(RsaKeyTest, CopyConstructor) {
|
||||
|
||||
std::unique_ptr<RsaPrivateKey> private_key_copy(
|
||||
new RsaPrivateKey(*private_key));
|
||||
std::unique_ptr<RsaPublicKey> public_key_copy(
|
||||
new RsaPublicKey(*public_key));
|
||||
std::unique_ptr<RsaPublicKey> public_key_copy(new RsaPublicKey(*public_key));
|
||||
|
||||
EXPECT_TRUE(public_key_copy->MatchesPublicKey(*public_key));
|
||||
EXPECT_TRUE(public_key_copy->MatchesPrivateKey(*private_key));
|
||||
|
||||
@@ -11,8 +11,12 @@
|
||||
|
||||
#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"
|
||||
@@ -23,98 +27,44 @@ using ClientCapabilities = ClientIdentification::ClientCapabilities;
|
||||
SecurityProfileList::SecurityProfileList(const std::string& profile_namespace)
|
||||
: profile_namespace_(profile_namespace) {}
|
||||
|
||||
int SecurityProfileList::Init() { return AddDefaultProfiles(); }
|
||||
int SecurityProfileList::Init() { return 0; }
|
||||
|
||||
int SecurityProfileList::AddDefaultProfiles() {
|
||||
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(
|
||||
int SecurityProfileList::GetQualifiedProfilesFromSpecifiedProfiles(
|
||||
const std::vector<std::string>& profiles_to_check,
|
||||
const ClientIdentification& client_id,
|
||||
const ProvisionedDeviceInfo& device_info,
|
||||
std::vector<std::string>* profiles_to_allow) const {
|
||||
if (profiles_to_allow == nullptr) {
|
||||
std::vector<std::string>* qualified_profiles) const {
|
||||
if (qualified_profiles == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
qualified_profiles->clear();
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
for (auto& profile_name : profiles_to_check) {
|
||||
SecurityProfile profile;
|
||||
if (GetProfileByName(profile_name, &profile)) {
|
||||
if (IsProfileAllowed(profile, client_id, device_info)) {
|
||||
profiles_to_allow->push_back(profile.name());
|
||||
if (DoesProfileQualify(profile, client_id, device_info)) {
|
||||
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 ProvisionedDeviceInfo& device_info,
|
||||
std::vector<std::string>* profiles_to_allow) const {
|
||||
if (profiles_to_allow == nullptr) {
|
||||
std::vector<std::string>* qualified_profiles) const {
|
||||
if (qualified_profiles == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
qualified_profiles->clear();
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
for (auto& profile : security_profiles_) {
|
||||
if (IsProfileAllowed(profile, client_id, device_info)) {
|
||||
profiles_to_allow->push_back(profile.name());
|
||||
if (DoesProfileQualify(profile, client_id, device_info)) {
|
||||
qualified_profiles->push_back(profile.name());
|
||||
}
|
||||
}
|
||||
return profiles_to_allow->size();
|
||||
return qualified_profiles->size();
|
||||
}
|
||||
|
||||
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());
|
||||
drm_info->mutable_output()->set_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());
|
||||
drm_info->mutable_security()->set_resource_rating_tier(
|
||||
client_id.client_capabilities().resource_rating_tier());
|
||||
drm_info->mutable_security()->set_security_level(
|
||||
device_info.security_level());
|
||||
drm_info->mutable_security()->set_request_model_info_status(false);
|
||||
drm_info->mutable_request_model_info()->set_manufacturer(
|
||||
GetClientInfo(client_id, kModDrmMake));
|
||||
drm_info->mutable_request_model_info()->set_model_name(
|
||||
GetClientInfo(client_id, kModDrmModel));
|
||||
drm_info->set_system_id(device_info.system_id());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecurityProfileList::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 {
|
||||
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;
|
||||
drm_info->mutable_request_model_info()->set_status(
|
||||
DeviceModel::MODEL_STATUS_UNVERIFIED);
|
||||
for (const auto& model_info : device_info.model_info()) {
|
||||
if (model_info.manufacturer() ==
|
||||
drm_info->request_model_info().manufacturer() &&
|
||||
model_info.model_name() ==
|
||||
drm_info->request_model_info().model_name()) {
|
||||
drm_info->mutable_request_model_info()->set_status(model_info.status());
|
||||
drm_info->mutable_request_model_info()->set_model_year(
|
||||
model_info.model_year());
|
||||
break;
|
||||
}
|
||||
}
|
||||
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(
|
||||
@@ -202,49 +155,91 @@ bool SecurityProfileList::GetProfileByName(
|
||||
bool SecurityProfileList::InsertProfile(
|
||||
const SecurityProfile& profile_to_insert) {
|
||||
// Check if profile already exist.
|
||||
if (GetProfileByLevel(profile_to_insert.level(), nullptr)) {
|
||||
return false;
|
||||
}
|
||||
if (GetProfileByName(profile_to_insert.name(), nullptr)) {
|
||||
LOG(ERROR) << "Unable to insert profile: " << profile_to_insert.name()
|
||||
<< ". Name already exist.";
|
||||
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_);
|
||||
security_profiles_.push_back(profile_to_insert);
|
||||
sort(security_profiles_.begin(), security_profiles_.end(),
|
||||
CompareProfileLevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecurityProfileList::CompareProfileLevel(const SecurityProfile& p1,
|
||||
const SecurityProfile& p2) {
|
||||
// Profiles are sorted from highest to lowest (strictest) level.
|
||||
return (p1.level() > p2.level());
|
||||
int SecurityProfileList::NumProfiles() const {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
return security_profiles_.size();
|
||||
}
|
||||
|
||||
bool SecurityProfileList::IsProfileAllowed(
|
||||
void SecurityProfileList::ClearAllProfiles() {
|
||||
absl::WriterMutexLock lock(&mutex_);
|
||||
security_profiles_.clear();
|
||||
}
|
||||
|
||||
bool SecurityProfileList::DoesProfileQualify(
|
||||
const SecurityProfile& profile, const ClientIdentification& client_id,
|
||||
const ProvisionedDeviceInfo& device_info) const {
|
||||
if (profile.min_security_requirements().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;
|
||||
}
|
||||
if (profile.min_security_requirements().oemcrypto_version() >
|
||||
if (profile.min_security_requirements().oemcrypto_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;
|
||||
}
|
||||
if (profile.min_output_requirements().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;
|
||||
}
|
||||
if (profile.min_output_requirements().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;
|
||||
}
|
||||
if (profile.min_security_requirements().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 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
|
||||
|
||||
@@ -7,25 +7,22 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Container of Widevine security profiles. Security profiles indicate the
|
||||
// level of security of a device based on the device's output protections,
|
||||
// version of OEMCrypto and security level.
|
||||
// Container of device security profiles. Security profiles indicate rules
|
||||
// to allow using the profile. The rules are based on DRM capabilities of a
|
||||
// device.
|
||||
|
||||
#ifndef COMMON_SECURITY_PROFILE_LIST_H_
|
||||
#define COMMON_SECURITY_PROFILE_LIST_H_
|
||||
|
||||
#include "absl/synchronization/mutex.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/security_profile.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
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
|
||||
// acquisition, information from the client and information from the server are
|
||||
// combined to deternmine the device's security profile level.
|
||||
@@ -33,57 +30,31 @@ const uint32_t kResourceTierHigh = 3;
|
||||
class SecurityProfileList {
|
||||
public:
|
||||
explicit SecurityProfileList(const std::string& profile_namespace);
|
||||
~SecurityProfileList() {}
|
||||
virtual ~SecurityProfileList() {}
|
||||
|
||||
// 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();
|
||||
// Initialize the security profile list. The size of the profile list is
|
||||
// returned.
|
||||
virtual int Init();
|
||||
|
||||
// Add the specified profile to the existing list of profiles. Returns true
|
||||
// if successfully inserted, false if unable 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
|
||||
// |profiles_to_check| list that meet the requirements for the this device.
|
||||
// The number of profiles is returned.
|
||||
int GetAllowedProfilesFromList(
|
||||
virtual int GetQualifiedProfilesFromSpecifiedProfiles(
|
||||
const std::vector<std::string>& profiles_to_check,
|
||||
const ClientIdentification& client_id,
|
||||
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|.
|
||||
// |security_profile| is owned by the caller and is populated if a profile
|
||||
// exist.
|
||||
bool GetProfileByLevel(SecurityProfile::Level level,
|
||||
SecurityProfile* security_profile) const;
|
||||
// Populates |profiles_to_allow| with a list of profiles that meet the
|
||||
// requirements for the this device. The number of profiles is returned.
|
||||
virtual int GetQualifiedProfiles(
|
||||
const ClientIdentification& client_id,
|
||||
const ProvisionedDeviceInfo& device_info,
|
||||
std::vector<std::string>* qualified_profiles) const;
|
||||
|
||||
// Return true if a profile exist matching the specified |name|.
|
||||
// |security_profile| is owned by the caller and is populated if a profile
|
||||
@@ -97,17 +68,20 @@ class SecurityProfileList {
|
||||
const ProvisionedDeviceInfo& device_info,
|
||||
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:
|
||||
// Initialize the list with Widevine default profiles. The size of the
|
||||
// profile list after the additions is returned.
|
||||
int AddDefaultProfiles();
|
||||
bool DoesProfileQualify(const SecurityProfile& profile,
|
||||
const ClientIdentification& client_id,
|
||||
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_;
|
||||
// Security profiles
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#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"
|
||||
|
||||
@@ -23,7 +24,16 @@ const char kMakeName[] = "company_name";
|
||||
const char kMakeValue[] = "Google";
|
||||
const char kModelName[] = "model_name";
|
||||
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 kResourceTierLow = 1;
|
||||
const uint32_t kResourceTierMed = 2;
|
||||
const uint32_t kResourceTierHigh = 3;
|
||||
|
||||
class SecurityProfileListTest : public ::testing::Test {
|
||||
public:
|
||||
@@ -32,75 +42,71 @@ class SecurityProfileListTest : public ::testing::Test {
|
||||
|
||||
void SetUp() override {
|
||||
const uint32_t oemcrypto_12 = 12;
|
||||
const bool make_model_not_verified = false;
|
||||
const ClientIdentification::ClientCapabilities::HdcpVersion hdcp_version =
|
||||
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";
|
||||
SecurityProfile profile;
|
||||
std::string profile_namespace = "widevine";
|
||||
profile_list_ = absl::make_unique<SecurityProfileList>(profile_namespace);
|
||||
|
||||
ClientIdentification_NameValue *nv = client_id_.add_client_info();
|
||||
nv->set_name(kMakeName);
|
||||
nv->set_value(kMakeValue);
|
||||
nv = client_id_.add_client_info();
|
||||
nv->set_name(kModelName);
|
||||
nv->set_value(kModelValue);
|
||||
AddClientInfo(&client_id_, kMakeName, kMakeValue);
|
||||
AddClientInfo(&client_id_, kModelName, kModelValue);
|
||||
AddClientInfo(&client_id_, kModDrmDeviceName, kDeviceNameValue);
|
||||
AddClientInfo(&client_id_, kModDrmProductName, kProductNameValue);
|
||||
AddClientInfo(&client_id_, kModDrmBuildInfo, kBuildInfoValue);
|
||||
AddClientInfo(&client_id_, kModDrmOemCryptoSecurityPatchLevel,
|
||||
kOemCryptoSecurityPatchLevelValue);
|
||||
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(
|
||||
oemcrypto_12);
|
||||
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
|
||||
hdcp_version);
|
||||
ClientCapabilities::HDCP_V2_2);
|
||||
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
|
||||
kResourceTierHigh);
|
||||
|
||||
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
|
||||
device_info_.set_system_id(kSystemId);
|
||||
}
|
||||
SecurityProfile test_profile_1_;
|
||||
std::unique_ptr<SecurityProfileList> profile_list_;
|
||||
ClientIdentification client_id_;
|
||||
ProvisionedDeviceInfo device_info_;
|
||||
};
|
||||
|
||||
TEST_F(SecurityProfileListTest, InsertProfile) {
|
||||
// This test will not initialize the SecurityProfileList, hence it's empty.
|
||||
// Insert test profile 1 into the list.
|
||||
EXPECT_TRUE(profile_list_->InsertProfile(test_profile_1_));
|
||||
// Should not allow insertion of an already existing level.
|
||||
EXPECT_FALSE(profile_list_->InsertProfile(test_profile_1_));
|
||||
SecurityProfile profile;
|
||||
// Should not allow insertion of an already existing name.
|
||||
// Make sure the level is not the same as already inserted level_1.
|
||||
profile.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_2);
|
||||
profile.set_name(test_profile_1_.name());
|
||||
EXPECT_FALSE(profile_list_->InsertProfile(test_profile_1_));
|
||||
ASSERT_TRUE(profile_list_->GetProfileByLevel(
|
||||
SecurityProfile::SECURITY_PROFILE_LEVEL_1, &profile));
|
||||
EXPECT_TRUE(
|
||||
google::protobuf::util::MessageDifferencer::Equals(test_profile_1_, profile));
|
||||
// Insert test profile1 into the list.
|
||||
SecurityProfileList profile_list("widevine-test");
|
||||
SecurityProfile profile1;
|
||||
profile1.set_name("profile1");
|
||||
profile1.mutable_min_security_requirements()->set_security_level(
|
||||
ProvisionedDeviceInfo::LEVEL_3);
|
||||
EXPECT_TRUE(profile_list.InsertProfile(profile1));
|
||||
// Verify the list still has one profile.
|
||||
EXPECT_EQ(1, profile_list.NumProfiles());
|
||||
// Should not allow insert if existing profile has the same name.
|
||||
SecurityProfile profile2;
|
||||
profile2.set_name(profile1.name());
|
||||
profile2.mutable_min_security_requirements()->set_security_level(
|
||||
ProvisionedDeviceInfo::LEVEL_3);
|
||||
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) {
|
||||
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));
|
||||
EXPECT_EQ(client_id_.client_capabilities().max_hdcp_version(),
|
||||
drm_info.output().hdcp_version());
|
||||
EXPECT_EQ(client_id_.client_capabilities().analog_output_capabilities(),
|
||||
drm_info.output().analog_output_capabilities());
|
||||
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(),
|
||||
drm_info.security().resource_rating_tier());
|
||||
|
||||
@@ -108,104 +114,92 @@ TEST_F(SecurityProfileListTest, GetDrmInfo) {
|
||||
drm_info.security().security_level());
|
||||
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(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) {
|
||||
SecurityProfile::DrmInfo drm_info;
|
||||
profile_list_->Init();
|
||||
TEST_F(SecurityProfileListTest, QualifiedProfiles) {
|
||||
SecurityProfile profile1;
|
||||
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(
|
||||
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(7);
|
||||
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
|
||||
kResourceTierLow);
|
||||
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_3);
|
||||
SecurityProfile profile2;
|
||||
profile2.set_name("profile2");
|
||||
profile2.mutable_min_security_requirements()->set_security_level(
|
||||
ProvisionedDeviceInfo::LEVEL_1);
|
||||
profile2.mutable_min_output_requirements()->set_hdcp_version(
|
||||
ClientCapabilities::HDCP_V2);
|
||||
profile_list_->InsertProfile(profile2);
|
||||
|
||||
// Lowest profile level requires OEMCrypto version 8.
|
||||
ASSERT_EQ(
|
||||
SecurityProfile::SECURITY_PROFILE_LEVEL_UNDEFINED,
|
||||
profile_list_->GetBestProfileLevel(client_id_, device_info_, &drm_info));
|
||||
// Both profiles should qualify based on client_info and device_info from the
|
||||
// Setup function.
|
||||
std::vector<std::string> qualified_profiles;
|
||||
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
|
||||
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
|
||||
// Reduce the DRM capabilities of the device so profile2 will not qualify.
|
||||
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
|
||||
ClientCapabilities::HDCP_V1);
|
||||
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
|
||||
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
|
||||
kResourceTierMed);
|
||||
ASSERT_EQ(
|
||||
SecurityProfile::SECURITY_PROFILE_LEVEL_3,
|
||||
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));
|
||||
ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id_, device_info_,
|
||||
&qualified_profiles));
|
||||
EXPECT_NE(qualified_profiles.end(),
|
||||
std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||
profile1.name()));
|
||||
}
|
||||
|
||||
TEST_F(SecurityProfileListTest, FindProfile) {
|
||||
// This test will not initialize the SecurityProfileList, hence it's empty.
|
||||
// Insert test profile 1 into the list.
|
||||
SecurityProfileList profile_list("widevine-test");
|
||||
SecurityProfile profile1;
|
||||
profile1.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_1);
|
||||
profile1.set_name("profile1");
|
||||
profile1.mutable_min_security_requirements()->set_security_level(
|
||||
ProvisionedDeviceInfo::LEVEL_3);
|
||||
EXPECT_EQ(kDefaultContentOwnerName, profile1.owner());
|
||||
SecurityProfile profile2;
|
||||
profile2.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_2);
|
||||
profile2.set_name("profile2");
|
||||
SecurityProfile profile3;
|
||||
profile3.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_3);
|
||||
profile3.set_name("profile3");
|
||||
|
||||
// Insert profiles 1 & 2, but not 3..
|
||||
EXPECT_TRUE(profile_list_->InsertProfile(profile1));
|
||||
EXPECT_TRUE(profile_list_->InsertProfile(profile2));
|
||||
|
||||
// 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));
|
||||
profile2.mutable_min_security_requirements()->set_security_level(
|
||||
ProvisionedDeviceInfo::LEVEL_3);
|
||||
// Override the default owner name.
|
||||
profile2.set_owner("owner2");
|
||||
// Insert profiles 1 & 2.
|
||||
EXPECT_TRUE(profile_list.InsertProfile(profile1));
|
||||
EXPECT_TRUE(profile_list.InsertProfile(profile2));
|
||||
EXPECT_EQ(2, profile_list.NumProfiles());
|
||||
|
||||
// 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.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.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
|
||||
|
||||
@@ -37,7 +37,8 @@ std::string Sha512_Hash(const std::string& message) {
|
||||
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.
|
||||
// - Allocate a UUID to use as a "name space identifier" for all UUIDs
|
||||
// generated from names in that name space.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/sha_util.h"
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ Status GenerateAesSignature(const std::string& message,
|
||||
|
||||
Status GenerateRsaSignature(const std::string& message,
|
||||
const std::string& private_key,
|
||||
HashAlgorithm hash_algorithm,
|
||||
std::string* signature) {
|
||||
if (signature == 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");
|
||||
}
|
||||
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");
|
||||
}
|
||||
if (sig.empty()) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
@@ -23,11 +24,13 @@ Status GenerateAesSignature(const std::string& message,
|
||||
const std::string& aes_key,
|
||||
const std::string& aes_iv, std::string* signature);
|
||||
|
||||
// Generates a RSA signature of |message| using |private_key|.
|
||||
// Signature is returned via |sigature| if generation was successful.
|
||||
// Returns a Status that carries the details of error if generation failed.
|
||||
// Generates a RSA signature of |message| using |private_key| and
|
||||
// |hash_algorithm|. Signature is returned via |sigature| if generation was
|
||||
// successful. Returns a Status that carries the details of error if generation
|
||||
// failed.
|
||||
Status GenerateRsaSignature(const std::string& message,
|
||||
const std::string& private_key,
|
||||
HashAlgorithm hash_algorithm,
|
||||
std::string* signature);
|
||||
|
||||
} // namespace signature_util
|
||||
|
||||
@@ -25,9 +25,10 @@ class SignerPublicKeyImpl : public SignerPublicKey {
|
||||
SignerPublicKeyImpl(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 {
|
||||
if (!signer_public_key_->VerifySignature(message, signature)) {
|
||||
if (!signer_public_key_->VerifySignature(message, hash_algorithm,
|
||||
signature)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
@@ -24,8 +25,9 @@ class SignerPublicKey {
|
||||
SignerPublicKey(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,
|
||||
HashAlgorithm hash_algorithm,
|
||||
const std::string& signature) const = 0;
|
||||
|
||||
// A factory method to create a SignerPublicKey. The |algorithm| is used to
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "testing/gunit.h"
|
||||
#include "common/ec_key.h"
|
||||
#include "common/ec_test_keys.h"
|
||||
#include "common/hash_algorithm.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
@@ -20,6 +21,7 @@
|
||||
namespace widevine {
|
||||
|
||||
static const char kMessage[] = "The rain in Spain falls mainly in the blank?";
|
||||
const HashAlgorithm kHashAlgorithm = HashAlgorithm::kSha256;
|
||||
|
||||
class SignerPublicKeyTest : public ::testing::Test {
|
||||
public:
|
||||
@@ -32,12 +34,13 @@ TEST_F(SignerPublicKeyTest, RSA) {
|
||||
RsaPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits()));
|
||||
|
||||
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(
|
||||
rsa_test_keys_.public_test_key_1_3072_bits(), DrmCertificate::RSA);
|
||||
ASSERT_NE(public_key, nullptr);
|
||||
EXPECT_TRUE(public_key->VerifySignature(kMessage, signature));
|
||||
EXPECT_TRUE(public_key->VerifySignature(kMessage, kHashAlgorithm, signature));
|
||||
}
|
||||
|
||||
TEST_F(SignerPublicKeyTest, ECC) {
|
||||
@@ -45,13 +48,14 @@ TEST_F(SignerPublicKeyTest, ECC) {
|
||||
ECPrivateKey::Create(ec_test_keys_.private_test_key_1_secp521r1());
|
||||
|
||||
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(ec_test_keys_.public_test_key_1_secp521r1(),
|
||||
DrmCertificate::ECC_SECP521R1);
|
||||
ASSERT_NE(public_key, nullptr);
|
||||
EXPECT_TRUE(public_key->VerifySignature(kMessage, signature));
|
||||
EXPECT_TRUE(public_key->VerifySignature(kMessage, kHashAlgorithm, signature));
|
||||
}
|
||||
|
||||
TEST_F(SignerPublicKeyTest, IncorrectAlgorithm) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/signing_key_util.h"
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/crypto_util.h"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/test_utils.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace widevine {
|
||||
// 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
|
||||
// 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,
|
||||
const std::string& message,
|
||||
std::string* signature);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/status.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
#include "common/vmp_checker.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/hash_algorithm_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/x509_cert.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::string message(binary_info.binary_hash());
|
||||
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 \""
|
||||
<< binary_info.file_name() << "\".";
|
||||
*result = kTampered;
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/vmp_checker.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/hash_algorithm_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/vmp_checker.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/verified_media_pipeline.pb.h"
|
||||
|
||||
@@ -184,7 +186,9 @@ class VmpCheckerTest : public ::testing::Test {
|
||||
std::string message(binary_hash);
|
||||
message += flags & 0xff;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/wvm_test_keys.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/wvm_test_keys.h"
|
||||
#include "common/wvm_token_handler.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -7,22 +7,23 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/wvm_token_handler.h"
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/wvm_test_keys.h"
|
||||
|
||||
using widevine::wvm_test_keys::kTestSystemId;
|
||||
using widevine::wvm_test_keys::kTestSystemId3Des;
|
||||
using widevine::wvm_test_keys::kTestPreprovKeyHex;
|
||||
using widevine::wvm_test_keys::GetPreprovKeyVector;
|
||||
using widevine::wvm_test_keys::kTestDeviceKey1Hex;
|
||||
using widevine::wvm_test_keys::kTestDeviceKey2Hex;
|
||||
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::kTestToken2Hex;
|
||||
using widevine::wvm_test_keys::kTestToken3DesHex;
|
||||
using widevine::wvm_test_keys::GetPreprovKeyVector;
|
||||
|
||||
namespace widevine {
|
||||
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
#define COMMON_X509_CERT_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/thread_annotations.h"
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/x509.h"
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/x509_cert.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/test_utils.h"
|
||||
#include "common/x509_cert.h"
|
||||
|
||||
namespace widevine {
|
||||
const char kTestRootCaDerCert[] =
|
||||
@@ -352,7 +353,6 @@ const char kTestDevCodeSigningCert[] =
|
||||
const char kDevCertFlagOid[] = "1.3.6.1.4.1.11129.4.1.2";
|
||||
const bool kTestDevCodeSigningCertFlagValue = true;
|
||||
|
||||
|
||||
TEST(X509CertTest, LoadCert) {
|
||||
X509Cert test_cert;
|
||||
EXPECT_EQ(OkStatus(),
|
||||
|
||||
Reference in New Issue
Block a user