Minimal implementation of Widevine MediaCAS ECMG.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=226515998
This commit is contained in:
110
common/BUILD
110
common/BUILD
@@ -16,6 +16,7 @@ filegroup(
|
|||||||
name = "binary_release_files",
|
name = "binary_release_files",
|
||||||
srcs = [
|
srcs = [
|
||||||
"certificate_type.h",
|
"certificate_type.h",
|
||||||
|
"status.h",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,9 +38,9 @@ cc_library(
|
|||||||
hdrs = ["status.h"],
|
hdrs = ["status.h"],
|
||||||
deps = [
|
deps = [
|
||||||
"//base",
|
"//base",
|
||||||
"//util:error_space",
|
|
||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
|
"//util:error_space",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,20 +63,21 @@ cc_library(
|
|||||||
":error_space",
|
":error_space",
|
||||||
":random_util",
|
":random_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
|
":sha_util",
|
||||||
":signing_key_util",
|
":signing_key_util",
|
||||||
|
":status",
|
||||||
":wvm_token_handler",
|
":wvm_token_handler",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
"//strings",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"@abseil_repo//absl/synchronization",
|
||||||
|
"@abseil_repo//absl/time",
|
||||||
|
"//util/gtl:map_util",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_proto",
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:drm_certificate_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:license_protocol_proto",
|
"//protos/public:license_protocol_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_proto",
|
||||||
"//strings",
|
|
||||||
"//util/gtl:map_util",
|
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
"@abseil_repo//absl/synchronization",
|
|
||||||
"@abseil_repo//absl/time",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,18 +88,20 @@ cc_test(
|
|||||||
":client_cert",
|
":client_cert",
|
||||||
":drm_root_certificate",
|
":drm_root_certificate",
|
||||||
":error_space",
|
":error_space",
|
||||||
|
":sha_util",
|
||||||
|
":test_drm_certificates",
|
||||||
":wvm_test_keys",
|
":wvm_test_keys",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:rsa_key",
|
|
||||||
"//common:rsa_test_keys",
|
|
||||||
"//protos/public:drm_certificate_proto",
|
|
||||||
"//protos/public:errors_proto",
|
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
|
||||||
"//strings",
|
"//strings",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"@abseil_repo//absl/synchronization",
|
||||||
"@abseil_repo//absl/time",
|
"@abseil_repo//absl/time",
|
||||||
|
"//common:rsa_key",
|
||||||
|
"//common:rsa_test_keys",
|
||||||
|
"//protos/public:drm_certificate_proto",
|
||||||
|
"//protos/public:errors_proto",
|
||||||
|
"//protos/public:signed_drm_certificate_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -114,15 +118,15 @@ cc_library(
|
|||||||
":random_util",
|
":random_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":signing_key_util",
|
":signing_key_util",
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
"@abseil_repo//absl/strings",
|
||||||
|
"@abseil_repo//absl/synchronization",
|
||||||
|
"//util/gtl:map_util",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_proto",
|
||||||
"//protos/public:device_certificate_status_proto",
|
"//protos/public:device_certificate_status_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:provisioned_device_info_proto",
|
"//protos/public:provisioned_device_info_proto",
|
||||||
"//util/gtl:map_util",
|
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
"@abseil_repo//absl/synchronization",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -134,14 +138,14 @@ cc_test(
|
|||||||
":client_cert",
|
":client_cert",
|
||||||
":device_status_list",
|
":device_status_list",
|
||||||
"//base",
|
"//base",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
"//common:rsa_key",
|
"//common:rsa_key",
|
||||||
"//common:rsa_test_keys",
|
"//common:rsa_test_keys",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:provisioned_device_info_proto",
|
"//protos/public:provisioned_device_info_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_proto",
|
||||||
"//testing:gunit_main",
|
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -154,15 +158,15 @@ cc_library(
|
|||||||
":error_space",
|
":error_space",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
"@abseil_repo//absl/memory",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"@abseil_repo//absl/synchronization",
|
||||||
"//external:openssl",
|
"//external:openssl",
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:drm_certificate_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_proto",
|
||||||
"@abseil_repo//absl/memory",
|
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
"@abseil_repo//absl/synchronization",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -178,10 +182,10 @@ cc_test(
|
|||||||
":test_drm_certificates",
|
":test_drm_certificates",
|
||||||
"//base",
|
"//base",
|
||||||
"//external:protobuf",
|
"//external:protobuf",
|
||||||
|
"//testing:gunit_main",
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:drm_certificate_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_proto",
|
||||||
"//testing:gunit_main",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -193,11 +197,11 @@ cc_library(
|
|||||||
":aes_cbc_util",
|
":aes_cbc_util",
|
||||||
":drm_service_certificate",
|
":drm_service_certificate",
|
||||||
":error_space",
|
":error_space",
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
"@abseil_repo//absl/strings",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -220,9 +224,9 @@ cc_test(
|
|||||||
":rsa_test_keys",
|
":rsa_test_keys",
|
||||||
":rsa_util",
|
":rsa_util",
|
||||||
"//base",
|
"//base",
|
||||||
"//external:openssl",
|
|
||||||
"//testing:gunit",
|
"//testing:gunit",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
|
"//external:openssl",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -307,9 +311,9 @@ cc_library(
|
|||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//base",
|
"//base",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
"//external:openssl",
|
"//external:openssl",
|
||||||
"//util/endian",
|
"//util/endian",
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -332,8 +336,8 @@ cc_library(
|
|||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//base",
|
"//base",
|
||||||
"//external:openssl",
|
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
|
"//external:openssl",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -415,8 +419,8 @@ cc_library(
|
|||||||
":aes_cbc_util",
|
":aes_cbc_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -438,10 +442,10 @@ cc_test(
|
|||||||
deps = [
|
deps = [
|
||||||
":crypto_util",
|
":crypto_util",
|
||||||
":signing_key_util",
|
":signing_key_util",
|
||||||
"//protos/public:license_protocol_proto",
|
|
||||||
"//testing:gunit",
|
"//testing:gunit",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
|
"//protos/public:license_protocol_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -464,12 +468,12 @@ cc_library(
|
|||||||
":aes_cbc_util",
|
":aes_cbc_util",
|
||||||
":ecb_util",
|
":ecb_util",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
|
||||||
"//util/endian",
|
|
||||||
"//util/gtl:map_util",
|
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"@abseil_repo//absl/synchronization",
|
||||||
|
"//util/endian",
|
||||||
|
"//util/gtl:map_util",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -503,9 +507,9 @@ cc_library(
|
|||||||
srcs = ["error_space.cc"],
|
srcs = ["error_space.cc"],
|
||||||
hdrs = ["error_space.h"],
|
hdrs = ["error_space.h"],
|
||||||
deps = [
|
deps = [
|
||||||
"//common:status",
|
"//util:error_space",
|
||||||
"//protos/public:errors_proto",
|
|
||||||
"//util:proto_status",
|
"//util:proto_status",
|
||||||
|
"//protos/public:errors_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -518,14 +522,14 @@ cc_library(
|
|||||||
":drm_service_certificate",
|
":drm_service_certificate",
|
||||||
":error_space",
|
":error_space",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
|
":status",
|
||||||
":x509_cert",
|
":x509_cert",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
"@abseil_repo//absl/strings",
|
||||||
|
"@abseil_repo//absl/synchronization",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:remote_attestation_proto",
|
"//protos/public:remote_attestation_proto",
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
"@abseil_repo//absl/synchronization",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -540,15 +544,15 @@ cc_library(
|
|||||||
":error_space",
|
":error_space",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":rsa_util",
|
":rsa_util",
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
"@abseil_repo//absl/strings",
|
||||||
|
"@abseil_repo//absl/synchronization",
|
||||||
|
"//util/gtl:map_util",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_proto",
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:drm_certificate_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_proto",
|
||||||
"//util/gtl:map_util",
|
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
"@abseil_repo//absl/synchronization",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -566,13 +570,13 @@ cc_test(
|
|||||||
":test_drm_certificates",
|
":test_drm_certificates",
|
||||||
"//base",
|
"//base",
|
||||||
"//external:protobuf",
|
"//external:protobuf",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_proto",
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:drm_certificate_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:license_server_sdk_proto",
|
"//protos/public:license_server_sdk_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_proto",
|
||||||
"//testing:gunit_main",
|
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -581,11 +585,11 @@ cc_library(
|
|||||||
srcs = ["verified_media_pipeline.cc"],
|
srcs = ["verified_media_pipeline.cc"],
|
||||||
hdrs = ["verified_media_pipeline.h"],
|
hdrs = ["verified_media_pipeline.h"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":status",
|
||||||
":vmp_checker",
|
":vmp_checker",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
|
||||||
"//protos/public:license_protocol_proto",
|
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
|
"//protos/public:license_protocol_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -597,11 +601,11 @@ cc_library(
|
|||||||
":error_space",
|
":error_space",
|
||||||
":openssl_util",
|
":openssl_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
|
||||||
"//external:openssl",
|
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"@abseil_repo//absl/synchronization",
|
||||||
|
"//external:openssl",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -611,8 +615,8 @@ cc_library(
|
|||||||
srcs = ["test_utils.cc"],
|
srcs = ["test_utils.cc"],
|
||||||
hdrs = ["test_utils.h"],
|
hdrs = ["test_utils.h"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
|
||||||
"//external:openssl",
|
"//external:openssl",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -639,9 +643,9 @@ cc_library(
|
|||||||
":certificate_type",
|
":certificate_type",
|
||||||
":error_space",
|
":error_space",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
|
":status",
|
||||||
":x509_cert",
|
":x509_cert",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:verified_media_pipeline_proto",
|
"//protos/public:verified_media_pipeline_proto",
|
||||||
],
|
],
|
||||||
@@ -655,10 +659,10 @@ cc_test(
|
|||||||
":rsa_key",
|
":rsa_key",
|
||||||
":vmp_checker",
|
":vmp_checker",
|
||||||
"//base",
|
"//base",
|
||||||
"//protos/public:errors_proto",
|
|
||||||
"//protos/public:verified_media_pipeline_proto",
|
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
|
"//protos/public:errors_proto",
|
||||||
|
"//protos/public:verified_media_pipeline_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -667,8 +671,8 @@ cc_library(
|
|||||||
srcs = ["string_util.cc"],
|
srcs = ["string_util.cc"],
|
||||||
hdrs = ["string_util.h"],
|
hdrs = ["string_util.h"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -17,19 +17,18 @@
|
|||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "util/gtl/map_util.h"
|
#include "util/gtl/map_util.h"
|
||||||
#include "common/status.h"
|
|
||||||
#include "common/crypto_util.h"
|
#include "common/crypto_util.h"
|
||||||
#include "common/drm_root_certificate.h"
|
#include "common/drm_root_certificate.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
#include "common/random_util.h"
|
#include "common/random_util.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
#include "common/signing_key_util.h"
|
#include "common/signing_key_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
#include "common/wvm_token_handler.h"
|
#include "common/wvm_token_handler.h"
|
||||||
#include "protos/public/drm_certificate.pb.h"
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
#include "protos/public/errors.pb.h"
|
#include "protos/public/errors.pb.h"
|
||||||
#include "protos/public/signed_drm_certificate.pb.h"
|
#include "protos/public/signed_drm_certificate.pb.h"
|
||||||
|
|
||||||
// TODO(user): Get rid of this horror.
|
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -37,45 +36,44 @@ const int kKeyboxSizeBytes = 72;
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// TODO(user): change to util::StatusOr<std::unique_ptr<ClientCert>>
|
|
||||||
// instead of ClientCert** to explicitly assigning ownership of the created
|
// instead of ClientCert** to explicitly assigning ownership of the created
|
||||||
// object to the caller.
|
// object to the caller.
|
||||||
|
|
||||||
util::Status ClientCert::Create(const DrmRootCertificate* root_certificate,
|
Status ClientCert::Create(const DrmRootCertificate* root_certificate,
|
||||||
ClientIdentification::TokenType token_type,
|
ClientIdentification::TokenType token_type,
|
||||||
const std::string& token, ClientCert** client_cert) {
|
const std::string& token, ClientCert** client_cert) {
|
||||||
DCHECK(client_cert);
|
DCHECK(client_cert);
|
||||||
if (token_type == ClientIdentification::KEYBOX) {
|
if (token_type == ClientIdentification::KEYBOX) {
|
||||||
*client_cert = nullptr;
|
*client_cert = nullptr;
|
||||||
if (token.size() < kKeyboxSizeBytes) {
|
if (token.size() < kKeyboxSizeBytes) {
|
||||||
return util::Status(error_space, INVALID_KEYBOX_TOKEN,
|
return Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||||
"keybox-token-is-too-short");
|
"keybox-token-is-too-short");
|
||||||
}
|
}
|
||||||
return ClientCert::CreateWithKeybox(token, client_cert);
|
return ClientCert::CreateWithKeybox(token, client_cert);
|
||||||
} else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
} else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
||||||
return CreateWithDrmCertificate(root_certificate, token, client_cert);
|
return CreateWithDrmCertificate(root_certificate, token, client_cert);
|
||||||
} else {
|
} else {
|
||||||
return util::Status(error_space, util::error::UNIMPLEMENTED,
|
return Status(error_space, error::UNIMPLEMENTED,
|
||||||
"client-type-not-implemented");
|
"client-type-not-implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
|
Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
|
||||||
ClientCert** client_cert) {
|
ClientCert** client_cert) {
|
||||||
CHECK(client_cert);
|
CHECK(client_cert);
|
||||||
*client_cert = nullptr;
|
*client_cert = nullptr;
|
||||||
|
|
||||||
std::unique_ptr<KeyboxClientCert> new_client_cert(new KeyboxClientCert);
|
std::unique_ptr<KeyboxClientCert> new_client_cert(new KeyboxClientCert);
|
||||||
util::Status status = new_client_cert->Initialize(keybox_token);
|
Status status = new_client_cert->Initialize(keybox_token);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
*client_cert = new_client_cert.release();
|
*client_cert = new_client_cert.release();
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status ClientCert::CreateWithDrmCertificate(
|
Status ClientCert::CreateWithDrmCertificate(
|
||||||
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
|
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
|
||||||
ClientCert** client_cert) {
|
ClientCert** client_cert) {
|
||||||
CHECK(client_cert);
|
CHECK(client_cert);
|
||||||
@@ -83,14 +81,14 @@ util::Status ClientCert::CreateWithDrmCertificate(
|
|||||||
|
|
||||||
std::unique_ptr<CertificateClientCert> new_client_cert(
|
std::unique_ptr<CertificateClientCert> new_client_cert(
|
||||||
new CertificateClientCert);
|
new CertificateClientCert);
|
||||||
util::Status status =
|
Status status =
|
||||||
new_client_cert->Initialize(root_certificate, drm_certificate);
|
new_client_cert->Initialize(root_certificate, drm_certificate);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
*client_cert = new_client_cert.release();
|
*client_cert = new_client_cert.release();
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientCert::CreateSignature(const std::string& message, std::string* signature) {
|
void ClientCert::CreateSignature(const std::string& message, std::string* signature) {
|
||||||
@@ -110,8 +108,10 @@ void ClientCert::GenerateSigningKey(const std::string& message,
|
|||||||
DCHECK(!key().empty());
|
DCHECK(!key().empty());
|
||||||
using crypto_util::DeriveKey;
|
using crypto_util::DeriveKey;
|
||||||
using crypto_util::kSigningKeyLabel;
|
using crypto_util::kSigningKeyLabel;
|
||||||
set_signing_key(DeriveKey(key(), kSigningKeyLabel, message,
|
set_signing_key(
|
||||||
SigningKeyMaterialSize(protocol_version)));
|
DeriveKey(key(), kSigningKeyLabel,
|
||||||
|
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||||
|
SigningKeyMaterialSizeBits(protocol_version)));
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboxClientCert::KeyboxClientCert() {}
|
KeyboxClientCert::KeyboxClientCert() {}
|
||||||
@@ -139,51 +139,51 @@ uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
|
|||||||
return WvmTokenHandler::GetSystemId(keybox_bytes);
|
return WvmTokenHandler::GetSystemId(keybox_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) {
|
Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) {
|
||||||
if (keybox_bytes.size() < kKeyboxSizeBytes) {
|
if (keybox_bytes.size() < kKeyboxSizeBytes) {
|
||||||
return util::Status(error_space, INVALID_KEYBOX_TOKEN,
|
return Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||||
"keybox-token-is-too-short");
|
"keybox-token-is-too-short");
|
||||||
}
|
}
|
||||||
|
|
||||||
set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes));
|
set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes));
|
||||||
set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes));
|
set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes));
|
||||||
bool insecure_keybox = false;
|
bool insecure_keybox = false;
|
||||||
util::Status status = WvmTokenHandler::DecryptDeviceKey(
|
Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &device_key_,
|
||||||
keybox_bytes, &device_key_, nullptr, &insecure_keybox);
|
nullptr, &insecure_keybox);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
Errors new_code = status.error_code() == util::error::NOT_FOUND
|
Errors new_code = status.error_code() == error::NOT_FOUND
|
||||||
? MISSING_PRE_PROV_KEY
|
? MISSING_PRE_PROV_KEY
|
||||||
: KEYBOX_DECRYPT_ERROR;
|
: KEYBOX_DECRYPT_ERROR;
|
||||||
return util::Status(error_space, new_code, status.error_message());
|
return Status(error_space, new_code, status.error_message());
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status KeyboxClientCert::VerifySignature(
|
Status KeyboxClientCert::VerifySignature(const std::string& message,
|
||||||
const std::string& message, const std::string& signature,
|
const std::string& signature,
|
||||||
ProtocolVersion protocol_version) {
|
ProtocolVersion protocol_version) {
|
||||||
DCHECK(!signing_key().empty());
|
DCHECK(!signing_key().empty());
|
||||||
using crypto_util::VerifySignatureHmacSha256;
|
using crypto_util::VerifySignatureHmacSha256;
|
||||||
if (!VerifySignatureHmacSha256(
|
if (!VerifySignatureHmacSha256(
|
||||||
GetClientSigningKey(signing_key(), protocol_version), signature,
|
GetClientSigningKey(signing_key(), protocol_version), signature,
|
||||||
message)) {
|
message)) {
|
||||||
return util::Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
|
return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
CertificateClientCert::CertificateClientCert() {}
|
CertificateClientCert::CertificateClientCert() {}
|
||||||
|
|
||||||
CertificateClientCert::~CertificateClientCert() {}
|
CertificateClientCert::~CertificateClientCert() {}
|
||||||
|
|
||||||
util::Status CertificateClientCert::Initialize(
|
Status CertificateClientCert::Initialize(
|
||||||
const DrmRootCertificate* drm_root_certificate,
|
const DrmRootCertificate* drm_root_certificate,
|
||||||
const std::string& serialized_certificate) {
|
const std::string& serialized_certificate) {
|
||||||
CHECK(drm_root_certificate);
|
CHECK(drm_root_certificate);
|
||||||
|
|
||||||
SignedDrmCertificate signed_device_cert;
|
SignedDrmCertificate signed_device_cert;
|
||||||
DrmCertificate device_cert;
|
DrmCertificate device_cert;
|
||||||
util::Status status = drm_root_certificate->VerifyCertificate(
|
Status status = drm_root_certificate->VerifyCertificate(
|
||||||
serialized_certificate, &signed_device_cert, &device_cert);
|
serialized_certificate, &signed_device_cert, &device_cert);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
@@ -192,12 +192,12 @@ util::Status CertificateClientCert::Initialize(
|
|||||||
const SignedDrmCertificate& signer = signed_device_cert.signer();
|
const SignedDrmCertificate& signer = signed_device_cert.signer();
|
||||||
DrmCertificate model_certificate;
|
DrmCertificate model_certificate;
|
||||||
if (!model_certificate.ParseFromString(signer.drm_certificate())) {
|
if (!model_certificate.ParseFromString(signer.drm_certificate())) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"drm-certificate-invalid-signer");
|
"drm-certificate-invalid-signer");
|
||||||
}
|
}
|
||||||
if (!model_certificate.has_serial_number()) {
|
if (!model_certificate.has_serial_number()) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"missing-signer-serial-number");
|
"missing-signer-serial-number");
|
||||||
}
|
}
|
||||||
// Check to see if this model certificate is signed by a
|
// Check to see if this model certificate is signed by a
|
||||||
// provisioner (entity using Widevine Provisioning Server SDK).
|
// provisioner (entity using Widevine Provisioning Server SDK).
|
||||||
@@ -205,56 +205,58 @@ util::Status CertificateClientCert::Initialize(
|
|||||||
DrmCertificate provisioner_certificate;
|
DrmCertificate provisioner_certificate;
|
||||||
if (!provisioner_certificate.ParseFromString(
|
if (!provisioner_certificate.ParseFromString(
|
||||||
signer.signer().drm_certificate())) {
|
signer.signer().drm_certificate())) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"model-certificate-invalid-signer");
|
"model-certificate-invalid-signer");
|
||||||
}
|
}
|
||||||
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
|
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
|
||||||
set_signed_by_provisioner(true);
|
set_signed_by_provisioner(true);
|
||||||
} else {
|
} else {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"expected-provisioning-provider-certificate-type");
|
"expected-provisioning-provider-certificate-type");
|
||||||
}
|
}
|
||||||
if (!provisioner_certificate.has_provider_id() ||
|
if (!provisioner_certificate.has_provider_id() ||
|
||||||
provisioner_certificate.provider_id().empty()) {
|
provisioner_certificate.provider_id().empty()) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"missing-provisioning-service-id");
|
"missing-provisioning-service-id");
|
||||||
}
|
}
|
||||||
set_service_id(provisioner_certificate.provider_id());
|
set_service_id(provisioner_certificate.provider_id());
|
||||||
}
|
}
|
||||||
set_signer_serial_number(model_certificate.serial_number());
|
set_signer_serial_number(model_certificate.serial_number());
|
||||||
set_signer_creation_time_seconds(model_certificate.creation_time_seconds());
|
set_signer_creation_time_seconds(model_certificate.creation_time_seconds());
|
||||||
if (!model_certificate.has_system_id()) {
|
if (!model_certificate.has_system_id()) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"model-certificate-missing-system-id");
|
"model-certificate-missing-system-id");
|
||||||
}
|
}
|
||||||
set_system_id(model_certificate.system_id());
|
set_system_id(model_certificate.system_id());
|
||||||
set_serial_number(device_cert.serial_number());
|
set_serial_number(device_cert.serial_number());
|
||||||
set_public_key(device_cert.public_key());
|
set_public_key(device_cert.public_key());
|
||||||
rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
|
rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
|
||||||
if (rsa_public_key_ == nullptr) {
|
if (rsa_public_key_ == nullptr) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"drm-certificate-public-key-failed");
|
"drm-certificate-public-key-failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(user): Move this somewhere else. It is license protocol.
|
// TODO(user): Move this somewhere else. It is license protocol.
|
||||||
set_key(Random16Bytes());
|
set_key(Random16Bytes());
|
||||||
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
|
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
|
||||||
return util::Status(error_space, ENCRYPT_ERROR,
|
return Status(error_space, ENCRYPT_ERROR,
|
||||||
"drm-certificate-failed-encrypt-session-key");
|
"drm-certificate-failed-encrypt-session-key");
|
||||||
}
|
}
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CertificateClientCert::VerifySignature(
|
Status CertificateClientCert::VerifySignature(
|
||||||
const std::string& message, const std::string& signature,
|
const std::string& message, const std::string& signature,
|
||||||
ProtocolVersion protocol_version) {
|
ProtocolVersion protocol_version) {
|
||||||
CHECK(rsa_public_key_);
|
CHECK(rsa_public_key_);
|
||||||
|
|
||||||
if (!rsa_public_key_->VerifySignature(message, signature)) {
|
if (!rsa_public_key_->VerifySignature(
|
||||||
return util::Status(error_space, INVALID_SIGNATURE, "");
|
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||||
|
signature)) {
|
||||||
|
return Status(error_space, INVALID_SIGNATURE, "");
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "common/status.h"
|
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
|
#include "common/status.h"
|
||||||
#include "protos/public/client_identification.pb.h"
|
#include "protos/public/client_identification.pb.h"
|
||||||
#include "protos/public/license_protocol.pb.h"
|
#include "protos/public/license_protocol.pb.h"
|
||||||
|
|
||||||
@@ -29,15 +29,15 @@ class SignedDrmCertificate;
|
|||||||
class ClientCert {
|
class ClientCert {
|
||||||
public:
|
public:
|
||||||
virtual ~ClientCert() {}
|
virtual ~ClientCert() {}
|
||||||
static util::Status Create(
|
static Status Create(
|
||||||
const DrmRootCertificate* root_certificate,
|
const DrmRootCertificate* root_certificate,
|
||||||
widevine::ClientIdentification::TokenType token_type,
|
widevine::ClientIdentification::TokenType token_type,
|
||||||
const std::string& token, ClientCert** client_cert);
|
const std::string& token, ClientCert** client_cert);
|
||||||
// Creates a Keybox based ClientCert.
|
// Creates a Keybox based ClientCert.
|
||||||
static util::Status CreateWithKeybox(const std::string& keybox_token,
|
static Status CreateWithKeybox(const std::string& keybox_token,
|
||||||
ClientCert** client_cert);
|
ClientCert** client_cert);
|
||||||
// Creates a Device Certificate based ClientCert.
|
// Creates a Device Certificate based ClientCert.
|
||||||
static util::Status CreateWithDrmCertificate(
|
static Status CreateWithDrmCertificate(
|
||||||
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
|
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
|
||||||
ClientCert** client_cert);
|
ClientCert** client_cert);
|
||||||
// Creates a HMAC SHA256 signature based on the message and the key().
|
// Creates a HMAC SHA256 signature based on the message and the key().
|
||||||
@@ -46,9 +46,8 @@ class ClientCert {
|
|||||||
// Checks the passed in signature against a signature created used the
|
// Checks the passed in signature against a signature created used the
|
||||||
// classes information and the passed in message. Returns OK if signature
|
// classes information and the passed in message. Returns OK if signature
|
||||||
// is valid.
|
// is valid.
|
||||||
virtual util::Status VerifySignature(const std::string& message,
|
virtual Status VerifySignature(const std::string& message, const std::string& signature,
|
||||||
const std::string& signature,
|
ProtocolVersion protocol_version) = 0;
|
||||||
ProtocolVersion protocol_version) = 0;
|
|
||||||
// Creates a signing_key that is accessible using signing_key(). Signing_key
|
// Creates a signing_key that is accessible using signing_key(). Signing_key
|
||||||
// is constructed by doing a key derivation using the key() and message.
|
// is constructed by doing a key derivation using the key() and message.
|
||||||
virtual void GenerateSigningKey(const std::string& message,
|
virtual void GenerateSigningKey(const std::string& message,
|
||||||
@@ -118,10 +117,10 @@ class KeyboxClientCert : public ClientCert {
|
|||||||
static bool IsSystemIdKnown(const uint32_t system_id);
|
static bool IsSystemIdKnown(const uint32_t system_id);
|
||||||
static uint32_t GetSystemId(const std::string& keybox_bytes);
|
static uint32_t GetSystemId(const std::string& keybox_bytes);
|
||||||
|
|
||||||
util::Status Initialize(const std::string& keybox_bytes);
|
Status Initialize(const std::string& keybox_bytes);
|
||||||
|
|
||||||
util::Status VerifySignature(const std::string& message, const std::string& signature,
|
Status VerifySignature(const std::string& message, const std::string& signature,
|
||||||
ProtocolVersion protocol_version) override;
|
ProtocolVersion protocol_version) override;
|
||||||
const std::string& key() const override { return device_key_; }
|
const std::string& key() const override { return device_key_; }
|
||||||
void set_key(const std::string& key) override { device_key_ = key; }
|
void set_key(const std::string& key) override { device_key_ = key; }
|
||||||
const std::string& encrypted_key() const override { return encrypted_device_key_; }
|
const std::string& encrypted_key() const override { return encrypted_device_key_; }
|
||||||
@@ -148,8 +147,8 @@ class CertificateClientCert : public ClientCert {
|
|||||||
public:
|
public:
|
||||||
~CertificateClientCert() override;
|
~CertificateClientCert() override;
|
||||||
|
|
||||||
util::Status VerifySignature(const std::string& message, const std::string& signature,
|
Status VerifySignature(const std::string& message, const std::string& signature,
|
||||||
ProtocolVersion protocol_version) override;
|
ProtocolVersion protocol_version) override;
|
||||||
const std::string& key() const override { return session_key_; }
|
const std::string& key() const override { return session_key_; }
|
||||||
void set_key(const std::string& key) override { session_key_ = key; }
|
void set_key(const std::string& key) override { session_key_ = key; }
|
||||||
const std::string& encrypted_key() const override {
|
const std::string& encrypted_key() const override {
|
||||||
@@ -162,8 +161,8 @@ class CertificateClientCert : public ClientCert {
|
|||||||
protected:
|
protected:
|
||||||
friend class ClientCert;
|
friend class ClientCert;
|
||||||
friend class MockCertificateClientCert;
|
friend class MockCertificateClientCert;
|
||||||
util::Status Initialize(const DrmRootCertificate* drm_root_certificate,
|
Status Initialize(const DrmRootCertificate* drm_root_certificate,
|
||||||
const std::string& serialized_certificate);
|
const std::string& serialized_certificate);
|
||||||
virtual void set_public_key(const std::string& public_key) {
|
virtual void set_public_key(const std::string& public_key) {
|
||||||
public_key_ = public_key;
|
public_key_ = public_key;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#include "common/drm_root_certificate.h"
|
#include "common/drm_root_certificate.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
#include "common/rsa_test_keys.h"
|
#include "common/rsa_test_keys.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
#include "common/test_drm_certificates.h"
|
||||||
#include "common/wvm_test_keys.h"
|
#include "common/wvm_test_keys.h"
|
||||||
#include "protos/public/drm_certificate.pb.h"
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
#include "protos/public/errors.pb.h"
|
#include "protos/public/errors.pb.h"
|
||||||
@@ -31,7 +33,7 @@
|
|||||||
|
|
||||||
// TODO(user): Change these tests to use on-the-fly generated intermediate
|
// TODO(user): Change these tests to use on-the-fly generated intermediate
|
||||||
// and device certificates based on RsaTestKeys.
|
// and device certificates based on RsaTestKeys.
|
||||||
// TODO(user): Add testcase(s) VerifySignature, CreateSignature,
|
// TODO(user): Add testcase(s) CreateSignature,
|
||||||
// and GenerateSigningKey.
|
// and GenerateSigningKey.
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
@@ -73,11 +75,10 @@ class ClientCertTest : public ::testing::Test {
|
|||||||
const std::string certificate_;
|
const std::string certificate_;
|
||||||
const std::string expected_serial_number_;
|
const std::string expected_serial_number_;
|
||||||
uint32_t expected_system_id_;
|
uint32_t expected_system_id_;
|
||||||
util::Status expected_status_;
|
Status expected_status_;
|
||||||
TestCertificateAndData(const std::string& certificate,
|
TestCertificateAndData(const std::string& certificate,
|
||||||
const std::string& expected_serial_number,
|
const std::string& expected_serial_number,
|
||||||
uint32_t expected_system_id,
|
uint32_t expected_system_id, Status expected_status)
|
||||||
util::Status expected_status)
|
|
||||||
: certificate_(certificate),
|
: certificate_(certificate),
|
||||||
expected_serial_number_(expected_serial_number),
|
expected_serial_number_(expected_serial_number),
|
||||||
expected_system_id_(expected_system_id),
|
expected_system_id_(expected_system_id),
|
||||||
@@ -111,7 +112,8 @@ class ClientCertTest : public ::testing::Test {
|
|||||||
SignedDrmCertificate* signer, uint32_t system_id,
|
SignedDrmCertificate* signer, uint32_t system_id,
|
||||||
const std::string& serial_number);
|
const std::string& serial_number);
|
||||||
|
|
||||||
RsaTestKeys test_keys_;
|
RsaTestKeys test_rsa_keys_;
|
||||||
|
TestDrmCertificates test_drm_certs_;
|
||||||
std::unique_ptr<DrmRootCertificate> root_cert_;
|
std::unique_ptr<DrmRootCertificate> root_cert_;
|
||||||
static bool setup_preprov_keys_;
|
static bool setup_preprov_keys_;
|
||||||
};
|
};
|
||||||
@@ -121,7 +123,7 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
|||||||
const bool expect_success,
|
const bool expect_success,
|
||||||
const bool compare_device_key) {
|
const bool compare_device_key) {
|
||||||
// Test validation of a valid request.
|
// Test validation of a valid request.
|
||||||
util::Status status;
|
Status status;
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
ClientCert* client_cert_ptr = nullptr;
|
||||||
|
|
||||||
// Two ways to create a client cert object, test both.
|
// Two ways to create a client cert object, test both.
|
||||||
@@ -136,7 +138,7 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
|||||||
}
|
}
|
||||||
std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
|
std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
|
||||||
if (expect_success) {
|
if (expect_success) {
|
||||||
ASSERT_EQ(util::OkStatus(), status);
|
ASSERT_EQ(OkStatus(), status);
|
||||||
ASSERT_TRUE(keybox_cert.get());
|
ASSERT_TRUE(keybox_cert.get());
|
||||||
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
|
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
|
||||||
EXPECT_EQ(expectation.expected_serial_number_,
|
EXPECT_EQ(expectation.expected_serial_number_,
|
||||||
@@ -145,7 +147,7 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
|||||||
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
|
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
EXPECT_NE(util::OkStatus(), status);
|
EXPECT_NE(OkStatus(), status);
|
||||||
EXPECT_FALSE(keybox_cert);
|
EXPECT_FALSE(keybox_cert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +161,7 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
|
|||||||
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
|
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
|
||||||
|
|
||||||
// Test validation of a valid request.
|
// Test validation of a valid request.
|
||||||
util::Status status;
|
Status status;
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
ClientCert* client_cert_ptr = nullptr;
|
||||||
status = ClientCert::Create(root_cert_.get(),
|
status = ClientCert::Create(root_cert_.get(),
|
||||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
@@ -209,7 +211,7 @@ DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
|
|||||||
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
|
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
|
||||||
intermediate_certificate->set_serial_number(serial_number);
|
intermediate_certificate->set_serial_number(serial_number);
|
||||||
intermediate_certificate->set_public_key(
|
intermediate_certificate->set_public_key(
|
||||||
test_keys_.public_test_key_2_2048_bits());
|
test_rsa_keys_.public_test_key_2_2048_bits());
|
||||||
intermediate_certificate->set_system_id(system_id);
|
intermediate_certificate->set_system_id(system_id);
|
||||||
intermediate_certificate->set_creation_time_seconds(1234);
|
intermediate_certificate->set_creation_time_seconds(1234);
|
||||||
return intermediate_certificate.release();
|
return intermediate_certificate.release();
|
||||||
@@ -221,7 +223,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
|
|||||||
std::unique_ptr<DrmCertificate> intermediate_certificate(
|
std::unique_ptr<DrmCertificate> intermediate_certificate(
|
||||||
GenerateIntermediateCertificate(system_id, serial_number));
|
GenerateIntermediateCertificate(system_id, serial_number));
|
||||||
return SignCertificate(*intermediate_certificate, signer,
|
return SignCertificate(*intermediate_certificate, signer,
|
||||||
test_keys_.private_test_key_1_3072_bits());
|
test_rsa_keys_.private_test_key_1_3072_bits());
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
||||||
@@ -230,7 +232,7 @@ DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
|||||||
drm_certificate->set_type(DrmCertificate::DEVICE);
|
drm_certificate->set_type(DrmCertificate::DEVICE);
|
||||||
drm_certificate->set_serial_number(serial_number);
|
drm_certificate->set_serial_number(serial_number);
|
||||||
drm_certificate->set_system_id(system_id);
|
drm_certificate->set_system_id(system_id);
|
||||||
drm_certificate->set_public_key(test_keys_.public_test_key_3_2048_bits());
|
drm_certificate->set_public_key(test_rsa_keys_.public_test_key_3_2048_bits());
|
||||||
drm_certificate->set_creation_time_seconds(4321);
|
drm_certificate->set_creation_time_seconds(4321);
|
||||||
return drm_certificate.release();
|
return drm_certificate.release();
|
||||||
}
|
}
|
||||||
@@ -241,7 +243,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
|
|||||||
std::unique_ptr<DrmCertificate> drm_certificate(
|
std::unique_ptr<DrmCertificate> drm_certificate(
|
||||||
GenerateDrmCertificate(system_id, serial_number));
|
GenerateDrmCertificate(system_id, serial_number));
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate(
|
||||||
*drm_certificate, signer, test_keys_.private_test_key_2_2048_bits()));
|
*drm_certificate, signer, test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
return signed_drm_certificate.release();
|
return signed_drm_certificate.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +254,7 @@ DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
|
|||||||
provisioner_certificate->set_serial_number(serial_number);
|
provisioner_certificate->set_serial_number(serial_number);
|
||||||
// TODO(user): Need to generate 3072 bit test for provisioner certificates.
|
// TODO(user): Need to generate 3072 bit test for provisioner certificates.
|
||||||
provisioner_certificate->set_public_key(
|
provisioner_certificate->set_public_key(
|
||||||
test_keys_.public_test_key_1_3072_bits());
|
test_rsa_keys_.public_test_key_1_3072_bits());
|
||||||
provisioner_certificate->set_system_id(system_id);
|
provisioner_certificate->set_system_id(system_id);
|
||||||
provisioner_certificate->set_provider_id(provider_id);
|
provisioner_certificate->set_provider_id(provider_id);
|
||||||
provisioner_certificate->set_creation_time_seconds(1234);
|
provisioner_certificate->set_creation_time_seconds(1234);
|
||||||
@@ -264,7 +266,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
|
|||||||
std::unique_ptr<DrmCertificate> provisioner_certificate(
|
std::unique_ptr<DrmCertificate> provisioner_certificate(
|
||||||
GenerateProvisionerCertificate(system_id, serial_number, service_id));
|
GenerateProvisionerCertificate(system_id, serial_number, service_id));
|
||||||
return SignCertificate(*provisioner_certificate, nullptr,
|
return SignCertificate(*provisioner_certificate, nullptr,
|
||||||
test_keys_.private_test_key_1_3072_bits());
|
test_rsa_keys_.private_test_key_1_3072_bits());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ClientCertTest, BasicValidation) {
|
TEST_F(ClientCertTest, BasicValidation) {
|
||||||
@@ -302,8 +304,7 @@ TEST_F(ClientCertTest, BasicCertValidation) {
|
|||||||
nullptr, system_id, serial_number),
|
nullptr, system_id, serial_number),
|
||||||
system_id, serial_number + "-device"));
|
system_id, serial_number + "-device"));
|
||||||
const TestCertificateAndData kValidCertificateAndExpectedData(
|
const TestCertificateAndData kValidCertificateAndExpectedData(
|
||||||
signed_cert->SerializeAsString(), serial_number, system_id,
|
signed_cert->SerializeAsString(), serial_number, system_id, OkStatus());
|
||||||
util::OkStatus());
|
|
||||||
const bool compare_data = true;
|
const bool compare_data = true;
|
||||||
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
|
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
|
||||||
compare_data);
|
compare_data);
|
||||||
@@ -347,7 +348,7 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
new SignedDrmCertificate);
|
new SignedDrmCertificate);
|
||||||
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
|
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
|
||||||
GenerateSignature(invalid_drm_cert->drm_certificate(),
|
GenerateSignature(invalid_drm_cert->drm_certificate(),
|
||||||
test_keys_.private_test_key_2_2048_bits(),
|
test_rsa_keys_.private_test_key_2_2048_bits(),
|
||||||
invalid_drm_cert->mutable_signature());
|
invalid_drm_cert->mutable_signature());
|
||||||
invalid_drm_cert->set_allocated_signer(
|
invalid_drm_cert->set_allocated_signer(
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
||||||
@@ -357,18 +358,18 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
|
||||||
*dev_cert,
|
*dev_cert,
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
||||||
test_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// Invalid serialized intermediate certificate.
|
// Invalid serialized intermediate certificate.
|
||||||
signed_signer.reset(
|
signed_signer.reset(
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
||||||
signed_signer->set_drm_certificate("bad-serialized-cert");
|
signed_signer->set_drm_certificate("bad-serialized-cert");
|
||||||
GenerateSignature(signed_signer->drm_certificate(),
|
GenerateSignature(signed_signer->drm_certificate(),
|
||||||
test_keys_.private_test_key_1_3072_bits(),
|
test_rsa_keys_.private_test_key_1_3072_bits(),
|
||||||
signed_signer->mutable_signature());
|
signed_signer->mutable_signature());
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||||
std::unique_ptr<SignedDrmCertificate> invalid_signer(
|
std::unique_ptr<SignedDrmCertificate> invalid_signer(
|
||||||
SignCertificate(*dev_cert, signed_signer.release(),
|
SignCertificate(*dev_cert, signed_signer.release(),
|
||||||
test_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// Invalid signer public key.
|
// Invalid signer public key.
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||||
@@ -376,8 +377,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
|
||||||
*dev_cert,
|
*dev_cert,
|
||||||
SignCertificate(*signer_cert, nullptr,
|
SignCertificate(*signer_cert, nullptr,
|
||||||
test_keys_.private_test_key_1_3072_bits()),
|
test_rsa_keys_.private_test_key_1_3072_bits()),
|
||||||
test_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// Invalid device certificate signature.
|
// Invalid device certificate signature.
|
||||||
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
|
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
|
||||||
GenerateSignedDrmCertificate(
|
GenerateSignedDrmCertificate(
|
||||||
@@ -391,8 +392,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
|
||||||
*dev_cert,
|
*dev_cert,
|
||||||
SignCertificate(*signer_cert, nullptr,
|
SignCertificate(*signer_cert, nullptr,
|
||||||
test_keys_.private_test_key_1_3072_bits()),
|
test_rsa_keys_.private_test_key_1_3072_bits()),
|
||||||
test_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// Missing signer serial number.
|
// Missing signer serial number.
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||||
@@ -400,8 +401,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
|
||||||
*dev_cert,
|
*dev_cert,
|
||||||
SignCertificate(*signer_cert, nullptr,
|
SignCertificate(*signer_cert, nullptr,
|
||||||
test_keys_.private_test_key_1_3072_bits()),
|
test_rsa_keys_.private_test_key_1_3072_bits()),
|
||||||
test_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// Invalid serialized intermediate certificate.
|
// Invalid serialized intermediate certificate.
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||||
signed_signer.reset(
|
signed_signer.reset(
|
||||||
@@ -409,37 +410,36 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
signed_signer->set_signature("bad-signature");
|
signed_signer->set_signature("bad-signature");
|
||||||
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
|
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
|
||||||
SignCertificate(*dev_cert, signed_signer.release(),
|
SignCertificate(*dev_cert, signed_signer.release(),
|
||||||
test_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
|
|
||||||
const TestCertificateAndData kInvalidCertificate[] = {
|
const TestCertificateAndData kInvalidCertificate[] = {
|
||||||
TestCertificateAndData("f", "", 0,
|
TestCertificateAndData("f", "", 0,
|
||||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signed-drm-certificate")),
|
"invalid-signed-drm-certificate")),
|
||||||
TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0,
|
TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0,
|
||||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-drm-certificate")),
|
"invalid-drm-certificate")),
|
||||||
TestCertificateAndData(bad_device_public_key->SerializeAsString(), "", 0,
|
TestCertificateAndData(bad_device_public_key->SerializeAsString(), "", 0,
|
||||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"drm-certificate-public-key-failed")),
|
"drm-certificate-public-key-failed")),
|
||||||
TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0,
|
TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0,
|
||||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signer-certificate")),
|
"invalid-signer-certificate")),
|
||||||
TestCertificateAndData(bad_signer_public_key->SerializeAsString(), "", 0,
|
TestCertificateAndData(bad_signer_public_key->SerializeAsString(), "", 0,
|
||||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-leaf-signer-public-key")),
|
"invalid-leaf-signer-public-key")),
|
||||||
TestCertificateAndData(bad_device_signature->SerializeAsString(), "", 0,
|
TestCertificateAndData(bad_device_signature->SerializeAsString(), "", 0,
|
||||||
util::Status(error_space, INVALID_SIGNATURE,
|
Status(error_space, INVALID_SIGNATURE,
|
||||||
"cache-miss-invalid-signature")),
|
"cache-miss-invalid-signature")),
|
||||||
TestCertificateAndData(
|
TestCertificateAndData(missing_model_sn->SerializeAsString(), "", 0,
|
||||||
missing_model_sn->SerializeAsString(), "", 0,
|
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
"model-certificate-missing-system-id")),
|
||||||
"model-certificate-missing-system-id")),
|
|
||||||
TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0,
|
TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0,
|
||||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"missing-signer-serial-number")),
|
"missing-signer-serial-number")),
|
||||||
TestCertificateAndData(bad_signer_signature->SerializeAsString(), "", 0,
|
TestCertificateAndData(bad_signer_signature->SerializeAsString(), "", 0,
|
||||||
util::Status(error_space, INVALID_SIGNATURE,
|
Status(error_space, INVALID_SIGNATURE,
|
||||||
"cache-miss-invalid-signature")),
|
"cache-miss-invalid-signature")),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) {
|
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) {
|
||||||
@@ -454,7 +454,7 @@ TEST_F(ClientCertTest, MissingPreProvKey) {
|
|||||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||||
"2517a12f4922953e"));
|
"2517a12f4922953e"));
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
ClientCert* client_cert_ptr = nullptr;
|
||||||
util::Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr);
|
Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr);
|
||||||
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
|
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,4 +563,55 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
|
|||||||
EXPECT_FALSE(client_cert_ptr);
|
EXPECT_FALSE(client_cert_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ClientCertTest, Protocol21WithDrmCert) {
|
||||||
|
const char message[] = "A weekend wasted is a weekend well spent.";
|
||||||
|
|
||||||
|
ClientCert* client_cert_ptr = nullptr;
|
||||||
|
ASSERT_OK(ClientCert::Create(
|
||||||
|
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
|
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
|
||||||
|
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
|
||||||
|
|
||||||
|
std::unique_ptr<RsaPrivateKey> private_key(
|
||||||
|
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
||||||
|
ASSERT_TRUE(private_key);
|
||||||
|
|
||||||
|
// Success
|
||||||
|
std::string signature;
|
||||||
|
ASSERT_TRUE(private_key->GenerateSignature(message, &signature));
|
||||||
|
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_1));
|
||||||
|
|
||||||
|
// Failure
|
||||||
|
ASSERT_EQ(256, signature.size());
|
||||||
|
++signature[127];
|
||||||
|
EXPECT_FALSE(
|
||||||
|
client_cert->VerifySignature(message, signature, VERSION_2_1).ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ClientCertTest, Protocol22WithDrmCert) {
|
||||||
|
const char message[] = "There is nothing permanent except change.";
|
||||||
|
const std::string message_hash(Sha512_Hash(message));
|
||||||
|
|
||||||
|
ClientCert* client_cert_ptr = nullptr;
|
||||||
|
ASSERT_OK(ClientCert::Create(
|
||||||
|
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
|
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
|
||||||
|
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
|
||||||
|
|
||||||
|
std::unique_ptr<RsaPrivateKey> private_key(
|
||||||
|
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
||||||
|
ASSERT_TRUE(private_key);
|
||||||
|
|
||||||
|
// Success
|
||||||
|
std::string signature;
|
||||||
|
ASSERT_TRUE(private_key->GenerateSignature(message_hash, &signature));
|
||||||
|
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_2));
|
||||||
|
|
||||||
|
// Failure
|
||||||
|
ASSERT_EQ(256, signature.size());
|
||||||
|
++signature[127];
|
||||||
|
EXPECT_FALSE(
|
||||||
|
client_cert->VerifySignature(message, signature, VERSION_2_2).ok());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -51,39 +51,39 @@ std::string GetClientInfo(const ClientIdentification& client_id,
|
|||||||
return default_value;
|
return default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DecryptEncryptedClientIdentification(
|
Status DecryptEncryptedClientIdentification(
|
||||||
const EncryptedClientIdentification& encrypted_client_id,
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
ClientIdentification* client_id) {
|
ClientIdentification* client_id) {
|
||||||
return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id,
|
return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id,
|
||||||
client_id);
|
client_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DecryptEncryptedClientIdentification(
|
Status DecryptEncryptedClientIdentification(
|
||||||
const EncryptedClientIdentification& encrypted_client_id,
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
const std::string& privacy_key, ClientIdentification* client_id) {
|
const std::string& privacy_key, ClientIdentification* client_id) {
|
||||||
DCHECK(client_id);
|
DCHECK(client_id);
|
||||||
if (!encrypted_client_id.has_encrypted_client_id() ||
|
if (!encrypted_client_id.has_encrypted_client_id() ||
|
||||||
encrypted_client_id.encrypted_client_id().empty()) {
|
encrypted_client_id.encrypted_client_id().empty()) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"missing-encrypted-client-id");
|
"missing-encrypted-client-id");
|
||||||
}
|
}
|
||||||
if (!encrypted_client_id.has_encrypted_client_id_iv() ||
|
if (!encrypted_client_id.has_encrypted_client_id_iv() ||
|
||||||
encrypted_client_id.encrypted_client_id_iv().empty()) {
|
encrypted_client_id.encrypted_client_id_iv().empty()) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"missing-encrypted-client-id-iv");
|
"missing-encrypted-client-id-iv");
|
||||||
}
|
}
|
||||||
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
||||||
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
||||||
encrypted_client_id.encrypted_client_id()));
|
encrypted_client_id.encrypted_client_id()));
|
||||||
if (serialized_client_id.empty()) {
|
if (serialized_client_id.empty()) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"client-id-decryption-failed");
|
"client-id-decryption-failed");
|
||||||
}
|
}
|
||||||
if (!client_id->ParseFromString(serialized_client_id)) {
|
if (!client_id->ParseFromString(serialized_client_id)) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"client-id-parse-failed");
|
"client-id-parse-failed");
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -43,16 +43,16 @@ std::string GetClientInfo(const ClientIdentification& client_id,
|
|||||||
// |client_id| using the private key for the service certificate which was
|
// |client_id| using the private key for the service certificate which was
|
||||||
// used to encrypt the information.
|
// used to encrypt the information.
|
||||||
// |client_id| is owned by caller.
|
// |client_id| is owned by caller.
|
||||||
// Returns util::Status::OK, if successful, else an error.
|
// Returns Status::OK, if successful, else an error.
|
||||||
util::Status DecryptEncryptedClientIdentification(
|
Status DecryptEncryptedClientIdentification(
|
||||||
const EncryptedClientIdentification& encrypted_client_id,
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
ClientIdentification* client_id);
|
ClientIdentification* client_id);
|
||||||
|
|
||||||
// Decrypts the encrypted client identification in |encrypted_client_id| into
|
// Decrypts the encrypted client identification in |encrypted_client_id| into
|
||||||
// |client_id| using |privacy_key|.
|
// |client_id| using |privacy_key|.
|
||||||
// |client_id| is owned by caller.
|
// |client_id| is owned by caller.
|
||||||
// Returns util::Status::OK, if successful, else an error.
|
// Returns Status::OK, if successful, else an error.
|
||||||
util::Status DecryptEncryptedClientIdentification(
|
Status DecryptEncryptedClientIdentification(
|
||||||
const EncryptedClientIdentification& encrypted_client_id,
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
const std::string& privacy_key, ClientIdentification* client_id);
|
const std::string& privacy_key, ClientIdentification* client_id);
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace crypto_util {
|
namespace crypto_util {
|
||||||
|
|
||||||
const char kEncryptionKeyLabel[] = "ENCRYPTION";
|
const char kWrappingKeyLabel[] = "ENCRYPTION";
|
||||||
const int kEncryptionKeySizeBits = 128;
|
const int kWrappingKeySizeBits = 128;
|
||||||
const char kSigningKeyLabel[] = "AUTHENTICATION";
|
const char kSigningKeyLabel[] = "AUTHENTICATION";
|
||||||
const int kSigningKeySizeBits = 256;
|
const int kSigningKeySizeBits = 256;
|
||||||
const size_t kSigningKeySizeBytes = 32;
|
const size_t kSigningKeySizeBytes = 32;
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ namespace crypto_util {
|
|||||||
|
|
||||||
// Default constants used for key derivation for encryption and signing.
|
// Default constants used for key derivation for encryption and signing.
|
||||||
// TODO(user): These are duplicated in session.cc in the sdk. de-dup.
|
// TODO(user): These are duplicated in session.cc in the sdk. de-dup.
|
||||||
extern const char kEncryptionKeyLabel[];
|
extern const char kWrappingKeyLabel[];
|
||||||
extern const int kEncryptionKeySizeBits;
|
extern const int kWrappingKeySizeBits;
|
||||||
extern const char kSigningKeyLabel[];
|
extern const char kSigningKeyLabel[];
|
||||||
extern const int kSigningKeySizeBits;
|
extern const int kSigningKeySizeBits;
|
||||||
extern const size_t kSigningKeySizeBytes;
|
extern const size_t kSigningKeySizeBytes;
|
||||||
|
|||||||
@@ -52,47 +52,47 @@ DeviceStatusList::DeviceStatusList()
|
|||||||
|
|
||||||
DeviceStatusList::~DeviceStatusList() {}
|
DeviceStatusList::~DeviceStatusList() {}
|
||||||
|
|
||||||
util::Status DeviceStatusList::UpdateStatusList(
|
Status DeviceStatusList::UpdateStatusList(
|
||||||
const std::string& root_certificate_public_key,
|
const std::string& root_certificate_public_key,
|
||||||
const std::string& serialized_certificate_status_list,
|
const std::string& serialized_certificate_status_list,
|
||||||
uint32_t expiration_period_seconds) {
|
uint32_t expiration_period_seconds) {
|
||||||
SignedDeviceCertificateStatusList signed_certificate_status_list;
|
SignedDeviceCertificateStatusList signed_certificate_status_list;
|
||||||
if (!signed_certificate_status_list.ParseFromString(
|
if (!signed_certificate_status_list.ParseFromString(
|
||||||
serialized_certificate_status_list)) {
|
serialized_certificate_status_list)) {
|
||||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"signed-certificate-status-list-parse-error");
|
"signed-certificate-status-list-parse-error");
|
||||||
}
|
}
|
||||||
if (!signed_certificate_status_list.has_certificate_status_list()) {
|
if (!signed_certificate_status_list.has_certificate_status_list()) {
|
||||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"missing-status-list");
|
"missing-status-list");
|
||||||
}
|
}
|
||||||
if (!signed_certificate_status_list.has_signature()) {
|
if (!signed_certificate_status_list.has_signature()) {
|
||||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"missing-status-list-signature");
|
"missing-status-list-signature");
|
||||||
}
|
}
|
||||||
std::unique_ptr<RsaPublicKey> root_key(
|
std::unique_ptr<RsaPublicKey> root_key(
|
||||||
RsaPublicKey::Create(root_certificate_public_key));
|
RsaPublicKey::Create(root_certificate_public_key));
|
||||||
if (root_key == nullptr) {
|
if (root_key == nullptr) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-root-public-key");
|
"invalid-root-public-key");
|
||||||
}
|
}
|
||||||
if (!root_key->VerifySignature(
|
if (!root_key->VerifySignature(
|
||||||
signed_certificate_status_list.certificate_status_list(),
|
signed_certificate_status_list.certificate_status_list(),
|
||||||
signed_certificate_status_list.signature())) {
|
signed_certificate_status_list.signature())) {
|
||||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"invalid-status-list-signature");
|
"invalid-status-list-signature");
|
||||||
}
|
}
|
||||||
DeviceCertificateStatusList certificate_status_list;
|
DeviceCertificateStatusList certificate_status_list;
|
||||||
if (!certificate_status_list.ParseFromString(
|
if (!certificate_status_list.ParseFromString(
|
||||||
signed_certificate_status_list.certificate_status_list())) {
|
signed_certificate_status_list.certificate_status_list())) {
|
||||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"certificate-status-list-parse-error");
|
"certificate-status-list-parse-error");
|
||||||
}
|
}
|
||||||
if (expiration_period_seconds &&
|
if (expiration_period_seconds &&
|
||||||
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
|
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
|
||||||
expiration_period_seconds))) {
|
expiration_period_seconds))) {
|
||||||
return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||||
"certificate-status-list-expired");
|
"certificate-status-list-expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::WriterMutexLock lock(&status_map_lock_);
|
absl::WriterMutexLock lock(&status_map_lock_);
|
||||||
@@ -106,44 +106,44 @@ util::Status DeviceStatusList::UpdateStatusList(
|
|||||||
if (device_info.has_system_id()) {
|
if (device_info.has_system_id()) {
|
||||||
device_status_map_[device_info.system_id()] = cert_status;
|
device_status_map_[device_info.system_id()] = cert_status;
|
||||||
} else {
|
} else {
|
||||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"device-info-missing-system-id");
|
"device-info-missing-system-id");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
creation_time_seconds_ = certificate_status_list.creation_time_seconds();
|
creation_time_seconds_ = certificate_status_list.creation_time_seconds();
|
||||||
expiration_period_seconds_ = expiration_period_seconds;
|
expiration_period_seconds_ = expiration_period_seconds;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DeviceStatusList::GetCertStatus(
|
Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
|
||||||
const ClientCert& client_cert, ProvisionedDeviceInfo* device_info) {
|
ProvisionedDeviceInfo* device_info) {
|
||||||
CHECK(device_info);
|
CHECK(device_info);
|
||||||
|
|
||||||
// Keybox checks.
|
// Keybox checks.
|
||||||
if (client_cert.type() == ClientIdentification::KEYBOX) {
|
if (client_cert.type() == ClientIdentification::KEYBOX) {
|
||||||
if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) {
|
if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) {
|
||||||
return util::Status(error_space, UNSUPPORTED_SYSTEM_ID,
|
return Status(error_space, UNSUPPORTED_SYSTEM_ID,
|
||||||
"keybox-unsupported-system-id");
|
"keybox-unsupported-system-id");
|
||||||
}
|
}
|
||||||
// Get device information from certificate status list if available.
|
// Get device information from certificate status list if available.
|
||||||
if (!GetDeviceInfo(client_cert, device_info)) {
|
if (!GetDeviceInfo(client_cert, device_info)) {
|
||||||
device_info->Clear();
|
device_info->Clear();
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// DRM certificate checks.
|
// DRM certificate checks.
|
||||||
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"device-certificate-unsupported-token-type");
|
"device-certificate-unsupported-token-type");
|
||||||
}
|
}
|
||||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||||
if (expiration_period_seconds_ &&
|
if (expiration_period_seconds_ &&
|
||||||
(GetCurrentTime() >
|
(GetCurrentTime() >
|
||||||
(creation_time_seconds_ + expiration_period_seconds_))) {
|
(creation_time_seconds_ + expiration_period_seconds_))) {
|
||||||
return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||||
"certificate-status-list-expired");
|
"certificate-status-list-expired");
|
||||||
}
|
}
|
||||||
DeviceCertificateStatus* device_cert_status =
|
DeviceCertificateStatus* device_cert_status =
|
||||||
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
||||||
@@ -155,15 +155,15 @@ util::Status DeviceStatusList::GetCertStatus(
|
|||||||
LOG(WARNING) << "Allowing REVOKED device: "
|
LOG(WARNING) << "Allowing REVOKED device: "
|
||||||
<< device_info->ShortDebugString();
|
<< device_info->ShortDebugString();
|
||||||
} else {
|
} else {
|
||||||
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||||
"device-certificate-revoked");
|
"device-certificate-revoked");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((device_cert_status->status() ==
|
if ((device_cert_status->status() ==
|
||||||
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
||||||
!allow_test_only_devices_) {
|
!allow_test_only_devices_) {
|
||||||
return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||||
"test-only-drm-certificate-not-allowed");
|
"test-only-drm-certificate-not-allowed");
|
||||||
}
|
}
|
||||||
if (!client_cert.signed_by_provisioner() &&
|
if (!client_cert.signed_by_provisioner() &&
|
||||||
(client_cert.signer_serial_number() !=
|
(client_cert.signer_serial_number() !=
|
||||||
@@ -175,21 +175,21 @@ util::Status DeviceStatusList::GetCertStatus(
|
|||||||
// list is older than the certificate, the certificate is for all purposes
|
// list is older than the certificate, the certificate is for all purposes
|
||||||
// unknown.
|
// unknown.
|
||||||
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
|
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"intermediate-certificate-serial-number-mismatch");
|
"intermediate-certificate-serial-number-mismatch");
|
||||||
}
|
}
|
||||||
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||||
"device-certificate-status-unknown");
|
"device-certificate-status-unknown");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!allow_unknown_devices_) {
|
if (!allow_unknown_devices_) {
|
||||||
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||||
"device-certificate-status-unknown");
|
"device-certificate-status-unknown");
|
||||||
}
|
}
|
||||||
device_info->Clear();
|
device_info->Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
|
bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
|
||||||
@@ -247,18 +247,18 @@ bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
|
|||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
||||||
const std::string& certificate_provisioning_service_response,
|
const std::string& certificate_provisioning_service_response,
|
||||||
std::string* signed_certificate_status_list, std::string* certificate_status_list) {
|
std::string* signed_certificate_status_list, std::string* certificate_status_list) {
|
||||||
util::Status status = util::OkStatus();
|
Status status = OkStatus();
|
||||||
size_t signed_list_start =
|
size_t signed_list_start =
|
||||||
certificate_provisioning_service_response.find(kSignedList);
|
certificate_provisioning_service_response.find(kSignedList);
|
||||||
if (signed_list_start != std::string::npos) {
|
if (signed_list_start != std::string::npos) {
|
||||||
size_t signed_list_end = certificate_provisioning_service_response.find(
|
size_t signed_list_end = certificate_provisioning_service_response.find(
|
||||||
kSignedListTerminator, signed_list_start);
|
kSignedListTerminator, signed_list_start);
|
||||||
if (signed_list_end == std::string::npos) {
|
if (signed_list_end == std::string::npos) {
|
||||||
return util::Status(
|
return Status(
|
||||||
error_space, util::error::INVALID_ARGUMENT,
|
error_space, error::INVALID_ARGUMENT,
|
||||||
"Unable to parse the certificate_provisioning_service_response. "
|
"Unable to parse the certificate_provisioning_service_response. "
|
||||||
"SignedList not terminated.");
|
"SignedList not terminated.");
|
||||||
}
|
}
|
||||||
@@ -284,8 +284,8 @@ util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
|||||||
if (!absl::WebSafeBase64Unescape(signed_list,
|
if (!absl::WebSafeBase64Unescape(signed_list,
|
||||||
signed_certificate_status_list)) {
|
signed_certificate_status_list)) {
|
||||||
if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) {
|
if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) {
|
||||||
return util::Status(error_space, util::error::INVALID_ARGUMENT,
|
return Status(error_space, error::INVALID_ARGUMENT,
|
||||||
"Base64 decode of signedlist failed.");
|
"Base64 decode of signedlist failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -295,42 +295,40 @@ util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
|||||||
signed_certificate_status_list)) {
|
signed_certificate_status_list)) {
|
||||||
if (!absl::Base64Unescape(certificate_provisioning_service_response,
|
if (!absl::Base64Unescape(certificate_provisioning_service_response,
|
||||||
signed_certificate_status_list)) {
|
signed_certificate_status_list)) {
|
||||||
return util::Status(error_space, util::error::INVALID_ARGUMENT,
|
return Status(error_space, error::INVALID_ARGUMENT,
|
||||||
"Base64 decode of certList failed.");
|
"Base64 decode of certList failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SignedDeviceCertificateStatusList signed_status_list;
|
SignedDeviceCertificateStatusList signed_status_list;
|
||||||
if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) {
|
if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) {
|
||||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"signed-certificate-status-list-parse-error");
|
"signed-certificate-status-list-parse-error");
|
||||||
}
|
}
|
||||||
if (!signed_status_list.has_certificate_status_list()) {
|
if (!signed_status_list.has_certificate_status_list()) {
|
||||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"missing-status-list");
|
"missing-status-list");
|
||||||
}
|
}
|
||||||
DeviceCertificateStatusList device_certificate_status_list;
|
DeviceCertificateStatusList device_certificate_status_list;
|
||||||
if (!device_certificate_status_list.ParseFromString(
|
if (!device_certificate_status_list.ParseFromString(
|
||||||
signed_status_list.certificate_status_list())) {
|
signed_status_list.certificate_status_list())) {
|
||||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"certificate-status-list-parse-error");
|
"certificate-status-list-parse-error");
|
||||||
}
|
}
|
||||||
*certificate_status_list = signed_status_list.certificate_status_list();
|
*certificate_status_list = signed_status_list.certificate_status_list();
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
||||||
const std::string& version,
|
const std::string& version,
|
||||||
std::string* signed_device_certificate_status_list_request) {
|
std::string* signed_device_certificate_status_list_request) {
|
||||||
if (version.empty()) {
|
if (version.empty()) {
|
||||||
return util::Status(error_space, util::error::INVALID_ARGUMENT,
|
return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty");
|
||||||
"SDK version is empty");
|
|
||||||
}
|
}
|
||||||
DCHECK(signed_device_certificate_status_list_request);
|
DCHECK(signed_device_certificate_status_list_request);
|
||||||
if (signed_device_certificate_status_list_request == nullptr) {
|
if (signed_device_certificate_status_list_request == nullptr) {
|
||||||
return util::Status(
|
return Status(error_space, error::INVALID_ARGUMENT,
|
||||||
error_space, util::error::INVALID_ARGUMENT,
|
"Signed_device_certificate_status_list_request is empty");
|
||||||
"Signed_device_certificate_status_list_request is empty");
|
|
||||||
}
|
}
|
||||||
// Construct SignedDeviceCertificateStatusListRequest.
|
// Construct SignedDeviceCertificateStatusListRequest.
|
||||||
DeviceCertificateStatusListRequest request;
|
DeviceCertificateStatusListRequest request;
|
||||||
@@ -345,15 +343,13 @@ util::Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
|||||||
DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
||||||
if (sc == nullptr) {
|
if (sc == nullptr) {
|
||||||
signed_device_certificate_status_list_request->clear();
|
signed_device_certificate_status_list_request->clear();
|
||||||
return util::Status(error_space,
|
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
|
||||||
widevine::INVALID_SERVICE_CERTIFICATE,
|
"Drm service certificate is not loaded.");
|
||||||
"Drm service certificate is not loaded.");
|
|
||||||
}
|
}
|
||||||
const RsaPrivateKey* private_key = sc->private_key();
|
const RsaPrivateKey* private_key = sc->private_key();
|
||||||
if (private_key == nullptr) {
|
if (private_key == nullptr) {
|
||||||
return util::Status(error_space,
|
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
|
||||||
widevine::INVALID_SERVICE_CERTIFICATE,
|
"Private key in the service certificate is null.");
|
||||||
"Private key in the service certificate is null.");
|
|
||||||
}
|
}
|
||||||
std::string signature;
|
std::string signature;
|
||||||
private_key->GenerateSignature(device_certificate_status_list_request,
|
private_key->GenerateSignature(device_certificate_status_list_request,
|
||||||
@@ -361,6 +357,6 @@ util::Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
|||||||
signed_request.set_signature(signature);
|
signed_request.set_signature(signature);
|
||||||
signed_request.SerializeToString(
|
signed_request.SerializeToString(
|
||||||
signed_device_certificate_status_list_request);
|
signed_device_certificate_status_list_request);
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -37,12 +37,12 @@ class DeviceStatusList {
|
|||||||
DeviceStatusList();
|
DeviceStatusList();
|
||||||
virtual ~DeviceStatusList();
|
virtual ~DeviceStatusList();
|
||||||
|
|
||||||
// Takes |signed_certificate_status_list| and copies to an internal map of
|
// Takes |serialized_certificate_status_list| and copies to an internal map of
|
||||||
// device certifcate status list. The internal map is used to verify
|
// device certifcate status list. The internal map is used to verify
|
||||||
// a device was not revoked. Returns true is the list was successfully parsed.
|
// a device was not revoked. Returns true is the list was successfully parsed.
|
||||||
util::Status UpdateStatusList(const std::string& root_certificate_public_key,
|
Status UpdateStatusList(const std::string& root_certificate_public_key,
|
||||||
const std::string& signed_certificate_status_list,
|
const std::string& serialized_certificate_status_list,
|
||||||
uint32_t expiration_period_seconds);
|
uint32_t expiration_period_seconds);
|
||||||
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
|
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
|
||||||
bool allow_unknown_devices() const { return allow_unknown_devices_; }
|
bool allow_unknown_devices() const { return allow_unknown_devices_; }
|
||||||
void set_allow_test_only_devices(bool allow) {
|
void set_allow_test_only_devices(bool allow) {
|
||||||
@@ -58,9 +58,8 @@ class DeviceStatusList {
|
|||||||
// DRM_DEVICE_CERTIFICATE_UNKNOWN
|
// DRM_DEVICE_CERTIFICATE_UNKNOWN
|
||||||
// If status is OK, a copy of the provisioned device info is copied
|
// 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.
|
// into |device_info|. Caller owns |device_info| and it must not be null.
|
||||||
util::Status GetCertStatus(
|
Status GetCertStatus(const ClientCert& client_cert,
|
||||||
const ClientCert& client_cert,
|
widevine::ProvisionedDeviceInfo* device_info);
|
||||||
widevine::ProvisionedDeviceInfo* device_info);
|
|
||||||
// Returns true if the pre-provisioning key or certificate for the specified
|
// Returns true if the pre-provisioning key or certificate for the specified
|
||||||
// system ID are active (not disallowed or revoked).
|
// system ID are active (not disallowed or revoked).
|
||||||
bool IsSystemIdActive(uint32_t system_id);
|
bool IsSystemIdActive(uint32_t system_id);
|
||||||
@@ -86,7 +85,7 @@ class DeviceStatusList {
|
|||||||
* @param certificate_status_list
|
* @param certificate_status_list
|
||||||
* @return WvPLStatus - Status::OK if success, else error.
|
* @return WvPLStatus - Status::OK if success, else error.
|
||||||
*/
|
*/
|
||||||
static util::Status ExtractFromProvisioningServiceResponse(
|
static Status ExtractFromProvisioningServiceResponse(
|
||||||
const std::string& certificate_provisioning_service_response,
|
const std::string& certificate_provisioning_service_response,
|
||||||
std::string* signed_certificate_status_list, std::string* certificate_status_list);
|
std::string* signed_certificate_status_list, std::string* certificate_status_list);
|
||||||
/**
|
/**
|
||||||
@@ -94,9 +93,9 @@ class DeviceStatusList {
|
|||||||
*
|
*
|
||||||
* @param signed_device_certificate_status_list_request
|
* @param signed_device_certificate_status_list_request
|
||||||
* @param version
|
* @param version
|
||||||
* @return util::Status - Status::OK if success, else error.
|
* @return Status - Status::OK if success, else error.
|
||||||
*/
|
*/
|
||||||
static util::Status GenerateSignedDeviceCertificateStatusListRequest(
|
static Status GenerateSignedDeviceCertificateStatusListRequest(
|
||||||
const std::string& version,
|
const std::string& version,
|
||||||
std::string* signed_device_certificate_status_list_request);
|
std::string* signed_device_certificate_status_list_request);
|
||||||
|
|
||||||
|
|||||||
@@ -114,10 +114,9 @@ class DeviceStatusListTest : public ::testing::Test {
|
|||||||
ASSERT_TRUE(
|
ASSERT_TRUE(
|
||||||
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
||||||
|
|
||||||
ASSERT_EQ(util::OkStatus(),
|
ASSERT_EQ(OkStatus(), device_status_list_.UpdateStatusList(
|
||||||
device_status_list_.UpdateStatusList(
|
test_keys_.public_test_key_1_3072_bits(),
|
||||||
test_keys_.public_test_key_1_3072_bits(),
|
serialized_status_list_, kDefaultExpirePeriod));
|
||||||
serialized_status_list_, kDefaultExpirePeriod));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceStatusList device_status_list_;
|
DeviceStatusList device_status_list_;
|
||||||
@@ -140,7 +139,7 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
|||||||
.WillRepeatedly(Return(kValidCertSystemId));
|
.WillRepeatedly(Return(kValidCertSystemId));
|
||||||
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
||||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||||
EXPECT_EQ(util::OkStatus(),
|
EXPECT_EQ(OkStatus(),
|
||||||
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
|
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
|
||||||
EXPECT_TRUE(device_info.has_model());
|
EXPECT_TRUE(device_info.has_model());
|
||||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||||
@@ -191,8 +190,8 @@ TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
|||||||
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
||||||
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
||||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||||
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus(
|
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(test_only_client_cert,
|
||||||
test_only_client_cert, &device_info));
|
&device_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
||||||
@@ -208,8 +207,8 @@ TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
|||||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||||
EXPECT_CALL(valid_client_keybox, system_id())
|
EXPECT_CALL(valid_client_keybox, system_id())
|
||||||
.WillRepeatedly(Return(kValidCertSystemId));
|
.WillRepeatedly(Return(kValidCertSystemId));
|
||||||
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus(
|
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(valid_client_keybox,
|
||||||
valid_client_keybox, &device_info));
|
&device_info));
|
||||||
EXPECT_TRUE(device_info.has_model());
|
EXPECT_TRUE(device_info.has_model());
|
||||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||||
|
|
||||||
@@ -249,7 +248,7 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
|||||||
// We allow this case only for certs signed by a provisioner cert.
|
// We allow this case only for certs signed by a provisioner cert.
|
||||||
EXPECT_CALL(older_client_cert, signed_by_provisioner())
|
EXPECT_CALL(older_client_cert, signed_by_provisioner())
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_EQ(util::OkStatus(),
|
EXPECT_EQ(OkStatus(),
|
||||||
device_status_list_.GetCertStatus(older_client_cert, &device_info));
|
device_status_list_.GetCertStatus(older_client_cert, &device_info));
|
||||||
EXPECT_TRUE(device_info.has_system_id());
|
EXPECT_TRUE(device_info.has_system_id());
|
||||||
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
|
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
|
||||||
@@ -314,9 +313,9 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
|
|||||||
.Times(2)
|
.Times(2)
|
||||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||||
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList(
|
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||||
test_keys_.public_test_key_1_3072_bits(),
|
test_keys_.public_test_key_1_3072_bits(),
|
||||||
serialized_status_list_, 100));
|
serialized_status_list_, 100));
|
||||||
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
|
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||||
mock_device_status_list
|
mock_device_status_list
|
||||||
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
||||||
@@ -331,9 +330,9 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
|||||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||||
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList(
|
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||||
test_keys_.public_test_key_1_3072_bits(),
|
test_keys_.public_test_key_1_3072_bits(),
|
||||||
serialized_status_list_, 100));
|
serialized_status_list_, 100));
|
||||||
|
|
||||||
ProvisionedDeviceInfo device_info;
|
ProvisionedDeviceInfo device_info;
|
||||||
MockCertificateClientCert valid_client_cert;
|
MockCertificateClientCert valid_client_cert;
|
||||||
@@ -346,8 +345,8 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
|||||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||||
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
|
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
|
||||||
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
||||||
EXPECT_EQ(util::OkStatus(), mock_device_status_list.GetCertStatus(
|
EXPECT_EQ(OkStatus(), mock_device_status_list.GetCertStatus(valid_client_cert,
|
||||||
valid_client_cert, &device_info));
|
&device_info));
|
||||||
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
EXPIRED_CERTIFICATE_STATUS_LIST,
|
EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||||
|
|||||||
@@ -265,10 +265,10 @@ class VerifiedCertSignatureCache {
|
|||||||
|
|
||||||
// Checks cache, on miss, uses public key. If successful, adds to
|
// Checks cache, on miss, uses public key. If successful, adds to
|
||||||
// cache.
|
// cache.
|
||||||
util::Status VerifySignature(const std::string& cert, const std::string& serial_number,
|
Status VerifySignature(const std::string& cert, const std::string& serial_number,
|
||||||
const std::string& signature,
|
const std::string& signature,
|
||||||
const std::string& signer_public_key,
|
const std::string& signer_public_key,
|
||||||
const std::string& signer_serial_number) {
|
const std::string& signer_serial_number) {
|
||||||
{
|
{
|
||||||
VerifiedCertSignatures::iterator cached_signature;
|
VerifiedCertSignatures::iterator cached_signature;
|
||||||
absl::ReaderMutexLock read_lock(&signature_cache_mutex_);
|
absl::ReaderMutexLock read_lock(&signature_cache_mutex_);
|
||||||
@@ -279,11 +279,11 @@ class VerifiedCertSignatureCache {
|
|||||||
(signature != cached_signature->second.signature) ||
|
(signature != cached_signature->second.signature) ||
|
||||||
(signer_serial_number != cached_signature->second.signer_serial)) {
|
(signer_serial_number != cached_signature->second.signer_serial)) {
|
||||||
// Cached signature mismatch.
|
// Cached signature mismatch.
|
||||||
return util::Status(error_space, INVALID_SIGNATURE,
|
return Status(error_space, INVALID_SIGNATURE,
|
||||||
"cached-signature-mismatch");
|
"cached-signature-mismatch");
|
||||||
}
|
}
|
||||||
// Cached signature match.
|
// Cached signature match.
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,12 +291,12 @@ class VerifiedCertSignatureCache {
|
|||||||
std::unique_ptr<RsaPublicKey> signer_key(
|
std::unique_ptr<RsaPublicKey> signer_key(
|
||||||
key_factory_->CreateFromPkcs1PublicKey(signer_public_key));
|
key_factory_->CreateFromPkcs1PublicKey(signer_public_key));
|
||||||
if (!signer_key) {
|
if (!signer_key) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signer-public-key");
|
"invalid-signer-public-key");
|
||||||
}
|
}
|
||||||
if (!signer_key->VerifySignature(cert, signature)) {
|
if (!signer_key->VerifySignature(cert, signature)) {
|
||||||
return util::Status(error_space, INVALID_SIGNATURE,
|
return Status(error_space, INVALID_SIGNATURE,
|
||||||
"cache-miss-invalid-signature");
|
"cache-miss-invalid-signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add signature to cache.
|
// Add signature to cache.
|
||||||
@@ -304,7 +304,7 @@ class VerifiedCertSignatureCache {
|
|||||||
signature_cache_.emplace(
|
signature_cache_.emplace(
|
||||||
serial_number,
|
serial_number,
|
||||||
VerifiedCertSignature(cert, signature, signer_serial_number));
|
VerifiedCertSignature(cert, signature, signer_serial_number));
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -313,7 +313,7 @@ class VerifiedCertSignatureCache {
|
|||||||
const RsaKeyFactory* key_factory_;
|
const RsaKeyFactory* key_factory_;
|
||||||
};
|
};
|
||||||
|
|
||||||
util::Status DrmRootCertificate::CreateByType(
|
Status DrmRootCertificate::CreateByType(
|
||||||
CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) {
|
CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||||
CHECK(cert);
|
CHECK(cert);
|
||||||
|
|
||||||
@@ -321,7 +321,7 @@ util::Status DrmRootCertificate::CreateByType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
|
std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
|
||||||
CertificateType cert_type, util::Status* status) {
|
CertificateType cert_type, Status* status) {
|
||||||
CHECK(status);
|
CHECK(status);
|
||||||
|
|
||||||
std::unique_ptr<DrmRootCertificate> new_root_cert;
|
std::unique_ptr<DrmRootCertificate> new_root_cert;
|
||||||
@@ -329,7 +329,7 @@ std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
|
|||||||
return new_root_cert;
|
return new_root_cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DrmRootCertificate::CreateByTypeString(
|
Status DrmRootCertificate::CreateByTypeString(
|
||||||
const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert) {
|
const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||||
CHECK(cert);
|
CHECK(cert);
|
||||||
|
|
||||||
@@ -341,17 +341,16 @@ util::Status DrmRootCertificate::CreateByTypeString(
|
|||||||
} else if (cert_type_string == kTestingString) {
|
} else if (cert_type_string == kTestingString) {
|
||||||
cert_type = kCertificateTypeTesting;
|
cert_type = kCertificateTypeTesting;
|
||||||
} else {
|
} else {
|
||||||
return util::Status(
|
return Status(error_space, INVALID_PARAMETER,
|
||||||
error_space, INVALID_PARAMETER,
|
absl::StrCat("invalid-certificate-type ", cert_type_string));
|
||||||
absl::StrCat("invalid-certificate-type ", cert_type_string));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CreateByType(cert_type, cert);
|
return CreateByType(cert_type, cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DrmRootCertificate::Create(
|
Status DrmRootCertificate::Create(CertificateType cert_type,
|
||||||
CertificateType cert_type, std::unique_ptr<RsaKeyFactory> key_factory,
|
std::unique_ptr<RsaKeyFactory> key_factory,
|
||||||
std::unique_ptr<DrmRootCertificate>* cert) {
|
std::unique_ptr<DrmRootCertificate>* cert) {
|
||||||
DCHECK(cert);
|
DCHECK(cert);
|
||||||
|
|
||||||
std::string serialized_certificate;
|
std::string serialized_certificate;
|
||||||
@@ -375,49 +374,48 @@ util::Status DrmRootCertificate::Create(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return util::Status(error_space, INVALID_PARAMETER,
|
return Status(error_space, INVALID_PARAMETER, "invalid-certificate-type");
|
||||||
"invalid-certificate-type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SignedDrmCertificate signed_root_cert;
|
SignedDrmCertificate signed_root_cert;
|
||||||
if (!signed_root_cert.ParseFromString(serialized_certificate)) {
|
if (!signed_root_cert.ParseFromString(serialized_certificate)) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"signed-root-cert-deserialize-fail");
|
"signed-root-cert-deserialize-fail");
|
||||||
}
|
}
|
||||||
DrmCertificate root_cert;
|
DrmCertificate root_cert;
|
||||||
if (!signed_root_cert.has_drm_certificate()) {
|
if (!signed_root_cert.has_drm_certificate()) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"missing-root-device-certificate");
|
"missing-root-device-certificate");
|
||||||
}
|
}
|
||||||
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
|
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"root-cert-deserialize-fail");
|
"root-cert-deserialize-fail");
|
||||||
}
|
}
|
||||||
if (!root_cert.has_public_key()) {
|
if (!root_cert.has_public_key()) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"missing-root-cert-public-key");
|
"missing-root-cert-public-key");
|
||||||
}
|
}
|
||||||
if (!signed_root_cert.has_signature()) {
|
if (!signed_root_cert.has_signature()) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"missing-root-certificate-signature");
|
"missing-root-certificate-signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<RsaPublicKey> public_key(
|
std::unique_ptr<RsaPublicKey> public_key(
|
||||||
key_factory->CreateFromPkcs1PublicKey(root_cert.public_key()));
|
key_factory->CreateFromPkcs1PublicKey(root_cert.public_key()));
|
||||||
if (!public_key) {
|
if (!public_key) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-root-public-key");
|
"invalid-root-public-key");
|
||||||
}
|
}
|
||||||
if (!public_key->VerifySignature(signed_root_cert.drm_certificate(),
|
if (!public_key->VerifySignature(signed_root_cert.drm_certificate(),
|
||||||
signed_root_cert.signature())) {
|
signed_root_cert.signature())) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-root-certificate-signature");
|
"invalid-root-certificate-signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
cert->reset(new DrmRootCertificate(
|
cert->reset(new DrmRootCertificate(
|
||||||
cert_type, serialized_certificate, root_cert.serial_number(),
|
cert_type, serialized_certificate, root_cert.serial_number(),
|
||||||
root_cert.public_key(), std::move(key_factory)));
|
root_cert.public_key(), std::move(key_factory)));
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmRootCertificate::DrmRootCertificate(
|
DrmRootCertificate::DrmRootCertificate(
|
||||||
@@ -437,7 +435,7 @@ std::string DrmRootCertificate::GetDigest() const {
|
|||||||
return absl::BytesToHexString(Sha256_Hash(serialized_certificate_));
|
return absl::BytesToHexString(Sha256_Hash(serialized_certificate_));
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DrmRootCertificate::VerifyCertificate(
|
Status DrmRootCertificate::VerifyCertificate(
|
||||||
const std::string& serialized_certificate,
|
const std::string& serialized_certificate,
|
||||||
SignedDrmCertificate* signed_certificate,
|
SignedDrmCertificate* signed_certificate,
|
||||||
DrmCertificate* certificate) const {
|
DrmCertificate* certificate) const {
|
||||||
@@ -447,8 +445,8 @@ util::Status DrmRootCertificate::VerifyCertificate(
|
|||||||
signed_certificate = local_signed_certificate.get();
|
signed_certificate = local_signed_certificate.get();
|
||||||
}
|
}
|
||||||
if (!signed_certificate->ParseFromString(serialized_certificate)) {
|
if (!signed_certificate->ParseFromString(serialized_certificate)) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signed-drm-certificate");
|
"invalid-signed-drm-certificate");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DrmCertificate> local_certificate;
|
std::unique_ptr<DrmCertificate> local_certificate;
|
||||||
@@ -458,20 +456,19 @@ util::Status DrmRootCertificate::VerifyCertificate(
|
|||||||
}
|
}
|
||||||
if (signed_certificate->drm_certificate().empty() ||
|
if (signed_certificate->drm_certificate().empty() ||
|
||||||
!certificate->ParseFromString(signed_certificate->drm_certificate())) {
|
!certificate->ParseFromString(signed_certificate->drm_certificate())) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-drm-certificate");
|
"invalid-drm-certificate");
|
||||||
}
|
}
|
||||||
if (certificate->serial_number().empty()) {
|
if (certificate->serial_number().empty()) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"missing-serial-number");
|
"missing-serial-number");
|
||||||
}
|
}
|
||||||
if (!certificate->has_creation_time_seconds()) {
|
if (!certificate->has_creation_time_seconds()) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"missing-creation-time");
|
"missing-creation-time");
|
||||||
}
|
}
|
||||||
if (certificate->public_key().empty()) {
|
if (certificate->public_key().empty()) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key");
|
||||||
"missing-public-key");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature chain, but do not use cache for leaf certificates.
|
// Verify signature chain, but do not use cache for leaf certificates.
|
||||||
@@ -485,7 +482,7 @@ util::Status DrmRootCertificate::VerifyCertificate(
|
|||||||
// the case of device-unique device certificates.
|
// the case of device-unique device certificates.
|
||||||
// Signatures for root-signed certificates are always cached, even if they are
|
// Signatures for root-signed certificates are always cached, even if they are
|
||||||
// leaf certificates. For example service, and provisioner certificates.
|
// leaf certificates. For example service, and provisioner certificates.
|
||||||
util::Status DrmRootCertificate::VerifySignatures(
|
Status DrmRootCertificate::VerifySignatures(
|
||||||
const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number,
|
const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number,
|
||||||
bool use_cache) const {
|
bool use_cache) const {
|
||||||
if (!signed_cert.has_signer()) {
|
if (!signed_cert.has_signer()) {
|
||||||
@@ -497,12 +494,12 @@ util::Status DrmRootCertificate::VerifySignatures(
|
|||||||
|
|
||||||
DrmCertificate signer;
|
DrmCertificate signer;
|
||||||
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
|
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signer-certificate");
|
"invalid-signer-certificate");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the signer before verifying signed_cert.
|
// Verify the signer before verifying signed_cert.
|
||||||
util::Status status =
|
Status status =
|
||||||
VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache);
|
VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
@@ -519,17 +516,17 @@ util::Status DrmRootCertificate::VerifySignatures(
|
|||||||
std::unique_ptr<RsaPublicKey> signer_public_key(
|
std::unique_ptr<RsaPublicKey> signer_public_key(
|
||||||
key_factory_->CreateFromPkcs1PublicKey(signer.public_key()));
|
key_factory_->CreateFromPkcs1PublicKey(signer.public_key()));
|
||||||
if (!signer_public_key) {
|
if (!signer_public_key) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-leaf-signer-public-key");
|
"invalid-leaf-signer-public-key");
|
||||||
}
|
}
|
||||||
if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(),
|
if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(),
|
||||||
signed_cert.signature())) {
|
signed_cert.signature())) {
|
||||||
return util::Status(error_space, INVALID_SIGNATURE,
|
return Status(error_space, INVALID_SIGNATURE,
|
||||||
"cache-miss-invalid-signature");
|
"cache-miss-invalid-signature");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ class DrmRootCertificate {
|
|||||||
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
|
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
|
||||||
// created const DrmRootCertificate* if successful. The caller assumes
|
// created const DrmRootCertificate* if successful. The caller assumes
|
||||||
// ownership of the new DrmRootCertificate. This method returns
|
// ownership of the new DrmRootCertificate. This method returns
|
||||||
// util::Status::OK on success, or appropriate error status otherwise.
|
// Status::OK on success, or appropriate error status otherwise.
|
||||||
static util::Status CreateByType(CertificateType cert_type,
|
static Status CreateByType(CertificateType cert_type,
|
||||||
std::unique_ptr<DrmRootCertificate>* cert);
|
std::unique_ptr<DrmRootCertificate>* cert);
|
||||||
|
|
||||||
// Variant on the method above to make CLIF happy until b/110539622 is fixed.
|
// Variant on the method above to make CLIF happy until b/110539622 is fixed.
|
||||||
static std::unique_ptr<DrmRootCertificate> CreateByType(
|
static std::unique_ptr<DrmRootCertificate> CreateByType(
|
||||||
CertificateType cert_type, util::Status* status);
|
CertificateType cert_type, Status* status);
|
||||||
|
|
||||||
// Creates a DrmRootCertificate object given a certificate type std::string, which
|
// Creates a DrmRootCertificate object given a certificate type std::string, which
|
||||||
// must be one of "prod", "qa", or "test".
|
// must be one of "prod", "qa", or "test".
|
||||||
@@ -56,19 +56,16 @@ class DrmRootCertificate {
|
|||||||
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
|
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
|
||||||
// created const DrmRootCertificate* if successful. The caller assumes
|
// created const DrmRootCertificate* if successful. The caller assumes
|
||||||
// ownership of the new DrmRootCertificate. This method returns
|
// ownership of the new DrmRootCertificate. This method returns
|
||||||
// util::Status::OK on success, or appropriate error status otherwise.
|
// Status::OK on success, or appropriate error status otherwise.
|
||||||
static util::Status CreateByTypeString(
|
static Status CreateByTypeString(const std::string& cert_type_string,
|
||||||
const std::string& cert_type_string,
|
std::unique_ptr<DrmRootCertificate>* cert);
|
||||||
std::unique_ptr<DrmRootCertificate>* cert);
|
|
||||||
|
|
||||||
// |certificate| will contgain the DRM certificate upon successful return.
|
// |certificate| will contgain the DRM certificate upon successful return.
|
||||||
// May be null.
|
// May be null.
|
||||||
// Returns util::Status::OK if successful, or an appropriate error code
|
// Returns Status::OK if successful, or an appropriate error code otherwise.
|
||||||
// otherwise.
|
virtual Status VerifyCertificate(const std::string& serialized_certificate,
|
||||||
virtual util::Status VerifyCertificate(
|
SignedDrmCertificate* signed_certificate,
|
||||||
const std::string& serialized_certificate,
|
DrmCertificate* certificate) const;
|
||||||
SignedDrmCertificate* signed_certificate,
|
|
||||||
DrmCertificate* certificate) const;
|
|
||||||
|
|
||||||
// Returns the hex-encoded SHA-256 digest for this certificate.
|
// Returns the hex-encoded SHA-256 digest for this certificate.
|
||||||
virtual std::string GetDigest() const;
|
virtual std::string GetDigest() const;
|
||||||
@@ -86,13 +83,13 @@ class DrmRootCertificate {
|
|||||||
private:
|
private:
|
||||||
friend class DrmRootCertificateTest;
|
friend class DrmRootCertificateTest;
|
||||||
|
|
||||||
static util::Status Create(CertificateType cert_type,
|
static Status Create(CertificateType cert_type,
|
||||||
std::unique_ptr<RsaKeyFactory> key_factory,
|
std::unique_ptr<RsaKeyFactory> key_factory,
|
||||||
std::unique_ptr<DrmRootCertificate>* cert);
|
std::unique_ptr<DrmRootCertificate>* cert);
|
||||||
|
|
||||||
util::Status VerifySignatures(const SignedDrmCertificate& signed_cert,
|
Status VerifySignatures(const SignedDrmCertificate& signed_cert,
|
||||||
const std::string& cert_serial_number,
|
const std::string& cert_serial_number,
|
||||||
bool use_cache) const;
|
bool use_cache) const;
|
||||||
|
|
||||||
CertificateType type_;
|
CertificateType type_;
|
||||||
std::string serialized_certificate_;
|
std::string serialized_certificate_;
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ TEST(DrmRootCertificateCreateTest, TestCertificate) {
|
|||||||
"49f917b1bdfed78002a58e799a58e940"
|
"49f917b1bdfed78002a58e799a58e940"
|
||||||
"1fffaaed9d8d80752782b066757e2c8c");
|
"1fffaaed9d8d80752782b066757e2c8c");
|
||||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||||
kCertificateTypeTesting, &root_cert));
|
kCertificateTypeTesting, &root_cert));
|
||||||
ASSERT_TRUE(root_cert != nullptr);
|
ASSERT_TRUE(root_cert != nullptr);
|
||||||
EXPECT_EQ(kTestCertificateHash, root_cert->GetDigest());
|
EXPECT_EQ(kTestCertificateHash, root_cert->GetDigest());
|
||||||
}
|
}
|
||||||
@@ -44,8 +44,8 @@ TEST(DrmRootCertificateCreateTest, DevCertificate) {
|
|||||||
"0e25ee95476a770f30b98ac5ef778b3f"
|
"0e25ee95476a770f30b98ac5ef778b3f"
|
||||||
"137b66c29385b84f547a361b4724b17d");
|
"137b66c29385b84f547a361b4724b17d");
|
||||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||||
kCertificateTypeDevelopment, &root_cert));
|
kCertificateTypeDevelopment, &root_cert));
|
||||||
ASSERT_TRUE(root_cert != nullptr);
|
ASSERT_TRUE(root_cert != nullptr);
|
||||||
EXPECT_EQ(kDevelopmentCertificateHash, root_cert->GetDigest());
|
EXPECT_EQ(kDevelopmentCertificateHash, root_cert->GetDigest());
|
||||||
}
|
}
|
||||||
@@ -55,8 +55,8 @@ TEST(DrmRootCertificateCreateTest, ProdCertificate) {
|
|||||||
"d62fdabc9286648a81f7d3bedaf2f5a5"
|
"d62fdabc9286648a81f7d3bedaf2f5a5"
|
||||||
"27bbad39bc38da034ba98a21569adb9b");
|
"27bbad39bc38da034ba98a21569adb9b");
|
||||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||||
kCertificateTypeProduction, &root_cert));
|
kCertificateTypeProduction, &root_cert));
|
||||||
ASSERT_TRUE(root_cert != nullptr);
|
ASSERT_TRUE(root_cert != nullptr);
|
||||||
EXPECT_EQ(kProductionCertificateHash, root_cert->GetDigest());
|
EXPECT_EQ(kProductionCertificateHash, root_cert->GetDigest());
|
||||||
}
|
}
|
||||||
@@ -111,8 +111,8 @@ class DrmRootCertificateTest : public testing::Test {
|
|||||||
drm_certificates_[2].set_public_key(
|
drm_certificates_[2].set_public_key(
|
||||||
test_keys_.public_test_key_3_2048_bits());
|
test_keys_.public_test_key_3_2048_bits());
|
||||||
|
|
||||||
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||||
kCertificateTypeTesting, &root_cert_));
|
kCertificateTypeTesting, &root_cert_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateSignedDrmCertificate() {
|
void GenerateSignedDrmCertificate() {
|
||||||
@@ -144,7 +144,7 @@ class DrmRootCertificateTest : public testing::Test {
|
|||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, SuccessNoOutput) {
|
TEST_F(DrmRootCertificateTest, SuccessNoOutput) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
ASSERT_EQ(util::OkStatus(),
|
ASSERT_EQ(OkStatus(),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||||
}
|
}
|
||||||
@@ -153,25 +153,25 @@ TEST_F(DrmRootCertificateTest, SuccessWithOutput) {
|
|||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
SignedDrmCertificate out_signed_cert;
|
SignedDrmCertificate out_signed_cert;
|
||||||
DrmCertificate out_cert;
|
DrmCertificate out_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), root_cert_->VerifyCertificate(
|
ASSERT_EQ(OkStatus(), root_cert_->VerifyCertificate(
|
||||||
signed_drm_certificate_.SerializeAsString(),
|
signed_drm_certificate_.SerializeAsString(),
|
||||||
&out_signed_cert, &out_cert));
|
&out_signed_cert, &out_cert));
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
MessageDifferencer::Equals(out_signed_cert, signed_drm_certificate_));
|
MessageDifferencer::Equals(out_signed_cert, signed_drm_certificate_));
|
||||||
EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2]));
|
EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidSignedDrmCertificate) {
|
TEST_F(DrmRootCertificateTest, InvalidSignedDrmCertificate) {
|
||||||
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signed-drm-certificate"),
|
"invalid-signed-drm-certificate"),
|
||||||
root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr));
|
root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
|
TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage");
|
signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage");
|
||||||
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signer-certificate"),
|
"invalid-signer-certificate"),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||||
}
|
}
|
||||||
@@ -179,86 +179,84 @@ TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
|
|||||||
TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
|
TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
signed_drm_certificate_.clear_drm_certificate();
|
signed_drm_certificate_.clear_drm_certificate();
|
||||||
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(
|
||||||
"invalid-drm-certificate"),
|
Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) {
|
TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
signed_drm_certificate_.set_drm_certificate("junk");
|
signed_drm_certificate_.set_drm_certificate("junk");
|
||||||
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(
|
||||||
"invalid-drm-certificate"),
|
Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidPublicKey) {
|
TEST_F(DrmRootCertificateTest, InvalidPublicKey) {
|
||||||
drm_certificates_[0].set_public_key("rubbish");
|
drm_certificates_[0].set_public_key("rubbish");
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(
|
||||||
"invalid-signer-public-key"),
|
Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-signer-public-key"),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, MissingPublicKey) {
|
TEST_F(DrmRootCertificateTest, MissingPublicKey) {
|
||||||
drm_certificates_[2].clear_public_key();
|
drm_certificates_[2].clear_public_key();
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"),
|
||||||
util::Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"),
|
root_cert_->VerifyCertificate(
|
||||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||||
nullptr, nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, MissingCreationTime) {
|
TEST_F(DrmRootCertificateTest, MissingCreationTime) {
|
||||||
drm_certificates_[2].clear_creation_time_seconds();
|
drm_certificates_[2].clear_creation_time_seconds();
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(
|
||||||
"missing-creation-time"),
|
Status(error_space, INVALID_DRM_CERTIFICATE, "missing-creation-time"),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, MissingSerialNumber) {
|
TEST_F(DrmRootCertificateTest, MissingSerialNumber) {
|
||||||
drm_certificates_[2].set_serial_number("");
|
drm_certificates_[2].set_serial_number("");
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(
|
||||||
"missing-serial-number"),
|
Status(error_space, INVALID_DRM_CERTIFICATE, "missing-serial-number"),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
|
TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
signed_drm_certificate_.mutable_signer()->set_signature(
|
signed_drm_certificate_.mutable_signer()->set_signature(
|
||||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
||||||
EXPECT_EQ(util::Status(error_space, INVALID_SIGNATURE,
|
EXPECT_EQ(
|
||||||
"cache-miss-invalid-signature"),
|
Status(error_space, INVALID_SIGNATURE, "cache-miss-invalid-signature"),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) {
|
TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
// Verify and cache.
|
// Verify and cache.
|
||||||
ASSERT_EQ(util::OkStatus(),
|
ASSERT_EQ(OkStatus(),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||||
|
|
||||||
// Verify success using cache.
|
// Verify success using cache.
|
||||||
ASSERT_EQ(util::OkStatus(),
|
ASSERT_EQ(OkStatus(),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||||
|
|
||||||
// Verify failure using cache.
|
// Verify failure using cache.
|
||||||
signed_drm_certificate_.mutable_signer()->set_signature(
|
signed_drm_certificate_.mutable_signer()->set_signature(
|
||||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"),
|
||||||
util::Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"),
|
root_cert_->VerifyCertificate(
|
||||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||||
nullptr, nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -107,41 +107,41 @@ DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
util::Status DrmServiceCertificate::AddDrmServiceCertificate(
|
Status DrmServiceCertificate::AddDrmServiceCertificate(
|
||||||
const DrmRootCertificate* root_cert, const std::string& service_certificate,
|
const DrmRootCertificate* root_drm_cert, const std::string& service_certificate,
|
||||||
const std::string& service_private_key,
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase) {
|
const std::string& service_private_key_passphrase) {
|
||||||
DrmCertificate drm_cert;
|
DrmCertificate drm_cert;
|
||||||
util::Status status =
|
Status status =
|
||||||
root_cert->VerifyCertificate(service_certificate, nullptr, &drm_cert);
|
root_drm_cert->VerifyCertificate(service_certificate, nullptr, &drm_cert);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drm_cert.type() != DrmCertificate::SERVICE) {
|
if (drm_cert.type() != DrmCertificate::SERVICE) {
|
||||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||||
"not-service-certificate");
|
"not-service-certificate");
|
||||||
}
|
}
|
||||||
if (drm_cert.provider_id().empty()) {
|
if (drm_cert.provider_id().empty()) {
|
||||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||||
"missing-certificate-service-id");
|
"missing-certificate-service-id");
|
||||||
}
|
}
|
||||||
std::unique_ptr<RsaPublicKey> public_key(
|
std::unique_ptr<RsaPublicKey> public_key(
|
||||||
RsaPublicKey::Create(drm_cert.public_key()));
|
RsaPublicKey::Create(drm_cert.public_key()));
|
||||||
if (!public_key) {
|
if (!public_key) {
|
||||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||||
"invalid-certificate-public-key");
|
"invalid-certificate-public-key");
|
||||||
}
|
}
|
||||||
std::string pkcs1_key;
|
std::string pkcs1_key;
|
||||||
if (!rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey(
|
if (!rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey(
|
||||||
service_private_key, service_private_key_passphrase, &pkcs1_key)) {
|
service_private_key, service_private_key_passphrase, &pkcs1_key)) {
|
||||||
return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
return Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
||||||
"key-decryption-failed");
|
"key-decryption-failed");
|
||||||
}
|
}
|
||||||
std::unique_ptr<RsaPrivateKey> private_key(RsaPrivateKey::Create(pkcs1_key));
|
std::unique_ptr<RsaPrivateKey> private_key(RsaPrivateKey::Create(pkcs1_key));
|
||||||
if (private_key == nullptr) {
|
if (private_key == nullptr) {
|
||||||
return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
return Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
||||||
"invalid-private-key");
|
"invalid-private-key");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DrmServiceCertificate> new_cert(new DrmServiceCertificate(
|
std::unique_ptr<DrmServiceCertificate> new_cert(new DrmServiceCertificate(
|
||||||
@@ -150,7 +150,7 @@ util::Status DrmServiceCertificate::AddDrmServiceCertificate(
|
|||||||
std::move(private_key)));
|
std::move(private_key)));
|
||||||
DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert));
|
DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert));
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
const DrmServiceCertificate*
|
const DrmServiceCertificate*
|
||||||
@@ -171,7 +171,7 @@ const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate(
|
|||||||
return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number);
|
return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||||
const DrmRootCertificate* root_drm_cert, const std::string& service_certificate,
|
const DrmRootCertificate* root_drm_cert, const std::string& service_certificate,
|
||||||
const std::string& service_private_key,
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase) {
|
const std::string& service_private_key_passphrase) {
|
||||||
@@ -181,36 +181,36 @@ util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
|||||||
service_private_key_passphrase);
|
service_private_key_passphrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DrmServiceCertificate::DecryptClientIdentification(
|
Status DrmServiceCertificate::DecryptClientIdentification(
|
||||||
const EncryptedClientIdentification& encrypted_client_id,
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
ClientIdentification* client_id) {
|
ClientIdentification* client_id) {
|
||||||
DCHECK(client_id);
|
DCHECK(client_id);
|
||||||
if (encrypted_client_id.service_certificate_serial_number().empty()) {
|
if (encrypted_client_id.service_certificate_serial_number().empty()) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"missing-service-certificate-serial-number");
|
"missing-service-certificate-serial-number");
|
||||||
}
|
}
|
||||||
if (encrypted_client_id.provider_id().empty()) {
|
if (encrypted_client_id.provider_id().empty()) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"missing-service-id");
|
"missing-service-id");
|
||||||
}
|
}
|
||||||
if (encrypted_client_id.encrypted_client_id().empty()) {
|
if (encrypted_client_id.encrypted_client_id().empty()) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"missing-encrypted-client-id");
|
"missing-encrypted-client-id");
|
||||||
}
|
}
|
||||||
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
|
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"missing-encrypted-client-id-iv");
|
"missing-encrypted-client-id-iv");
|
||||||
}
|
}
|
||||||
if (encrypted_client_id.encrypted_privacy_key().empty()) {
|
if (encrypted_client_id.encrypted_privacy_key().empty()) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"missing-encrypted-privacy-key");
|
"missing-encrypted-privacy-key");
|
||||||
}
|
}
|
||||||
std::string privacy_key;
|
std::string privacy_key;
|
||||||
std::string provider_id;
|
std::string provider_id;
|
||||||
const DrmServiceCertificate* cert = GetDrmServiceCertificate(
|
const DrmServiceCertificate* cert = GetDrmServiceCertificate(
|
||||||
encrypted_client_id.service_certificate_serial_number());
|
encrypted_client_id.service_certificate_serial_number());
|
||||||
if (!cert) {
|
if (!cert) {
|
||||||
return util::Status(
|
return Status(
|
||||||
error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||||
"service-certificate-not-found (SN " +
|
"service-certificate-not-found (SN " +
|
||||||
absl::BytesToHexString(
|
absl::BytesToHexString(
|
||||||
@@ -219,56 +219,56 @@ util::Status DrmServiceCertificate::DecryptClientIdentification(
|
|||||||
}
|
}
|
||||||
if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(),
|
if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(),
|
||||||
&privacy_key)) {
|
&privacy_key)) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"privacy-key-decryption-failed");
|
"privacy-key-decryption-failed");
|
||||||
}
|
}
|
||||||
if (cert->provider_id() != encrypted_client_id.provider_id()) {
|
if (cert->provider_id() != encrypted_client_id.provider_id()) {
|
||||||
return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||||
std::string("provider-id-mismatch (") + cert->provider_id() +
|
std::string("provider-id-mismatch (") + cert->provider_id() +
|
||||||
" / " + encrypted_client_id.provider_id() + ")");
|
" / " + encrypted_client_id.provider_id() + ")");
|
||||||
}
|
}
|
||||||
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
||||||
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
||||||
encrypted_client_id.encrypted_client_id()));
|
encrypted_client_id.encrypted_client_id()));
|
||||||
if (serialized_client_id.empty()) {
|
if (serialized_client_id.empty()) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"client-id-decryption-failed");
|
"client-id-decryption-failed");
|
||||||
}
|
}
|
||||||
if (!client_id->ParseFromString(serialized_client_id)) {
|
if (!client_id->ParseFromString(serialized_client_id)) {
|
||||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"client-id-parse-failed");
|
"client-id-parse-failed");
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmServiceCertificate::ResetServiceCertificates() {
|
void DrmServiceCertificate::ResetServiceCertificates() {
|
||||||
DrmServiceCertificateMap::GetInstance()->Reset();
|
DrmServiceCertificateMap::GetInstance()->Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status DrmServiceCertificate::ValidateDrmServiceCertificate() {
|
Status DrmServiceCertificate::ValidateDrmServiceCertificate() {
|
||||||
const DrmServiceCertificate* service_certificate =
|
const DrmServiceCertificate* service_certificate =
|
||||||
GetDefaultDrmServiceCertificate();
|
GetDefaultDrmServiceCertificate();
|
||||||
if (!service_certificate) {
|
if (!service_certificate) {
|
||||||
return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||||
"drm service certificate is not found.");
|
"drm service certificate is not found.");
|
||||||
}
|
}
|
||||||
SignedDrmCertificate signed_cert;
|
SignedDrmCertificate signed_cert;
|
||||||
if (!signed_cert.ParseFromString(service_certificate->certificate())) {
|
if (!signed_cert.ParseFromString(service_certificate->certificate())) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"signed drm service certificate is failed to parse.");
|
"signed drm service certificate is failed to parse.");
|
||||||
}
|
}
|
||||||
DrmCertificate drm_cert;
|
DrmCertificate drm_cert;
|
||||||
if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) {
|
if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) {
|
||||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"Drm service certificate is failed to parse.");
|
"Drm service certificate is failed to parse.");
|
||||||
}
|
}
|
||||||
if (!drm_cert.has_creation_time_seconds()) {
|
if (!drm_cert.has_creation_time_seconds()) {
|
||||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||||
"missing certificate creation time");
|
"missing certificate creation time");
|
||||||
}
|
}
|
||||||
// TODO(user): Check creation_time_seconds field in DrmCertificate and also
|
// TODO(user): Check creation_time_seconds field in DrmCertificate and also
|
||||||
// export the absl/time dependency through moe.
|
// export the absl/time dependency through moe.
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmServiceCertificate::DrmServiceCertificate(
|
DrmServiceCertificate::DrmServiceCertificate(
|
||||||
|
|||||||
@@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "common/status.h"
|
|
||||||
#include "common/certificate_type.h"
|
#include "common/certificate_type.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
class RequestInspectorTest;
|
class RequestInspectorTest;
|
||||||
@@ -48,7 +48,7 @@ class DrmServiceCertificate {
|
|||||||
// If the default service certificate is not set, this certificate will be
|
// If the default service certificate is not set, this certificate will be
|
||||||
// used as the default service certificate.
|
// used as the default service certificate.
|
||||||
// This method is thread-safe.
|
// This method is thread-safe.
|
||||||
static util::Status AddDrmServiceCertificate(
|
static Status AddDrmServiceCertificate(
|
||||||
const DrmRootCertificate* root_drm_cert,
|
const DrmRootCertificate* root_drm_cert,
|
||||||
const std::string& service_certificate, const std::string& service_private_key,
|
const std::string& service_certificate, const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase);
|
const std::string& service_private_key_passphrase);
|
||||||
@@ -56,7 +56,7 @@ class DrmServiceCertificate {
|
|||||||
// Same as AddDrmServiceCertificate(), but will clear the default service
|
// Same as AddDrmServiceCertificate(), but will clear the default service
|
||||||
// certificate if it's set. This will result in this service certificate
|
// certificate if it's set. This will result in this service certificate
|
||||||
// being set as the default service certificate.
|
// being set as the default service certificate.
|
||||||
static util::Status SetDefaultDrmServiceCertificate(
|
static Status SetDefaultDrmServiceCertificate(
|
||||||
const DrmRootCertificate* root_drm_cert,
|
const DrmRootCertificate* root_drm_cert,
|
||||||
const std::string& service_certificate, const std::string& service_private_key,
|
const std::string& service_certificate, const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase);
|
const std::string& service_private_key_passphrase);
|
||||||
@@ -79,7 +79,7 @@ class DrmServiceCertificate {
|
|||||||
// certificate which was used to encrypt the information. |client_id| must
|
// certificate which was used to encrypt the information. |client_id| must
|
||||||
// not be NULL. Returns status::OK if successful, or an appropriate error
|
// not be NULL. Returns status::OK if successful, or an appropriate error
|
||||||
// otherwise. This method is thread-safe.
|
// otherwise. This method is thread-safe.
|
||||||
static util::Status DecryptClientIdentification(
|
static Status DecryptClientIdentification(
|
||||||
const EncryptedClientIdentification& encrypted_client_id,
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
ClientIdentification* client_id);
|
ClientIdentification* client_id);
|
||||||
|
|
||||||
@@ -93,18 +93,18 @@ class DrmServiceCertificate {
|
|||||||
// status::OK if successful, or in case of error, contact
|
// status::OK if successful, or in case of error, contact
|
||||||
// widevine-tam@google.com to get the next valid service certificate renewed
|
// widevine-tam@google.com to get the next valid service certificate renewed
|
||||||
// via get deviceCertificate StatusList.
|
// via get deviceCertificate StatusList.
|
||||||
static util::Status ValidateDrmServiceCertificate();
|
static Status ValidateDrmServiceCertificate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class DrmServiceCertificateTest;
|
friend class DrmServiceCertificateTest;
|
||||||
friend class widevine::RequestInspectorTest;
|
friend class widevine::RequestInspectorTest;
|
||||||
|
|
||||||
static util::Status AddDrmServiceCertificate(
|
static Status AddDrmServiceCertificate(
|
||||||
const std::string& root_public_key, const std::string& service_certificate,
|
const std::string& root_public_key, const std::string& service_certificate,
|
||||||
const std::string& service_private_key,
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase);
|
const std::string& service_private_key_passphrase);
|
||||||
|
|
||||||
static util::Status SetDefaultDrmServiceCertificate(
|
static Status SetDefaultDrmServiceCertificate(
|
||||||
const std::string& root_public_key, const std::string& service_certificate,
|
const std::string& root_public_key, const std::string& service_certificate,
|
||||||
const std::string& service_private_key,
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase);
|
const std::string& service_private_key_passphrase);
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
|||||||
return serialized_cert;
|
return serialized_cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status SetDefaultDrmServiceCertificate(const std::string& serial_number,
|
Status SetDefaultDrmServiceCertificate(const std::string& serial_number,
|
||||||
const std::string& provider_id,
|
const std::string& provider_id,
|
||||||
uint32_t creation_time_seconds) {
|
uint32_t creation_time_seconds) {
|
||||||
std::string signed_cert(GenerateDrmServiceCertificate(
|
std::string signed_cert(GenerateDrmServiceCertificate(
|
||||||
serial_number, provider_id, creation_time_seconds,
|
serial_number, provider_id, creation_time_seconds,
|
||||||
test_keys_.public_test_key_2_2048_bits()));
|
test_keys_.public_test_key_2_2048_bits()));
|
||||||
@@ -79,15 +79,15 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
|||||||
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||||
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
||||||
&encrypted_private_key)) {
|
&encrypted_private_key)) {
|
||||||
return util::Status(util::error::INTERNAL, "");
|
return Status(error::INTERNAL, "");
|
||||||
}
|
}
|
||||||
return DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
return DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||||
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
|
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status AddDrmServiceCertificate(const std::string& serial_number,
|
Status AddDrmServiceCertificate(const std::string& serial_number,
|
||||||
const std::string& provider_id,
|
const std::string& provider_id,
|
||||||
uint32_t creation_time_seconds) {
|
uint32_t creation_time_seconds) {
|
||||||
std::string signed_cert(GenerateDrmServiceCertificate(
|
std::string signed_cert(GenerateDrmServiceCertificate(
|
||||||
serial_number, provider_id, creation_time_seconds,
|
serial_number, provider_id, creation_time_seconds,
|
||||||
test_keys_.public_test_key_2_2048_bits()));
|
test_keys_.public_test_key_2_2048_bits()));
|
||||||
@@ -95,7 +95,7 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
|||||||
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||||
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
||||||
&encrypted_private_key)) {
|
&encrypted_private_key)) {
|
||||||
return util::Status(util::error::INTERNAL, "");
|
return Status(error::INTERNAL, "");
|
||||||
}
|
}
|
||||||
return DrmServiceCertificate::AddDrmServiceCertificate(
|
return DrmServiceCertificate::AddDrmServiceCertificate(
|
||||||
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
|
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
|
||||||
@@ -140,9 +140,8 @@ TEST_F(DrmServiceCertificateTest, BasicClientIdDecrypt) {
|
|||||||
test_keys_.public_test_key_2_2048_bits(),
|
test_keys_.public_test_key_2_2048_bits(),
|
||||||
&encrypted_client_id);
|
&encrypted_client_id);
|
||||||
ClientIdentification decrypted_client_id;
|
ClientIdentification decrypted_client_id;
|
||||||
EXPECT_EQ(util::OkStatus(),
|
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||||
DrmServiceCertificate::DecryptClientIdentification(
|
encrypted_client_id, &decrypted_client_id));
|
||||||
encrypted_client_id, &decrypted_client_id));
|
|
||||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||||
decrypted_client_id));
|
decrypted_client_id));
|
||||||
}
|
}
|
||||||
@@ -220,27 +219,24 @@ TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) {
|
|||||||
test_keys_.public_test_key_2_2048_bits(),
|
test_keys_.public_test_key_2_2048_bits(),
|
||||||
&encrypted_client_id);
|
&encrypted_client_id);
|
||||||
ClientIdentification decrypted_client_id;
|
ClientIdentification decrypted_client_id;
|
||||||
EXPECT_EQ(util::OkStatus(),
|
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||||
DrmServiceCertificate::DecryptClientIdentification(
|
encrypted_client_id, &decrypted_client_id));
|
||||||
encrypted_client_id, &decrypted_client_id));
|
|
||||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||||
decrypted_client_id));
|
decrypted_client_id));
|
||||||
|
|
||||||
EncryptClientIdentification(serial_number2, provider_id,
|
EncryptClientIdentification(serial_number2, provider_id,
|
||||||
test_keys_.public_test_key_2_2048_bits(),
|
test_keys_.public_test_key_2_2048_bits(),
|
||||||
&encrypted_client_id);
|
&encrypted_client_id);
|
||||||
EXPECT_EQ(util::OkStatus(),
|
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||||
DrmServiceCertificate::DecryptClientIdentification(
|
encrypted_client_id, &decrypted_client_id));
|
||||||
encrypted_client_id, &decrypted_client_id));
|
|
||||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||||
decrypted_client_id));
|
decrypted_client_id));
|
||||||
|
|
||||||
EncryptClientIdentification(serial_number3, provider_id,
|
EncryptClientIdentification(serial_number3, provider_id,
|
||||||
test_keys_.public_test_key_2_2048_bits(),
|
test_keys_.public_test_key_2_2048_bits(),
|
||||||
&encrypted_client_id);
|
&encrypted_client_id);
|
||||||
EXPECT_EQ(util::OkStatus(),
|
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||||
DrmServiceCertificate::DecryptClientIdentification(
|
encrypted_client_id, &decrypted_client_id));
|
||||||
encrypted_client_id, &decrypted_client_id));
|
|
||||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||||
decrypted_client_id));
|
decrypted_client_id));
|
||||||
|
|
||||||
@@ -292,9 +288,8 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
|||||||
test_keys_.public_test_key_2_2048_bits(),
|
test_keys_.public_test_key_2_2048_bits(),
|
||||||
&encrypted_client_id);
|
&encrypted_client_id);
|
||||||
ClientIdentification decrypted_client_id;
|
ClientIdentification decrypted_client_id;
|
||||||
ASSERT_EQ(util::OkStatus(),
|
ASSERT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||||
DrmServiceCertificate::DecryptClientIdentification(
|
encrypted_client_id, &decrypted_client_id));
|
||||||
encrypted_client_id, &decrypted_client_id));
|
|
||||||
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||||
decrypted_client_id));
|
decrypted_client_id));
|
||||||
|
|
||||||
@@ -310,9 +305,8 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
|||||||
|
|
||||||
invalid = encrypted_client_id;
|
invalid = encrypted_client_id;
|
||||||
++(*invalid.mutable_encrypted_client_id_iv())[4];
|
++(*invalid.mutable_encrypted_client_id_iv())[4];
|
||||||
EXPECT_NE(util::OkStatus(),
|
EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||||
DrmServiceCertificate::DecryptClientIdentification(
|
invalid, &decrypted_client_id));
|
||||||
invalid, &decrypted_client_id));
|
|
||||||
|
|
||||||
invalid.clear_encrypted_client_id_iv();
|
invalid.clear_encrypted_client_id_iv();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -324,9 +318,8 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
|||||||
|
|
||||||
invalid = encrypted_client_id;
|
invalid = encrypted_client_id;
|
||||||
++(*invalid.mutable_encrypted_client_id())[0];
|
++(*invalid.mutable_encrypted_client_id())[0];
|
||||||
EXPECT_NE(util::OkStatus(),
|
EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||||
DrmServiceCertificate::DecryptClientIdentification(
|
invalid, &decrypted_client_id));
|
||||||
invalid, &decrypted_client_id));
|
|
||||||
|
|
||||||
invalid.clear_encrypted_client_id();
|
invalid.clear_encrypted_client_id();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -349,9 +342,8 @@ TEST_F(DrmServiceCertificateTest, PrivateKeyDecryptError) {
|
|||||||
test_keys_.public_test_key_2_2048_bits(),
|
test_keys_.public_test_key_2_2048_bits(),
|
||||||
&encrypted_client_id);
|
&encrypted_client_id);
|
||||||
ClientIdentification decrypted_client_id;
|
ClientIdentification decrypted_client_id;
|
||||||
ASSERT_EQ(util::OkStatus(),
|
ASSERT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||||
DrmServiceCertificate::DecryptClientIdentification(
|
encrypted_client_id, &decrypted_client_id));
|
||||||
encrypted_client_id, &decrypted_client_id));
|
|
||||||
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||||
decrypted_client_id));
|
decrypted_client_id));
|
||||||
|
|
||||||
|
|||||||
@@ -113,99 +113,99 @@ void RemoteAttestationVerifier::EnableTestDrmCertificates(bool enable) {
|
|||||||
ca_.reset();
|
ca_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||||
std::string* remote_attestation_cert_sn) {
|
std::string* remote_attestation_cert_sn) {
|
||||||
DCHECK(remote_attestation_cert_sn);
|
DCHECK(remote_attestation_cert_sn);
|
||||||
|
|
||||||
// Sanity check RemoteAttestation.
|
// Sanity check RemoteAttestation.
|
||||||
if (!remote_attestation.has_certificate()) {
|
if (!remote_attestation.has_certificate()) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
"remote-attestation-certificate-missing"));
|
"remote-attestation-certificate-missing"));
|
||||||
}
|
}
|
||||||
if (!remote_attestation.has_salt()) {
|
if (!remote_attestation.has_salt()) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
"remote-attestation-salt-missing"));
|
"remote-attestation-salt-missing"));
|
||||||
}
|
}
|
||||||
if (!remote_attestation.has_signature()) {
|
if (!remote_attestation.has_signature()) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
"remote-attestation-signature-missing"));
|
"remote-attestation-signature-missing"));
|
||||||
}
|
}
|
||||||
// Decrypt ClientIdentification containing remote attestation certificate.
|
// Decrypt ClientIdentification containing remote attestation certificate.
|
||||||
// A service cert would be looked up first, then that cert will be used
|
// A service cert would be looked up first, then that cert will be used
|
||||||
// to decrypt the ClientIdentification.
|
// to decrypt the ClientIdentification.
|
||||||
ClientIdentification client_id;
|
ClientIdentification client_id;
|
||||||
util::Status status = DrmServiceCertificate::DecryptClientIdentification(
|
Status status = DrmServiceCertificate::DecryptClientIdentification(
|
||||||
remote_attestation.certificate(), &client_id);
|
remote_attestation.certificate(), &client_id);
|
||||||
if (!status.ok()) return status;
|
if (!status.ok()) return status;
|
||||||
|
|
||||||
if (client_id.type() !=
|
if (client_id.type() !=
|
||||||
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
std::string("remote-attestation-invalid-client-id-type (") +
|
std::string("remote-attestation-invalid-client-id-type (") +
|
||||||
absl::StrCat(client_id.type()) + ")"));
|
absl::StrCat(client_id.type()) + ")"));
|
||||||
}
|
}
|
||||||
return VerifyRemoteAttestation(message, remote_attestation, client_id,
|
return VerifyRemoteAttestation(message, remote_attestation, client_id,
|
||||||
remote_attestation_cert_sn);
|
remote_attestation_cert_sn);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||||
const std::string& privacy_key) {
|
const std::string& privacy_key) {
|
||||||
// Sanity check RemoteAttestation.
|
// Sanity check RemoteAttestation.
|
||||||
if (!remote_attestation.has_certificate()) {
|
if (!remote_attestation.has_certificate()) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
"remote-attestation-certificate-missing"));
|
"remote-attestation-certificate-missing"));
|
||||||
}
|
}
|
||||||
if (!remote_attestation.has_salt()) {
|
if (!remote_attestation.has_salt()) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
"remote-attestation-salt-missing"));
|
"remote-attestation-salt-missing"));
|
||||||
}
|
}
|
||||||
if (!remote_attestation.has_signature()) {
|
if (!remote_attestation.has_signature()) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
"remote-attestation-signature-missing"));
|
"remote-attestation-signature-missing"));
|
||||||
}
|
}
|
||||||
// Decrypt ClientIdentification containing remote attestation certificate,
|
// Decrypt ClientIdentification containing remote attestation certificate,
|
||||||
// directly using an explicitly provided key |privacy_key|.
|
// directly using an explicitly provided key |privacy_key|.
|
||||||
ClientIdentification client_id;
|
ClientIdentification client_id;
|
||||||
util::Status status = DecryptEncryptedClientIdentification(
|
Status status = DecryptEncryptedClientIdentification(
|
||||||
remote_attestation.certificate(), privacy_key, &client_id);
|
remote_attestation.certificate(), privacy_key, &client_id);
|
||||||
if (!status.ok()) return status;
|
if (!status.ok()) return status;
|
||||||
|
|
||||||
if (client_id.type() !=
|
if (client_id.type() !=
|
||||||
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
std::string("remote-attestation-invalid-client-id-type (") +
|
std::string("remote-attestation-invalid-client-id-type (") +
|
||||||
absl::StrCat(client_id.type()) + ")"));
|
absl::StrCat(client_id.type()) + ")"));
|
||||||
}
|
}
|
||||||
std::string remote_attestation_cert_sn;
|
std::string remote_attestation_cert_sn;
|
||||||
return VerifyRemoteAttestation(message, remote_attestation, client_id,
|
return VerifyRemoteAttestation(message, remote_attestation, client_id,
|
||||||
&remote_attestation_cert_sn);
|
&remote_attestation_cert_sn);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||||
const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) {
|
const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) {
|
||||||
if (!client_id.has_token()) {
|
if (!client_id.has_token()) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
"remote-attestation-token-missing"));
|
"remote-attestation-token-missing"));
|
||||||
}
|
}
|
||||||
// Load and verify the certificate chain.
|
// Load and verify the certificate chain.
|
||||||
std::unique_ptr<X509CertChain> cert_chain(new X509CertChain);
|
std::unique_ptr<X509CertChain> cert_chain(new X509CertChain);
|
||||||
util::Status status = cert_chain->LoadPem(client_id.token());
|
Status status = cert_chain->LoadPem(client_id.token());
|
||||||
if (!status.ok()) return status;
|
if (!status.ok()) return status;
|
||||||
|
|
||||||
if (cert_chain->GetNumCerts() < 1) {
|
if (cert_chain->GetNumCerts() < 1) {
|
||||||
return (util::Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
"remote-attestation-empty-certificate-chain"));
|
"remote-attestation-empty-certificate-chain"));
|
||||||
}
|
}
|
||||||
std::string device_mode_string =
|
std::string device_mode_string =
|
||||||
cert_chain->GetCert(0)->GetSubjectNameField(kDeviceModeFieldName);
|
cert_chain->GetCert(0)->GetSubjectNameField(kDeviceModeFieldName);
|
||||||
if (device_mode_string != kExpectedDeviceMode) {
|
if (device_mode_string != kExpectedDeviceMode) {
|
||||||
return (util::Status(error_space, REMOTE_ATTESTATION_FAILED,
|
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||||
std::string("remote-attestation-device-not-verified (") +
|
std::string("remote-attestation-device-not-verified (") +
|
||||||
device_mode_string + " / " + kDeviceModeFieldName +
|
device_mode_string + " / " + kDeviceModeFieldName +
|
||||||
")"));
|
")"));
|
||||||
}
|
}
|
||||||
ca_mutex_.ReaderLock();
|
ca_mutex_.ReaderLock();
|
||||||
if (ca_ == NULL) {
|
if (ca_ == NULL) {
|
||||||
@@ -217,10 +217,9 @@ util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
|||||||
status = ca_->VerifyCertChain(*cert_chain);
|
status = ca_->VerifyCertChain(*cert_chain);
|
||||||
ca_mutex_.ReaderUnlock();
|
ca_mutex_.ReaderUnlock();
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return (util::Status(
|
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||||
error_space, REMOTE_ATTESTATION_FAILED,
|
std::string("remote-attestation-cert-chain-validation-failed: ") +
|
||||||
std::string("remote-attestation-cert-chain-validation-failed: ") +
|
status.error_message()));
|
||||||
status.error_message()));
|
|
||||||
}
|
}
|
||||||
// Verify the remote attestation signature.
|
// Verify the remote attestation signature.
|
||||||
std::unique_ptr<RsaPublicKey> leaf_key;
|
std::unique_ptr<RsaPublicKey> leaf_key;
|
||||||
@@ -232,30 +231,30 @@ util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!leaf_key) {
|
if (!leaf_key) {
|
||||||
return util::Status(error_space, REMOTE_ATTESTATION_FAILED,
|
return Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||||
"remote-attestation-cert-chain-no-leaf");
|
"remote-attestation-cert-chain-no-leaf");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt,
|
if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt,
|
||||||
remote_attestation.signature())) {
|
remote_attestation.signature())) {
|
||||||
return (util::Status(error_space, REMOTE_ATTESTATION_FAILED,
|
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||||
"remote-attestation-signature-verification-failed: "));
|
"remote-attestation-signature-verification-failed: "));
|
||||||
}
|
}
|
||||||
|
|
||||||
*remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber();
|
*remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber();
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status RemoteAttestationVerifier::LoadCa() {
|
Status RemoteAttestationVerifier::LoadCa() {
|
||||||
absl::WriterMutexLock lock(&ca_mutex_);
|
absl::WriterMutexLock lock(&ca_mutex_);
|
||||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||||
util::Status status = ca_cert->LoadDer(absl::HexStringToBytes(
|
Status status = ca_cert->LoadDer(absl::HexStringToBytes(
|
||||||
enable_test_certificates_ ? kTestRootCaDerCert : kProdRootCaDerCert));
|
enable_test_certificates_ ? kTestRootCaDerCert : kProdRootCaDerCert));
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
ca_.reset(new X509CA(ca_cert.release()));
|
ca_.reset(new X509CA(ca_cert.release()));
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ class RemoteAttestationVerifier {
|
|||||||
// return will contain the serial number for the client's remote attestation
|
// return will contain the serial number for the client's remote attestation
|
||||||
// certificate.
|
// certificate.
|
||||||
// This method is thread-safe.
|
// This method is thread-safe.
|
||||||
util::Status VerifyRemoteAttestation(
|
Status VerifyRemoteAttestation(const std::string& message,
|
||||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
const RemoteAttestation& remote_attestation,
|
||||||
std::string* remote_attestation_cert_sn);
|
std::string* remote_attestation_cert_sn);
|
||||||
|
|
||||||
// Call to verify a RemoteAttestation challenge response, used in certificate
|
// Call to verify a RemoteAttestation challenge response, used in certificate
|
||||||
// provisioning protocol.
|
// provisioning protocol.
|
||||||
@@ -61,9 +61,9 @@ class RemoteAttestationVerifier {
|
|||||||
// |privacy_key| is used to decrypt the EncryptedClientIdentification within
|
// |privacy_key| is used to decrypt the EncryptedClientIdentification within
|
||||||
// the |remote_attestation| message.
|
// the |remote_attestation| message.
|
||||||
// This method is thread-safe.
|
// This method is thread-safe.
|
||||||
util::Status VerifyRemoteAttestation(
|
Status VerifyRemoteAttestation(const std::string& message,
|
||||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
const RemoteAttestation& remote_attestation,
|
||||||
const std::string& privacy_key);
|
const std::string& privacy_key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Common subroutine to perform the verification.
|
// Common subroutine to perform the verification.
|
||||||
@@ -73,12 +73,12 @@ class RemoteAttestationVerifier {
|
|||||||
// |remote_attestation_cert_sn| is a pointer to a std::string which on successful
|
// |remote_attestation_cert_sn| is a pointer to a std::string which on successful
|
||||||
// return will contain the serial number for the client's remote attestation
|
// return will contain the serial number for the client's remote attestation
|
||||||
// certificate.
|
// certificate.
|
||||||
util::Status VerifyRemoteAttestation(
|
Status VerifyRemoteAttestation(const std::string& message,
|
||||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
const RemoteAttestation& remote_attestation,
|
||||||
const ClientIdentification& client_id,
|
const ClientIdentification& client_id,
|
||||||
std::string* remote_attestation_cert_sn);
|
std::string* remote_attestation_cert_sn);
|
||||||
|
|
||||||
util::Status LoadCa();
|
Status LoadCa();
|
||||||
|
|
||||||
bool enable_test_certificates_;
|
bool enable_test_certificates_;
|
||||||
absl::Mutex ca_mutex_;
|
absl::Mutex ca_mutex_;
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ std::string Sha256_Hash(const std::string& message) {
|
|||||||
return digest;
|
return digest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Sha512_Hash(const std::string& message) {
|
||||||
|
std::string digest;
|
||||||
|
digest.resize(SHA512_DIGEST_LENGTH);
|
||||||
|
SHA512(reinterpret_cast<const uint8_t*>(message.data()), message.size(),
|
||||||
|
reinterpret_cast<uint8_t*>(&digest[0]));
|
||||||
|
return digest;
|
||||||
|
}
|
||||||
|
|
||||||
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name) {
|
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name) {
|
||||||
// X.667 14 Setting the fields of a name-based UUID.
|
// X.667 14 Setting the fields of a name-based UUID.
|
||||||
// - Allocate a UUID to use as a "name space identifier" for all UUIDs
|
// - Allocate a UUID to use as a "name space identifier" for all UUIDs
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ std::string Sha1_Hash(const std::string& message);
|
|||||||
// Calculates SHA256 hash.
|
// Calculates SHA256 hash.
|
||||||
std::string Sha256_Hash(const std::string& message);
|
std::string Sha256_Hash(const std::string& message);
|
||||||
|
|
||||||
|
// Calculate SHA512 hash.
|
||||||
|
std::string Sha512_Hash(const std::string& message);
|
||||||
|
|
||||||
// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based,
|
// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based,
|
||||||
// 16-byte binary representation. Name_space is a GUID prefix; name is a unique
|
// 16-byte binary representation. Name_space is a GUID prefix; name is a unique
|
||||||
// name in the namespace.
|
// name in the namespace.
|
||||||
|
|||||||
@@ -48,6 +48,18 @@ TEST(ShaUtilTest, Sha256) {
|
|||||||
Sha256_Hash("random data"));
|
Sha256_Hash("random data"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ShaUtilTest, Sha512) {
|
||||||
|
const uint8_t kExpected[] = {
|
||||||
|
0x8f, 0x49, 0x93, 0x1f, 0x4d, 0x4a, 0x67, 0x6f, 0x9a, 0x7e, 0x62,
|
||||||
|
0x60, 0xea, 0xe1, 0x54, 0xfe, 0xe2, 0x75, 0x3c, 0xec, 0x3b, 0xb2,
|
||||||
|
0x2e, 0xd7, 0x51, 0xcc, 0x39, 0xf9, 0x89, 0x69, 0xad, 0xd0, 0x14,
|
||||||
|
0xaa, 0xbe, 0x40, 0xce, 0xe3, 0xab, 0xef, 0x10, 0x2f, 0x24, 0x0e,
|
||||||
|
0xd8, 0x26, 0x7b, 0xb5, 0x7d, 0x86, 0xce, 0xbd, 0xd7, 0x32, 0x86,
|
||||||
|
0x3a, 0x5e, 0x9e, 0x8d, 0x23, 0x77, 0x10, 0x80, 0x0c};
|
||||||
|
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||||
|
Sha512_Hash("random data"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ShaUtilTest, GenerateSha1Uuid) {
|
TEST(ShaUtilTest, GenerateSha1Uuid) {
|
||||||
std::string name_space =
|
std::string name_space =
|
||||||
absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774");
|
absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774");
|
||||||
|
|||||||
@@ -11,55 +11,50 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "common/status.h"
|
|
||||||
#include "common/aes_cbc_util.h"
|
#include "common/aes_cbc_util.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "common/sha_util.h"
|
#include "common/sha_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace signature_util {
|
namespace signature_util {
|
||||||
|
|
||||||
util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
||||||
const std::string& aes_iv, std::string* signature) {
|
const std::string& aes_iv, std::string* signature) {
|
||||||
if (signature == nullptr) {
|
if (signature == nullptr) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT, "signature is nullptr");
|
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
|
||||||
}
|
}
|
||||||
std::string hash = Sha1_Hash(message);
|
std::string hash = Sha1_Hash(message);
|
||||||
if (hash.empty()) {
|
if (hash.empty()) {
|
||||||
return util::Status(util::error::INTERNAL, "Computed hash is empty");
|
return Status(error::INTERNAL, "Computed hash is empty");
|
||||||
}
|
}
|
||||||
std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash);
|
std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash);
|
||||||
if (sig.empty()) {
|
if (sig.empty()) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "Computed AES signature is empty");
|
||||||
"Computed AES signature is empty");
|
|
||||||
}
|
}
|
||||||
*signature = sig;
|
*signature = sig;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status GenerateRsaSignature(const std::string& message,
|
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
|
||||||
const std::string& private_key,
|
std::string* signature) {
|
||||||
std::string* signature) {
|
|
||||||
if (signature == nullptr) {
|
if (signature == nullptr) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT, "signature is nullptr");
|
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
|
||||||
}
|
}
|
||||||
std::unique_ptr<RsaPrivateKey> rsa_private_key(
|
std::unique_ptr<RsaPrivateKey> rsa_private_key(
|
||||||
RsaPrivateKey::Create(private_key));
|
RsaPrivateKey::Create(private_key));
|
||||||
if (rsa_private_key == nullptr) {
|
if (rsa_private_key == nullptr) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "Failed to construct a RsaPrivateKey");
|
||||||
"Failed to construct a RsaPrivateKey");
|
|
||||||
}
|
}
|
||||||
std::string sig;
|
std::string sig;
|
||||||
if (!rsa_private_key->GenerateSignature(message, &sig)) {
|
if (!rsa_private_key->GenerateSignature(message, &sig)) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "Failed to generate a RSA signature");
|
||||||
"Failed to generate a RSA signature");
|
|
||||||
}
|
}
|
||||||
if (sig.empty()) {
|
if (sig.empty()) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "Computed RSA signature is empty");
|
||||||
"Computed RSA signature is empty");
|
|
||||||
}
|
}
|
||||||
*signature = sig;
|
*signature = sig;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace signature_util
|
} // namespace signature_util
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ namespace signature_util {
|
|||||||
// Generates an AES signature of |message| using |aes_key| and |aes_iv|.
|
// Generates an AES signature of |message| using |aes_key| and |aes_iv|.
|
||||||
// Signature is returned via |signature| if generation was successful.
|
// Signature is returned via |signature| if generation was successful.
|
||||||
// Returns a Status that carries the details of error if generation failed.
|
// Returns a Status that carries the details of error if generation failed.
|
||||||
util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
||||||
const std::string& aes_iv, std::string* signature);
|
const std::string& aes_iv, std::string* signature);
|
||||||
|
|
||||||
// Generates a RSA signature of |message| using |private_key|.
|
// Generates a RSA signature of |message| using |private_key|.
|
||||||
// Signature is returned via |sigature| if generation was successful.
|
// Signature is returned via |sigature| if generation was successful.
|
||||||
// Returns a Status that carries the details of error if generation failed.
|
// Returns a Status that carries the details of error if generation failed.
|
||||||
util::Status GenerateRsaSignature(const std::string& message,
|
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
|
||||||
const std::string& private_key, std::string* signature);
|
std::string* signature);
|
||||||
|
|
||||||
} // namespace signature_util
|
} // namespace signature_util
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace widevine {
|
|||||||
|
|
||||||
using crypto_util::kSigningKeySizeBits;
|
using crypto_util::kSigningKeySizeBits;
|
||||||
|
|
||||||
uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version) {
|
uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version) {
|
||||||
if (protocol_version <= VERSION_2_0) {
|
if (protocol_version <= VERSION_2_0) {
|
||||||
return kSigningKeySizeBits;
|
return kSigningKeySizeBits;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
// std::string derived_key = DeriveKey(key_str,
|
// std::string derived_key = DeriveKey(key_str,
|
||||||
// label,
|
// label,
|
||||||
// context,
|
// context,
|
||||||
// SigningKeyMaterialSize(VERSION_2_1));
|
// SigningKeyMaterialSizeBits(VERSION_2_1));
|
||||||
// std::string server_derived_key = GetServerSigningKey(derived_key);
|
// std::string server_derived_key = GetServerSigningKey(derived_key);
|
||||||
// std::string client_derived_key = GetClientSigninKey(derived_key);
|
// std::string client_derived_key = GetClientSigninKey(derived_key);
|
||||||
#ifndef COMMON_SIGNING_KEY_UTIL_H_
|
#ifndef COMMON_SIGNING_KEY_UTIL_H_
|
||||||
@@ -36,7 +36,7 @@ namespace widevine {
|
|||||||
// Returns the size of the signing key based on the License Protocol
|
// Returns the size of the signing key based on the License Protocol
|
||||||
// Version. Signing keys for version 2.0 have a length of 256. Signing
|
// Version. Signing keys for version 2.0 have a length of 256. Signing
|
||||||
// keys for version 2.1 have a length of 512.
|
// keys for version 2.1 have a length of 512.
|
||||||
uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version);
|
uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version);
|
||||||
|
|
||||||
// Returns the client portion of the derived_key. The client portion
|
// Returns the client portion of the derived_key. The client portion
|
||||||
// depend on the size of the key. Keys that are 512 bits in length
|
// depend on the size of the key. Keys that are 512 bits in length
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_0) {
|
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_0) {
|
||||||
ASSERT_EQ(crypto_util::kSigningKeySizeBits,
|
ASSERT_EQ(crypto_util::kSigningKeySizeBits,
|
||||||
SigningKeyMaterialSize(VERSION_2_0));
|
SigningKeyMaterialSizeBits(VERSION_2_0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_1) {
|
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_1) {
|
||||||
ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2,
|
ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2,
|
||||||
SigningKeyMaterialSize(VERSION_2_1));
|
SigningKeyMaterialSizeBits(VERSION_2_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) {
|
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) {
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
|
|
||||||
#include "absl/base/macros.h"
|
#include "absl/base/macros.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "common/status.h"
|
#include "util/error_space.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace util {
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -72,5 +71,4 @@ std::ostream& operator<<(std::ostream& os, const Status& x) {
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace util
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include "util/error_space.h"
|
#include "util/error_space.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace util {
|
|
||||||
namespace error {
|
namespace error {
|
||||||
|
|
||||||
enum StatusCode {
|
enum StatusCode {
|
||||||
@@ -104,7 +103,6 @@ std::ostream& operator<<(std::ostream& os, const Status& x);
|
|||||||
#define CHECK_OK(expression) CHECK(expression.ok()) << expression.ToString()
|
#define CHECK_OK(expression) CHECK(expression.ok()) << expression.ToString()
|
||||||
|
|
||||||
|
|
||||||
} // namespace util
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // COMMON_STATUS_H_
|
#endif // COMMON_STATUS_H_
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace util {
|
|
||||||
|
|
||||||
TEST(StatusTest, OK_Status) {
|
TEST(StatusTest, OK_Status) {
|
||||||
// test case for ok status.
|
// test case for ok status.
|
||||||
@@ -59,5 +58,4 @@ TEST(StatusTest, NOT_EQUAL_OPERATOR_NONE_MSG) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace util
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace string_util {
|
namespace string_util {
|
||||||
|
|
||||||
util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) {
|
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) {
|
||||||
if (output == nullptr) {
|
if (output == nullptr) {
|
||||||
return util::Status(util::error::INTERNAL, "output is nullptr.");
|
return Status(error::INTERNAL, "output is nullptr.");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream sstream(bitset);
|
std::stringstream sstream(bitset);
|
||||||
@@ -29,7 +29,7 @@ util::Status BitsetStringToBinaryString(const std::string& bitset, std::string*
|
|||||||
*output += c;
|
*output += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace string_util
|
} // namespace string_util
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace string_util {
|
|||||||
|
|
||||||
// Converts std::string representation of a bitset to its binary equivalent string.
|
// Converts std::string representation of a bitset to its binary equivalent string.
|
||||||
// For example, converts "01110100011001010111001101110100" to "test".
|
// For example, converts "01110100011001010111001101110100" to "test".
|
||||||
util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output);
|
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output);
|
||||||
|
|
||||||
} // namespace string_util
|
} // namespace string_util
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -18,32 +18,31 @@
|
|||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
std::string* signature) {
|
std::string* signature) {
|
||||||
CHECK(signature);
|
CHECK(signature);
|
||||||
if (pem_private_key.empty()) {
|
if (pem_private_key.empty()) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM private key");
|
return Status(error::INVALID_ARGUMENT, "Empty PEM private key");
|
||||||
}
|
}
|
||||||
if (message.empty()) {
|
if (message.empty()) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty message");
|
return Status(error::INVALID_ARGUMENT, "Empty message");
|
||||||
}
|
}
|
||||||
BIO* bio(NULL);
|
BIO* bio(NULL);
|
||||||
bio = BIO_new_mem_buf(const_cast<char*>(pem_private_key.data()),
|
bio = BIO_new_mem_buf(const_cast<char*>(pem_private_key.data()),
|
||||||
pem_private_key.size());
|
pem_private_key.size());
|
||||||
if (bio == NULL) {
|
if (bio == NULL) {
|
||||||
return util::Status(util::error::INTERNAL, "BIO allocation failed");
|
return Status(error::INTERNAL, "BIO allocation failed");
|
||||||
}
|
}
|
||||||
util::Status status;
|
Status status;
|
||||||
RSA* key(NULL);
|
RSA* key(NULL);
|
||||||
std::unique_ptr<char[]> sig_buffer;
|
std::unique_ptr<char[]> sig_buffer;
|
||||||
unsigned int sig_size;
|
unsigned int sig_size;
|
||||||
unsigned char digest[SHA256_DIGEST_LENGTH];
|
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||||
key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
|
key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
|
||||||
if (key == NULL) {
|
if (key == NULL) {
|
||||||
status = util::Status(util::Status::canonical_space(),
|
status = Status(Status::canonical_space(), error::INVALID_ARGUMENT,
|
||||||
util::error::INVALID_ARGUMENT,
|
"PEM RSA private key load failed");
|
||||||
"PEM RSA private key load failed");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
SHA256(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
|
SHA256(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
|
||||||
@@ -53,9 +52,8 @@ util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
|||||||
if (RSA_sign(NID_sha256, digest, sizeof(digest),
|
if (RSA_sign(NID_sha256, digest, sizeof(digest),
|
||||||
reinterpret_cast<unsigned char*>(sig_buffer.get()), &sig_size,
|
reinterpret_cast<unsigned char*>(sig_buffer.get()), &sig_size,
|
||||||
key) != 1) {
|
key) != 1) {
|
||||||
status =
|
status = Status(Status::canonical_space(), error::INTERNAL,
|
||||||
util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
"RSA private encrypt failed");
|
||||||
"RSA private encrypt failed");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
signature->assign(sig_buffer.get(), sig_size);
|
signature->assign(sig_buffer.get(), sig_size);
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ namespace widevine {
|
|||||||
// |message| is the message to be signed, and |signature| is a pointer to a
|
// |message| is the message to be signed, and |signature| is a pointer to a
|
||||||
// std::string where the signature will be stored. The caller returns ownership of
|
// std::string where the signature will be stored. The caller returns ownership of
|
||||||
// all paramters.
|
// all paramters.
|
||||||
util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
std::string* signature);
|
std::string* signature);
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,11 @@
|
|||||||
#include "common/vmp_checker.h"
|
#include "common/vmp_checker.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
util::Status VerifyVmpData(
|
Status VerifyVmpData(const std::string& vmp_data,
|
||||||
const std::string& vmp_data,
|
PlatformVerificationStatus* platform_verification_status) {
|
||||||
PlatformVerificationStatus* platform_verification_status) {
|
|
||||||
*platform_verification_status = PLATFORM_UNVERIFIED;
|
*platform_verification_status = PLATFORM_UNVERIFIED;
|
||||||
VmpChecker::Result vmp_result;
|
VmpChecker::Result vmp_result;
|
||||||
util::Status status =
|
Status status = VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result);
|
||||||
VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result);
|
|
||||||
if (status.ok()) {
|
if (status.ok()) {
|
||||||
switch (vmp_result) {
|
switch (vmp_result) {
|
||||||
case VmpChecker::kUnverified:
|
case VmpChecker::kUnverified:
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ namespace widevine {
|
|||||||
|
|
||||||
// Retrieve the PlatformVerificationStatus for |vmp_data|. The
|
// Retrieve the PlatformVerificationStatus for |vmp_data|. The
|
||||||
// PlatformVerificationStatus is defined at
|
// PlatformVerificationStatus is defined at
|
||||||
util::Status VerifyVmpData(
|
Status VerifyVmpData(const std::string& vmp_data,
|
||||||
const std::string& vmp_data,
|
PlatformVerificationStatus* platform_verification_status);
|
||||||
PlatformVerificationStatus* platform_verification_status);
|
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
#endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
#endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
||||||
|
|||||||
@@ -248,9 +248,9 @@ VmpChecker::VmpChecker() : allow_development_vmp_(false) {}
|
|||||||
|
|
||||||
VmpChecker::~VmpChecker() {}
|
VmpChecker::~VmpChecker() {}
|
||||||
|
|
||||||
util::Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
|
Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
|
||||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||||
util::Status status = ca_cert->LoadDer(
|
Status status = ca_cert->LoadDer(
|
||||||
cert_type == kCertificateTypeProduction
|
cert_type == kCertificateTypeProduction
|
||||||
? std::string(reinterpret_cast<const char*>(
|
? std::string(reinterpret_cast<const char*>(
|
||||||
kProdVmpCodeSigningDrmRootCertificate),
|
kProdVmpCodeSigningDrmRootCertificate),
|
||||||
@@ -262,7 +262,7 @@ util::Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
|
|||||||
|
|
||||||
ca_.reset(new X509CA(ca_cert.release()));
|
ca_.reset(new X509CA(ca_cert.release()));
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
VmpChecker* VmpChecker::Instance() {
|
VmpChecker* VmpChecker::Instance() {
|
||||||
@@ -271,17 +271,16 @@ VmpChecker* VmpChecker::Instance() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify VMP data and return appropriate result.
|
// Verify VMP data and return appropriate result.
|
||||||
util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) {
|
Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) {
|
||||||
DCHECK(!vmp_data.empty());
|
DCHECK(!vmp_data.empty());
|
||||||
DCHECK(result);
|
DCHECK(result);
|
||||||
|
|
||||||
if (!ca_) return util::Status(error_space, CERT_CHAIN_NOT_SELECTED, "");
|
if (!ca_) return Status(error_space, CERT_CHAIN_NOT_SELECTED, "");
|
||||||
|
|
||||||
vmp::VmpData vmp_data_obj;
|
vmp::VmpData vmp_data_obj;
|
||||||
if (!vmp_data_obj.ParseFromString(vmp_data)) {
|
if (!vmp_data_obj.ParseFromString(vmp_data)) {
|
||||||
LOG(INFO) << "Error deserializing VmpData.";
|
LOG(INFO) << "Error deserializing VmpData.";
|
||||||
return util::Status(error_space, INVALID_MESSAGE,
|
return Status(error_space, INVALID_MESSAGE, "vmp-data-deserialize-failed");
|
||||||
"vmp-data-deserialize-failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<X509Cert>> code_signing_certs;
|
std::vector<std::unique_ptr<X509Cert>> code_signing_certs;
|
||||||
@@ -290,7 +289,7 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
|||||||
for (int cert_idx = 0; cert_idx < vmp_data_obj.certificates_size();
|
for (int cert_idx = 0; cert_idx < vmp_data_obj.certificates_size();
|
||||||
++cert_idx) {
|
++cert_idx) {
|
||||||
code_signing_certs.emplace_back(new X509Cert);
|
code_signing_certs.emplace_back(new X509Cert);
|
||||||
util::Status status(code_signing_certs.back()->LoadDer(
|
Status status(code_signing_certs.back()->LoadDer(
|
||||||
vmp_data_obj.certificates(cert_idx)));
|
vmp_data_obj.certificates(cert_idx)));
|
||||||
if (!status.ok()) return status;
|
if (!status.ok()) return status;
|
||||||
|
|
||||||
@@ -299,8 +298,8 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
|||||||
if (code_signing_certs.back()->GetV3BooleanExtension(kDevelopmentFlagOid,
|
if (code_signing_certs.back()->GetV3BooleanExtension(kDevelopmentFlagOid,
|
||||||
&dev_flag) &&
|
&dev_flag) &&
|
||||||
dev_flag) {
|
dev_flag) {
|
||||||
return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||||
"development-vmp-certificate-not-allowed");
|
"development-vmp-certificate-not-allowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
status = ca_->VerifyCert(*code_signing_certs.back());
|
status = ca_->VerifyCert(*code_signing_certs.back());
|
||||||
@@ -324,12 +323,12 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
|||||||
if (binary_info.signature().empty()) {
|
if (binary_info.signature().empty()) {
|
||||||
LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\".";
|
LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\".";
|
||||||
*result = kTampered;
|
*result = kTampered;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
if (binary_info.certificate_index() >= code_signing_certs.size()) {
|
if (binary_info.certificate_index() >= code_signing_certs.size()) {
|
||||||
LOG(INFO) << "Invalid code signing certificate index.";
|
LOG(INFO) << "Invalid code signing certificate index.";
|
||||||
*result = kTampered;
|
*result = kTampered;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get();
|
X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get();
|
||||||
std::unique_ptr<RsaPublicKey> key(cert->GetRsaPublicKey());
|
std::unique_ptr<RsaPublicKey> key(cert->GetRsaPublicKey());
|
||||||
@@ -339,7 +338,7 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
|||||||
LOG(INFO) << "Code signature verification failed for file \""
|
LOG(INFO) << "Code signature verification failed for file \""
|
||||||
<< binary_info.file_name() << "\".";
|
<< binary_info.file_name() << "\".";
|
||||||
*result = kTampered;
|
*result = kTampered;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
if (binary_info.flags() & kBlessedBinaryFlag) ++num_blessed_binaries;
|
if (binary_info.flags() & kBlessedBinaryFlag) ++num_blessed_binaries;
|
||||||
}
|
}
|
||||||
@@ -347,13 +346,13 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
|||||||
LOG(INFO) << "Invalid number of blessed binaries (" << num_blessed_binaries
|
LOG(INFO) << "Invalid number of blessed binaries (" << num_blessed_binaries
|
||||||
<< ").";
|
<< ").";
|
||||||
*result = kTampered;
|
*result = kTampered;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
VLOG(2) << "VMP verification success. Secure storage: "
|
VLOG(2) << "VMP verification success. Secure storage: "
|
||||||
<< secure_storage_verified;
|
<< secure_storage_verified;
|
||||||
*result = secure_storage_verified ? kSecureStorageVerified : kVerified;
|
*result = secure_storage_verified ? kSecureStorageVerified : kVerified;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "common/status.h"
|
|
||||||
#include "common/certificate_type.h"
|
#include "common/certificate_type.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
class X509CA;
|
class X509CA;
|
||||||
@@ -35,10 +35,10 @@ class VmpChecker {
|
|||||||
static VmpChecker* Instance();
|
static VmpChecker* Instance();
|
||||||
|
|
||||||
// Select the type of root to use. Not thread-safe.
|
// Select the type of root to use. Not thread-safe.
|
||||||
virtual util::Status SelectCertificateType(CertificateType cert_type);
|
virtual Status SelectCertificateType(CertificateType cert_type);
|
||||||
|
|
||||||
// Verify VMP data and return appropriate result.
|
// Verify VMP data and return appropriate result.
|
||||||
virtual util::Status VerifyVmpData(const std::string& vmp_data, Result* result);
|
virtual Status VerifyVmpData(const std::string& vmp_data, Result* result);
|
||||||
|
|
||||||
// Enable/disable development code signing certificates.
|
// Enable/disable development code signing certificates.
|
||||||
void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; }
|
void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; }
|
||||||
|
|||||||
@@ -16,10 +16,10 @@
|
|||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "util/endian/endian.h"
|
#include "util/endian/endian.h"
|
||||||
#include "util/gtl/map_util.h"
|
#include "util/gtl/map_util.h"
|
||||||
#include "common/status.h"
|
|
||||||
#include "common/aes_cbc_util.h"
|
#include "common/aes_cbc_util.h"
|
||||||
#include "common/ecb_util.h"
|
#include "common/ecb_util.h"
|
||||||
#include "common/sha_util.h"
|
#include "common/sha_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
@@ -108,20 +108,20 @@ bool WvmTokenHandler::IsSystemIdKnown(uint32_t system_id) {
|
|||||||
return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id);
|
return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||||
std::string* device_key_out,
|
std::string* device_key_out,
|
||||||
Cipher* cipher_out,
|
Cipher* cipher_out,
|
||||||
bool* insecure_out) {
|
bool* insecure_out) {
|
||||||
const std::string default_make_model;
|
const std::string default_make_model;
|
||||||
return DecryptDeviceKey(token, default_make_model, device_key_out, cipher_out,
|
return DecryptDeviceKey(token, default_make_model, device_key_out, cipher_out,
|
||||||
insecure_out);
|
insecure_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||||
const std::string& make_model,
|
const std::string& make_model,
|
||||||
std::string* device_key_out,
|
std::string* device_key_out,
|
||||||
Cipher* cipher_out,
|
Cipher* cipher_out,
|
||||||
bool* insecure_out) {
|
bool* insecure_out) {
|
||||||
DCHECK(device_key_out);
|
DCHECK(device_key_out);
|
||||||
// DCHECK below is commented out because preprov_keys_ being nullptr
|
// DCHECK below is commented out because preprov_keys_ being nullptr
|
||||||
// is a valid test in wvm_token_handler_test.cc. If we have
|
// is a valid test in wvm_token_handler_test.cc. If we have
|
||||||
@@ -129,12 +129,11 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
|||||||
// presubmit because evidently Kokoro does debug build.
|
// presubmit because evidently Kokoro does debug build.
|
||||||
// DCHECK(preprov_keys_);
|
// DCHECK(preprov_keys_);
|
||||||
if (token.size() < kKeyboxSizeBytes) {
|
if (token.size() < kKeyboxSizeBytes) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
|
||||||
"Keybox token is too short.");
|
|
||||||
}
|
}
|
||||||
if (PreprovKeysMap::GetSingleton()->IsEmpty()) {
|
if (PreprovKeysMap::GetSingleton()->IsEmpty()) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT,
|
||||||
"Pre-provisioning key map is nullptr.");
|
"Pre-provisioning key map is nullptr.");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t system_id = GetSystemId(token);
|
uint32_t system_id = GetSystemId(token);
|
||||||
@@ -143,7 +142,7 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
|||||||
std::vector<PreprovKey> key_vector =
|
std::vector<PreprovKey> key_vector =
|
||||||
PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id);
|
PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id);
|
||||||
|
|
||||||
util::Status status;
|
Status status;
|
||||||
// First pass through the matching system Ids is an attempt to find an
|
// First pass through the matching system Ids is an attempt to find an
|
||||||
// alternate preprov key specific to this make/model.
|
// alternate preprov key specific to this make/model.
|
||||||
const PreprovKey* preferred_ppk = NULL;
|
const PreprovKey* preferred_ppk = NULL;
|
||||||
@@ -162,8 +161,8 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
|||||||
ppk.key_bytes, token, device_key_out, insecure_out, &version);
|
ppk.key_bytes, token, device_key_out, insecure_out, &version);
|
||||||
if (version != 2) {
|
if (version != 2) {
|
||||||
// Only version 2 keyboxes supported.
|
// Only version 2 keyboxes supported.
|
||||||
return util::Status(util::error::PERMISSION_DENIED,
|
return Status(error::PERMISSION_DENIED,
|
||||||
absl::StrCat("invalid-keybox-version ", version));
|
absl::StrCat("invalid-keybox-version ", version));
|
||||||
}
|
}
|
||||||
if (status.ok()) {
|
if (status.ok()) {
|
||||||
if (cipher_out) {
|
if (cipher_out) {
|
||||||
@@ -176,8 +175,8 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
|||||||
// Return error from last attempt.
|
// Return error from last attempt.
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
return util::Status(
|
return Status(
|
||||||
util::error::NOT_FOUND,
|
error::NOT_FOUND,
|
||||||
absl::StrCat("Unknown system id: ", system_id).c_str()); // NOLINT
|
absl::StrCat("Unknown system id: ", system_id).c_str()); // NOLINT
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,12 +184,13 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
|||||||
// decrypted device key to encrypt the given asset key. Returns the encrypted
|
// decrypted device key to encrypt the given asset key. Returns the encrypted
|
||||||
// asset key in |result|.
|
// asset key in |result|.
|
||||||
// On failure, returns an error from the Widevine Server SDK error space.
|
// On failure, returns an error from the Widevine Server SDK error space.
|
||||||
util::Status WvmTokenHandler::GetEncryptedAssetKey(
|
Status WvmTokenHandler::GetEncryptedAssetKey(absl::string_view token,
|
||||||
absl::string_view token, absl::string_view raw_asset_key,
|
absl::string_view raw_asset_key,
|
||||||
const std::string& make_model, std::string* result) {
|
const std::string& make_model,
|
||||||
|
std::string* result) {
|
||||||
std::string device_key;
|
std::string device_key;
|
||||||
Cipher cipher = AES;
|
Cipher cipher = AES;
|
||||||
util::Status status =
|
Status status =
|
||||||
DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr);
|
DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
@@ -215,20 +215,19 @@ std::string WvmTokenHandler::GetEncryptedUniqueId(absl::string_view token) {
|
|||||||
return encrypted_unique_id;
|
return encrypted_unique_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||||
absl::string_view preprov_key, absl::string_view token,
|
absl::string_view preprov_key, absl::string_view token,
|
||||||
std::string* device_key_out) {
|
std::string* device_key_out) {
|
||||||
return DecryptDeviceKeyWithPreprovKey(preprov_key, token, device_key_out,
|
return DecryptDeviceKeyWithPreprovKey(preprov_key, token, device_key_out,
|
||||||
nullptr, nullptr);
|
nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||||
absl::string_view preprov_key, absl::string_view token,
|
absl::string_view preprov_key, absl::string_view token,
|
||||||
std::string* device_key_out, bool* insecure_out, uint32_t* version) {
|
std::string* device_key_out, bool* insecure_out, uint32_t* version) {
|
||||||
CHECK(device_key_out);
|
CHECK(device_key_out);
|
||||||
if (token.size() < kKeyboxSizeBytes) {
|
if (token.size() < kKeyboxSizeBytes) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
|
||||||
"Keybox token is too short.");
|
|
||||||
}
|
}
|
||||||
if (version) {
|
if (version) {
|
||||||
*version = BigEndian::Load32(token.data());
|
*version = BigEndian::Load32(token.data());
|
||||||
@@ -243,7 +242,7 @@ util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
|||||||
if (unique_id.size() != 16) {
|
if (unique_id.size() != 16) {
|
||||||
// Decrypting 16 bytes should result in 16 bytes.
|
// Decrypting 16 bytes should result in 16 bytes.
|
||||||
LOG(WARNING) << "Internal error decrypting unique id from token.";
|
LOG(WARNING) << "Internal error decrypting unique id from token.";
|
||||||
return util::Status(util::error::INTERNAL, "Wrong size after decrypt/16.");
|
return Status(error::INTERNAL, "Wrong size after decrypt/16.");
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::string_view encrypted_bits = token.substr(24, 48);
|
absl::string_view encrypted_bits = token.substr(24, 48);
|
||||||
@@ -252,7 +251,7 @@ util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
|||||||
if (decrypted_bits.size() != 48) {
|
if (decrypted_bits.size() != 48) {
|
||||||
// Decrypting 48 bytes should result in 48 bytes.
|
// Decrypting 48 bytes should result in 48 bytes.
|
||||||
LOG(WARNING) << "Internal error decrypting device key from token.";
|
LOG(WARNING) << "Internal error decrypting device key from token.";
|
||||||
return util::Status(util::error::INTERNAL, "Wrong size after decrypt/48.");
|
return Status(error::INTERNAL, "Wrong size after decrypt/48.");
|
||||||
}
|
}
|
||||||
uint8_t keybox_flags = decrypted_bits[36];
|
uint8_t keybox_flags = decrypted_bits[36];
|
||||||
absl::string_view device_key =
|
absl::string_view device_key =
|
||||||
@@ -269,48 +268,43 @@ util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
|||||||
keybox_flags = 0;
|
keybox_flags = 0;
|
||||||
}
|
}
|
||||||
if (expected_hash != actual_hash) {
|
if (expected_hash != actual_hash) {
|
||||||
return util::Status(util::error::PERMISSION_DENIED,
|
return Status(error::PERMISSION_DENIED, "Keybox validation failed.");
|
||||||
"Keybox validation failed.");
|
|
||||||
}
|
}
|
||||||
*device_key_out = std::string(device_key);
|
*device_key_out = std::string(device_key);
|
||||||
if (insecure_out) {
|
if (insecure_out) {
|
||||||
*insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0;
|
*insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0;
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key,
|
Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key,
|
||||||
absl::string_view raw_asset_key,
|
absl::string_view raw_asset_key,
|
||||||
Cipher cipher, std::string* result) {
|
Cipher cipher, std::string* result) {
|
||||||
CHECK(result);
|
CHECK(result);
|
||||||
if (device_key.size() != 16) {
|
if (device_key.size() != 16) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT, "Invalid device key: size != 16");
|
||||||
"Invalid device key: size != 16");
|
|
||||||
}
|
}
|
||||||
if (raw_asset_key.size() < 16) {
|
if (raw_asset_key.size() < 16) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT, "Invalid asset key: size < 16");
|
||||||
"Invalid asset key: size < 16");
|
|
||||||
}
|
}
|
||||||
// Truncate extra characters in the key; wvm always uses 16.
|
// Truncate extra characters in the key; wvm always uses 16.
|
||||||
absl::string_view asset_key = raw_asset_key.substr(0, 16);
|
absl::string_view asset_key = raw_asset_key.substr(0, 16);
|
||||||
switch (cipher) {
|
switch (cipher) {
|
||||||
case DES3:
|
case DES3:
|
||||||
if (!crypto_util::Encrypt3DesEcb(device_key, asset_key, result)) {
|
if (!crypto_util::Encrypt3DesEcb(device_key, asset_key, result)) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "Error encrypting asset key with 3DES.");
|
||||||
"Error encrypting asset key with 3DES.");
|
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
case AES:
|
case AES:
|
||||||
if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) {
|
if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "Error encrypting asset key with AES.");
|
||||||
"Error encrypting asset key with AES.");
|
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
case PASS_THRU:
|
case PASS_THRU:
|
||||||
result->assign(raw_asset_key.data(), raw_asset_key.size());
|
result->assign(raw_asset_key.data(), raw_asset_key.size());
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
default:
|
default:
|
||||||
return util::Status(util::error::INVALID_ARGUMENT, "Unknown cipher type");
|
return Status(error::INVALID_ARGUMENT, "Unknown cipher type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,24 +70,23 @@ class WvmTokenHandler {
|
|||||||
// to use with the device key.
|
// to use with the device key.
|
||||||
// insecure_out may be null; if not, *insecure_out will be set to the
|
// insecure_out may be null; if not, *insecure_out will be set to the
|
||||||
// decrypted value of the 'insecure keybox' flag.
|
// decrypted value of the 'insecure keybox' flag.
|
||||||
static util::Status DecryptDeviceKey(absl::string_view token,
|
static Status DecryptDeviceKey(absl::string_view token,
|
||||||
std::string* device_key_out,
|
std::string* device_key_out, Cipher* cipher_out,
|
||||||
Cipher* cipher_out, bool* insecure_out);
|
bool* insecure_out);
|
||||||
// Same as above, except takes in the make/model from the license request.
|
// Same as above, except takes in the make/model from the license request.
|
||||||
// For legacy WVM license, we have some special cases where we need to inspect
|
// For legacy WVM license, we have some special cases where we need to inspect
|
||||||
// the make/model as we apply alternate keys.
|
// the make/model as we apply alternate keys.
|
||||||
static util::Status DecryptDeviceKey(absl::string_view token,
|
static Status DecryptDeviceKey(absl::string_view token,
|
||||||
const std::string& make_model,
|
const std::string& make_model,
|
||||||
std::string* device_key_out,
|
std::string* device_key_out, Cipher* cipher_out,
|
||||||
Cipher* cipher_out, bool* insecure_out);
|
bool* insecure_out);
|
||||||
|
|
||||||
// Decrypt a token using the preprov key for its system ID, and use the
|
// Decrypt a token using the preprov key for its system ID, and use the
|
||||||
// decrypted device key to encrypt the given asset key. Returns the encrypted
|
// decrypted device key to encrypt the given asset key. Returns the encrypted
|
||||||
// asset key in result.
|
// asset key in result.
|
||||||
static util::Status GetEncryptedAssetKey(absl::string_view token,
|
static Status GetEncryptedAssetKey(absl::string_view token,
|
||||||
absl::string_view raw_asset_key,
|
absl::string_view raw_asset_key,
|
||||||
const std::string& make_model,
|
const std::string& make_model, std::string* result);
|
||||||
std::string* result);
|
|
||||||
|
|
||||||
// Extract the system ID component of a token (bytes 4-8).
|
// Extract the system ID component of a token (bytes 4-8).
|
||||||
static uint32_t GetSystemId(absl::string_view token);
|
static uint32_t GetSystemId(absl::string_view token);
|
||||||
@@ -101,21 +100,21 @@ class WvmTokenHandler {
|
|||||||
// Note that the if the input std::string lengths are correct (16 and 72 bytes),
|
// Note that the if the input std::string lengths are correct (16 and 72 bytes),
|
||||||
// the only possible cause of failure is the decrypted device key hash
|
// the only possible cause of failure is the decrypted device key hash
|
||||||
// being incorrect.
|
// being incorrect.
|
||||||
static util::Status DecryptDeviceKeyWithPreprovKey(
|
static Status DecryptDeviceKeyWithPreprovKey(
|
||||||
absl::string_view preprov_key_bytes, absl::string_view token,
|
absl::string_view preprov_key_bytes, absl::string_view token,
|
||||||
std::string* device_key_out);
|
std::string* device_key_out);
|
||||||
|
|
||||||
// Same as above, but allows extracting the 'insecure keybox' flag and keybox
|
// Same as above, but allows extracting the 'insecure keybox' flag and keybox
|
||||||
// version.
|
// version.
|
||||||
static util::Status DecryptDeviceKeyWithPreprovKey(
|
static Status DecryptDeviceKeyWithPreprovKey(
|
||||||
absl::string_view preprov_key_bytes, absl::string_view token,
|
absl::string_view preprov_key_bytes, absl::string_view token,
|
||||||
std::string* device_key_out, bool* insecure_out, uint32_t* version);
|
std::string* device_key_out, bool* insecure_out, uint32_t* version);
|
||||||
|
|
||||||
// Given a decrypted device key as returned by DecryptToken(), use it to
|
// Given a decrypted device key as returned by DecryptToken(), use it to
|
||||||
// encrypt an asset key with the given cipher.
|
// encrypt an asset key with the given cipher.
|
||||||
static util::Status EncryptAssetKey(absl::string_view device_key,
|
static Status EncryptAssetKey(absl::string_view device_key,
|
||||||
absl::string_view raw_asset_key,
|
absl::string_view raw_asset_key, Cipher cipher,
|
||||||
Cipher cipher, std::string* result);
|
std::string* result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler);
|
DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler);
|
||||||
|
|||||||
@@ -29,12 +29,6 @@ namespace widevine {
|
|||||||
using absl::BytesToHexString;
|
using absl::BytesToHexString;
|
||||||
using absl::HexStringToBytes;
|
using absl::HexStringToBytes;
|
||||||
|
|
||||||
// TODO(user): Add EXPECT_OK macro to testing/gmock.h.
|
|
||||||
// (b/37545268).
|
|
||||||
#define EXPECT_OK(expression) \
|
|
||||||
EXPECT_EQ(util::error::OK, expression.error_code())
|
|
||||||
|
|
||||||
|
|
||||||
TEST(WvmTokenHandlerTest, GetSystemId) {
|
TEST(WvmTokenHandlerTest, GetSystemId) {
|
||||||
EXPECT_EQ(kTestSystemId,
|
EXPECT_EQ(kTestSystemId,
|
||||||
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex)));
|
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex)));
|
||||||
@@ -52,7 +46,7 @@ TEST(WvmTokenHandlerTest, GetEncryptedUniqueId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
|
TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
|
||||||
util::Status status;
|
Status status;
|
||||||
std::string device_key;
|
std::string device_key;
|
||||||
|
|
||||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||||
@@ -76,7 +70,7 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
|
|||||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||||
HexStringToBytes(kTestPreprovKeyHex), token, &device_key);
|
HexStringToBytes(kTestPreprovKeyHex), token, &device_key);
|
||||||
EXPECT_FALSE(status.ok());
|
EXPECT_FALSE(status.ok());
|
||||||
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code());
|
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
|
||||||
EXPECT_TRUE(device_key.empty());
|
EXPECT_TRUE(device_key.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,18 +80,18 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
|
|||||||
TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) {
|
TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) {
|
||||||
// Not calling WvmTokenHandler::SetPreprovKeys()
|
// Not calling WvmTokenHandler::SetPreprovKeys()
|
||||||
// So preprov_keys_ would be nullptr.
|
// So preprov_keys_ would be nullptr.
|
||||||
util::Status status;
|
Status status;
|
||||||
std::string device_key;
|
std::string device_key;
|
||||||
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
|
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
|
||||||
&device_key, nullptr, nullptr);
|
&device_key, nullptr, nullptr);
|
||||||
EXPECT_FALSE(status.ok());
|
EXPECT_FALSE(status.ok());
|
||||||
EXPECT_EQ(util::error::INVALID_ARGUMENT, status.error_code());
|
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same tests as DecryptDeviceKeyWithPreprovKey(), but we use the handler's
|
// Same tests as DecryptDeviceKeyWithPreprovKey(), but we use the handler's
|
||||||
// table of preprov keys instead of providing our own.
|
// table of preprov keys instead of providing our own.
|
||||||
TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
|
TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
|
||||||
util::Status status;
|
Status status;
|
||||||
std::string device_key;
|
std::string device_key;
|
||||||
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
|
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
|
||||||
|
|
||||||
@@ -120,7 +114,7 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
|
|||||||
status =
|
status =
|
||||||
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
||||||
EXPECT_FALSE(status.ok());
|
EXPECT_FALSE(status.ok());
|
||||||
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code());
|
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
|
||||||
EXPECT_TRUE(device_key.empty());
|
EXPECT_TRUE(device_key.empty());
|
||||||
|
|
||||||
// Test with nonexistent system id. Should produce NOT_FOUND.
|
// Test with nonexistent system id. Should produce NOT_FOUND.
|
||||||
@@ -132,7 +126,7 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
|
|||||||
status =
|
status =
|
||||||
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
||||||
EXPECT_FALSE(status.ok());
|
EXPECT_FALSE(status.ok());
|
||||||
EXPECT_EQ(util::error::NOT_FOUND, status.error_code());
|
EXPECT_EQ(error::NOT_FOUND, status.error_code());
|
||||||
EXPECT_TRUE(device_key.empty());
|
EXPECT_TRUE(device_key.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +136,7 @@ TEST(WvmTokenHandlerTest, GetEncryptedAssetKey) {
|
|||||||
std::string raw_asset_key = "asset-key-000000";
|
std::string raw_asset_key = "asset-key-000000";
|
||||||
std::string asset_key;
|
std::string asset_key;
|
||||||
std::string make_model;
|
std::string make_model;
|
||||||
util::Status status = WvmTokenHandler::GetEncryptedAssetKey(
|
Status status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||||
HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key);
|
HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key);
|
||||||
EXPECT_OK(status);
|
EXPECT_OK(status);
|
||||||
EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key));
|
EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key));
|
||||||
@@ -194,7 +188,7 @@ TEST(WvmTokenHandlerTest, FilterOnMakeModel) {
|
|||||||
std::string raw_asset_key = "asset-key-000000";
|
std::string raw_asset_key = "asset-key-000000";
|
||||||
std::string asset_key;
|
std::string asset_key;
|
||||||
// Check 3DES encryption of asset keys
|
// Check 3DES encryption of asset keys
|
||||||
util::Status status = WvmTokenHandler::EncryptAssetKey(
|
Status status = WvmTokenHandler::EncryptAssetKey(
|
||||||
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
|
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
|
||||||
WvmTokenHandler::DES3, &asset_key);
|
WvmTokenHandler::DES3, &asset_key);
|
||||||
EXPECT_OK(status);
|
EXPECT_OK(status);
|
||||||
@@ -220,7 +214,7 @@ TEST(WvmTokenHandlerTest, FilterOnMakeModel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(WvmTokenHandlerTest, AncientKeybox) {
|
TEST(WvmTokenHandlerTest, AncientKeybox) {
|
||||||
util::Status status;
|
Status status;
|
||||||
std::string device_key;
|
std::string device_key;
|
||||||
|
|
||||||
std::string v1_token(
|
std::string v1_token(
|
||||||
@@ -228,7 +222,7 @@ TEST(WvmTokenHandlerTest, AncientKeybox) {
|
|||||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||||
HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex),
|
HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex),
|
||||||
&device_key);
|
&device_key);
|
||||||
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code());
|
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
|
||||||
EXPECT_TRUE(device_key.empty());
|
EXPECT_TRUE(device_key.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,22 +67,20 @@ X509Cert::~X509Cert() {
|
|||||||
|
|
||||||
X509Cert::X509Cert(X509* openssl_cert) : openssl_cert_(openssl_cert) {}
|
X509Cert::X509Cert(X509* openssl_cert) : openssl_cert_(openssl_cert) {}
|
||||||
|
|
||||||
util::Status X509Cert::LoadPem(const std::string& pem_cert) {
|
Status X509Cert::LoadPem(const std::string& pem_cert) {
|
||||||
if (pem_cert.empty()) {
|
if (pem_cert.empty()) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM certificate");
|
return Status(error::INVALID_ARGUMENT, "Empty PEM certificate");
|
||||||
}
|
}
|
||||||
BIO* bio(NULL);
|
BIO* bio(NULL);
|
||||||
X509* new_cert(NULL);
|
X509* new_cert(NULL);
|
||||||
bio = BIO_new_mem_buf(const_cast<char*>(pem_cert.data()), pem_cert.size());
|
bio = BIO_new_mem_buf(const_cast<char*>(pem_cert.data()), pem_cert.size());
|
||||||
if (bio == NULL) {
|
if (bio == NULL) {
|
||||||
return util::Status(util::error::INTERNAL, "BIO allocation failed");
|
return Status(error::INTERNAL, "BIO allocation failed");
|
||||||
}
|
}
|
||||||
util::Status status;
|
Status status;
|
||||||
new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
|
new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
|
||||||
if (new_cert == NULL) {
|
if (new_cert == NULL) {
|
||||||
status = util::Status(util::Status::canonical_space(),
|
status = Status(error::INVALID_ARGUMENT, "PEM certificate load failed");
|
||||||
util::error::INVALID_ARGUMENT,
|
|
||||||
"PEM certificate load failed");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
if (openssl_cert_ != NULL) {
|
if (openssl_cert_ != NULL) {
|
||||||
@@ -97,22 +95,21 @@ cleanup:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status X509Cert::LoadDer(const std::string& der_cert) {
|
Status X509Cert::LoadDer(const std::string& der_cert) {
|
||||||
if (der_cert.empty()) {
|
if (der_cert.empty()) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty DER certificate");
|
return Status(error::INVALID_ARGUMENT, "Empty DER certificate");
|
||||||
}
|
}
|
||||||
const unsigned char* cert_data =
|
const unsigned char* cert_data =
|
||||||
reinterpret_cast<const unsigned char*>(der_cert.data());
|
reinterpret_cast<const unsigned char*>(der_cert.data());
|
||||||
X509* new_cert = d2i_X509(NULL, &cert_data, der_cert.size());
|
X509* new_cert = d2i_X509(NULL, &cert_data, der_cert.size());
|
||||||
if (new_cert == NULL) {
|
if (new_cert == NULL) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT, "DER certificate load failed");
|
||||||
"DER certificate load failed");
|
|
||||||
}
|
}
|
||||||
if (openssl_cert_ != NULL) {
|
if (openssl_cert_ != NULL) {
|
||||||
X509_free(openssl_cert_);
|
X509_free(openssl_cert_);
|
||||||
}
|
}
|
||||||
openssl_cert_ = new_cert;
|
openssl_cert_ = new_cert;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string X509Cert::GetPem() const {
|
std::string X509Cert::GetPem() const {
|
||||||
@@ -222,38 +219,36 @@ bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status X509Cert::Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
|
Status X509Cert::Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
|
||||||
int64_t* epoch_seconds) const {
|
int64_t* epoch_seconds) const {
|
||||||
if (asn1_time == nullptr) {
|
if (asn1_time == nullptr) {
|
||||||
// This code is exported to shared source. The exported code does not yet
|
// This code is exported to shared source. The exported code does not yet
|
||||||
// support MakeStatus.
|
// support MakeStatus.
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT, "asn1_time cannot be null.");
|
||||||
"asn1_time cannot be null.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (epoch_seconds == nullptr) {
|
if (epoch_seconds == nullptr) {
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT, "epoch_seconds cannot be null.");
|
||||||
"epoch_seconds cannot be null.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedAsn1Time epoch_time(ASN1_TIME_new());
|
ScopedAsn1Time epoch_time(ASN1_TIME_new());
|
||||||
if (!ASN1_TIME_set(epoch_time.get(), 0)) {
|
if (!ASN1_TIME_set(epoch_time.get(), 0)) {
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
return util::Status(util::error::INTERNAL, "Failed to set epoch time.");
|
return Status(error::INTERNAL, "Failed to set epoch time.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int day = 0;
|
int day = 0;
|
||||||
int seconds = 0;
|
int seconds = 0;
|
||||||
if (!ASN1_TIME_diff(&day, &seconds, epoch_time.get(), asn1_time)) {
|
if (!ASN1_TIME_diff(&day, &seconds, epoch_time.get(), asn1_time)) {
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
"Failed to convert asn1 time to epoch time.");
|
"Failed to convert asn1 time to epoch time.");
|
||||||
}
|
}
|
||||||
|
|
||||||
*epoch_seconds = 24L * 3600L * day + seconds;
|
*epoch_seconds = 24L * 3600L * day + seconds;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
X509CertChain::~X509CertChain() { Reset(); }
|
X509CertChain::~X509CertChain() { Reset(); }
|
||||||
@@ -265,7 +260,7 @@ void X509CertChain::Reset() {
|
|||||||
cert_chain_.clear();
|
cert_chain_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
||||||
static const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----";
|
static const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----";
|
||||||
static const char kEndCertificate[] = "-----END CERTIFICATE-----";
|
static const char kEndCertificate[] = "-----END CERTIFICATE-----";
|
||||||
|
|
||||||
@@ -277,7 +272,7 @@ util::Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
|||||||
if (end_pos != std::string::npos) {
|
if (end_pos != std::string::npos) {
|
||||||
end_pos += sizeof(kEndCertificate) - 1;
|
end_pos += sizeof(kEndCertificate) - 1;
|
||||||
std::unique_ptr<X509Cert> new_cert(new X509Cert);
|
std::unique_ptr<X509Cert> new_cert(new X509Cert);
|
||||||
util::Status status = new_cert->LoadPem(
|
Status status = new_cert->LoadPem(
|
||||||
pem_cert_chain.substr(begin_pos, end_pos - begin_pos));
|
pem_cert_chain.substr(begin_pos, end_pos - begin_pos));
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
@@ -286,17 +281,17 @@ util::Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
|||||||
begin_pos = pem_cert_chain.find(kBeginCertificate, end_pos);
|
begin_pos = pem_cert_chain.find(kBeginCertificate, end_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) {
|
Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) {
|
||||||
ScopedX509Stack cert_stack(sk_X509_new_null());
|
ScopedX509Stack cert_stack(sk_X509_new_null());
|
||||||
CBS cbs;
|
CBS cbs;
|
||||||
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pk7_cert_chain.data()),
|
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pk7_cert_chain.data()),
|
||||||
pk7_cert_chain.size());
|
pk7_cert_chain.size());
|
||||||
if (!PKCS7_get_certificates(cert_stack.get(), &cbs)) {
|
if (!PKCS7_get_certificates(cert_stack.get(), &cbs)) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT,
|
||||||
"Unable to load PKCS#7 certificate chain");
|
"Unable to load PKCS#7 certificate chain");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sk_X509_num(cert_stack.get()) > 0) {
|
while (sk_X509_num(cert_stack.get()) > 0) {
|
||||||
@@ -304,7 +299,7 @@ util::Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) {
|
|||||||
new X509Cert(sk_X509_pop(cert_stack.get())));
|
new X509Cert(sk_X509_pop(cert_stack.get())));
|
||||||
}
|
}
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string X509CertChain::GetPkcs7() {
|
std::string X509CertChain::GetPkcs7() {
|
||||||
@@ -357,44 +352,42 @@ X509CA::~X509CA() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status X509CA::InitializeStore() {
|
Status X509CA::InitializeStore() {
|
||||||
absl::WriterMutexLock lock(&openssl_store_mutex_);
|
absl::WriterMutexLock lock(&openssl_store_mutex_);
|
||||||
if (openssl_store_ == NULL) {
|
if (openssl_store_ == NULL) {
|
||||||
if (ca_cert_ == NULL) {
|
if (ca_cert_ == NULL) {
|
||||||
return util::Status(util::error::INTERNAL, "CA X.509Cert is NULL");
|
return Status(error::INTERNAL, "CA X.509Cert is NULL");
|
||||||
}
|
}
|
||||||
openssl_store_ = X509_STORE_new();
|
openssl_store_ = X509_STORE_new();
|
||||||
if (openssl_store_ == NULL) {
|
if (openssl_store_ == NULL) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "Failed to allocate X.509 store");
|
||||||
"Failed to allocate X.509 store");
|
|
||||||
}
|
}
|
||||||
if (X509_STORE_add_cert(openssl_store_,
|
if (X509_STORE_add_cert(openssl_store_,
|
||||||
const_cast<X509*>(ca_cert_->openssl_cert())) == 0) {
|
const_cast<X509*>(ca_cert_->openssl_cert())) == 0) {
|
||||||
X509_STORE_free(openssl_store_);
|
X509_STORE_free(openssl_store_);
|
||||||
openssl_store_ = NULL;
|
openssl_store_ = NULL;
|
||||||
|
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
"Failed to add X.509 CA certificate to store");
|
"Failed to add X.509 CA certificate to store");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status X509CA::VerifyCert(const X509Cert& cert) {
|
Status X509CA::VerifyCert(const X509Cert& cert) {
|
||||||
return OpenSslX509Verify(cert.openssl_cert(), nullptr);
|
return OpenSslX509Verify(cert.openssl_cert(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) {
|
Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) {
|
||||||
if (cert_chain.GetNumCerts() < 1) {
|
if (cert_chain.GetNumCerts() < 1) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT,
|
||||||
"Cannot verify empty certificate chain");
|
"Cannot verify empty certificate chain");
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedX509StackOnly intermediates(sk_X509_new_null());
|
ScopedX509StackOnly intermediates(sk_X509_new_null());
|
||||||
if (!intermediates) {
|
if (!intermediates) {
|
||||||
return util::Status(
|
return Status(error::INTERNAL,
|
||||||
util::Status::canonical_space(), util::error::INTERNAL,
|
"Failed to allocate X.509 intermediate certificate stack");
|
||||||
"Failed to allocate X.509 intermediate certificate stack");
|
|
||||||
}
|
}
|
||||||
const X509Cert* leaf_cert(nullptr);
|
const X509Cert* leaf_cert(nullptr);
|
||||||
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
|
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
|
||||||
@@ -406,23 +399,21 @@ util::Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!leaf_cert) {
|
if (!leaf_cert) {
|
||||||
return util::Status(util::Status::canonical_space(),
|
return Status(error::INVALID_ARGUMENT,
|
||||||
util::error::INVALID_ARGUMENT,
|
"X.509 certificate chain without leaf certificate.");
|
||||||
"X.509 certificate chain without leaf certificate.");
|
|
||||||
}
|
}
|
||||||
return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get());
|
return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status X509CA::VerifyCertWithChain(const X509Cert& cert,
|
Status X509CA::VerifyCertWithChain(const X509Cert& cert,
|
||||||
const X509CertChain& cert_chain) {
|
const X509CertChain& cert_chain) {
|
||||||
ScopedX509StackOnly intermediates(sk_X509_new_null());
|
ScopedX509StackOnly intermediates(sk_X509_new_null());
|
||||||
if (!intermediates) {
|
if (!intermediates) {
|
||||||
// MakeStatus is now preferred. But we don't support it in the exported
|
// MakeStatus is now preferred. But we don't support it in the exported
|
||||||
// version, yet. So, ignore lint here.
|
// version, yet. So, ignore lint here.
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
return util::Status(
|
return Status(error::INTERNAL,
|
||||||
util::Status::canonical_space(), util::error::INTERNAL,
|
"Failed to allocate X.509 intermediate certificate stack");
|
||||||
"Failed to allocate X.509 intermediate certificate stack");
|
|
||||||
}
|
}
|
||||||
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
|
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
|
||||||
sk_X509_push(intermediates.get(),
|
sk_X509_push(intermediates.get(),
|
||||||
@@ -432,14 +423,14 @@ util::Status X509CA::VerifyCertWithChain(const X509Cert& cert,
|
|||||||
return OpenSslX509Verify(cert.openssl_cert(), intermediates.get());
|
return OpenSslX509Verify(cert.openssl_cert(), intermediates.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status X509CA::OpenSslX509Verify(const X509* cert,
|
Status X509CA::OpenSslX509Verify(const X509* cert,
|
||||||
STACK_OF(X509) * intermediates) {
|
STACK_OF(X509) * intermediates) {
|
||||||
DCHECK(cert);
|
DCHECK(cert);
|
||||||
|
|
||||||
absl::ReaderMutexLock lock(&openssl_store_mutex_);
|
absl::ReaderMutexLock lock(&openssl_store_mutex_);
|
||||||
if (openssl_store_ == NULL) {
|
if (openssl_store_ == NULL) {
|
||||||
openssl_store_mutex_.ReaderUnlock();
|
openssl_store_mutex_.ReaderUnlock();
|
||||||
util::Status status = InitializeStore();
|
Status status = InitializeStore();
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -447,23 +438,21 @@ util::Status X509CA::OpenSslX509Verify(const X509* cert,
|
|||||||
}
|
}
|
||||||
ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new());
|
ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new());
|
||||||
if (!store_ctx) {
|
if (!store_ctx) {
|
||||||
return util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
return Status(error::INTERNAL, "Failed to allocate X.509 store context");
|
||||||
"Failed to allocate X.509 store context");
|
|
||||||
}
|
}
|
||||||
if (X509_STORE_CTX_init(store_ctx.get(), openssl_store_,
|
if (X509_STORE_CTX_init(store_ctx.get(), openssl_store_,
|
||||||
const_cast<X509*>(cert), intermediates) == 0) {
|
const_cast<X509*>(cert), intermediates) == 0) {
|
||||||
return util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
return Status(error::INTERNAL, "Failed to initialize X.509 store context");
|
||||||
"Failed to initialize X.509 store context");
|
|
||||||
}
|
}
|
||||||
int x509_status = X509_verify_cert(store_ctx.get());
|
int x509_status = X509_verify_cert(store_ctx.get());
|
||||||
if (x509_status != 1) {
|
if (x509_status != 1) {
|
||||||
return util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
std::string("X.509 certificate chain validation failed: ") +
|
std::string("X.509 certificate chain validation failed: ") +
|
||||||
X509_verify_cert_error_string(
|
X509_verify_cert_error_string(
|
||||||
X509_STORE_CTX_get_error(store_ctx.get())));
|
X509_STORE_CTX_get_error(store_ctx.get())));
|
||||||
}
|
}
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -24,13 +24,13 @@
|
|||||||
#include "openssl/pem.h"
|
#include "openssl/pem.h"
|
||||||
#include "openssl/x509.h"
|
#include "openssl/x509.h"
|
||||||
#include "openssl/x509v3.h"
|
#include "openssl/x509v3.h"
|
||||||
#include "common/status.h"
|
|
||||||
#include "common/openssl_util.h"
|
#include "common/openssl_util.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
// NOTE: All util::Status codes are in the canonical error space.
|
// NOTE: All Status codes are in the canonical error space.
|
||||||
|
|
||||||
// Class which holds a single X.509 certificates.
|
// Class which holds a single X.509 certificates.
|
||||||
class X509Cert {
|
class X509Cert {
|
||||||
@@ -43,11 +43,11 @@ class X509Cert {
|
|||||||
|
|
||||||
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
||||||
// a PEM-encoded certificate.
|
// a PEM-encoded certificate.
|
||||||
util::Status LoadPem(const std::string& pem_cert);
|
Status LoadPem(const std::string& pem_cert);
|
||||||
|
|
||||||
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
||||||
// a DER-encoded certificate.
|
// a DER-encoded certificate.
|
||||||
util::Status LoadDer(const std::string& der_cert);
|
Status LoadDer(const std::string& der_cert);
|
||||||
|
|
||||||
// Return a std::string containing the PEM-encoded certificate.
|
// Return a std::string containing the PEM-encoded certificate.
|
||||||
std::string GetPem() const;
|
std::string GetPem() const;
|
||||||
@@ -91,8 +91,8 @@ class X509Cert {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
explicit X509Cert(X509* openssl_cert);
|
explicit X509Cert(X509* openssl_cert);
|
||||||
util::Status Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
|
Status Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
|
||||||
int64_t* epoch_seconds) const;
|
int64_t* epoch_seconds) const;
|
||||||
|
|
||||||
X509* openssl_cert_;
|
X509* openssl_cert_;
|
||||||
std::string subject_name_;
|
std::string subject_name_;
|
||||||
@@ -112,12 +112,12 @@ class X509CertChain {
|
|||||||
// |pem_cert_chain|, which is the concatenation of a number of PEM X.509
|
// |pem_cert_chain|, which is the concatenation of a number of PEM X.509
|
||||||
// certificates, beginning with the leaf certificate, and ending with the
|
// certificates, beginning with the leaf certificate, and ending with the
|
||||||
// certificate signed by the root CA.
|
// certificate signed by the root CA.
|
||||||
util::Status LoadPem(const std::string& pem_cert_chain);
|
Status LoadPem(const std::string& pem_cert_chain);
|
||||||
|
|
||||||
// Loads a chain of DER-encoded PKCS#7 certificates. Takes a single parameter,
|
// Loads a chain of DER-encoded PKCS#7 certificates. Takes a single parameter,
|
||||||
// |pk7_cert_chain|, which is a DER-encoded PKCS#7 X.509 certificate
|
// |pk7_cert_chain|, which is a DER-encoded PKCS#7 X.509 certificate
|
||||||
// container.
|
// container.
|
||||||
util::Status LoadPkcs7(const std::string& pk7_cert_chain);
|
Status LoadPkcs7(const std::string& pk7_cert_chain);
|
||||||
|
|
||||||
// Writes the |cert_chain_| to a DER-encoded PKCS#7 X.509 cryptographic
|
// Writes the |cert_chain_| to a DER-encoded PKCS#7 X.509 cryptographic
|
||||||
// message. The final message does not include signed data.
|
// message. The final message does not include signed data.
|
||||||
@@ -148,21 +148,21 @@ class X509CA {
|
|||||||
|
|
||||||
// Does X.509 PKI validation of |cert| against the root CA certificate
|
// Does X.509 PKI validation of |cert| against the root CA certificate
|
||||||
// used when constructing X509CA. This method is thread-safe.
|
// used when constructing X509CA. This method is thread-safe.
|
||||||
util::Status VerifyCert(const X509Cert& cert);
|
Status VerifyCert(const X509Cert& cert);
|
||||||
|
|
||||||
// Does X.509 PKI validation of |cert_chain| against the root CA certificate
|
// Does X.509 PKI validation of |cert_chain| against the root CA certificate
|
||||||
// used when constructing X509CA. This method is thread-safe.
|
// used when constructing X509CA. This method is thread-safe.
|
||||||
util::Status VerifyCertChain(const X509CertChain& cert_chain);
|
Status VerifyCertChain(const X509CertChain& cert_chain);
|
||||||
|
|
||||||
// Does X.509 PKI validation of |cert| using the |cert_chain|
|
// Does X.509 PKI validation of |cert| using the |cert_chain|
|
||||||
// certificates. This method allows |cert| to be an ICA. This method is
|
// certificates. This method allows |cert| to be an ICA. This method is
|
||||||
// thread-safe.
|
// thread-safe.
|
||||||
util::Status VerifyCertWithChain(const X509Cert& cert,
|
Status VerifyCertWithChain(const X509Cert& cert,
|
||||||
const X509CertChain& cert_chain);
|
const X509CertChain& cert_chain);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
util::Status InitializeStore();
|
Status InitializeStore();
|
||||||
util::Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * stack);
|
Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * intermediates);
|
||||||
|
|
||||||
std::unique_ptr<X509Cert> ca_cert_;
|
std::unique_ptr<X509Cert> ca_cert_;
|
||||||
absl::Mutex openssl_store_mutex_;
|
absl::Mutex openssl_store_mutex_;
|
||||||
|
|||||||
@@ -355,23 +355,23 @@ const bool kTestDevCodeSigningCertFlagValue = true;
|
|||||||
|
|
||||||
TEST(X509CertTest, LoadCert) {
|
TEST(X509CertTest, LoadCert) {
|
||||||
X509Cert test_cert;
|
X509Cert test_cert;
|
||||||
EXPECT_EQ(util::OkStatus(),
|
EXPECT_EQ(OkStatus(),
|
||||||
test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||||
EXPECT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
EXPECT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||||
// TODO(user): Add more specific status checks to failure tests.
|
// TODO(user): Add more specific status checks to failure tests.
|
||||||
EXPECT_NE(util::OkStatus(), test_cert.LoadDer("bad cert"));
|
EXPECT_NE(OkStatus(), test_cert.LoadDer("bad cert"));
|
||||||
EXPECT_NE(util::OkStatus(), test_cert.LoadPem("bad cert"));
|
EXPECT_NE(OkStatus(), test_cert.LoadPem("bad cert"));
|
||||||
EXPECT_NE(util::OkStatus(), test_cert.LoadDer(""));
|
EXPECT_NE(OkStatus(), test_cert.LoadDer(""));
|
||||||
EXPECT_NE(util::OkStatus(), test_cert.LoadPem(""));
|
EXPECT_NE(OkStatus(), test_cert.LoadPem(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(X509CertTest, VerifySignature) {
|
TEST(X509CertTest, VerifySignature) {
|
||||||
X509Cert test_cert;
|
X509Cert test_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||||
std::string message(absl::HexStringToBytes(kTestMessage));
|
std::string message(absl::HexStringToBytes(kTestMessage));
|
||||||
std::string signature;
|
std::string signature;
|
||||||
ASSERT_EQ(util::OkStatus(), GenerateRsaSignatureSha256Pkcs1(
|
ASSERT_EQ(OkStatus(), GenerateRsaSignatureSha256Pkcs1(kTestCertPrivateKey,
|
||||||
kTestCertPrivateKey, message, &signature));
|
message, &signature));
|
||||||
std::unique_ptr<RsaPublicKey> pub_key(test_cert.GetRsaPublicKey());
|
std::unique_ptr<RsaPublicKey> pub_key(test_cert.GetRsaPublicKey());
|
||||||
ASSERT_TRUE(pub_key);
|
ASSERT_TRUE(pub_key);
|
||||||
EXPECT_TRUE(pub_key->VerifySignatureSha256Pkcs7(message, signature));
|
EXPECT_TRUE(pub_key->VerifySignatureSha256Pkcs7(message, signature));
|
||||||
@@ -384,7 +384,7 @@ TEST(X509CertTest, VerifySignature) {
|
|||||||
|
|
||||||
TEST(X509CertTest, GetSubjectNameField) {
|
TEST(X509CertTest, GetSubjectNameField) {
|
||||||
X509Cert test_cert;
|
X509Cert test_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||||
EXPECT_EQ(kTestPemCertSubjectField_C, test_cert.GetSubjectNameField("C"));
|
EXPECT_EQ(kTestPemCertSubjectField_C, test_cert.GetSubjectNameField("C"));
|
||||||
EXPECT_EQ(kTestPemCertSubjectField_CN, test_cert.GetSubjectNameField("CN"));
|
EXPECT_EQ(kTestPemCertSubjectField_CN, test_cert.GetSubjectNameField("CN"));
|
||||||
EXPECT_EQ("", test_cert.GetSubjectNameField("invalid_field"));
|
EXPECT_EQ("", test_cert.GetSubjectNameField("invalid_field"));
|
||||||
@@ -392,13 +392,13 @@ TEST(X509CertTest, GetSubjectNameField) {
|
|||||||
|
|
||||||
TEST(X509CertTest, GetSerialNumber) {
|
TEST(X509CertTest, GetSerialNumber) {
|
||||||
X509Cert test_cert;
|
X509Cert test_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||||
EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber());
|
EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(X509CertTest, GetNotBeforeSeconds) {
|
TEST(X509CertTest, GetNotBeforeSeconds) {
|
||||||
X509Cert test_cert;
|
X509Cert test_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||||
int64_t not_before_seconds = 0;
|
int64_t not_before_seconds = 0;
|
||||||
ASSERT_TRUE(test_cert.GetNotBeforeSeconds(¬_before_seconds));
|
ASSERT_TRUE(test_cert.GetNotBeforeSeconds(¬_before_seconds));
|
||||||
EXPECT_EQ(kTestPemCertNotBeforeSeconds, not_before_seconds);
|
EXPECT_EQ(kTestPemCertNotBeforeSeconds, not_before_seconds);
|
||||||
@@ -406,7 +406,7 @@ TEST(X509CertTest, GetNotBeforeSeconds) {
|
|||||||
|
|
||||||
TEST(X509CertTest, GetNotAfterSeconds) {
|
TEST(X509CertTest, GetNotAfterSeconds) {
|
||||||
X509Cert test_cert;
|
X509Cert test_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||||
int64_t not_after_seconds = 0;
|
int64_t not_after_seconds = 0;
|
||||||
ASSERT_TRUE(test_cert.GetNotAfterSeconds(¬_after_seconds));
|
ASSERT_TRUE(test_cert.GetNotAfterSeconds(¬_after_seconds));
|
||||||
EXPECT_EQ(kTestPemCertNotAfterSeconds, not_after_seconds);
|
EXPECT_EQ(kTestPemCertNotAfterSeconds, not_after_seconds);
|
||||||
@@ -414,7 +414,7 @@ TEST(X509CertTest, GetNotAfterSeconds) {
|
|||||||
|
|
||||||
TEST(X509CertTest, CertChain) {
|
TEST(X509CertTest, CertChain) {
|
||||||
X509CertChain test_chain;
|
X509CertChain test_chain;
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||||
ASSERT_EQ(2, test_chain.GetNumCerts());
|
ASSERT_EQ(2, test_chain.GetNumCerts());
|
||||||
EXPECT_FALSE(test_chain.GetCert(0) == NULL);
|
EXPECT_FALSE(test_chain.GetCert(0) == NULL);
|
||||||
EXPECT_FALSE(test_chain.GetCert(1) == NULL);
|
EXPECT_FALSE(test_chain.GetCert(1) == NULL);
|
||||||
@@ -423,7 +423,7 @@ TEST(X509CertTest, CertChain) {
|
|||||||
|
|
||||||
TEST(X509CertTest, IsCaCertificate) {
|
TEST(X509CertTest, IsCaCertificate) {
|
||||||
X509CertChain test_chain;
|
X509CertChain test_chain;
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||||
ASSERT_EQ(2, test_chain.GetNumCerts());
|
ASSERT_EQ(2, test_chain.GetNumCerts());
|
||||||
EXPECT_FALSE(test_chain.GetCert(0)->IsCaCertificate());
|
EXPECT_FALSE(test_chain.GetCert(0)->IsCaCertificate());
|
||||||
EXPECT_TRUE(test_chain.GetCert(1)->IsCaCertificate());
|
EXPECT_TRUE(test_chain.GetCert(1)->IsCaCertificate());
|
||||||
@@ -431,84 +431,84 @@ TEST(X509CertTest, IsCaCertificate) {
|
|||||||
|
|
||||||
TEST(X509CertTest, ChainVerificationPem) {
|
TEST(X509CertTest, ChainVerificationPem) {
|
||||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||||
ASSERT_EQ(util::OkStatus(),
|
ASSERT_EQ(OkStatus(),
|
||||||
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||||
X509CA ca(ca_cert.release());
|
X509CA ca(ca_cert.release());
|
||||||
X509CertChain test_chain;
|
X509CertChain test_chain;
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert));
|
||||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||||
EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(X509CertTest, ChainVerificationPkcs7) {
|
TEST(X509CertTest, ChainVerificationPkcs7) {
|
||||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||||
ASSERT_EQ(util::OkStatus(),
|
ASSERT_EQ(OkStatus(),
|
||||||
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||||
X509CA ca(ca_cert.release());
|
X509CA ca(ca_cert.release());
|
||||||
X509CertChain test_chain;
|
X509CertChain test_chain;
|
||||||
ASSERT_EQ(util::OkStatus(),
|
ASSERT_EQ(OkStatus(),
|
||||||
test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain)));
|
test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain)));
|
||||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert));
|
||||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||||
EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(X509CertTest, VerifyCertWithChainIca) {
|
TEST(X509CertTest, VerifyCertWithChainIca) {
|
||||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||||
ASSERT_EQ(util::OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||||
X509CA ca(ca_cert.release());
|
X509CA ca(ca_cert.release());
|
||||||
|
|
||||||
// Verify the ICA with the root succeeds.
|
// Verify the ICA with the root succeeds.
|
||||||
X509CertChain test_chain;
|
X509CertChain test_chain;
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
||||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||||
X509Cert ica_cert;
|
X509Cert ica_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), ica_cert.LoadPem(kTestPemIca));
|
ASSERT_EQ(OkStatus(), ica_cert.LoadPem(kTestPemIca));
|
||||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertWithChain(ica_cert, test_chain));
|
EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(ica_cert, test_chain));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(X509CertTest, VerifyCertWithChainLeaf) {
|
TEST(X509CertTest, VerifyCertWithChainLeaf) {
|
||||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||||
ASSERT_EQ(util::OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||||
X509CA ca(ca_cert.release());
|
X509CA ca(ca_cert.release());
|
||||||
|
|
||||||
// Verify the leaf with the root and ICA succeeds.
|
// Verify the leaf with the root and ICA succeeds.
|
||||||
X509CertChain test_chain;
|
X509CertChain test_chain;
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemIca));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemIca));
|
||||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||||
X509Cert leaf_cert;
|
X509Cert leaf_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), leaf_cert.LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert));
|
||||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
|
EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(X509CertTest, VerifyCertWithChainLeafMissincIca) {
|
TEST(X509CertTest, VerifyCertWithChainLeafMissincIca) {
|
||||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||||
ASSERT_EQ(util::OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||||
X509CA ca(ca_cert.release());
|
X509CA ca(ca_cert.release());
|
||||||
|
|
||||||
// Verify the leaf with only the root fails (ICA missing).
|
// Verify the leaf with only the root fails (ICA missing).
|
||||||
X509CertChain test_chain;
|
X509CertChain test_chain;
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
||||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||||
X509Cert leaf_cert;
|
X509Cert leaf_cert;
|
||||||
ASSERT_EQ(util::OkStatus(), leaf_cert.LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert));
|
||||||
EXPECT_NE(util::OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
|
EXPECT_NE(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(X509CertTest, GetPkcs7) {
|
TEST(X509CertTest, GetPkcs7) {
|
||||||
X509CertChain test_chain;
|
X509CertChain test_chain;
|
||||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||||
std::string pkcs7_certificate = test_chain.GetPkcs7();
|
std::string pkcs7_certificate = test_chain.GetPkcs7();
|
||||||
ASSERT_NE(pkcs7_certificate.size(), 0);
|
ASSERT_NE(pkcs7_certificate.size(), 0);
|
||||||
X509CertChain new_test_chain;
|
X509CertChain new_test_chain;
|
||||||
ASSERT_EQ(util::OkStatus(), new_test_chain.LoadPkcs7(pkcs7_certificate));
|
ASSERT_EQ(OkStatus(), new_test_chain.LoadPkcs7(pkcs7_certificate));
|
||||||
ASSERT_EQ(test_chain.GetNumCerts(), new_test_chain.GetNumCerts());
|
ASSERT_EQ(test_chain.GetNumCerts(), new_test_chain.GetNumCerts());
|
||||||
for (int i = 0; i < test_chain.GetNumCerts(); i++) {
|
for (int i = 0; i < test_chain.GetNumCerts(); i++) {
|
||||||
ASSERT_EQ(test_chain.GetCert(i)->GetPem(),
|
ASSERT_EQ(test_chain.GetCert(i)->GetPem(),
|
||||||
@@ -518,12 +518,12 @@ TEST(X509CertTest, GetPkcs7) {
|
|||||||
|
|
||||||
TEST(X509CertTest, BooleanExtension) {
|
TEST(X509CertTest, BooleanExtension) {
|
||||||
std::unique_ptr<X509Cert> cert1(new X509Cert);
|
std::unique_ptr<X509Cert> cert1(new X509Cert);
|
||||||
ASSERT_EQ(util::OkStatus(), cert1->LoadPem(kTestPemCert));
|
ASSERT_EQ(OkStatus(), cert1->LoadPem(kTestPemCert));
|
||||||
bool extension_value;
|
bool extension_value;
|
||||||
EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
|
EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
|
||||||
|
|
||||||
std::unique_ptr<X509Cert> cert2(new X509Cert);
|
std::unique_ptr<X509Cert> cert2(new X509Cert);
|
||||||
ASSERT_EQ(util::OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert));
|
ASSERT_EQ(OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert));
|
||||||
ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
|
ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
|
||||||
EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value);
|
EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value);
|
||||||
}
|
}
|
||||||
|
|||||||
584
curl.BUILD
584
curl.BUILD
@@ -1,584 +0,0 @@
|
|||||||
################################################################################
|
|
||||||
# Copyright 2018 Google LLC.
|
|
||||||
#
|
|
||||||
# This software is licensed under the terms defined in the Widevine Master
|
|
||||||
# License Agreement. For a copy of this agreement, please contact
|
|
||||||
# widevine-licensing@google.com.
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
# Build file for curl.
|
|
||||||
|
|
||||||
exports_files(["COPYING"])
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "curl",
|
|
||||||
srcs = [
|
|
||||||
"include/curl_config.h", # generated by genrule below
|
|
||||||
"lib/amigaos.c",
|
|
||||||
"lib/amigaos.h",
|
|
||||||
"lib/arpa_telnet.h",
|
|
||||||
"lib/asyn-ares.c",
|
|
||||||
"lib/asyn-thread.c",
|
|
||||||
"lib/asyn.h",
|
|
||||||
"lib/base64.c",
|
|
||||||
"lib/config-amigaos.h",
|
|
||||||
"lib/config-dos.h",
|
|
||||||
"lib/config-mac.h",
|
|
||||||
"lib/config-os400.h",
|
|
||||||
"lib/config-riscos.h",
|
|
||||||
"lib/config-symbian.h",
|
|
||||||
"lib/config-tpf.h",
|
|
||||||
"lib/config-vxworks.h",
|
|
||||||
"lib/config-win32.h",
|
|
||||||
"lib/config-win32ce.h",
|
|
||||||
"lib/conncache.c",
|
|
||||||
"lib/conncache.h",
|
|
||||||
"lib/connect.c",
|
|
||||||
"lib/connect.h",
|
|
||||||
"lib/content_encoding.c",
|
|
||||||
"lib/content_encoding.h",
|
|
||||||
"lib/cookie.c",
|
|
||||||
"lib/cookie.h",
|
|
||||||
"lib/curl_addrinfo.c",
|
|
||||||
"lib/curl_addrinfo.h",
|
|
||||||
"lib/curl_base64.h",
|
|
||||||
"lib/curl_ctype.c",
|
|
||||||
"lib/curl_ctype.h",
|
|
||||||
"lib/curl_des.c",
|
|
||||||
"lib/curl_des.h",
|
|
||||||
"lib/curl_endian.c",
|
|
||||||
"lib/curl_endian.h",
|
|
||||||
"lib/curl_fnmatch.c",
|
|
||||||
"lib/curl_fnmatch.h",
|
|
||||||
"lib/curl_gethostname.c",
|
|
||||||
"lib/curl_gethostname.h",
|
|
||||||
"lib/curl_gssapi.c",
|
|
||||||
"lib/curl_gssapi.h",
|
|
||||||
"lib/curl_hmac.h",
|
|
||||||
"lib/curl_ldap.h",
|
|
||||||
"lib/curl_md4.h",
|
|
||||||
"lib/curl_md5.h",
|
|
||||||
"lib/curl_memory.h",
|
|
||||||
"lib/curl_memrchr.c",
|
|
||||||
"lib/curl_memrchr.h",
|
|
||||||
"lib/curl_multibyte.c",
|
|
||||||
"lib/curl_multibyte.h",
|
|
||||||
"lib/curl_ntlm_core.c",
|
|
||||||
"lib/curl_ntlm_core.h",
|
|
||||||
"lib/curl_ntlm_wb.c",
|
|
||||||
"lib/curl_ntlm_wb.h",
|
|
||||||
"lib/curl_path.c",
|
|
||||||
"lib/curl_path.h",
|
|
||||||
"lib/curl_printf.h",
|
|
||||||
"lib/curl_range.c",
|
|
||||||
"lib/curl_range.h",
|
|
||||||
"lib/curl_rtmp.c",
|
|
||||||
"lib/curl_rtmp.h",
|
|
||||||
"lib/curl_sasl.c",
|
|
||||||
"lib/curl_sasl.h",
|
|
||||||
"lib/curl_sec.h",
|
|
||||||
"lib/curl_setup.h",
|
|
||||||
"lib/curl_setup_once.h",
|
|
||||||
"lib/curl_sha256.h",
|
|
||||||
"lib/curl_sspi.c",
|
|
||||||
"lib/curl_sspi.h",
|
|
||||||
"lib/curl_threads.c",
|
|
||||||
"lib/curl_threads.h",
|
|
||||||
"lib/curlx.h",
|
|
||||||
"lib/dict.c",
|
|
||||||
"lib/dict.h",
|
|
||||||
"lib/doh.c",
|
|
||||||
"lib/doh.h",
|
|
||||||
"lib/dotdot.c",
|
|
||||||
"lib/dotdot.h",
|
|
||||||
"lib/easy.c",
|
|
||||||
"lib/easyif.h",
|
|
||||||
"lib/escape.c",
|
|
||||||
"lib/escape.h",
|
|
||||||
"lib/file.c",
|
|
||||||
"lib/file.h",
|
|
||||||
"lib/fileinfo.c",
|
|
||||||
"lib/fileinfo.h",
|
|
||||||
"lib/formdata.c",
|
|
||||||
"lib/formdata.h",
|
|
||||||
"lib/ftp.c",
|
|
||||||
"lib/ftp.h",
|
|
||||||
"lib/ftplistparser.c",
|
|
||||||
"lib/ftplistparser.h",
|
|
||||||
"lib/getenv.c",
|
|
||||||
"lib/getinfo.c",
|
|
||||||
"lib/getinfo.h",
|
|
||||||
"lib/gopher.c",
|
|
||||||
"lib/gopher.h",
|
|
||||||
"lib/hash.c",
|
|
||||||
"lib/hash.h",
|
|
||||||
"lib/hmac.c",
|
|
||||||
"lib/hostasyn.c",
|
|
||||||
"lib/hostcheck.c",
|
|
||||||
"lib/hostcheck.h",
|
|
||||||
"lib/hostip.c",
|
|
||||||
"lib/hostip.h",
|
|
||||||
"lib/hostip4.c",
|
|
||||||
"lib/hostip6.c",
|
|
||||||
"lib/hostsyn.c",
|
|
||||||
"lib/http.c",
|
|
||||||
"lib/http.h",
|
|
||||||
"lib/http2.c",
|
|
||||||
"lib/http2.h",
|
|
||||||
"lib/http_chunks.c",
|
|
||||||
"lib/http_chunks.h",
|
|
||||||
"lib/http_digest.c",
|
|
||||||
"lib/http_digest.h",
|
|
||||||
"lib/http_negotiate.c",
|
|
||||||
"lib/http_negotiate.h",
|
|
||||||
"lib/http_ntlm.c",
|
|
||||||
"lib/http_ntlm.h",
|
|
||||||
"lib/http_proxy.c",
|
|
||||||
"lib/http_proxy.h",
|
|
||||||
"lib/idn_win32.c",
|
|
||||||
"lib/if2ip.c",
|
|
||||||
"lib/if2ip.h",
|
|
||||||
"lib/imap.c",
|
|
||||||
"lib/imap.h",
|
|
||||||
"lib/inet_ntop.c",
|
|
||||||
"lib/inet_ntop.h",
|
|
||||||
"lib/inet_pton.c",
|
|
||||||
"lib/inet_pton.h",
|
|
||||||
"lib/krb5.c",
|
|
||||||
"lib/ldap.c",
|
|
||||||
"lib/llist.c",
|
|
||||||
"lib/llist.h",
|
|
||||||
"lib/md4.c",
|
|
||||||
"lib/md5.c",
|
|
||||||
"lib/memdebug.c",
|
|
||||||
"lib/memdebug.h",
|
|
||||||
"lib/mime.c",
|
|
||||||
"lib/mime.h",
|
|
||||||
"lib/mprintf.c",
|
|
||||||
"lib/multi.c",
|
|
||||||
"lib/multihandle.h",
|
|
||||||
"lib/multiif.h",
|
|
||||||
"lib/netrc.c",
|
|
||||||
"lib/netrc.h",
|
|
||||||
"lib/non-ascii.c",
|
|
||||||
"lib/non-ascii.h",
|
|
||||||
"lib/nonblock.c",
|
|
||||||
"lib/nonblock.h",
|
|
||||||
"lib/nwlib.c",
|
|
||||||
"lib/nwos.c",
|
|
||||||
"lib/openldap.c",
|
|
||||||
"lib/parsedate.c",
|
|
||||||
"lib/parsedate.h",
|
|
||||||
"lib/pingpong.c",
|
|
||||||
"lib/pingpong.h",
|
|
||||||
"lib/pipeline.c",
|
|
||||||
"lib/pipeline.h",
|
|
||||||
"lib/pop3.c",
|
|
||||||
"lib/pop3.h",
|
|
||||||
"lib/progress.c",
|
|
||||||
"lib/progress.h",
|
|
||||||
"lib/psl.c",
|
|
||||||
"lib/psl.h",
|
|
||||||
"lib/rand.c",
|
|
||||||
"lib/rand.h",
|
|
||||||
"lib/rtsp.c",
|
|
||||||
"lib/rtsp.h",
|
|
||||||
"lib/security.c",
|
|
||||||
"lib/select.c",
|
|
||||||
"lib/select.h",
|
|
||||||
"lib/sendf.c",
|
|
||||||
"lib/sendf.h",
|
|
||||||
"lib/setopt.c",
|
|
||||||
"lib/setopt.h",
|
|
||||||
"lib/setup-os400.h",
|
|
||||||
"lib/setup-vms.h",
|
|
||||||
"lib/sha256.c",
|
|
||||||
"lib/share.c",
|
|
||||||
"lib/share.h",
|
|
||||||
"lib/sigpipe.h",
|
|
||||||
"lib/slist.c",
|
|
||||||
"lib/slist.h",
|
|
||||||
"lib/smb.c",
|
|
||||||
"lib/smb.h",
|
|
||||||
"lib/smtp.c",
|
|
||||||
"lib/smtp.h",
|
|
||||||
"lib/sockaddr.h",
|
|
||||||
"lib/socks.c",
|
|
||||||
"lib/socks.h",
|
|
||||||
"lib/socks_gssapi.c",
|
|
||||||
"lib/socks_sspi.c",
|
|
||||||
"lib/speedcheck.c",
|
|
||||||
"lib/speedcheck.h",
|
|
||||||
"lib/splay.c",
|
|
||||||
"lib/splay.h",
|
|
||||||
"lib/ssh-libssh.c",
|
|
||||||
"lib/ssh.c",
|
|
||||||
"lib/ssh.h",
|
|
||||||
"lib/strcase.c",
|
|
||||||
"lib/strcase.h",
|
|
||||||
"lib/strdup.c",
|
|
||||||
"lib/strdup.h",
|
|
||||||
"lib/strerror.c",
|
|
||||||
"lib/strerror.h",
|
|
||||||
"lib/strtok.c",
|
|
||||||
"lib/strtok.h",
|
|
||||||
"lib/strtoofft.c",
|
|
||||||
"lib/strtoofft.h",
|
|
||||||
"lib/system_win32.c",
|
|
||||||
"lib/system_win32.h",
|
|
||||||
"lib/telnet.c",
|
|
||||||
"lib/telnet.h",
|
|
||||||
"lib/tftp.c",
|
|
||||||
"lib/tftp.h",
|
|
||||||
"lib/timeval.c",
|
|
||||||
"lib/timeval.h",
|
|
||||||
"lib/transfer.c",
|
|
||||||
"lib/transfer.h",
|
|
||||||
"lib/url.c",
|
|
||||||
"lib/url.h",
|
|
||||||
"lib/urlapi-int.h",
|
|
||||||
"lib/urlapi.c",
|
|
||||||
"lib/urldata.h",
|
|
||||||
"lib/vauth/cleartext.c",
|
|
||||||
"lib/vauth/cram.c",
|
|
||||||
"lib/vauth/digest.c",
|
|
||||||
"lib/vauth/digest.h",
|
|
||||||
"lib/vauth/digest_sspi.c",
|
|
||||||
"lib/vauth/krb5_gssapi.c",
|
|
||||||
"lib/vauth/krb5_sspi.c",
|
|
||||||
"lib/vauth/ntlm.c",
|
|
||||||
"lib/vauth/ntlm.h",
|
|
||||||
"lib/vauth/ntlm_sspi.c",
|
|
||||||
"lib/vauth/oauth2.c",
|
|
||||||
"lib/vauth/spnego_gssapi.c",
|
|
||||||
"lib/vauth/spnego_sspi.c",
|
|
||||||
"lib/vauth/vauth.c",
|
|
||||||
"lib/vauth/vauth.h",
|
|
||||||
"lib/version.c",
|
|
||||||
"lib/vtls/cyassl.c",
|
|
||||||
"lib/vtls/cyassl.h",
|
|
||||||
"lib/vtls/darwinssl.c",
|
|
||||||
"lib/vtls/darwinssl.h",
|
|
||||||
"lib/vtls/gskit.c",
|
|
||||||
"lib/vtls/gskit.h",
|
|
||||||
"lib/vtls/gtls.c",
|
|
||||||
"lib/vtls/gtls.h",
|
|
||||||
"lib/vtls/mbedtls.c",
|
|
||||||
"lib/vtls/mbedtls.h",
|
|
||||||
"lib/vtls/mesalink.c",
|
|
||||||
"lib/vtls/mesalink.h",
|
|
||||||
"lib/vtls/nss.c",
|
|
||||||
"lib/vtls/nssg.h",
|
|
||||||
"lib/vtls/openssl.c",
|
|
||||||
"lib/vtls/openssl.h",
|
|
||||||
"lib/vtls/polarssl.c",
|
|
||||||
"lib/vtls/polarssl.h",
|
|
||||||
"lib/vtls/polarssl_threadlock.c",
|
|
||||||
"lib/vtls/polarssl_threadlock.h",
|
|
||||||
"lib/vtls/schannel.c",
|
|
||||||
"lib/vtls/schannel.h",
|
|
||||||
"lib/vtls/schannel_verify.c",
|
|
||||||
"lib/vtls/vtls.c",
|
|
||||||
"lib/vtls/vtls.h",
|
|
||||||
"lib/warnless.c",
|
|
||||||
"lib/warnless.h",
|
|
||||||
"lib/wildcard.c",
|
|
||||||
"lib/wildcard.h",
|
|
||||||
"lib/x509asn1.c",
|
|
||||||
"lib/x509asn1.h",
|
|
||||||
],
|
|
||||||
hdrs = [
|
|
||||||
"include/curl/curl.h",
|
|
||||||
"include/curl/curlver.h",
|
|
||||||
"include/curl/easy.h",
|
|
||||||
"include/curl/mprintf.h",
|
|
||||||
"include/curl/multi.h",
|
|
||||||
"include/curl/stdcheaders.h",
|
|
||||||
"include/curl/system.h",
|
|
||||||
"include/curl/typecheck-gcc.h",
|
|
||||||
"include/curl/urlapi.h",
|
|
||||||
],
|
|
||||||
copts = [
|
|
||||||
"-Iexternal/curl/lib",
|
|
||||||
"-D_GNU_SOURCE",
|
|
||||||
"-DHAVE_CONFIG_H",
|
|
||||||
"-DCURL_DISABLE_FTP",
|
|
||||||
"-DCURL_DISABLE_NTLM", # turning it off in configure is not enough
|
|
||||||
"-DHAVE_LIBZ",
|
|
||||||
"-DHAVE_ZLIB_H",
|
|
||||||
"-Wno-std::string-plus-int",
|
|
||||||
"-DCURL_MAX_WRITE_SIZE=65536",
|
|
||||||
# dealing with conflicting types when building
|
|
||||||
"-DBUILDING_LIBCURL",
|
|
||||||
],
|
|
||||||
includes = [
|
|
||||||
"include",
|
|
||||||
# lib/curl_setup.h is included by many .c files under lib/
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
linkopts = [
|
|
||||||
"-lrt",
|
|
||||||
],
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
deps = [
|
|
||||||
"@zlib_repo//:zlib",
|
|
||||||
"@boringssl_repo//:ssl",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
genrule(
|
|
||||||
name = "configure",
|
|
||||||
outs = ["include/curl_config.h"],
|
|
||||||
cmd = "\n".join([
|
|
||||||
"cat <<'EOF' >$@",
|
|
||||||
"#ifndef EXTERNAL_CURL_INCLUDE_CURL_CONFIG_H_",
|
|
||||||
"#define EXTERNAL_CURL_INCLUDE_CURL_CONFIG_H_",
|
|
||||||
"",
|
|
||||||
"#if !defined(_WIN32) && !defined(__APPLE__)",
|
|
||||||
"# include <openssl/opensslv.h>",
|
|
||||||
"# if defined(OPENSSL_IS_BORINGSSL)",
|
|
||||||
"# define HAVE_BORINGSSL 1",
|
|
||||||
"# endif",
|
|
||||||
"#endif",
|
|
||||||
"",
|
|
||||||
"#if defined(_WIN32)",
|
|
||||||
"# include \"lib/config-win32.h\"",
|
|
||||||
"# define BUILDING_LIBCURL 1",
|
|
||||||
"# define CURL_DISABLE_CRYPTO_AUTH 1",
|
|
||||||
"# define CURL_DISABLE_IMAP 1",
|
|
||||||
"# define CURL_DISABLE_LDAP 1",
|
|
||||||
"# define CURL_DISABLE_LDAPS 1",
|
|
||||||
"# define CURL_DISABLE_POP3 1",
|
|
||||||
"# define CURL_PULL_WS2TCPIP_H 1",
|
|
||||||
"# define HTTP_ONLY 1",
|
|
||||||
"#elif defined(__APPLE__)",
|
|
||||||
"# define HAVE_FSETXATTR_6 1",
|
|
||||||
"# define HAVE_SETMODE 1",
|
|
||||||
"# define HAVE_SYS_FILIO_H 1",
|
|
||||||
"# define HAVE_SYS_SOCKIO_H 1",
|
|
||||||
"# define OS \"x86_64-apple-darwin15.5.0\"",
|
|
||||||
"# define USE_DARWINSSL 1",
|
|
||||||
"#else",
|
|
||||||
"# define CURL_CA_BUNDLE \"/etc/ssl/certs/ca-certificates.crt\"",
|
|
||||||
"# define GETSERVBYPORT_R_ARGS 6",
|
|
||||||
"# define GETSERVBYPORT_R_BUFSIZE 4096",
|
|
||||||
"# define HAVE_BORINGSSL 1",
|
|
||||||
"# define HAVE_CLOCK_GETTIME_MONOTONIC 1",
|
|
||||||
"# define HAVE_CRYPTO_CLEANUP_ALL_EX_DATA 1",
|
|
||||||
"# define HAVE_FSETXATTR_5 1",
|
|
||||||
"# define HAVE_GETHOSTBYADDR_R 1",
|
|
||||||
"# define HAVE_GETHOSTBYADDR_R_8 1",
|
|
||||||
"# define HAVE_GETHOSTBYNAME_R 1",
|
|
||||||
"# define HAVE_GETHOSTBYNAME_R_6 1",
|
|
||||||
"# define HAVE_GETSERVBYPORT_R 1",
|
|
||||||
"# define HAVE_LIBSSL 1",
|
|
||||||
"# define HAVE_MALLOC_H 1",
|
|
||||||
"# define HAVE_MSG_NOSIGNAL 1",
|
|
||||||
"# define HAVE_OPENSSL_CRYPTO_H 1",
|
|
||||||
"# define HAVE_OPENSSL_ERR_H 1",
|
|
||||||
"# define HAVE_OPENSSL_PEM_H 1",
|
|
||||||
"# define HAVE_OPENSSL_PKCS12_H 1",
|
|
||||||
"# define HAVE_OPENSSL_RSA_H 1",
|
|
||||||
"# define HAVE_OPENSSL_SSL_H 1",
|
|
||||||
"# define HAVE_OPENSSL_X509_H 1",
|
|
||||||
"# define HAVE_RAND_EGD 1",
|
|
||||||
"# define HAVE_RAND_STATUS 1",
|
|
||||||
"# define HAVE_SSL_GET_SHUTDOWN 1",
|
|
||||||
"# define HAVE_STROPTS_H 1",
|
|
||||||
"# define HAVE_TERMIOS_H 1",
|
|
||||||
"# define OS \"x86_64-pc-linux-gnu\"",
|
|
||||||
"# define RANDOM_FILE \"/dev/urandom\"",
|
|
||||||
"# define USE_OPENSSL 1",
|
|
||||||
"#endif",
|
|
||||||
"",
|
|
||||||
"#if !defined(_WIN32)",
|
|
||||||
"# define CURL_DISABLE_DICT 1",
|
|
||||||
"# define CURL_DISABLE_FILE 1",
|
|
||||||
"# define CURL_DISABLE_GOPHER 1",
|
|
||||||
"# define CURL_DISABLE_IMAP 1",
|
|
||||||
"# define CURL_DISABLE_LDAP 1",
|
|
||||||
"# define CURL_DISABLE_LDAPS 1",
|
|
||||||
"# define CURL_DISABLE_POP3 1",
|
|
||||||
"# define CURL_DISABLE_SMTP 1",
|
|
||||||
"# define CURL_DISABLE_TELNET 1",
|
|
||||||
"# define CURL_DISABLE_TFTP 1",
|
|
||||||
"# define CURL_EXTERN_SYMBOL __attribute__ ((__visibility__ (\"default\")))",
|
|
||||||
"# define ENABLE_IPV6 1",
|
|
||||||
"# define GETHOSTNAME_TYPE_ARG2 size_t",
|
|
||||||
"# define GETNAMEINFO_QUAL_ARG1 const",
|
|
||||||
"# define GETNAMEINFO_TYPE_ARG1 struct sockaddr *",
|
|
||||||
"# define GETNAMEINFO_TYPE_ARG2 socklen_t",
|
|
||||||
"# define GETNAMEINFO_TYPE_ARG46 socklen_t",
|
|
||||||
"# define GETNAMEINFO_TYPE_ARG7 int",
|
|
||||||
"# define HAVE_ALARM 1",
|
|
||||||
"# define HAVE_ALLOCA_H 1",
|
|
||||||
"# define HAVE_ARPA_INET_H 1",
|
|
||||||
"# define HAVE_ARPA_TFTP_H 1",
|
|
||||||
"# define HAVE_ASSERT_H 1",
|
|
||||||
"# define HAVE_BASENAME 1",
|
|
||||||
"# define HAVE_BOOL_T 1",
|
|
||||||
"# define HAVE_CONNECT 1",
|
|
||||||
"# define HAVE_DLFCN_H 1",
|
|
||||||
"# define HAVE_ERRNO_H 1",
|
|
||||||
"# define HAVE_FCNTL 1",
|
|
||||||
"# define HAVE_FCNTL_H 1",
|
|
||||||
"# define HAVE_FCNTL_O_NONBLOCK 1",
|
|
||||||
"# define HAVE_FDOPEN 1",
|
|
||||||
"# define HAVE_FORK 1",
|
|
||||||
"# define HAVE_FREEADDRINFO 1",
|
|
||||||
"# define HAVE_FREEIFADDRS 1",
|
|
||||||
"# if !defined(__ANDROID__)",
|
|
||||||
"# define HAVE_FSETXATTR 1",
|
|
||||||
"# endif",
|
|
||||||
"# define HAVE_FTRUNCATE 1",
|
|
||||||
"# define HAVE_GAI_STRERROR 1",
|
|
||||||
"# define HAVE_GETADDRINFO 1",
|
|
||||||
"# define HAVE_GETADDRINFO_THREADSAFE 1",
|
|
||||||
"# define HAVE_GETEUID 1",
|
|
||||||
"# define HAVE_GETHOSTBYADDR 1",
|
|
||||||
"# define HAVE_GETHOSTBYNAME 1",
|
|
||||||
"# define HAVE_GETHOSTNAME 1",
|
|
||||||
"# if !defined(__ANDROID__)",
|
|
||||||
"# define HAVE_GETIFADDRS 1",
|
|
||||||
"# endif",
|
|
||||||
"# define HAVE_GETNAMEINFO 1",
|
|
||||||
"# define HAVE_GETPPID 1",
|
|
||||||
"# define HAVE_GETPROTOBYNAME 1",
|
|
||||||
"# define HAVE_GETPWUID 1",
|
|
||||||
"# if !defined(__ANDROID__)",
|
|
||||||
"# define HAVE_GETPWUID_R 1",
|
|
||||||
"# endif",
|
|
||||||
"# define HAVE_GETRLIMIT 1",
|
|
||||||
"# define HAVE_GETTIMEOFDAY 1",
|
|
||||||
"# define HAVE_GMTIME_R 1",
|
|
||||||
"# if !defined(__ANDROID__)",
|
|
||||||
"# define HAVE_IFADDRS_H 1",
|
|
||||||
"# endif",
|
|
||||||
"# define HAVE_IF_NAMETOINDEX 1",
|
|
||||||
"# define HAVE_INET_ADDR 1",
|
|
||||||
"# define HAVE_INET_NTOP 1",
|
|
||||||
"# define HAVE_INET_PTON 1",
|
|
||||||
"# define HAVE_INTTYPES_H 1",
|
|
||||||
"# define HAVE_IOCTL 1",
|
|
||||||
"# define HAVE_IOCTL_FIONBIO 1",
|
|
||||||
"# define HAVE_IOCTL_SIOCGIFADDR 1",
|
|
||||||
"# define HAVE_LIBGEN_H 1",
|
|
||||||
"# define HAVE_LIBZ 1",
|
|
||||||
"# define HAVE_LIMITS_H 1",
|
|
||||||
"# define HAVE_LL 1",
|
|
||||||
"# define HAVE_LOCALE_H 1",
|
|
||||||
"# define HAVE_LOCALTIME_R 1",
|
|
||||||
"# define HAVE_LONGLONG 1",
|
|
||||||
"# define HAVE_MEMORY_H 1",
|
|
||||||
"# define HAVE_NETDB_H 1",
|
|
||||||
"# define HAVE_NETINET_IN_H 1",
|
|
||||||
"# define HAVE_NETINET_TCP_H 1",
|
|
||||||
"# define HAVE_NET_IF_H 1",
|
|
||||||
"# define HAVE_PERROR 1",
|
|
||||||
"# define HAVE_PIPE 1",
|
|
||||||
"# define HAVE_POLL 1",
|
|
||||||
"# define HAVE_POLL_FINE 1",
|
|
||||||
"# define HAVE_POLL_H 1",
|
|
||||||
"# define HAVE_POSIX_STRERROR_R 1",
|
|
||||||
"# define HAVE_PWD_H 1",
|
|
||||||
"# define HAVE_RECV 1",
|
|
||||||
"# define HAVE_SELECT 1",
|
|
||||||
"# define HAVE_SEND 1",
|
|
||||||
"# define HAVE_SETJMP_H 1",
|
|
||||||
"# define HAVE_SETLOCALE 1",
|
|
||||||
"# define HAVE_SETRLIMIT 1",
|
|
||||||
"# define HAVE_SETSOCKOPT 1",
|
|
||||||
"# define HAVE_SGTTY_H 1",
|
|
||||||
"# define HAVE_SIGACTION 1",
|
|
||||||
"# define HAVE_SIGINTERRUPT 1",
|
|
||||||
"# define HAVE_SIGNAL 1",
|
|
||||||
"# define HAVE_SIGNAL_H 1",
|
|
||||||
"# define HAVE_SIGSETJMP 1",
|
|
||||||
"# define HAVE_SIG_ATOMIC_T 1",
|
|
||||||
"# define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1",
|
|
||||||
"# define HAVE_SOCKET 1",
|
|
||||||
"# define HAVE_SOCKETPAIR 1",
|
|
||||||
"# define HAVE_STDBOOL_H 1",
|
|
||||||
"# define HAVE_STDINT_H 1",
|
|
||||||
"# define HAVE_STDIO_H 1",
|
|
||||||
"# define HAVE_STDLIB_H 1",
|
|
||||||
"# define HAVE_STRCASECMP 1",
|
|
||||||
"# define HAVE_STRDUP 1",
|
|
||||||
"# define HAVE_STRERROR_R 1",
|
|
||||||
"# define HAVE_STRINGS_H 1",
|
|
||||||
"# define HAVE_STRING_H 1",
|
|
||||||
"# define HAVE_STRNCASECMP 1",
|
|
||||||
"# define HAVE_STRSTR 1",
|
|
||||||
"# define HAVE_STRTOK_R 1",
|
|
||||||
"# define HAVE_STRTOLL 1",
|
|
||||||
"# define HAVE_STRUCT_SOCKADDR_STORAGE 1",
|
|
||||||
"# define HAVE_STRUCT_TIMEVAL 1",
|
|
||||||
"# define HAVE_SYS_IOCTL_H 1",
|
|
||||||
"# define HAVE_SYS_PARAM_H 1",
|
|
||||||
"# define HAVE_SYS_POLL_H 1",
|
|
||||||
"# define HAVE_SYS_RESOURCE_H 1",
|
|
||||||
"# define HAVE_SYS_SELECT_H 1",
|
|
||||||
"# define HAVE_SYS_SOCKET_H 1",
|
|
||||||
"# define HAVE_SYS_STAT_H 1",
|
|
||||||
"# define HAVE_SYS_TIME_H 1",
|
|
||||||
"# define HAVE_SYS_TYPES_H 1",
|
|
||||||
"# define HAVE_SYS_UIO_H 1",
|
|
||||||
"# define HAVE_SYS_UN_H 1",
|
|
||||||
"# define HAVE_SYS_WAIT_H 1",
|
|
||||||
"# define HAVE_SYS_XATTR_H 1",
|
|
||||||
"# define HAVE_TIME_H 1",
|
|
||||||
"# define HAVE_UNAME 1",
|
|
||||||
"# define HAVE_UNISTD_H 1",
|
|
||||||
"# define HAVE_UTIME 1",
|
|
||||||
"# define HAVE_UTIME_H 1",
|
|
||||||
"# define HAVE_VARIADIC_MACROS_C99 1",
|
|
||||||
"# define HAVE_VARIADIC_MACROS_GCC 1",
|
|
||||||
"# define HAVE_WRITABLE_ARGV 1",
|
|
||||||
"# define HAVE_WRITEV 1",
|
|
||||||
"# define HAVE_ZLIB_H 1",
|
|
||||||
"# define LT_OBJDIR \".libs/\"",
|
|
||||||
"# define PACKAGE \"curl\"",
|
|
||||||
"# define PACKAGE_BUGREPORT \"a suitable curl mailing list: https://curl.haxx.se/mail/\"",
|
|
||||||
"# define PACKAGE_NAME \"curl\"",
|
|
||||||
"# define PACKAGE_STRING \"curl -\"",
|
|
||||||
"# define PACKAGE_TARNAME \"curl\"",
|
|
||||||
"# define PACKAGE_URL \"\"",
|
|
||||||
"# define PACKAGE_VERSION \"-\"",
|
|
||||||
"# define RECV_TYPE_ARG1 int",
|
|
||||||
"# define RECV_TYPE_ARG2 void *",
|
|
||||||
"# define RECV_TYPE_ARG3 size_t",
|
|
||||||
"# define RECV_TYPE_ARG4 int",
|
|
||||||
"# define RECV_TYPE_RETV ssize_t",
|
|
||||||
"# define RETSIGTYPE void",
|
|
||||||
"# define SELECT_QUAL_ARG5",
|
|
||||||
"# define SELECT_TYPE_ARG1 int",
|
|
||||||
"# define SELECT_TYPE_ARG234 fd_set *",
|
|
||||||
"# define SELECT_TYPE_ARG5 struct timeval *",
|
|
||||||
"# define SELECT_TYPE_RETV int",
|
|
||||||
"# define SEND_QUAL_ARG2 const",
|
|
||||||
"# define SEND_TYPE_ARG1 int",
|
|
||||||
"# define SEND_TYPE_ARG2 void *",
|
|
||||||
"# define SEND_TYPE_ARG3 size_t",
|
|
||||||
"# define SEND_TYPE_ARG4 int",
|
|
||||||
"# define SEND_TYPE_RETV ssize_t",
|
|
||||||
"# define SIZEOF_INT 4",
|
|
||||||
"# define SIZEOF_LONG 8",
|
|
||||||
"# define SIZEOF_OFF_T 8",
|
|
||||||
"# define SIZEOF_SHORT 2",
|
|
||||||
"# define SIZEOF_SIZE_T 8",
|
|
||||||
"# define SIZEOF_TIME_T 8",
|
|
||||||
"# define SIZEOF_VOIDP 8",
|
|
||||||
"# define SIZEOF_CURL_OFF_T 8",
|
|
||||||
"# define STDC_HEADERS 1",
|
|
||||||
"# define STRERROR_R_TYPE_ARG3 size_t",
|
|
||||||
"# define TIME_WITH_SYS_TIME 1",
|
|
||||||
"# define VERSION \"-\"",
|
|
||||||
"# ifndef _DARWIN_USE_64_BIT_INODE",
|
|
||||||
"# define _DARWIN_USE_64_BIT_INODE 1",
|
|
||||||
"# endif",
|
|
||||||
"#endif",
|
|
||||||
"",
|
|
||||||
"#endif // EXTERNAL_CURL_INCLUDE_CURL_CONFIG_H_",
|
|
||||||
"EOF",
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
@@ -16,6 +16,7 @@ package(
|
|||||||
filegroup(
|
filegroup(
|
||||||
name = "binary_release_files",
|
name = "binary_release_files",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"test_ecmg_messages.h",
|
||||||
"wv_cas_ecm_example.cc",
|
"wv_cas_ecm_example.cc",
|
||||||
":wv_cas_ecm_example",
|
":wv_cas_ecm_example",
|
||||||
],
|
],
|
||||||
@@ -26,20 +27,9 @@ cc_library(
|
|||||||
hdrs = ["constants.h"],
|
hdrs = ["constants.h"],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "simulcrypt_client",
|
|
||||||
srcs = ["simulcrypt_client.cc"],
|
|
||||||
deps = [
|
|
||||||
"//base",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "test_simulcrypt_messages",
|
name = "test_ecmg_messages",
|
||||||
hdrs = ["test_simulcrypt_messages.h"],
|
hdrs = ["test_ecmg_messages.h"],
|
||||||
deps = [
|
|
||||||
"//base",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_binary(
|
cc_binary(
|
||||||
@@ -57,7 +47,7 @@ cc_binary(
|
|||||||
srcs = ["wv_cas_key_fetcher_example.cc"],
|
srcs = ["wv_cas_key_fetcher_example.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
"//base",
|
"//base",
|
||||||
"//util:status",
|
"//common:status",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
|
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_proto",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright 2018 Google LLC.
|
|
||||||
//
|
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
|
||||||
// widevine-licensing@google.com.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Example client for demonstrating network calls to public/simulcrypt_server.
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "gflags/gflags.h"
|
|
||||||
#include "glog/logging.h"
|
|
||||||
|
|
||||||
DEFINE_string(server, "", "Server host name");
|
|
||||||
DEFINE_int32(port, 0, "Server port number");
|
|
||||||
|
|
||||||
constexpr uint32_t kBufferSize = 256;
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
|
||||||
CHECK(!FLAGS_server.empty()) << "need --server";
|
|
||||||
CHECK(FLAGS_port != 0) << "need --port";
|
|
||||||
|
|
||||||
struct hostent server;
|
|
||||||
{
|
|
||||||
int buflen = 1024;
|
|
||||||
char buf[1024];
|
|
||||||
struct hostent *result;
|
|
||||||
int h_errnop;
|
|
||||||
gethostbyname_r(/* __name= */ FLAGS_server.c_str(),
|
|
||||||
/* __result_buf= */ &server, /* __buf= */ buf,
|
|
||||||
/* __buflen= */ buflen,
|
|
||||||
/* __result= */ &result, /* __h_errnop= */ &h_errnop);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in server_addr;
|
|
||||||
bzero(reinterpret_cast<char *>(&server_addr), sizeof(server_addr));
|
|
||||||
server_addr.sin_family = AF_INET;
|
|
||||||
// TODO(user): Consider using inet_pton() to populate server_addr.sin_addr.
|
|
||||||
bcopy(server.h_addr, reinterpret_cast<char *>(&server_addr.sin_addr.s_addr),
|
|
||||||
server.h_length);
|
|
||||||
server_addr.sin_port = htons(FLAGS_port);
|
|
||||||
|
|
||||||
int socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);
|
|
||||||
CHECK(socket_fd >= 0) << "failed to opening socket";
|
|
||||||
CHECK(connect(socket_fd, (struct sockaddr *)&server_addr,
|
|
||||||
sizeof(server_addr)) >= 0)
|
|
||||||
<< "failed to connect to socket";
|
|
||||||
|
|
||||||
printf("Please enter the message: ");
|
|
||||||
char buffer[kBufferSize];
|
|
||||||
bzero(buffer, kBufferSize);
|
|
||||||
fgets(buffer, kBufferSize - 1, stdin);
|
|
||||||
int total_bytes_written = 0;
|
|
||||||
while (total_bytes_written != strlen(buffer)) {
|
|
||||||
int num_bytes_written = write(socket_fd, buffer, strlen(buffer));
|
|
||||||
if (num_bytes_written < 0) {
|
|
||||||
LOG(FATAL) << "ERROR writing to socket: " << strerror(errno);
|
|
||||||
}
|
|
||||||
total_bytes_written += num_bytes_written;
|
|
||||||
}
|
|
||||||
bzero(buffer, kBufferSize);
|
|
||||||
if (read(socket_fd, buffer, kBufferSize - 1) < 0) {
|
|
||||||
LOG(FATAL) << "ERROR reading from socket: " << strerror(errno);
|
|
||||||
}
|
|
||||||
printf("Read from buffer: %s\n", buffer);
|
|
||||||
|
|
||||||
close(socket_fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
203
example/test_ecmg_messages.h
Normal file
203
example/test_ecmg_messages.h
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2018 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Example ECMG messages used in unit tests and example client.
|
||||||
|
|
||||||
|
#ifndef MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_ECMG_MESSAGES_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_ECMG_MESSAGES_H_
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
const char kTestChannelSetup[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x00', '\x01', // message_type - Channel_setup
|
||||||
|
'\x00', '\x0e', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x01', // parameter_type- SUPER_CAS_ID
|
||||||
|
'\x00', '\x04', // parameter_length
|
||||||
|
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
const char kTestChannelStatus[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x00', '\x03', // message_type - Channel_status
|
||||||
|
'\x00', '\x39', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x02', // parameter_type - setion_TSpkt_flag
|
||||||
|
'\x00', '\x01', // parameter_length
|
||||||
|
'\x01', // parameter_value
|
||||||
|
'\x00', '\x03', // parameter_type - delay_start
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\xc8', // parameter_value
|
||||||
|
'\x00', '\x04', // parameter_type - delay_stop
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\xc8', // parameter_value
|
||||||
|
'\x00', '\x07', // parameter_type - ECM_rep_period
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x64', // parameter_value
|
||||||
|
'\x00', '\x08', // parameter_type - max_streams
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x09', // parameter_type - min_CP_duration
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x64', // parameter_value
|
||||||
|
'\x00', '\x0a', // parameter_type - lead_CW
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x01', // parameter_value
|
||||||
|
'\x00', '\x0b', // parameter_type - CW_per_msg
|
||||||
|
'\x00', '\x01', // parameter_length
|
||||||
|
'\x02', // parameter_value
|
||||||
|
'\x00', '\x0c', // parameter_type - max_comp_time
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x64' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
const char kTestStreamSetup[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x01', '\x01', // message_type - Stream_setup
|
||||||
|
'\x00', '\x18', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x19', // parameter_type - ECM_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x02', // parameter_value
|
||||||
|
'\x00', '\x10', // parameter_type - nominal_CP_duration
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x64' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
const char kTestStreamStatus[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x01', '\x03', // message_type - Stream_status
|
||||||
|
'\x00', '\x17', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x19', // parameter_type - ECM_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x02', // parameter_value
|
||||||
|
'\x00', '\x11', // parameter_type - access_criteria_transfer_mode
|
||||||
|
'\x00', '\x01', // parameter_length
|
||||||
|
'\x01' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
const char kTestCwProvision[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x02', '\x01', // message_type - CW_provision
|
||||||
|
'\x00', '\x44', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x12', // parameter_type - CP_number
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x13', // parameter_type - CP_duration
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x64', // parameter_value
|
||||||
|
'\x00', '\x14', // parameter_type - CP_CW_Combination
|
||||||
|
'\x00', '\x12', // parameter_length
|
||||||
|
'\x00', '\x00', // parameter_value - CP (2 bytes) then CW next (16 bytes)
|
||||||
|
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
|
||||||
|
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
|
||||||
|
'\x00', '\x14', // parameter_type - CP_CW_Combination
|
||||||
|
'\x00', '\x12', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value - CP (2 bytes) then CW next (16 bytes)
|
||||||
|
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
|
||||||
|
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
|
||||||
|
|
||||||
|
// CW is encrypted using hardcoded fixed entitlement key.
|
||||||
|
const char kTestEcmResponse[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x02', '\x02', // message_type - ECM_response
|
||||||
|
'\x00', '\xd2', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x12', // parameter_type - CP_number
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x15', // parameter_type - ECM_datagram
|
||||||
|
'\x00', '\xbc', // parameter_length
|
||||||
|
// parameter_value - ECM_datagram
|
||||||
|
'\x47', '\x40', '\x02', '\x10', '\x00', '\x80', '\x70', '\x95', '\x4a',
|
||||||
|
'\xd4', '\x01', '\x05', '\x80', '\x66', '\x61', '\x6b', '\x65', '\x5f',
|
||||||
|
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x31', '\x2e', '\x2e',
|
||||||
|
'\x2e', '\x2e', '\xef', '\x40', '\x57', '\x48', '\xa7', '\xad', '\xdd',
|
||||||
|
'\x34', '\x73', '\xfe', '\x5d', '\x1c', '\x65', '\xa0', '\xbf', '\x93',
|
||||||
|
'\xfe', '\x01', '\x4b', '\x1d', '\xcd', '\x9e', '\x1d', '\x3a', '\x36',
|
||||||
|
'\x99', '\x8f', '\x47', '\xa1', '\x3b', '\x46', '\xf1', '\xde', '\x9e',
|
||||||
|
'\xc2', '\x88', '\xf8', '\x27', '\x2f', '\xea', '\xa1', '\x63', '\x9b',
|
||||||
|
'\x1b', '\x6a', '\x56', '\x2d', '\x26', '\x31', '\x32', '\x33', '\x34',
|
||||||
|
'\x35', '\x36', '\x37', '\x38', '\x66', '\x61', '\x6b', '\x65', '\x5f',
|
||||||
|
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x32', '\x2e', '\x2e',
|
||||||
|
'\x2e', '\x2e', '\xf4', '\x71', '\x2a', '\x4b', '\x6d', '\x6d', '\x14',
|
||||||
|
'\x4d', '\x2e', '\x53', '\xe7', '\x4b', '\x9f', '\x4b', '\x0a', '\x34',
|
||||||
|
'\xb4', '\xfd', '\xbe', '\x86', '\x21', '\x35', '\x1e', '\xda', '\x81',
|
||||||
|
'\x89', '\x6f', '\x70', '\xd3', '\xd2', '\xb2', '\x79', '\xf2', '\xcd',
|
||||||
|
'\xeb', '\xc5', '\xaf', '\x89', '\xab', '\xeb', '\xf0', '\x1b', '\xd0',
|
||||||
|
'\xd3', '\xe9', '\x7d', '\x81', '\x8a', '\x31', '\x32', '\x33', '\x34',
|
||||||
|
'\x35', '\x36', '\x37', '\x38', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||||
|
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||||
|
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||||
|
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'};
|
||||||
|
|
||||||
|
const char kTestStreamCloseRequest[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x01', '\x04', // message_type - Stream_close_request
|
||||||
|
'\x00', '\x0c', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
const char kTestStreamCloseResponse[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x01', '\x05', // message_type - Stream_close_response
|
||||||
|
'\x00', '\x0c', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
const char kTestChannelClose[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x00', '\x04', // message_type - Channel_close
|
||||||
|
'\x00', '\x06', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01' // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_ECMG_MESSAGES_H_
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright 2018 Google LLC.
|
|
||||||
//
|
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
|
||||||
// widevine-licensing@google.com.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Example Simulcrypt messages used in unit tests and example client.
|
|
||||||
|
|
||||||
#ifndef MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_SIMULCRYPT_MESSAGES_H_
|
|
||||||
#define MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_SIMULCRYPT_MESSAGES_H_
|
|
||||||
|
|
||||||
namespace widevine {
|
|
||||||
namespace cas {
|
|
||||||
|
|
||||||
const char kTestEcmgStreamSetupMessage[] = { // protocol_version
|
|
||||||
'\x01',
|
|
||||||
// message_type - Stream_set-up
|
|
||||||
'\x01', '\x01',
|
|
||||||
// message_length
|
|
||||||
// 18 bytes below, 3 parameters 6 bytes each
|
|
||||||
'\x00', '\x12',
|
|
||||||
// parameter_type - ECM_channel_id
|
|
||||||
'\x00', '\x0e',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x01',
|
|
||||||
// parameter_type - ECM_stream_id
|
|
||||||
'\x00', '\x0f',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_type - nominal_CP_duration
|
|
||||||
'\x00', '\x10',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x03'};
|
|
||||||
|
|
||||||
const char kTestEcmgCwProvisionMessageWithOneCw[] = {
|
|
||||||
// protocol_version
|
|
||||||
'\x01',
|
|
||||||
// message_type - CW_provision
|
|
||||||
'\x02', '\x01',
|
|
||||||
// message_length
|
|
||||||
// 64 bytes below, 3 * 6 + 8 + 10 + 1 * 22 + 6
|
|
||||||
'\x00', '\x40',
|
|
||||||
// parameter_type - ECM_channel_id
|
|
||||||
'\x00', '\x0e',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x01',
|
|
||||||
// parameter_type - ECM_stream_id
|
|
||||||
'\x00', '\x0f',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_type - CP_number
|
|
||||||
'\x00', '\x12',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\xa1',
|
|
||||||
// parameter_type - access_criteria
|
|
||||||
'\x00', '\x0d',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x04',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x00', '\x00', '\x00',
|
|
||||||
// parameter_type - CW_encryption
|
|
||||||
'\x00', '\x18',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x06',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
|
||||||
// parameter_type - CP_CW_combination
|
|
||||||
'\x00', '\x14',
|
|
||||||
// parameter_length - 2 + 16
|
|
||||||
'\x00', '\x12',
|
|
||||||
// parameter_value - CP then CW
|
|
||||||
// CP
|
|
||||||
'\x00', '\xa1',
|
|
||||||
// CW (16 bytes)
|
|
||||||
'\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11',
|
|
||||||
'\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11',
|
|
||||||
// parameter_type - CP_duration
|
|
||||||
'\x00', '\x13',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x0a'};
|
|
||||||
|
|
||||||
const char kTestEcmgCwProvisionMessageWithTwoCw[] = {
|
|
||||||
// protocol_version
|
|
||||||
'\x01',
|
|
||||||
// message_type - CW_provision
|
|
||||||
'\x02', '\x01',
|
|
||||||
// message_length
|
|
||||||
// 86 bytes below, 3 * 6 + 8 + 10 + 2 * 22 + 6
|
|
||||||
'\x00', '\x56',
|
|
||||||
// parameter_type - ECM_channel_id
|
|
||||||
'\x00', '\x0e',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x01',
|
|
||||||
// parameter_type - ECM_stream_id
|
|
||||||
'\x00', '\x0f',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_type - CP_number
|
|
||||||
'\x00', '\x12',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\xa1',
|
|
||||||
// parameter_type - access_criteria
|
|
||||||
'\x00', '\x0d',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x04',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x00', '\x00', '\x00',
|
|
||||||
// parameter_type - CW_encryption
|
|
||||||
'\x00', '\x18',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x06',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
|
||||||
// parameter_type - CP_CW_combination
|
|
||||||
'\x00', '\x14',
|
|
||||||
// parameter_length - 2 + 16
|
|
||||||
'\x00', '\x12',
|
|
||||||
// parameter_value - CP then CW
|
|
||||||
// CP
|
|
||||||
'\x00', '\xa1',
|
|
||||||
// CW (16 bytes)
|
|
||||||
'\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11',
|
|
||||||
'\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11',
|
|
||||||
// parameter_type - CP_CW_combination
|
|
||||||
'\x00', '\x14',
|
|
||||||
// parameter_length - 2 + 16
|
|
||||||
'\x00', '\x12',
|
|
||||||
// parameter_value - CP then CW
|
|
||||||
// CP
|
|
||||||
'\x00', '\xa2',
|
|
||||||
// CW (16 bytes)
|
|
||||||
'\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22',
|
|
||||||
'\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22',
|
|
||||||
// parameter_type - CP_duration
|
|
||||||
'\x00', '\x13',
|
|
||||||
// parameter_length
|
|
||||||
'\x00', '\x02',
|
|
||||||
// parameter_value
|
|
||||||
'\x00', '\x0a'};
|
|
||||||
|
|
||||||
} // namespace cas
|
|
||||||
} // namespace widevine
|
|
||||||
|
|
||||||
#endif // MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_SIMULCRYPT_MESSAGES_H_
|
|
||||||
@@ -20,8 +20,6 @@ DEFINE_string(content_id, "21140844", "Content ID");
|
|||||||
DEFINE_bool(key_rotation, true, "Whether key rotation is enabled");
|
DEFINE_bool(key_rotation, true, "Whether key rotation is enabled");
|
||||||
DEFINE_string(track_type, "SD", "Provider name");
|
DEFINE_string(track_type, "SD", "Provider name");
|
||||||
|
|
||||||
namespace util = widevine::util;
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
CHECK(!FLAGS_content_id.empty() && !FLAGS_track_type.empty())
|
CHECK(!FLAGS_content_id.empty() && !FLAGS_track_type.empty())
|
||||||
@@ -47,7 +45,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
std::string signed_response_str;
|
std::string signed_response_str;
|
||||||
widevine::cas::WvCasKeyFetcher key_fetcher;
|
widevine::cas::WvCasKeyFetcher key_fetcher;
|
||||||
util::Status status =
|
widevine::Status status =
|
||||||
key_fetcher.RequestEntitlementKey(request_str, &signed_response_str);
|
key_fetcher.RequestEntitlementKey(request_str, &signed_response_str);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << "Failed to request entitlement key";
|
LOG(ERROR) << "Failed to request entitlement key";
|
||||||
|
|||||||
0
external_build_files/curl.BUILD
Executable file → Normal file
0
external_build_files/curl.BUILD
Executable file → Normal file
0
external_build_files/zlib.BUILD
Executable file → Normal file
0
external_build_files/zlib.BUILD
Executable file → Normal file
@@ -28,9 +28,9 @@ cc_library(
|
|||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//common:status",
|
|
||||||
"//common:aes_cbc_util",
|
"//common:aes_cbc_util",
|
||||||
"//common:random_util",
|
"//common:random_util",
|
||||||
|
"//common:status",
|
||||||
"//common:string_util",
|
"//common:string_util",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_proto",
|
||||||
@@ -76,27 +76,42 @@ cc_test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "ecmg",
|
name = "ecmg_client_handler",
|
||||||
srcs = ["ecmg.cc"],
|
srcs = ["ecmg_client_handler.cc"],
|
||||||
hdrs = [
|
hdrs = [
|
||||||
"ecmg.h",
|
"ecmg_client_handler.h",
|
||||||
"ecmg_constants.h",
|
"ecmg_constants.h",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
":ecm",
|
":ecm",
|
||||||
":ecm_generator",
|
":ecm_generator",
|
||||||
":fixed_key_fetcher",
|
":fixed_key_fetcher",
|
||||||
|
":mpeg2ts",
|
||||||
":util",
|
":util",
|
||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"@abseil_repo//absl/memory",
|
"@abseil_repo//absl/memory",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
|
"//common:crypto_util",
|
||||||
"//common:status",
|
"//common:status",
|
||||||
"//example:constants",
|
"//example:constants",
|
||||||
|
"//media_cas_packager_sdk/public:wv_cas_ecm",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "ecmg_client_handler_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["ecmg_client_handler_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":ecmg_client_handler",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"//example:test_ecmg_messages",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "fixed_key_fetcher",
|
name = "fixed_key_fetcher",
|
||||||
srcs = [
|
srcs = [
|
||||||
@@ -131,34 +146,6 @@ cc_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "simulcrypt",
|
|
||||||
srcs = ["simulcrypt.cc"],
|
|
||||||
hdrs = [
|
|
||||||
"simulcrypt.h",
|
|
||||||
"simulcrypt_constants.h",
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
":ecmg",
|
|
||||||
":util",
|
|
||||||
"//base",
|
|
||||||
"@abseil_repo//absl/base:core_headers",
|
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
"//common:status",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "simulcrypt_test",
|
|
||||||
size = "small",
|
|
||||||
srcs = ["simulcrypt_test.cc"],
|
|
||||||
deps = [
|
|
||||||
":simulcrypt",
|
|
||||||
"//testing:gunit_main",
|
|
||||||
"//example:test_simulcrypt_messages",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "ts_packet",
|
name = "ts_packet",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "common/status.h"
|
|
||||||
#include "common/aes_cbc_util.h"
|
#include "common/aes_cbc_util.h"
|
||||||
#include "common/random_util.h"
|
#include "common/random_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "protos/public/media_cas_encryption.pb.h"
|
#include "protos/public/media_cas_encryption.pb.h"
|
||||||
|
|
||||||
@@ -112,31 +112,31 @@ bool ConvertIvSizeParam(EcmIvSize param, size_t* size) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
util::Status CasEcm::Initialize(const std::string& content_id,
|
Status CasEcm::Initialize(const std::string& content_id,
|
||||||
const std::string& content_provider,
|
const std::string& content_provider,
|
||||||
const EcmInitParameters& ecm_init_parameters,
|
const EcmInitParameters& ecm_init_parameters,
|
||||||
std::string* key_request_message) {
|
std::string* key_request_message) {
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
return {util::error::INTERNAL, "Already initialized."};
|
return {error::INTERNAL, "Already initialized."};
|
||||||
}
|
}
|
||||||
if (content_id.empty()) {
|
if (content_id.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Content ID is empty."};
|
return {error::INVALID_ARGUMENT, "Content ID is empty."};
|
||||||
}
|
}
|
||||||
if (content_provider.empty()) {
|
if (content_provider.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Content Provider is empty."};
|
return {error::INVALID_ARGUMENT, "Content Provider is empty."};
|
||||||
}
|
}
|
||||||
if (key_request_message == nullptr) {
|
if (key_request_message == nullptr) {
|
||||||
return {util::error::INVALID_ARGUMENT, "key_request_message is null."};
|
return {error::INVALID_ARGUMENT, "key_request_message is null."};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ecm_init_parameters.track_types.empty()) {
|
if (ecm_init_parameters.track_types.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT,
|
return {error::INVALID_ARGUMENT,
|
||||||
"Parameter track_ids must be set with one or more Track IDs."};
|
"Parameter track_ids must be set with one or more Track IDs."};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ConvertIvSizeParam(ecm_init_parameters.content_iv_size,
|
if (!ConvertIvSizeParam(ecm_init_parameters.content_iv_size,
|
||||||
&content_iv_size_)) {
|
&content_iv_size_)) {
|
||||||
return {util::error::INVALID_ARGUMENT,
|
return {error::INVALID_ARGUMENT,
|
||||||
"Parameter content_iv_size must be kIvSize8 or kIvSize16."};
|
"Parameter content_iv_size must be kIvSize8 or kIvSize16."};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ util::Status CasEcm::Initialize(const std::string& content_id,
|
|||||||
generation_ = kMaxGeneration;
|
generation_ = kMaxGeneration;
|
||||||
|
|
||||||
// Construct and return CasEncryptionRequest message for caller to use.
|
// Construct and return CasEncryptionRequest message for caller to use.
|
||||||
util::Status status = CreateEntitlementRequest(key_request_message);
|
Status status = CreateEntitlementRequest(key_request_message);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << "Entitlement request message could not be created.";
|
LOG(ERROR) << "Entitlement request message could not be created.";
|
||||||
return status;
|
return status;
|
||||||
@@ -160,31 +160,30 @@ util::Status CasEcm::Initialize(const std::string& content_id,
|
|||||||
// Everything is set up except entitlement keys.
|
// Everything is set up except entitlement keys.
|
||||||
ClearEntitlementKeys();
|
ClearEntitlementKeys();
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::ProcessCasEncryptionResponse(const std::string& response) {
|
Status CasEcm::ProcessCasEncryptionResponse(const std::string& response) {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return {util::error::INTERNAL, "Not initialized."};
|
return {error::INTERNAL, "Not initialized."};
|
||||||
}
|
}
|
||||||
if (response.empty()) {
|
if (response.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Response std::string is empty."};
|
return {error::INVALID_ARGUMENT, "Response std::string is empty."};
|
||||||
}
|
}
|
||||||
return ParseEntitlementResponse(response);
|
return ParseEntitlementResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::GenerateEcm(EntitledKeyInfo* even_key,
|
Status CasEcm::GenerateEcm(EntitledKeyInfo* even_key, EntitledKeyInfo* odd_key,
|
||||||
EntitledKeyInfo* odd_key,
|
const std::string& track_type, std::string* serialized_ecm,
|
||||||
const std::string& track_type,
|
uint32_t* generation) {
|
||||||
std::string* serialized_ecm, uint32_t* generation) {
|
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return {util::error::INTERNAL, "Not initialized."};
|
return {error::INTERNAL, "Not initialized."};
|
||||||
}
|
}
|
||||||
if (!HaveEntitlementKeys()) {
|
if (!HaveEntitlementKeys()) {
|
||||||
return {util::error::INTERNAL, "Need entitlement key."};
|
return {error::INTERNAL, "Need entitlement key."};
|
||||||
}
|
}
|
||||||
if (!paired_keys_required_) {
|
if (!paired_keys_required_) {
|
||||||
return {util::error::INVALID_ARGUMENT,
|
return {error::INVALID_ARGUMENT,
|
||||||
"Key rotation not enabled - use GenerateSingleKeyEcm()."};
|
"Key rotation not enabled - use GenerateSingleKeyEcm()."};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,18 +194,18 @@ util::Status CasEcm::GenerateEcm(EntitledKeyInfo* even_key,
|
|||||||
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
|
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::GenerateSingleKeyEcm(EntitledKeyInfo* key,
|
Status CasEcm::GenerateSingleKeyEcm(EntitledKeyInfo* key,
|
||||||
const std::string& track_type,
|
const std::string& track_type,
|
||||||
std::string* serialized_ecm,
|
std::string* serialized_ecm,
|
||||||
uint32_t* generation) {
|
uint32_t* generation) {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return {util::error::INTERNAL, "Not initialized."};
|
return {error::INTERNAL, "Not initialized."};
|
||||||
}
|
}
|
||||||
if (!HaveEntitlementKeys()) {
|
if (!HaveEntitlementKeys()) {
|
||||||
return {util::error::INTERNAL, "Need entitlement key."};
|
return {error::INTERNAL, "Need entitlement key."};
|
||||||
}
|
}
|
||||||
if (paired_keys_required_) {
|
if (paired_keys_required_) {
|
||||||
return {util::error::INVALID_ARGUMENT,
|
return {error::INVALID_ARGUMENT,
|
||||||
"Key rotation enabled - use GenerateEcm()."};
|
"Key rotation enabled - use GenerateEcm()."};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,17 +215,17 @@ util::Status CasEcm::GenerateSingleKeyEcm(EntitledKeyInfo* key,
|
|||||||
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
|
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::GenerateEcmCommon(
|
Status CasEcm::GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
|
||||||
const std::vector<EntitledKeyInfo*>& keys, const std::string& track_type,
|
const std::string& track_type,
|
||||||
std::string* serialized_ecm, uint32_t* generation) {
|
std::string* serialized_ecm, uint32_t* generation) {
|
||||||
if (serialized_ecm == nullptr) {
|
if (serialized_ecm == nullptr) {
|
||||||
return {util::error::INVALID_ARGUMENT, "No return ecm std::string pointer."};
|
return {error::INVALID_ARGUMENT, "No return ecm std::string pointer."};
|
||||||
}
|
}
|
||||||
if (generation == nullptr) {
|
if (generation == nullptr) {
|
||||||
return {util::error::INVALID_ARGUMENT, "No return generation pointer."};
|
return {error::INVALID_ARGUMENT, "No return generation pointer."};
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status status = ValidateKeys(keys);
|
Status status = ValidateKeys(keys);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -254,37 +253,34 @@ util::Status CasEcm::GenerateEcmCommon(
|
|||||||
if (kMaxEcmSizeBytes < serialized_ecm->size()) {
|
if (kMaxEcmSizeBytes < serialized_ecm->size()) {
|
||||||
generation_ = previous_generation;
|
generation_ = previous_generation;
|
||||||
serialized_ecm->clear();
|
serialized_ecm->clear();
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "Maximum size of ECM has been exceeded.");
|
||||||
"Maximum size of ECM has been exceeded.");
|
|
||||||
}
|
}
|
||||||
*generation = generation_;
|
*generation = generation_;
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CasEcm::IncrementGeneration() {
|
void CasEcm::IncrementGeneration() {
|
||||||
generation_ = (generation_ >= kMaxGeneration) ? 0 : generation_ + 1;
|
generation_ = (generation_ >= kMaxGeneration) ? 0 : generation_ + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::WrapEntitledKeys(
|
Status CasEcm::WrapEntitledKeys(const std::string& track_type,
|
||||||
const std::string& track_type, const std::vector<EntitledKeyInfo*> keys) {
|
const std::vector<EntitledKeyInfo*> keys) {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return {util::error::INTERNAL, "Not initialized."};
|
return {error::INTERNAL, "Not initialized."};
|
||||||
}
|
}
|
||||||
if (keys.empty()) {
|
if (keys.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT,
|
return {error::INVALID_ARGUMENT, "Vector of EntitledKeyInfo is empty."};
|
||||||
"Vector of EntitledKeyInfo is empty."};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ekey_map_entry = entitlement_keys_.find(track_type);
|
auto ekey_map_entry = entitlement_keys_.find(track_type);
|
||||||
if (ekey_map_entry == entitlement_keys_.end()) {
|
if (ekey_map_entry == entitlement_keys_.end()) {
|
||||||
return {util::error::INTERNAL,
|
return {error::INTERNAL, "No Entitlement Key found for given track_type."};
|
||||||
"No Entitlement Key found for given track_type."};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& ekey_list = ekey_map_entry->second;
|
const auto& ekey_list = ekey_map_entry->second;
|
||||||
if (ekey_list.size() != keys.size()) {
|
if (ekey_list.size() != keys.size()) {
|
||||||
return {util::error::INTERNAL,
|
return {error::INTERNAL,
|
||||||
"Number of Entitled keys and Entitlement keys must match."};
|
"Number of Entitled keys and Entitlement keys must match."};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +294,7 @@ util::Status CasEcm::WrapEntitledKeys(
|
|||||||
if (entitled_key->wrapped_key_iv.empty()) {
|
if (entitled_key->wrapped_key_iv.empty()) {
|
||||||
CHECK(RandomBytes(kWrappedKeyIvSizeBytes, &entitled_key->wrapped_key_iv));
|
CHECK(RandomBytes(kWrappedKeyIvSizeBytes, &entitled_key->wrapped_key_iv));
|
||||||
}
|
}
|
||||||
util::Status status =
|
Status status =
|
||||||
WrapKey(entitlement_key->key_value, entitled_key->wrapped_key_iv,
|
WrapKey(entitlement_key->key_value, entitled_key->wrapped_key_iv,
|
||||||
entitled_key->key_value, &entitled_key->wrapped_key_value);
|
entitled_key->key_value, &entitled_key->wrapped_key_value);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
@@ -306,13 +302,12 @@ util::Status CasEcm::WrapEntitledKeys(
|
|||||||
}
|
}
|
||||||
entitlement_key++;
|
entitlement_key++;
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::WrapKey(const std::string& wrapping_key,
|
Status CasEcm::WrapKey(const std::string& wrapping_key, const std::string& wrapping_iv,
|
||||||
const std::string& wrapping_iv, const std::string& key_value,
|
const std::string& key_value, std::string* wrapped_key) {
|
||||||
std::string* wrapped_key) {
|
Status status = ValidateKeyValue(wrapping_key, kWrappingKeySizeBytes);
|
||||||
util::Status status = ValidateKeyValue(wrapping_key, kWrappingKeySizeBytes);
|
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -328,14 +323,14 @@ util::Status CasEcm::WrapKey(const std::string& wrapping_key,
|
|||||||
*wrapped_key =
|
*wrapped_key =
|
||||||
crypto_util::EncryptAesCbcNoPad(wrapping_key, wrapping_iv, key_value);
|
crypto_util::EncryptAesCbcNoPad(wrapping_key, wrapping_iv, key_value);
|
||||||
if (wrapped_key->empty()) {
|
if (wrapped_key->empty()) {
|
||||||
return util::Status(util::error::INTERNAL, "Failed to wrap key");
|
return Status(error::INTERNAL, "Failed to wrap key");
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) {
|
Status CasEcm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) {
|
||||||
for (const auto& key : keys) {
|
for (const auto& key : keys) {
|
||||||
util::Status status;
|
Status status;
|
||||||
status = ValidateKeyId(key->key_id);
|
status = ValidateKeyId(key->key_id);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
@@ -349,13 +344,12 @@ util::Status CasEcm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::ValidateWrappedKeys(
|
Status CasEcm::ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys) {
|
||||||
const std::vector<EntitledKeyInfo*>& keys) {
|
|
||||||
for (const auto& key : keys) {
|
for (const auto& key : keys) {
|
||||||
util::Status status;
|
Status status;
|
||||||
status = ValidateKeyId(key->key_id);
|
status = ValidateKeyId(key->key_id);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
@@ -374,31 +368,31 @@ util::Status CasEcm::ValidateWrappedKeys(
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::ValidateKeyId(const std::string& key_id) {
|
Status CasEcm::ValidateKeyId(const std::string& key_id) {
|
||||||
if (key_id.size() != kKeyIdSizeBytes) {
|
if (key_id.size() != kKeyIdSizeBytes) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Key ID must be 16 bytes."};
|
return {error::INVALID_ARGUMENT, "Key ID must be 16 bytes."};
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::ValidateKeyValue(const std::string& key_value,
|
Status CasEcm::ValidateKeyValue(const std::string& key_value,
|
||||||
size_t key_value_size) {
|
size_t key_value_size) {
|
||||||
if (key_value.size() != key_value_size) {
|
if (key_value.size() != key_value_size) {
|
||||||
return util::Status(
|
return Status(
|
||||||
util::error::INVALID_ARGUMENT,
|
error::INVALID_ARGUMENT,
|
||||||
absl::StrCat("Key is wrong size (", key_value.size(), " bytes)."));
|
absl::StrCat("Key is wrong size (", key_value.size(), " bytes)."));
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::ValidateIv(const std::string& iv, size_t size) {
|
Status CasEcm::ValidateIv(const std::string& iv, size_t size) {
|
||||||
if (iv.size() != size) {
|
if (iv.size() != size) {
|
||||||
return {util::error::INVALID_ARGUMENT, "IV is wrong size."};
|
return {error::INVALID_ARGUMENT, "IV is wrong size."};
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CasEcm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
|
std::string CasEcm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
|
||||||
@@ -427,7 +421,7 @@ std::string CasEcm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
|
|||||||
LOG(FATAL) << "ECM bitset incorret size: " << ecm_bitset.size();
|
LOG(FATAL) << "ECM bitset incorret size: " << ecm_bitset.size();
|
||||||
}
|
}
|
||||||
std::string serialized_ecm;
|
std::string serialized_ecm;
|
||||||
util::Status status =
|
Status status =
|
||||||
string_util::BitsetStringToBinaryString(ecm_bitset, &serialized_ecm);
|
string_util::BitsetStringToBinaryString(ecm_bitset, &serialized_ecm);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(FATAL) << "Failed to convert ECM bitset to std::string";
|
LOG(FATAL) << "Failed to convert ECM bitset to std::string";
|
||||||
@@ -441,7 +435,7 @@ std::string CasEcm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
|
|||||||
return serialized_ecm;
|
return serialized_ecm;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::CreateEntitlementRequest(std::string* request_string) {
|
Status CasEcm::CreateEntitlementRequest(std::string* request_string) {
|
||||||
CasEncryptionRequest request;
|
CasEncryptionRequest request;
|
||||||
|
|
||||||
request.set_content_id(content_id_);
|
request.set_content_id(content_id_);
|
||||||
@@ -454,43 +448,42 @@ util::Status CasEcm::CreateEntitlementRequest(std::string* request_string) {
|
|||||||
|
|
||||||
if (!request.SerializeToString(request_string)) {
|
if (!request.SerializeToString(request_string)) {
|
||||||
request_string->clear();
|
request_string->clear();
|
||||||
return {util::error::INTERNAL, "Failure serializing request."};
|
return {error::INTERNAL, "Failure serializing request."};
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcm::ParseEntitlementResponse(const std::string& response_string) {
|
Status CasEcm::ParseEntitlementResponse(const std::string& response_string) {
|
||||||
// TODO(user): parse valid response. NOT Implemented.
|
// TODO(user): parse valid response. NOT Implemented.
|
||||||
ClearEntitlementKeys();
|
ClearEntitlementKeys();
|
||||||
|
|
||||||
SignedCasEncryptionResponse signed_response;
|
SignedCasEncryptionResponse signed_response;
|
||||||
if (!signed_response.ParseFromString(response_string)) {
|
if (!signed_response.ParseFromString(response_string)) {
|
||||||
return {util::error::INTERNAL, "Failure parsing signed response."};
|
return {error::INTERNAL, "Failure parsing signed response."};
|
||||||
}
|
}
|
||||||
// TODO(user): Should verify signature.
|
// TODO(user): Should verify signature.
|
||||||
CasEncryptionResponse response;
|
CasEncryptionResponse response;
|
||||||
if (!response.ParseFromString(signed_response.response())) {
|
if (!response.ParseFromString(signed_response.response())) {
|
||||||
return {util::error::INTERNAL, "Failure parsing signed response."};
|
return {error::INTERNAL, "Failure parsing signed response."};
|
||||||
}
|
}
|
||||||
if (response.status() != CasEncryptionResponse_Status_OK) {
|
if (response.status() != CasEncryptionResponse_Status_OK) {
|
||||||
return util::Status(
|
return Status(error::INTERNAL, absl::StrCat("Failure reported by server: ",
|
||||||
util::error::INTERNAL,
|
response.status(), " : ",
|
||||||
absl::StrCat("Failure reported by server: ", response.status(), " : ",
|
response.status_message()));
|
||||||
response.status_message()));
|
|
||||||
}
|
}
|
||||||
if (content_id_ != response.content_id()) {
|
if (content_id_ != response.content_id()) {
|
||||||
return util::Status(
|
return Status(
|
||||||
util::error::INTERNAL,
|
error::INTERNAL,
|
||||||
absl::StrCat("Content ID mismatch in Entitlement Response - expected: ",
|
absl::StrCat("Content ID mismatch in Entitlement Response - expected: ",
|
||||||
content_id_, " received: ", response.content_id()));
|
content_id_, " received: ", response.content_id()));
|
||||||
}
|
}
|
||||||
if (response.entitlement_keys().empty()) {
|
if (response.entitlement_keys().empty()) {
|
||||||
return {util::error::INTERNAL, "Failure: no entitlement keys in response."};
|
return {error::INTERNAL, "Failure: no entitlement keys in response."};
|
||||||
}
|
}
|
||||||
size_t keys_needed = (paired_keys_required_ ? 2 : 1) * track_types_.size();
|
size_t keys_needed = (paired_keys_required_ ? 2 : 1) * track_types_.size();
|
||||||
if (keys_needed > response.entitlement_keys().size()) {
|
if (keys_needed > response.entitlement_keys().size()) {
|
||||||
return util::Status(
|
return Status(
|
||||||
util::error::INTERNAL,
|
error::INTERNAL,
|
||||||
absl::StrCat(
|
absl::StrCat(
|
||||||
"Wrong number of keys in Entitlement Response - expected: ",
|
"Wrong number of keys in Entitlement Response - expected: ",
|
||||||
keys_needed, " got: ", response.entitlement_keys().size()));
|
keys_needed, " got: ", response.entitlement_keys().size()));
|
||||||
@@ -509,7 +502,7 @@ util::Status CasEcm::ParseEntitlementResponse(const std::string& response_string
|
|||||||
EntitlementKeyInfo ekey;
|
EntitlementKeyInfo ekey;
|
||||||
ekey.key_id = key.key_id();
|
ekey.key_id = key.key_id();
|
||||||
ekey.key_value = key.key();
|
ekey.key_value = key.key();
|
||||||
util::Status status = ValidateKeyValue(key.key(), kWrappingKeySizeBytes);
|
Status status = ValidateKeyValue(key.key(), kWrappingKeySizeBytes);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -534,10 +527,9 @@ util::Status CasEcm::ParseEntitlementResponse(const std::string& response_string
|
|||||||
if (!CheckEntitlementKeys()) {
|
if (!CheckEntitlementKeys()) {
|
||||||
LOG(ERROR) << "Could not stage entitlement keys from response:";
|
LOG(ERROR) << "Could not stage entitlement keys from response:";
|
||||||
response.ShortDebugString();
|
response.ShortDebugString();
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "No suitable entitlement key was found.");
|
||||||
"No suitable entitlement key was found.");
|
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CasEcm::CountEntitlementKeys() const {
|
size_t CasEcm::CountEntitlementKeys() const {
|
||||||
|
|||||||
@@ -91,10 +91,10 @@ class CasEcm {
|
|||||||
// Notes:
|
// Notes:
|
||||||
// The returned |key_request_message| must be sent to the server and
|
// The returned |key_request_message| must be sent to the server and
|
||||||
// the response correctly parsed before ECMs can be generated.
|
// the response correctly parsed before ECMs can be generated.
|
||||||
virtual util::Status Initialize(const std::string& content_id,
|
virtual Status Initialize(const std::string& content_id,
|
||||||
const std::string& content_provider,
|
const std::string& content_provider,
|
||||||
const EcmInitParameters& ecm_init_parameters,
|
const EcmInitParameters& ecm_init_parameters,
|
||||||
std::string* key_request_message);
|
std::string* key_request_message);
|
||||||
|
|
||||||
// Parse a CasEncryptionResponse message holding the entitlement keys for
|
// Parse a CasEncryptionResponse message holding the entitlement keys for
|
||||||
// generating the ECM stream. The entitlement keys are used to encrypt the
|
// generating the ECM stream. The entitlement keys are used to encrypt the
|
||||||
@@ -102,7 +102,7 @@ class CasEcm {
|
|||||||
// Args:
|
// Args:
|
||||||
// |response| a serialized CasEncryptionRequest message from the server
|
// |response| a serialized CasEncryptionRequest message from the server
|
||||||
// holding entitlement key information (or error information).
|
// holding entitlement key information (or error information).
|
||||||
virtual util::Status ProcessCasEncryptionResponse(const std::string& response);
|
virtual Status ProcessCasEncryptionResponse(const std::string& response);
|
||||||
|
|
||||||
// Accept keys and IVs and construct an ECM that will fit into a Transport
|
// Accept keys and IVs and construct an ECM that will fit into a Transport
|
||||||
// Stream packet payload (184 bytes).
|
// Stream packet payload (184 bytes).
|
||||||
@@ -118,10 +118,9 @@ class CasEcm {
|
|||||||
// entitlement key. Wrapping modifies the original structure.
|
// entitlement key. Wrapping modifies the original structure.
|
||||||
// Generation is a mod 32 counter. If the ECM has any changes from the
|
// Generation is a mod 32 counter. If the ECM has any changes from the
|
||||||
// previous ECM, the generation is increased by one.
|
// previous ECM, the generation is increased by one.
|
||||||
virtual util::Status GenerateEcm(EntitledKeyInfo* even_key,
|
virtual Status GenerateEcm(EntitledKeyInfo* even_key,
|
||||||
EntitledKeyInfo* odd_key,
|
EntitledKeyInfo* odd_key, const std::string& track_type,
|
||||||
const std::string& track_type,
|
std::string* serialized_ecm, uint32_t* generation);
|
||||||
std::string* serialized_ecm, uint32_t* generation);
|
|
||||||
|
|
||||||
// Accept a key and IV and construct an ECM that will fit into a Transport
|
// Accept a key and IV and construct an ECM that will fit into a Transport
|
||||||
// Stream packet payload (184 bytes). This call is specifically for the case
|
// Stream packet payload (184 bytes). This call is specifically for the case
|
||||||
@@ -135,10 +134,10 @@ class CasEcm {
|
|||||||
// with the initialized settings.
|
// with the initialized settings.
|
||||||
// Generation is a mod 32 counter. If the ECM has any changes from the
|
// Generation is a mod 32 counter. If the ECM has any changes from the
|
||||||
// previous ECM, the generation is increased by one.
|
// previous ECM, the generation is increased by one.
|
||||||
virtual util::Status GenerateSingleKeyEcm(EntitledKeyInfo* key,
|
virtual Status GenerateSingleKeyEcm(EntitledKeyInfo* key,
|
||||||
const std::string& track_type,
|
const std::string& track_type,
|
||||||
std::string* serialized_ecm,
|
std::string* serialized_ecm,
|
||||||
uint32_t* generation);
|
uint32_t* generation);
|
||||||
|
|
||||||
protected: // For unit tests.
|
protected: // For unit tests.
|
||||||
// Take the input entitled |keys| and our current state, and generate
|
// Take the input entitled |keys| and our current state, and generate
|
||||||
@@ -154,8 +153,8 @@ class CasEcm {
|
|||||||
// |track_type| the track type for the keys. The type_track must match one
|
// |track_type| the track type for the keys. The type_track must match one
|
||||||
// of the |EcmInitParameters::track_types| strings passed into Initialize().
|
// of the |EcmInitParameters::track_types| strings passed into Initialize().
|
||||||
// |key| the Entitled Key to be wrapped.
|
// |key| the Entitled Key to be wrapped.
|
||||||
virtual util::Status WrapEntitledKeys(
|
virtual Status WrapEntitledKeys(const std::string& track_type,
|
||||||
const std::string& track_type, const std::vector<EntitledKeyInfo*> keys);
|
const std::vector<EntitledKeyInfo*> keys);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Entitlement key - |key_value| is used to wrap the content key, and |key_id|
|
// Entitlement key - |key_value| is used to wrap the content key, and |key_id|
|
||||||
@@ -194,29 +193,27 @@ class CasEcm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Common helper for GenerateEcm() and GenerateSingleKeyEcm()
|
// Common helper for GenerateEcm() and GenerateSingleKeyEcm()
|
||||||
virtual util::Status GenerateEcmCommon(
|
virtual Status GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
|
||||||
const std::vector<EntitledKeyInfo*>& keys, const std::string& track_type,
|
const std::string& track_type,
|
||||||
std::string* serialized_ecm, uint32_t* generation);
|
std::string* serialized_ecm, uint32_t* generation);
|
||||||
|
|
||||||
// Wrap |key_value| using |wrapping_key| (entitlement key) and |wrapping_iv|.
|
// Wrap |key_value| using |wrapping_key| (entitlement key) and |wrapping_iv|.
|
||||||
// Returns the resulting wrapped key in |wrapped_key|.
|
// Returns the resulting wrapped key in |wrapped_key|.
|
||||||
// Return a status indicating whether there has been any error.
|
// Return a status indicating whether there has been any error.
|
||||||
virtual util::Status WrapKey(const std::string& wrapping_key,
|
virtual Status WrapKey(const std::string& wrapping_key, const std::string& wrapping_iv,
|
||||||
const std::string& wrapping_iv,
|
const std::string& key_value, std::string* wrapped_key);
|
||||||
const std::string& key_value, std::string* wrapped_key);
|
|
||||||
|
|
||||||
virtual util::Status ValidateKeys(const std::vector<EntitledKeyInfo*>& keys);
|
virtual Status ValidateKeys(const std::vector<EntitledKeyInfo*>& keys);
|
||||||
virtual util::Status ValidateWrappedKeys(
|
virtual Status ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys);
|
||||||
const std::vector<EntitledKeyInfo*>& keys);
|
|
||||||
|
|
||||||
util::Status ValidateKeyId(const std::string& key_id);
|
Status ValidateKeyId(const std::string& key_id);
|
||||||
util::Status ValidateKeyValue(const std::string& key_value, size_t key_value_size);
|
Status ValidateKeyValue(const std::string& key_value, size_t key_value_size);
|
||||||
util::Status ValidateIv(const std::string& iv, size_t size);
|
Status ValidateIv(const std::string& iv, size_t size);
|
||||||
|
|
||||||
// TODO(user): need unit tests for CreateEntitlementRequest.
|
// TODO(user): need unit tests for CreateEntitlementRequest.
|
||||||
virtual util::Status CreateEntitlementRequest(std::string* request_string);
|
virtual Status CreateEntitlementRequest(std::string* request_string);
|
||||||
// TODO(user): need unit tests for ParseEntitlementResponse.
|
// TODO(user): need unit tests for ParseEntitlementResponse.
|
||||||
virtual util::Status ParseEntitlementResponse(const std::string& response_string);
|
virtual Status ParseEntitlementResponse(const std::string& response_string);
|
||||||
|
|
||||||
virtual uint32_t generation() const { return generation_; }
|
virtual uint32_t generation() const { return generation_; }
|
||||||
virtual CryptoMode crypto_mode() const { return crypto_mode_; }
|
virtual CryptoMode crypto_mode() const { return crypto_mode_; }
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ static constexpr int kMaxBytesKeyIdField = 16;
|
|||||||
|
|
||||||
std::string CasEcmGenerator::GenerateEcm(const EcmParameters& params) {
|
std::string CasEcmGenerator::GenerateEcm(const EcmParameters& params) {
|
||||||
std::vector<EntitledKeyInfo> keys;
|
std::vector<EntitledKeyInfo> keys;
|
||||||
util::Status status = ProcessEcmParameters(params, &keys);
|
Status status = ProcessEcmParameters(params, &keys);
|
||||||
if (!status.ok() || !initialized_) {
|
if (!status.ok() || !initialized_) {
|
||||||
LOG(ERROR) << " EcmParameters is not set up properly: " << status;
|
LOG(ERROR) << " EcmParameters is not set up properly: " << status;
|
||||||
return "";
|
return "";
|
||||||
@@ -43,7 +43,7 @@ std::string CasEcmGenerator::GenerateEcm(const EcmParameters& params) {
|
|||||||
return serialized_ecm;
|
return serialized_ecm;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcmGenerator::ProcessEcmParameters(
|
Status CasEcmGenerator::ProcessEcmParameters(
|
||||||
const EcmParameters& ecm_params, std::vector<EntitledKeyInfo>* keys) {
|
const EcmParameters& ecm_params, std::vector<EntitledKeyInfo>* keys) {
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
rotation_enabled_ = ecm_params.rotation_enabled;
|
rotation_enabled_ = ecm_params.rotation_enabled;
|
||||||
@@ -52,11 +52,11 @@ util::Status CasEcmGenerator::ProcessEcmParameters(
|
|||||||
keys->clear();
|
keys->clear();
|
||||||
uint32_t keys_needed = ecm_params.rotation_enabled ? 2 : 1;
|
uint32_t keys_needed = ecm_params.rotation_enabled ? 2 : 1;
|
||||||
if (ecm_params.key_params.size() < keys_needed) {
|
if (ecm_params.key_params.size() < keys_needed) {
|
||||||
return {util::error::INVALID_ARGUMENT,
|
return {error::INVALID_ARGUMENT,
|
||||||
"Number of supplied keys is wrong (check rotation periods)."};
|
"Number of supplied keys is wrong (check rotation periods)."};
|
||||||
}
|
}
|
||||||
for (int i = 0; i < keys_needed; i++) {
|
for (int i = 0; i < keys_needed; i++) {
|
||||||
util::Status status = ValidateKeyParameters(ecm_params.key_params[i]);
|
Status status = ValidateKeyParameters(ecm_params.key_params[i]);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -70,80 +70,77 @@ util::Status CasEcmGenerator::ProcessEcmParameters(
|
|||||||
current_key_index_ = 0;
|
current_key_index_ = 0;
|
||||||
current_key_even_ = true;
|
current_key_even_ = true;
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcmGenerator::ValidateKeyId(const std::string& id) {
|
Status CasEcmGenerator::ValidateKeyId(const std::string& id) {
|
||||||
if (id.empty()) {
|
if (id.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Key id is empty."};
|
return {error::INVALID_ARGUMENT, "Key id is empty."};
|
||||||
}
|
}
|
||||||
if (id.size() > kMaxBytesKeyIdField) {
|
if (id.size() > kMaxBytesKeyIdField) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Key id is too long."};
|
return {error::INVALID_ARGUMENT, "Key id is too long."};
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcmGenerator::ValidateKeyData(const std::string& key_data) {
|
Status CasEcmGenerator::ValidateKeyData(const std::string& key_data) {
|
||||||
if (key_data.empty()) {
|
if (key_data.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Key data is empty."};
|
return {error::INVALID_ARGUMENT, "Key data is empty."};
|
||||||
}
|
}
|
||||||
if (key_data.size() != kKeyDataSize) {
|
if (key_data.size() != kKeyDataSize) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Key data is wrong size."};
|
return {error::INVALID_ARGUMENT, "Key data is wrong size."};
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcmGenerator::ValidateIv(const std::string& iv,
|
Status CasEcmGenerator::ValidateIv(const std::string& iv, size_t required_size) {
|
||||||
size_t required_size) {
|
|
||||||
if (iv.empty()) {
|
if (iv.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT, "IV is empty."};
|
return {error::INVALID_ARGUMENT, "IV is empty."};
|
||||||
}
|
}
|
||||||
if (required_size != 8 && required_size != 16) {
|
if (required_size != 8 && required_size != 16) {
|
||||||
return {util::error::INTERNAL, "IV size has not been set up correctly."};
|
return {error::INTERNAL, "IV size has not been set up correctly."};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iv.size() != required_size) {
|
if (iv.size() != required_size) {
|
||||||
return {util::error::INVALID_ARGUMENT,
|
return {error::INVALID_ARGUMENT, "IV has wrong or inconsistent size."};
|
||||||
"IV has wrong or inconsistent size."};
|
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcmGenerator::ValidateWrappedKeyIv(const std::string& iv) {
|
Status CasEcmGenerator::ValidateWrappedKeyIv(const std::string& iv) {
|
||||||
// All wrapped key IVs must be 16 bytes.
|
// All wrapped key IVs must be 16 bytes.
|
||||||
util::Status status = ValidateIv(iv, kIvSize16);
|
Status status = ValidateIv(iv, kIvSize16);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << " Wrapped key IV is not valid: " << status;
|
LOG(ERROR) << " Wrapped key IV is not valid: " << status;
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcmGenerator::ValidateContentIv(const std::string& iv) {
|
Status CasEcmGenerator::ValidateContentIv(const std::string& iv) {
|
||||||
// If content_iv_size_ is zero, use this IV as the size for all future IVs in
|
// If content_iv_size_ is zero, use this IV as the size for all future IVs in
|
||||||
// this stream.
|
// this stream.
|
||||||
if (content_iv_size_ == 0) {
|
if (content_iv_size_ == 0) {
|
||||||
content_iv_size_ = iv.size();
|
content_iv_size_ = iv.size();
|
||||||
}
|
}
|
||||||
util::Status status = ValidateIv(iv, content_iv_size_);
|
Status status = ValidateIv(iv, content_iv_size_);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
LOG(ERROR) << " Content IV is not valid: " << status;
|
LOG(ERROR) << " Content IV is not valid: " << status;
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status CasEcmGenerator::ValidateKeyParameters(
|
Status CasEcmGenerator::ValidateKeyParameters(const KeyParameters& key_params) {
|
||||||
const KeyParameters& key_params) {
|
Status status;
|
||||||
util::Status status;
|
|
||||||
status = ValidateKeyId(key_params.key_id);
|
status = ValidateKeyId(key_params.key_id);
|
||||||
if (!status.ok()) return status;
|
if (!status.ok()) return status;
|
||||||
if (key_params.content_ivs.empty()) {
|
if (key_params.content_ivs.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Content IVs is empty."};
|
return {error::INVALID_ARGUMENT, "Content IVs is empty."};
|
||||||
}
|
}
|
||||||
for (int i = 0; i < key_params.content_ivs.size(); i++) {
|
for (int i = 0; i < key_params.content_ivs.size(); i++) {
|
||||||
status = ValidateContentIv(key_params.content_ivs[i]);
|
status = ValidateContentIv(key_params.content_ivs[i]);
|
||||||
if (!status.ok()) return status;
|
if (!status.ok()) return status;
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
|
|||||||
@@ -74,16 +74,16 @@ class CasEcmGenerator {
|
|||||||
private:
|
private:
|
||||||
friend class CasEcmGeneratorTest;
|
friend class CasEcmGeneratorTest;
|
||||||
|
|
||||||
util::Status ProcessEcmParameters(const EcmParameters& ecm_params,
|
Status ProcessEcmParameters(const EcmParameters& ecm_params,
|
||||||
std::vector<EntitledKeyInfo>* keys);
|
std::vector<EntitledKeyInfo>* keys);
|
||||||
|
|
||||||
util::Status ProcessEcmParameters(const EcmParameters& ecm_params);
|
Status ProcessEcmParameters(const EcmParameters& ecm_params);
|
||||||
util::Status ValidateKeyId(const std::string& id);
|
Status ValidateKeyId(const std::string& id);
|
||||||
util::Status ValidateKeyData(const std::string& key_data);
|
Status ValidateKeyData(const std::string& key_data);
|
||||||
util::Status ValidateWrappedKeyIv(const std::string& iv);
|
Status ValidateWrappedKeyIv(const std::string& iv);
|
||||||
util::Status ValidateIv(const std::string& iv, size_t required_size);
|
Status ValidateIv(const std::string& iv, size_t required_size);
|
||||||
util::Status ValidateContentIv(const std::string& iv);
|
Status ValidateContentIv(const std::string& iv);
|
||||||
util::Status ValidateKeyParameters(const KeyParameters& key_params);
|
Status ValidateKeyParameters(const KeyParameters& key_params);
|
||||||
|
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
uint32_t generation_ = 0;
|
uint32_t generation_ = 0;
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ constexpr char kFakeCasEncryptionResponseKeyId[] = "fake_key_id.....";
|
|||||||
constexpr char kFakeCasEncryptionResponseKeyData[] =
|
constexpr char kFakeCasEncryptionResponseKeyData[] =
|
||||||
"fakefakefakefakefakefakefakefake";
|
"fakefakefakefakefakefakefakefake";
|
||||||
|
|
||||||
util::Status HandleCasEncryptionRequest(const std::string& request_string,
|
Status HandleCasEncryptionRequest(const std::string& request_string,
|
||||||
std::string* signed_response_string) {
|
std::string* signed_response_string) {
|
||||||
CasEncryptionRequest request;
|
CasEncryptionRequest request;
|
||||||
request.ParseFromString(request_string);
|
request.ParseFromString(request_string);
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ util::Status HandleCasEncryptionRequest(const std::string& request_string,
|
|||||||
SignedCasEncryptionResponse signed_response;
|
SignedCasEncryptionResponse signed_response;
|
||||||
signed_response.set_response(response_string);
|
signed_response.set_response(response_string);
|
||||||
signed_response.SerializeToString(signed_response_string);
|
signed_response.SerializeToString(signed_response_string);
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -99,8 +99,8 @@ class CasEcmGeneratorTest : public testing::Test {
|
|||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status ProcessEcmParameters(const EcmParameters& params,
|
Status ProcessEcmParameters(const EcmParameters& params,
|
||||||
std::vector<EntitledKeyInfo>* keys) {
|
std::vector<EntitledKeyInfo>* keys) {
|
||||||
return ecm_gen_.ProcessEcmParameters(params, keys);
|
return ecm_gen_.ProcessEcmParameters(params, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ TEST_F(CasEcmGeneratorTest, InitializeNoRotation) {
|
|||||||
|
|
||||||
SetTestConfig1(&ecm_params);
|
SetTestConfig1(&ecm_params);
|
||||||
|
|
||||||
util::Status status = ProcessEcmParameters(ecm_params, &keys);
|
Status status = ProcessEcmParameters(ecm_params, &keys);
|
||||||
ASSERT_OK(status);
|
ASSERT_OK(status);
|
||||||
|
|
||||||
ASSERT_EQ(EntitlementKeySize(ecm_params), 16);
|
ASSERT_EQ(EntitlementKeySize(ecm_params), 16);
|
||||||
@@ -254,7 +254,7 @@ TEST_F(CasEcmGeneratorTest, InitializeSimpleRotation) {
|
|||||||
SetTestConfig2(&ecm_params);
|
SetTestConfig2(&ecm_params);
|
||||||
ecm_init_params_.key_rotation_enabled = true;
|
ecm_init_params_.key_rotation_enabled = true;
|
||||||
|
|
||||||
util::Status status = ProcessEcmParameters(ecm_params, &keys);
|
Status status = ProcessEcmParameters(ecm_params, &keys);
|
||||||
|
|
||||||
EXPECT_TRUE(status.ok());
|
EXPECT_TRUE(status.ok());
|
||||||
EXPECT_TRUE(ecm_gen_.initialized());
|
EXPECT_TRUE(ecm_gen_.initialized());
|
||||||
|
|||||||
@@ -68,14 +68,14 @@ class MockCasEcm : public CasEcm {
|
|||||||
return SerializeEcm(keys);
|
return SerializeEcm(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual util::Status MockWrapEntitledKeys(
|
virtual Status MockWrapEntitledKeys(
|
||||||
const std::string& track_type, const std::vector<EntitledKeyInfo*>& keys) {
|
const std::string& track_type, const std::vector<EntitledKeyInfo*>& keys) {
|
||||||
for (auto entitled_key : keys) {
|
for (auto entitled_key : keys) {
|
||||||
entitled_key->entitlement_key_id = "entitlement_Mock";
|
entitled_key->entitlement_key_id = "entitlement_Mock";
|
||||||
entitled_key->wrapped_key_value = "MockMockMockMock";
|
entitled_key->wrapped_key_value = "MockMockMockMock";
|
||||||
entitled_key->wrapped_key_iv = "WRAPPED_KIV....x";
|
entitled_key->wrapped_key_iv = "WRAPPED_KIV....x";
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MockSetup(bool two_keys, const std::string& track_type, uint32_t generation,
|
void MockSetup(bool two_keys, const std::string& track_type, uint32_t generation,
|
||||||
@@ -226,7 +226,7 @@ TEST_F(CasEcmTest, GenerateEcmNotInitialized) {
|
|||||||
EntitledKeyInfo key1;
|
EntitledKeyInfo key1;
|
||||||
std::string ecm_data;
|
std::string ecm_data;
|
||||||
uint32_t gen;
|
uint32_t gen;
|
||||||
EXPECT_EQ(util::error::INTERNAL,
|
EXPECT_EQ(error::INTERNAL,
|
||||||
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm_data, &gen)
|
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm_data, &gen)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -237,7 +237,7 @@ TEST_F(CasEcmTest, GenerateEcm2NotInitialized) {
|
|||||||
EntitledKeyInfo key2;
|
EntitledKeyInfo key2;
|
||||||
std::string ecm_data;
|
std::string ecm_data;
|
||||||
uint32_t gen;
|
uint32_t gen;
|
||||||
EXPECT_EQ(util::error::INTERNAL,
|
EXPECT_EQ(error::INTERNAL,
|
||||||
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm_data, &gen)
|
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm_data, &gen)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ TEST_F(CasEcmTest, GenerateEcm2NotInitialized) {
|
|||||||
TEST_F(CasEcmTest, SetResponseNotInitialized) {
|
TEST_F(CasEcmTest, SetResponseNotInitialized) {
|
||||||
CasEcm ecm_gen;
|
CasEcm ecm_gen;
|
||||||
const std::string response("anything");
|
const std::string response("anything");
|
||||||
EXPECT_EQ(util::error::INTERNAL,
|
EXPECT_EQ(error::INTERNAL,
|
||||||
ecm_gen.ProcessCasEncryptionResponse(response).error_code());
|
ecm_gen.ProcessCasEncryptionResponse(response).error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ TEST_F(CasEcmTest, InitNoTracksFail) {
|
|||||||
CasEcm ecm_gen;
|
CasEcm ecm_gen;
|
||||||
std::string request;
|
std::string request;
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
util::error::INVALID_ARGUMENT,
|
error::INVALID_ARGUMENT,
|
||||||
ecm_gen.Initialize(content_id_, provider_, params_default_, &request)
|
ecm_gen.Initialize(content_id_, provider_, params_default_, &request)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -270,7 +270,7 @@ TEST_F(CasEcmTest, SecondInitFail) {
|
|||||||
std::string request;
|
std::string request;
|
||||||
ASSERT_OK(
|
ASSERT_OK(
|
||||||
ecm_gen.Initialize(content_id_, provider_, params_simple_, &request));
|
ecm_gen.Initialize(content_id_, provider_, params_simple_, &request));
|
||||||
EXPECT_EQ(util::error::INTERNAL,
|
EXPECT_EQ(error::INTERNAL,
|
||||||
ecm_gen.Initialize(content_id_, provider_, params_simple_, &request)
|
ecm_gen.Initialize(content_id_, provider_, params_simple_, &request)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -280,7 +280,7 @@ TEST_F(CasEcmTest, InitEmptyContentIdFail) {
|
|||||||
const std::string empty_content_id;
|
const std::string empty_content_id;
|
||||||
std::string request;
|
std::string request;
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
util::error::INVALID_ARGUMENT,
|
error::INVALID_ARGUMENT,
|
||||||
ecm_gen.Initialize(empty_content_id, provider_, params_simple_, &request)
|
ecm_gen.Initialize(empty_content_id, provider_, params_simple_, &request)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -290,7 +290,7 @@ TEST_F(CasEcmTest, InitEmptyProviderFail) {
|
|||||||
const std::string empty_provider;
|
const std::string empty_provider;
|
||||||
std::string request;
|
std::string request;
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
util::error::INVALID_ARGUMENT,
|
error::INVALID_ARGUMENT,
|
||||||
ecm_gen.Initialize(content_id_, empty_provider, params_simple_, &request)
|
ecm_gen.Initialize(content_id_, empty_provider, params_simple_, &request)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ TEST_F(CasEcmTest, GenerateWithNoEntitlementOneKeyFail) {
|
|||||||
uint32_t gen;
|
uint32_t gen;
|
||||||
// This will fail because the entitlement key has not been acquired
|
// This will fail because the entitlement key has not been acquired
|
||||||
// (via server call and ProcessCasEncryptionResponse()).
|
// (via server call and ProcessCasEncryptionResponse()).
|
||||||
EXPECT_EQ(util::error::INTERNAL,
|
EXPECT_EQ(error::INTERNAL,
|
||||||
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)
|
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -334,13 +334,13 @@ TEST_F(CasEcmTest, GenerateWithNoEntitlementTwoKeysFail) {
|
|||||||
// This will fail because the entitlement keys have not been acquired
|
// This will fail because the entitlement keys have not been acquired
|
||||||
// (via server call and ProcessCasEncryptionResponse()).
|
// (via server call and ProcessCasEncryptionResponse()).
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
util::error::INTERNAL,
|
error::INTERNAL,
|
||||||
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
|
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CasEcmTest, RequestNullFail) {
|
TEST_F(CasEcmTest, RequestNullFail) {
|
||||||
CasEcm ecm_gen;
|
CasEcm ecm_gen;
|
||||||
EXPECT_EQ(util::error::INVALID_ARGUMENT,
|
EXPECT_EQ(error::INVALID_ARGUMENT,
|
||||||
ecm_gen.Initialize(content_id_, provider_, params_simple_, nullptr)
|
ecm_gen.Initialize(content_id_, provider_, params_simple_, nullptr)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -374,7 +374,7 @@ TEST_F(CasEcmTest, BadResponseFail) {
|
|||||||
std::string response;
|
std::string response;
|
||||||
ServerCall(request, &response, false, true);
|
ServerCall(request, &response, false, true);
|
||||||
|
|
||||||
EXPECT_EQ(util::error::INTERNAL,
|
EXPECT_EQ(error::INTERNAL,
|
||||||
ecm_gen.ProcessCasEncryptionResponse(response).error_code());
|
ecm_gen.ProcessCasEncryptionResponse(response).error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,7 +411,7 @@ TEST_F(CasEcmTest, GenerateOneKeyNoGenFail) {
|
|||||||
ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response));
|
ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response));
|
||||||
|
|
||||||
std::string ecm;
|
std::string ecm;
|
||||||
EXPECT_EQ(util::error::INVALID_ARGUMENT,
|
EXPECT_EQ(error::INVALID_ARGUMENT,
|
||||||
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, nullptr)
|
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, nullptr)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -431,7 +431,7 @@ TEST_F(CasEcmTest, GenerateOneKeyBadKeyIdFail) {
|
|||||||
|
|
||||||
std::string ecm;
|
std::string ecm;
|
||||||
uint32_t gen;
|
uint32_t gen;
|
||||||
EXPECT_EQ(util::error::INVALID_ARGUMENT,
|
EXPECT_EQ(error::INVALID_ARGUMENT,
|
||||||
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)
|
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
@@ -455,7 +455,7 @@ TEST_F(CasEcmTest, GenerateOneKeyWrong) {
|
|||||||
EXPECT_THAT(0, gen);
|
EXPECT_THAT(0, gen);
|
||||||
EXPECT_THAT(77, ecm.size());
|
EXPECT_THAT(77, ecm.size());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
util::error::INVALID_ARGUMENT,
|
error::INVALID_ARGUMENT,
|
||||||
ecm_gen.GenerateEcm(&key1, &key1, kTrackTypeSD, &ecm, &gen).error_code());
|
ecm_gen.GenerateEcm(&key1, &key1, kTrackTypeSD, &ecm, &gen).error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,7 +477,7 @@ TEST_F(CasEcmTest, GenerateTwoKeysIvSizeFail) {
|
|||||||
std::string ecm;
|
std::string ecm;
|
||||||
uint32_t gen;
|
uint32_t gen;
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
util::error::INVALID_ARGUMENT,
|
error::INVALID_ARGUMENT,
|
||||||
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
|
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,7 +548,7 @@ TEST_F(CasEcmTest, GenerateThreeKeysIvSize16x16Fail) {
|
|||||||
std::string ecm;
|
std::string ecm;
|
||||||
uint32_t gen;
|
uint32_t gen;
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
util::error::INTERNAL,
|
error::INTERNAL,
|
||||||
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
|
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,282 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright 2018 Google LLC.
|
|
||||||
//
|
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
|
||||||
// widevine-licensing@google.com.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include "media_cas_packager_sdk/internal/ecmg.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "glog/logging.h"
|
|
||||||
#include "absl/memory/memory.h"
|
|
||||||
#include "absl/strings/str_cat.h"
|
|
||||||
#include "common/status.h"
|
|
||||||
#include "example/constants.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/ecmg_constants.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/util.h"
|
|
||||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
|
||||||
|
|
||||||
namespace widevine {
|
|
||||||
namespace cas {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Local helper function that processes all the parameters in an ECMG message.
|
|
||||||
util::Status ProcessParameters(const char* message, size_t message_length,
|
|
||||||
EcmgParameters* parameters) {
|
|
||||||
DCHECK(message);
|
|
||||||
DCHECK(parameters);
|
|
||||||
|
|
||||||
uint16_t parameter_type;
|
|
||||||
uint16_t parameter_length;
|
|
||||||
// 'offset' is used to track where we are within |message|.
|
|
||||||
size_t offset = 0;
|
|
||||||
// There could be CW_per_msg instances of CP_CW_combinations,
|
|
||||||
// so we need to track how many we have processed so far
|
|
||||||
// in order to know where to store the next CP_CW_combination.
|
|
||||||
int current_cp_cw_combination_index = 0;
|
|
||||||
while (offset != message_length) {
|
|
||||||
BigEndianToHost16(¶meter_type, message + offset);
|
|
||||||
offset += PARAMETER_TYPE_SIZE;
|
|
||||||
BigEndianToHost16(¶meter_length, message + offset);
|
|
||||||
offset += PARAMETER_LENGTH_SIZE;
|
|
||||||
switch (parameter_type) {
|
|
||||||
case ACCESS_CRITERIA: {
|
|
||||||
LOG(WARNING) << "Ignoring access_criteria parameter of "
|
|
||||||
<< parameter_length << " bytes long";
|
|
||||||
offset += parameter_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ECM_CHANNEL_ID: {
|
|
||||||
if (parameter_length != ECM_CHANNEL_ID_SIZE) {
|
|
||||||
return util::Status(
|
|
||||||
util::error::INVALID_ARGUMENT,
|
|
||||||
absl::StrCat("Invalid parameter length ", parameter_length,
|
|
||||||
" for parameter type ", parameter_type));
|
|
||||||
}
|
|
||||||
BigEndianToHost16(¶meters->ecm_channel_id, message + offset);
|
|
||||||
offset += parameter_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ECM_STREAM_ID: {
|
|
||||||
if (parameter_length != ECM_STREAM_ID_SIZE) {
|
|
||||||
return util::Status(
|
|
||||||
util::error::INVALID_ARGUMENT,
|
|
||||||
absl::StrCat("Invalid parameter length ", parameter_length,
|
|
||||||
" for parameter type ", parameter_type));
|
|
||||||
}
|
|
||||||
BigEndianToHost16(¶meters->ecm_stream_id, message + offset);
|
|
||||||
offset += parameter_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CP_CW_COMBINATION: {
|
|
||||||
if (current_cp_cw_combination_index > 2) {
|
|
||||||
// We can have at most 3 CP_CW_Combinations.
|
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
|
||||||
"We only support up to 2 control words in the "
|
|
||||||
"CW_provision message");
|
|
||||||
}
|
|
||||||
EcmgCpCwCombination* combination =
|
|
||||||
¶meters->cp_cw_combinations[current_cp_cw_combination_index++];
|
|
||||||
BigEndianToHost16(&combination->cp, message + offset);
|
|
||||||
offset += CP_SIZE;
|
|
||||||
size_t cw_size = parameter_length - CP_SIZE;
|
|
||||||
combination->cw = std::string(message + offset, cw_size);
|
|
||||||
offset += cw_size;
|
|
||||||
// TODO(user): This is a temporary hack to let the ECM generator
|
|
||||||
// know how many keys to include in the ECM.
|
|
||||||
// CW_per_msg should have been set during channel set-up instead.
|
|
||||||
parameters->cw_per_msg = current_cp_cw_combination_index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CP_DURATION: {
|
|
||||||
if (parameter_length != CP_DURATION_SIZE) {
|
|
||||||
return util::Status(
|
|
||||||
util::error::INVALID_ARGUMENT,
|
|
||||||
absl::StrCat("Invalid parameter length ", parameter_length,
|
|
||||||
" for parameter type ", parameter_type));
|
|
||||||
}
|
|
||||||
BigEndianToHost16(¶meters->cp_duration, message + offset);
|
|
||||||
offset += parameter_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CP_NUMBER: {
|
|
||||||
if (parameter_length != CP_NUMBER_SIZE) {
|
|
||||||
return util::Status(
|
|
||||||
util::error::INVALID_ARGUMENT,
|
|
||||||
absl::StrCat("Invalid parameter length ", parameter_length,
|
|
||||||
" for parameter type ", parameter_type));
|
|
||||||
}
|
|
||||||
BigEndianToHost16(¶meters->cp_number, message + offset);
|
|
||||||
offset += parameter_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CW_ENCRYPTION: {
|
|
||||||
LOG(WARNING) << "Ignoring CW_encryption parameter of "
|
|
||||||
<< parameter_length << " bytes long";
|
|
||||||
offset += parameter_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NOMINAL_CP_DURATION: {
|
|
||||||
if (parameter_length != NOMINAL_CP_DURATION_SIZE) {
|
|
||||||
return util::Status(
|
|
||||||
util::error::INVALID_ARGUMENT,
|
|
||||||
absl::StrCat("Invalid parameter length ", parameter_length,
|
|
||||||
" for parameter type ", parameter_type));
|
|
||||||
}
|
|
||||||
BigEndianToHost16(¶meters->nominal_cp_duration, message + offset);
|
|
||||||
offset += parameter_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return util::Status(
|
|
||||||
util::error::UNIMPLEMENTED,
|
|
||||||
absl::StrCat("No implementation yet to process parameter of type ",
|
|
||||||
parameter_type));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return util::OkStatus();
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
util::Status Ecmg::ProcessStreamSetupMessage(const char* message,
|
|
||||||
size_t message_length) {
|
|
||||||
DCHECK(message);
|
|
||||||
|
|
||||||
EcmgParameters parameters;
|
|
||||||
util::Status status = ProcessParameters(message, message_length, ¶meters);
|
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
if (parameters.ecm_channel_id == 0 || parameters.ecm_stream_id == 0 ||
|
|
||||||
parameters.nominal_cp_duration == 0) {
|
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
|
||||||
"Missing required parameter");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channels_.find(parameters.ecm_channel_id) == channels_.end()) {
|
|
||||||
std::unique_ptr<EcmgChannel> new_channel = absl::make_unique<EcmgChannel>();
|
|
||||||
channels_[parameters.ecm_channel_id] = std::move(new_channel);
|
|
||||||
}
|
|
||||||
EcmgChannel* channel =
|
|
||||||
channels_.find(parameters.ecm_channel_id)->second.get();
|
|
||||||
auto stream_entry = channel->streams.find(parameters.ecm_stream_id);
|
|
||||||
if (stream_entry == channel->streams.end()) {
|
|
||||||
std::unique_ptr<EcmgStream> new_stream = absl::make_unique<EcmgStream>();
|
|
||||||
new_stream->nominal_cp_duration = parameters.nominal_cp_duration;
|
|
||||||
channel->streams[parameters.ecm_stream_id] = std::move(new_stream);
|
|
||||||
} else {
|
|
||||||
EcmgStream* existing_stream = stream_entry->second.get();
|
|
||||||
existing_stream->nominal_cp_duration = parameters.nominal_cp_duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
return util::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
util::Status Ecmg::ProcessCwProvisionMessage(const char* message,
|
|
||||||
size_t message_length,
|
|
||||||
std::string* response) {
|
|
||||||
DCHECK(message);
|
|
||||||
DCHECK(response);
|
|
||||||
|
|
||||||
EcmgParameters parameters;
|
|
||||||
util::Status status = ProcessParameters(message, message_length, ¶meters);
|
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
if (parameters.ecm_channel_id == 0 || parameters.ecm_stream_id == 0 ||
|
|
||||||
parameters.cp_number == 0 || parameters.cw_per_msg == 0) {
|
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
|
||||||
"Missing required parameter");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(user): Figure out what to do with ECM_channel_ID and ECM_stream_ID.
|
|
||||||
// - We certainly need to check the channel/stream has been setup
|
|
||||||
// - Retrieve config parameters such as lead_CW and CW_per_msg
|
|
||||||
// - In some config, we need to keep CW for previous CP to be included in the
|
|
||||||
// current ECM
|
|
||||||
// TODO(user): Remove debug loop below.
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
for (int j = 0; j < parameters.cp_cw_combinations[i].cw.size(); j++) {
|
|
||||||
printf("%x ",
|
|
||||||
static_cast<uint16_t>(parameters.cp_cw_combinations[i].cw[j]));
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool key_rotation_enabled = parameters.cw_per_msg > 1;
|
|
||||||
|
|
||||||
// Create an instance of CasEcm in order to set the entitlement keys.
|
|
||||||
// TODO(user): The section of code below for constructing CasEcm should
|
|
||||||
// be optimized. There should be a single instance of CasEcm for each stream.
|
|
||||||
// Right now, this is hard to do because CasEcmGenerator contains the CasEcm.
|
|
||||||
std::unique_ptr<CasEcm> ecm = absl::make_unique<CasEcm>();
|
|
||||||
// TODO(user): Revisit this hardcoded ecm_init_params.
|
|
||||||
EcmInitParameters ecm_init_params;
|
|
||||||
ecm_init_params.content_iv_size = kIvSize8;
|
|
||||||
ecm_init_params.key_rotation_enabled = key_rotation_enabled;
|
|
||||||
// TODO(user): Allow caller to specify the crypto mode.
|
|
||||||
ecm_init_params.crypto_mode = CryptoMode::kAesCtr;
|
|
||||||
// Only encrypt one video track.
|
|
||||||
ecm_init_params.track_types.push_back(kDefaultTrackTypeSd);
|
|
||||||
std::string entitlement_request;
|
|
||||||
std::string entitlement_response;
|
|
||||||
// 'content_id' and 'provider' are used in entitlement key request/response
|
|
||||||
// only, NOT needed for generating ECM.
|
|
||||||
// So for initial demo, we can just use hardcoded value because we are using
|
|
||||||
// hardcoded entitlement key anyway.
|
|
||||||
// TODO(user): When we want to retrieve entitlement key from License Server
|
|
||||||
// we need to figure out a way to provide real 'content_id' and 'provder'
|
|
||||||
// to this function here from the Simulcrypt API.
|
|
||||||
if (!(status = ecm->Initialize(kDefaultContentId, kDefaultProvider,
|
|
||||||
ecm_init_params, &entitlement_request))
|
|
||||||
.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
if (!(status = fixed_key_fetcher_.RequestEntitlementKey(
|
|
||||||
entitlement_request, &entitlement_response))
|
|
||||||
.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
if (!(status = ecm->ProcessCasEncryptionResponse(entitlement_response))
|
|
||||||
.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
CasEcmGenerator ecm_generator;
|
|
||||||
ecm_generator.set_ecm(std::move(ecm));
|
|
||||||
EcmParameters ecm_param;
|
|
||||||
ecm_param.rotation_enabled = key_rotation_enabled;
|
|
||||||
for (int i = 0; i <= parameters.cw_per_msg; i++) {
|
|
||||||
ecm_param.key_params.emplace_back();
|
|
||||||
ecm_param.key_params[i].key_data = parameters.cp_cw_combinations[i].cw;
|
|
||||||
// TODO(user): MUST have a better way to derive/retrieve key_id.
|
|
||||||
// Currently set it to be the same as the key itself just for demo purpose.
|
|
||||||
ecm_param.key_params[i].key_id = ecm_param.key_params[i].key_data;
|
|
||||||
// TODO(user): MUST have a better way to generate/retrieve content_iv.
|
|
||||||
ecm_param.key_params[i].content_ivs.push_back(
|
|
||||||
std::string(kDefaultContentIv8Bytes));
|
|
||||||
}
|
|
||||||
std::string serialized_ecm = ecm_generator.GenerateEcm(ecm_param);
|
|
||||||
std::cout << "serialized_ecm: " << serialized_ecm << std::endl;
|
|
||||||
for (int i = 0; i < serialized_ecm.size(); i++) {
|
|
||||||
printf("'\\x%x', ", static_cast<uint16_t>(serialized_ecm.at(i)));
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
LOG(INFO) << "ECM size: " << serialized_ecm.size();
|
|
||||||
return util::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace cas
|
|
||||||
} // namespace widevine
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright 2018 Google LLC.
|
|
||||||
//
|
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
|
||||||
// widevine-licensing@google.com.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_H_
|
|
||||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include "common/status.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/ecm.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
|
|
||||||
|
|
||||||
namespace widevine {
|
|
||||||
namespace cas {
|
|
||||||
|
|
||||||
// A struct that represent a CP_CW_Combination.
|
|
||||||
struct EcmgCpCwCombination {
|
|
||||||
uint16_t cp = 0; // crypto period
|
|
||||||
std::string cw; // control word
|
|
||||||
};
|
|
||||||
|
|
||||||
// A struct that is used to hold all possible parameters for a ECMG message.
|
|
||||||
struct EcmgParameters {
|
|
||||||
// Default value of 0 for fields below is usually considered invalid.
|
|
||||||
// Hence checking against 0 is used to detect whether each parameter
|
|
||||||
// is set in the message.
|
|
||||||
uint8_t cw_per_msg = 0;
|
|
||||||
uint16_t ecm_channel_id = 0;
|
|
||||||
uint16_t ecm_stream_id = 0;
|
|
||||||
uint16_t nominal_cp_duration = 0;
|
|
||||||
uint16_t cp_number = 0; // crypto period number
|
|
||||||
uint16_t cp_duration = 0; // crypto period duration
|
|
||||||
// CW_per_msg could 1, 2, or 3,
|
|
||||||
// so there can be up to 3 CP_CW_Combinations
|
|
||||||
EcmgCpCwCombination cp_cw_combinations[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
// A struct that holds information about a ECMG stream within a channel.
|
|
||||||
struct EcmgStream {
|
|
||||||
uint16_t nominal_cp_duration = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A struct that holds information about a ECMG channel.
|
|
||||||
struct EcmgChannel {
|
|
||||||
// Map from ECM_stream_ID to an instance of EcmgStream.
|
|
||||||
std::map<uint16_t, std::unique_ptr<EcmgStream>> streams;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A class that process Simulcrypt ECMG messages.
|
|
||||||
// This class is NOT thread-safe.
|
|
||||||
class Ecmg {
|
|
||||||
public:
|
|
||||||
Ecmg() = default;
|
|
||||||
Ecmg(const Ecmg&) = delete;
|
|
||||||
Ecmg& operator=(const Ecmg&) = delete;
|
|
||||||
virtual ~Ecmg() = default;
|
|
||||||
|
|
||||||
// Process |message| of length |message_length|.
|
|
||||||
// |message| is expected to be a Stream_set-up message.
|
|
||||||
// Any error during processing would be turned via util::Status.
|
|
||||||
util::Status ProcessStreamSetupMessage(const char* message,
|
|
||||||
size_t message_length);
|
|
||||||
|
|
||||||
// Process |message| of length |message_length|.
|
|
||||||
// |message| is expected to be a CW_provision request message.
|
|
||||||
// ECM_response response message will be returned via |response|.
|
|
||||||
// Any error during processing would be turned via util::Status.
|
|
||||||
util::Status ProcessCwProvisionMessage(const char* message,
|
|
||||||
size_t message_length,
|
|
||||||
std::string* response);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Keep track of all the channels.
|
|
||||||
std::map<uint16_t, std::unique_ptr<EcmgChannel>> channels_;
|
|
||||||
FixedKeyFetcher fixed_key_fetcher_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace cas
|
|
||||||
} // namespace widevine
|
|
||||||
|
|
||||||
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_H_
|
|
||||||
619
media_cas_packager_sdk/internal/ecmg_client_handler.cc
Normal file
619
media_cas_packager_sdk/internal/ecmg_client_handler.cc
Normal file
@@ -0,0 +1,619 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2018 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "common/crypto_util.h"
|
||||||
|
#include "example/constants.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/ecmg_constants.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/util.h"
|
||||||
|
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
||||||
|
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||||
|
|
||||||
|
// 'section_TSpkt_flag' defines the format of the ECM.
|
||||||
|
// We only support MPEG-2 transport stream packet format for now.
|
||||||
|
// We do NOT support MPEG-2 section format yet.
|
||||||
|
// TODO(user): Understand the difference between the two formats.
|
||||||
|
static constexpr uint8_t kSectionTSpktFlag = 0x01;
|
||||||
|
// 'max_stream' parameter defines the max number of simultaneous opened streams
|
||||||
|
// suppported by an ECMG on a channel.
|
||||||
|
// A value of 0 means that this maximum is not known.
|
||||||
|
static constexpr uint16_t kMaxStream = 0;
|
||||||
|
// 'lead_CW' parameter defines the number of contro lwords required in
|
||||||
|
// advance to build an ECM.
|
||||||
|
// TODO(user): Support other values of 'lead_CW' in combination with
|
||||||
|
// other values for 'CW_per_msg'.
|
||||||
|
static constexpr uint8_t kLeadCw = 1;
|
||||||
|
// ' CW_per_msg' parameter defines the number of control words needed by the
|
||||||
|
// ECMG per control word provision message.
|
||||||
|
static constexpr uint8_t kCwPerMsg = 2;
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Local helper function that processes all the params in an ECMG request.
|
||||||
|
Status HandleParameters(const char* const request, size_t request_length,
|
||||||
|
EcmgParameters* params) {
|
||||||
|
DCHECK(request);
|
||||||
|
DCHECK(params);
|
||||||
|
uint16_t param_type;
|
||||||
|
uint16_t param_length;
|
||||||
|
// 'offset' is used to track where we are within |request|.
|
||||||
|
size_t offset = 0;
|
||||||
|
// There could be CW_per_msg instances of CP_CW_combinations,
|
||||||
|
// so we need to track how many we have processed so far
|
||||||
|
// in order to know where to store the next CP_CW_combination.
|
||||||
|
int current_cp_cw_combination_index = 0;
|
||||||
|
while (offset != request_length) {
|
||||||
|
BigEndianToHost16(¶m_type, request + offset);
|
||||||
|
offset += PARAMETER_TYPE_SIZE;
|
||||||
|
BigEndianToHost16(¶m_length, request + offset);
|
||||||
|
offset += PARAMETER_LENGTH_SIZE;
|
||||||
|
switch (param_type) {
|
||||||
|
case ACCESS_CRITERIA: {
|
||||||
|
LOG(WARNING) << "Ignoring access_criteria parameter of " << param_length
|
||||||
|
<< " bytes long";
|
||||||
|
offset += param_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CP_CW_COMBINATION: {
|
||||||
|
if (current_cp_cw_combination_index > 2) {
|
||||||
|
// We can have at most 3 CP_CW_Combinations.
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
"We only support up to 2 control words in the "
|
||||||
|
"CW_provision request");
|
||||||
|
}
|
||||||
|
EcmgCpCwCombination* combination =
|
||||||
|
¶ms->cp_cw_combinations[current_cp_cw_combination_index++];
|
||||||
|
BigEndianToHost16(&combination->cp, request + offset);
|
||||||
|
offset += CP_SIZE;
|
||||||
|
size_t cw_size = param_length - CP_SIZE;
|
||||||
|
combination->cw = std::string(request + offset, cw_size);
|
||||||
|
offset += cw_size;
|
||||||
|
// TODO(user): This is a temporary hack to let the ECM generator
|
||||||
|
// know how many keys to include in the ECM.
|
||||||
|
// CW_per_msg should have been set during channel set-up instead.
|
||||||
|
params->cw_per_msg = current_cp_cw_combination_index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CP_DURATION: {
|
||||||
|
if (param_length != CP_DURATION_SIZE) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
absl::StrCat("Invalid parameter length ", param_length,
|
||||||
|
" for parameter type ", param_type));
|
||||||
|
}
|
||||||
|
BigEndianToHost16(¶ms->cp_duration, request + offset);
|
||||||
|
offset += param_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CP_NUMBER: {
|
||||||
|
if (param_length != CP_NUMBER_SIZE) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
absl::StrCat("Invalid parameter length ", param_length,
|
||||||
|
" for parameter type ", param_type));
|
||||||
|
}
|
||||||
|
BigEndianToHost16(¶ms->cp_number, request + offset);
|
||||||
|
offset += param_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CW_ENCRYPTION: {
|
||||||
|
LOG(WARNING) << "Ignoring CW_encryption parameter of " << param_length
|
||||||
|
<< " bytes long";
|
||||||
|
offset += param_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ECM_CHANNEL_ID: {
|
||||||
|
if (param_length != ECM_CHANNEL_ID_SIZE) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
absl::StrCat("Invalid parameter length ", param_length,
|
||||||
|
" for parameter type ", param_type));
|
||||||
|
}
|
||||||
|
BigEndianToHost16(¶ms->ecm_channel_id, request + offset);
|
||||||
|
offset += param_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ECM_ID: {
|
||||||
|
if (param_length != ECM_ID_SIZE) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
absl::StrCat("Invalid parameter length ", param_length,
|
||||||
|
" for parameter type ", param_type));
|
||||||
|
}
|
||||||
|
BigEndianToHost16(¶ms->ecm_id, request + offset);
|
||||||
|
offset += param_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ECM_STREAM_ID: {
|
||||||
|
if (param_length != ECM_STREAM_ID_SIZE) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
absl::StrCat("Invalid parameter length ", param_length,
|
||||||
|
" for parameter type ", param_type));
|
||||||
|
}
|
||||||
|
BigEndianToHost16(¶ms->ecm_stream_id, request + offset);
|
||||||
|
offset += param_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NOMINAL_CP_DURATION: {
|
||||||
|
if (param_length != NOMINAL_CP_DURATION_SIZE) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
absl::StrCat("Invalid parameter length ", param_length,
|
||||||
|
" for parameter type ", param_type));
|
||||||
|
}
|
||||||
|
BigEndianToHost16(¶ms->nominal_cp_duration, request + offset);
|
||||||
|
offset += param_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SUPER_CAS_ID: {
|
||||||
|
if (param_length != SUPER_CAS_ID_SIZE) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
absl::StrCat("Invalid parameter length ", param_length,
|
||||||
|
" for parameter type ", param_type));
|
||||||
|
}
|
||||||
|
BigEndianToHost32(¶ms->super_cas_id, request + offset);
|
||||||
|
offset += param_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return Status(
|
||||||
|
error::UNIMPLEMENTED,
|
||||||
|
absl::StrCat("No implementation yet to process parameter of type ",
|
||||||
|
param_type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 'protocol_version', 'message_type', 'message_length' to the message.
|
||||||
|
// TODO(user): Per jfore@, consider pass in a pointer to a structure
|
||||||
|
// #pragma pack(push, 1) // exact fit - no padding
|
||||||
|
// struct MessageHeader{
|
||||||
|
// uint8_t protocol_version;
|
||||||
|
// uint16_t message_type;
|
||||||
|
// uint16_t message_length;
|
||||||
|
// };
|
||||||
|
// #pragma pack(pop) // restore previous pack
|
||||||
|
void BuildMessageHeader(uint16_t message_type, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
*message_length = 0;
|
||||||
|
message[*message_length] = ECMG_SCS_PROTOCOL_VERSION;
|
||||||
|
*message_length += PROTOCOL_VERSION_SIZE;
|
||||||
|
Host16ToBigEndian(message + *message_length, &message_type);
|
||||||
|
*message_length += MESSAGE_TYPE_SIZE;
|
||||||
|
// NOTE: 'message_length' needs to be updated later after we have added all
|
||||||
|
// the params so we know the exact length of the all the params.
|
||||||
|
// Use 0 for 'total_param_length' until we know the length of the message.
|
||||||
|
uint16_t total_param_length = 0;
|
||||||
|
Host16ToBigEndian(message + *message_length, &total_param_length);
|
||||||
|
*message_length += MESSAGE_LENGTH_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a uint16_t parameter to the message.
|
||||||
|
void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_type);
|
||||||
|
*message_length += 2;
|
||||||
|
uint16_t param_length = 2;
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_length);
|
||||||
|
*message_length += 2;
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_value);
|
||||||
|
*message_length += param_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a uint8_t parameter to the message.
|
||||||
|
void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_type);
|
||||||
|
*message_length += 2;
|
||||||
|
uint16_t param_length = 1;
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_length);
|
||||||
|
*message_length += 2;
|
||||||
|
memcpy(message + *message_length, ¶m_value, param_length);
|
||||||
|
*message_length += param_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a param that is |param_length| bytes long.
|
||||||
|
void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length,
|
||||||
|
char* message, size_t* message_length) {
|
||||||
|
DCHECK(param_value);
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_type);
|
||||||
|
*message_length += 2;
|
||||||
|
Host16ToBigEndian(message + *message_length, ¶m_length);
|
||||||
|
*message_length += 2;
|
||||||
|
memcpy(message + *message_length, param_value, param_length);
|
||||||
|
*message_length += param_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildChannelError(uint16_t channel_id, uint16_t error_status, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
BuildMessageHeader(ECMG_CHANNEL_ERROR, message, message_length);
|
||||||
|
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
||||||
|
AddUint16Param(ERROR_STATUS, error_status, message, message_length);
|
||||||
|
// No setting Error_information parameter yet.
|
||||||
|
uint16_t total_param_length = *message_length - 5;
|
||||||
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildChannelStatus(uint16_t channel_id, EcmgConfig* config, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(config);
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
BuildMessageHeader(ECMG_CHANNEL_STATUS, message, message_length);
|
||||||
|
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
||||||
|
AddUint8Param(SECTION_TSPKT_FLAG, kSectionTSpktFlag, message, message_length);
|
||||||
|
// No setting AC_delay_start parameter yet.
|
||||||
|
// No setting AC_delay_stop parameter yet.
|
||||||
|
AddUint16Param(DELAY_START, config->delay_start, message, message_length);
|
||||||
|
AddUint16Param(DELAY_STOP, config->delay_stop, message, message_length);
|
||||||
|
// No setting transition_delay_start parameter yet.
|
||||||
|
// No setting transition_delay_stop parameter yet.
|
||||||
|
AddUint16Param(ECM_REP_PERIOD, config->ecm_rep_period, message,
|
||||||
|
message_length);
|
||||||
|
AddUint16Param(MAX_STREAMS, kMaxStream, message, message_length);
|
||||||
|
// min_CP_duration needs to be at least max_comp_time. So we just use
|
||||||
|
// max_comp_time here for now.
|
||||||
|
AddUint16Param(MIN_CP_DURATION, config->max_comp_time, message,
|
||||||
|
message_length);
|
||||||
|
AddUint8Param(LEAD_CW, kLeadCw, message, message_length);
|
||||||
|
AddUint8Param(CW_PER_MESSAGE, kCwPerMsg, message, message_length);
|
||||||
|
AddUint16Param(MAX_COMP_TIME, config->max_comp_time, message, message_length);
|
||||||
|
uint16_t total_param_length = *message_length - 5;
|
||||||
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildStreamError(uint16_t channel_id, uint16_t stream_id, uint16_t error_status,
|
||||||
|
char* message, size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
BuildMessageHeader(ECMG_STREAM_ERROR, message, message_length);
|
||||||
|
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
||||||
|
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
|
||||||
|
AddUint16Param(ERROR_STATUS, error_status, message, message_length);
|
||||||
|
// No setting Error_information parameter yet.
|
||||||
|
uint16_t total_param_length = *message_length - 5;
|
||||||
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildStreamStatus(uint16_t channel_id, uint16_t stream_id, uint16_t ecm_id,
|
||||||
|
uint8_t access_criteria_transfer_mode, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
BuildMessageHeader(ECMG_STREAM_STATUS, message, message_length);
|
||||||
|
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
||||||
|
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
|
||||||
|
AddUint16Param(ECM_ID, ecm_id, message, message_length);
|
||||||
|
AddUint8Param(ACCESS_CRITERIA_TRANSFER_MODE, access_criteria_transfer_mode,
|
||||||
|
message, message_length);
|
||||||
|
uint16_t total_param_length = *message_length - 5;
|
||||||
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildStreamCloseResponse(uint16_t channel_id, uint16_t stream_id,
|
||||||
|
char* message, size_t* message_length) {
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
BuildMessageHeader(ECMG_STREAM_CLOSE_RESPONSE, message, message_length);
|
||||||
|
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
||||||
|
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
|
||||||
|
uint16_t total_param_length = *message_length - 5;
|
||||||
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildEcmResponse(uint16_t channel_id, uint16_t stream_id, uint16_t cp_number,
|
||||||
|
uint8_t* ecm_datagram, char* message,
|
||||||
|
size_t* message_length) {
|
||||||
|
DCHECK(ecm_datagram);
|
||||||
|
DCHECK(message);
|
||||||
|
DCHECK(message_length);
|
||||||
|
BuildMessageHeader(ECMG_ECM_RESPONSE, message, message_length);
|
||||||
|
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
|
||||||
|
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
|
||||||
|
AddUint16Param(CP_NUMBER, cp_number, message, message_length);
|
||||||
|
AddParam(ECM_DATAGRAM, ecm_datagram, kTsPacketSize, message, message_length);
|
||||||
|
uint16_t total_param_length = *message_length - 5;
|
||||||
|
Host16ToBigEndian(message + 3, &total_param_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
EcmgClientHandler::EcmgClientHandler(EcmgConfig* ecmg_config) {
|
||||||
|
DCHECK(ecmg_config);
|
||||||
|
ecmg_config_ = ecmg_config;
|
||||||
|
channel_id_set_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EcmgClientHandler::HandleRequest(const char* const request, char* response,
|
||||||
|
size_t* response_length) {
|
||||||
|
DCHECK(request);
|
||||||
|
DCHECK(response);
|
||||||
|
|
||||||
|
uint8_t protocol_version;
|
||||||
|
uint16_t request_type;
|
||||||
|
uint16_t request_length;
|
||||||
|
// 'offset' is used to track where we are within |request|.
|
||||||
|
size_t offset = 0;
|
||||||
|
memcpy(&protocol_version, request, PROTOCOL_VERSION_SIZE);
|
||||||
|
if (protocol_version != ECMG_SCS_PROTOCOL_VERSION) {
|
||||||
|
// TODO(user): Should send an error response.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
offset += PROTOCOL_VERSION_SIZE;
|
||||||
|
BigEndianToHost16(&request_type, request + offset);
|
||||||
|
offset += MESSAGE_TYPE_SIZE;
|
||||||
|
BigEndianToHost16(&request_length, request + offset);
|
||||||
|
offset += MESSAGE_LENGTH_SIZE;
|
||||||
|
EcmgParameters params;
|
||||||
|
Status status = HandleParameters(request + offset, request_length, ¶ms);
|
||||||
|
if (!status.ok()) {
|
||||||
|
// TODO(user): Should send an error response.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (request_type) {
|
||||||
|
case ECMG_CHANNEL_SETUP: {
|
||||||
|
HandleChannelSetup(params, response, response_length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ECMG_CHANNEL_CLOSE: {
|
||||||
|
HandleChannelClose(params, response, response_length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ECMG_STREAM_SETUP: {
|
||||||
|
HandleStreamSetup(params, response, response_length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ECMG_STREAM_CLOSE_REQUEST: {
|
||||||
|
HandleStreamCloseRequest(params, response, response_length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ECMG_CW_PROVISION: {
|
||||||
|
HandleCwProvision(params, response, response_length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// Unhandled or unknown request types.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EcmgClientHandler::HandleChannelSetup(const EcmgParameters& params,
|
||||||
|
char* response,
|
||||||
|
size_t* response_length) {
|
||||||
|
DCHECK(response);
|
||||||
|
DCHECK(response_length);
|
||||||
|
// TODO(user): Check SUPER_CAS_ID, if it is unexpected, return
|
||||||
|
// UNKNOWN_SUPER_CAS_ID_VALUE error.
|
||||||
|
if (channel_id_set_) {
|
||||||
|
BuildChannelError(params.ecm_channel_id, INVAID_MESSAGE, response,
|
||||||
|
response_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO(user): To support multi-threading of serving concurrent clients,
|
||||||
|
// there needs to be a set of channel_ids shared by multiple client handlers
|
||||||
|
// threads.
|
||||||
|
// And here we need to check if the channel_id is already in the set, if
|
||||||
|
// yes, return an error.
|
||||||
|
channel_id_ = params.ecm_channel_id;
|
||||||
|
channel_id_set_ = true;
|
||||||
|
BuildChannelStatus(params.ecm_channel_id, ecmg_config_, response,
|
||||||
|
response_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EcmgClientHandler::HandleChannelClose(const EcmgParameters& params,
|
||||||
|
char* response,
|
||||||
|
size_t* response_length) {
|
||||||
|
DCHECK(response);
|
||||||
|
DCHECK(response_length);
|
||||||
|
if (channel_id_ != params.ecm_channel_id) {
|
||||||
|
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
|
||||||
|
UNKNOWN_ECM_CHANNEL_ID_VALUE, response, response_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
channel_id_set_ = false;
|
||||||
|
streams_.clear();
|
||||||
|
*response_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EcmgClientHandler::HandleStreamSetup(const EcmgParameters& params,
|
||||||
|
char* response,
|
||||||
|
size_t* response_length) {
|
||||||
|
DCHECK(response);
|
||||||
|
DCHECK(response_length);
|
||||||
|
if (channel_id_ != params.ecm_channel_id) {
|
||||||
|
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
|
||||||
|
UNKNOWN_ECM_CHANNEL_ID_VALUE, response, response_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO(user): To support multi-threading of serving concurrent clients,
|
||||||
|
// there needs to be a set of stream_ids shared by multiple client handlers
|
||||||
|
// threads.
|
||||||
|
// And here we need to check if the stream_id is already in the set, if
|
||||||
|
// yes, return an error.
|
||||||
|
if (streams_.count(params.ecm_stream_id) > 0) {
|
||||||
|
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
|
||||||
|
ECM_STREAM_ID_VALUE_ALREADY_IN_USE, response,
|
||||||
|
response_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO(user): What do I do with the ECM_id here, currently not doing
|
||||||
|
// anything with it?
|
||||||
|
streams_[params.ecm_stream_id] = params.ecm_id;
|
||||||
|
BuildStreamStatus(params.ecm_channel_id, params.ecm_stream_id, params.ecm_id,
|
||||||
|
ecmg_config_->access_criteria_transfer_mode, response,
|
||||||
|
response_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EcmgClientHandler::HandleStreamCloseRequest(const EcmgParameters& params,
|
||||||
|
char* response,
|
||||||
|
size_t* response_length) {
|
||||||
|
DCHECK(response);
|
||||||
|
DCHECK(response_length);
|
||||||
|
if (channel_id_ != params.ecm_channel_id) {
|
||||||
|
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
|
||||||
|
UNKNOWN_ECM_CHANNEL_ID_VALUE, response, response_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (streams_.count(params.ecm_stream_id) == 0) {
|
||||||
|
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
|
||||||
|
UNKNOWN_ECM_STREAM_ID_VALUE, response, response_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
streams_.erase(params.ecm_stream_id);
|
||||||
|
BuildStreamCloseResponse(params.ecm_channel_id, params.ecm_stream_id,
|
||||||
|
response, response_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EcmgClientHandler::HandleCwProvision(const EcmgParameters& params,
|
||||||
|
char* response,
|
||||||
|
size_t* response_length) {
|
||||||
|
DCHECK(response);
|
||||||
|
DCHECK(response_length);
|
||||||
|
if (channel_id_ != params.ecm_channel_id) {
|
||||||
|
BuildChannelError(params.ecm_channel_id, UNKNOWN_ECM_CHANNEL_ID_VALUE,
|
||||||
|
response, response_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (streams_.count(params.ecm_stream_id) == 0) {
|
||||||
|
BuildChannelError(params.ecm_channel_id, UNKNOWN_ECM_STREAM_ID_VALUE,
|
||||||
|
response, response_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (params.cw_per_msg < kCwPerMsg) {
|
||||||
|
BuildChannelError(params.ecm_channel_id,
|
||||||
|
NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM, response,
|
||||||
|
response_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t ecm_datagram[kTsPacketSize];
|
||||||
|
BuildEcmDatagram(params, ecm_datagram);
|
||||||
|
BuildEcmResponse(params.ecm_channel_id, params.ecm_stream_id,
|
||||||
|
params.cp_number, ecm_datagram, response, response_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params,
|
||||||
|
uint8_t* ecm_datagram) {
|
||||||
|
DCHECK(ecm_datagram);
|
||||||
|
// TODO(user): Remove debug loop below.
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
std::cout << "CW: ";
|
||||||
|
for (int j = 0; j < params.cp_cw_combinations[i].cw.size(); j++) {
|
||||||
|
printf("'\\x%02x', ",
|
||||||
|
static_cast<uint16_t>(params.cp_cw_combinations[i].cw[j]));
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Generate entitlement keys.
|
||||||
|
bool key_rotation_enabled = params.cw_per_msg > 1;
|
||||||
|
// Create an instance of CasEcm in order to set the entitlement keys.
|
||||||
|
// TODO(user): The section of code below for constructing CasEcm should
|
||||||
|
// be optimized. There should be a single instance of CasEcm for each stream.
|
||||||
|
// Right now, this is hard to do because CasEcmGenerator contains the CasEcm.
|
||||||
|
std::unique_ptr<CasEcm> ecm = absl::make_unique<CasEcm>();
|
||||||
|
// TODO(user): Revisit this hardcoded ecm_init_params.
|
||||||
|
EcmInitParameters ecm_init_params;
|
||||||
|
ecm_init_params.content_iv_size = kIvSize8;
|
||||||
|
ecm_init_params.key_rotation_enabled = key_rotation_enabled;
|
||||||
|
// TODO(user): Allow crypto mode to be set to different value? Via flag?
|
||||||
|
ecm_init_params.crypto_mode = CryptoMode::kDvbCsa2;
|
||||||
|
// Only encrypt one video track.
|
||||||
|
// TODO(user): Support multiple tracks? How?
|
||||||
|
ecm_init_params.track_types.push_back(kDefaultTrackTypeSd);
|
||||||
|
std::string entitlement_request;
|
||||||
|
std::string entitlement_response;
|
||||||
|
// 'content_id' and 'provider' are used in entitlement key request/response
|
||||||
|
// only, NOT needed for generating ECM.
|
||||||
|
// So for initial demo, we can just use hardcoded value because we are using
|
||||||
|
// hardcoded entitlement key anyway.
|
||||||
|
// TODO(user): When we want to retrieve entitlement key from License Server
|
||||||
|
// we need to figure out a way to provide real 'content_id' and 'provder'
|
||||||
|
// to this function here from the Simulcrypt API.
|
||||||
|
Status status;
|
||||||
|
if (!(status = ecm->Initialize(kDefaultContentId, kDefaultProvider,
|
||||||
|
ecm_init_params, &entitlement_request))
|
||||||
|
.ok()) {
|
||||||
|
// TODO(user): Should send an error response.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(status = fixed_key_fetcher_.RequestEntitlementKey(
|
||||||
|
entitlement_request, &entitlement_response))
|
||||||
|
.ok()) {
|
||||||
|
// TODO(user): Should send an error Channel_status.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(status = ecm->ProcessCasEncryptionResponse(entitlement_response))
|
||||||
|
.ok()) {
|
||||||
|
// TODO(user): Should send an error Channel_status.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Generate serialized ECM.
|
||||||
|
CasEcmGenerator ecm_generator;
|
||||||
|
ecm_generator.set_ecm(std::move(ecm));
|
||||||
|
EcmParameters ecm_param;
|
||||||
|
ecm_param.rotation_enabled = key_rotation_enabled;
|
||||||
|
for (int i = 0; i <= params.cw_per_msg; i++) {
|
||||||
|
ecm_param.key_params.emplace_back();
|
||||||
|
ecm_param.key_params[i].key_data = params.cp_cw_combinations[i].cw;
|
||||||
|
ecm_param.key_params[i].key_id = widevine::crypto_util::DeriveKeyId(
|
||||||
|
ecm_param.key_params[i].key_data);
|
||||||
|
// TODO(user): How to derive this content_iv, maybe based on key?
|
||||||
|
std::string content_iv = "12345678";
|
||||||
|
ecm_param.key_params[i].content_ivs.push_back(content_iv);
|
||||||
|
}
|
||||||
|
std::string serialized_ecm = ecm_generator.GenerateEcm(ecm_param);
|
||||||
|
std::cout << "serialized_ecm: " << serialized_ecm << std::endl;
|
||||||
|
for (int i = 0; i < serialized_ecm.size(); i++) {
|
||||||
|
printf("'\\x%x', ", static_cast<uint16_t>(serialized_ecm.at(i)));
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
LOG(INFO) << "ECM size: " << serialized_ecm.size();
|
||||||
|
|
||||||
|
// Step 3: Make a TS packet carrying the serialized ECM.
|
||||||
|
// TODO(user): Is it correct to set 'pid' using ECM_id?
|
||||||
|
// TODO(user): Where do I get the continuity_counter?
|
||||||
|
uint8_t continuity_counter = 0;
|
||||||
|
WvCasEcm wv_cas_ecm;
|
||||||
|
WvCasStatus cas_status = wv_cas_ecm.GenerateTsPacket(
|
||||||
|
serialized_ecm, params.ecm_id,
|
||||||
|
params.cp_number % 2 == 0 ? kTsPacketTableId80 : kTsPacketTableId81,
|
||||||
|
&continuity_counter, ecm_datagram);
|
||||||
|
if (cas_status != OK) {
|
||||||
|
// TODO(user): Should send an error Channel_status.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
101
media_cas_packager_sdk/internal/ecmg_client_handler.h
Normal file
101
media_cas_packager_sdk/internal/ecmg_client_handler.h
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2018 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "common/status.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/ecm.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
// A struct that captures the Ecmg configs.
|
||||||
|
struct EcmgConfig {
|
||||||
|
int16_t delay_start;
|
||||||
|
int16_t delay_stop;
|
||||||
|
uint16_t ecm_rep_period;
|
||||||
|
uint16_t max_comp_time;
|
||||||
|
uint8_t access_criteria_transfer_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A struct that represent a CP_CW_Combination.
|
||||||
|
struct EcmgCpCwCombination {
|
||||||
|
uint16_t cp; // crypto period
|
||||||
|
std::string cw; // control word
|
||||||
|
};
|
||||||
|
|
||||||
|
// A struct that is used to hold all possible params for a ECMG request.
|
||||||
|
struct EcmgParameters {
|
||||||
|
// CW_per_msg could 1, 2, or 3,
|
||||||
|
// so there can be up to 3 CP_CW_Combinations
|
||||||
|
EcmgCpCwCombination cp_cw_combinations[3];
|
||||||
|
uint16_t cp_duration; // crypto period duration
|
||||||
|
uint16_t cp_number; // crypto period number
|
||||||
|
uint8_t cw_per_msg;
|
||||||
|
uint16_t ecm_channel_id;
|
||||||
|
uint16_t ecm_stream_id;
|
||||||
|
uint16_t ecm_id;
|
||||||
|
uint16_t nominal_cp_duration;
|
||||||
|
uint32_t super_cas_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A class that handles one (and only one) ECMG client.
|
||||||
|
// This class is NOT thread-safe.
|
||||||
|
class EcmgClientHandler {
|
||||||
|
public:
|
||||||
|
explicit EcmgClientHandler(EcmgConfig* ecmg_config);
|
||||||
|
EcmgClientHandler(const EcmgClientHandler&) = delete;
|
||||||
|
EcmgClientHandler& operator=(const EcmgClientHandler&) = delete;
|
||||||
|
virtual ~EcmgClientHandler() = default;
|
||||||
|
|
||||||
|
// Handle a |request| from the client.
|
||||||
|
// If any response is generated, it would returned via |response|
|
||||||
|
// and |response_length|.
|
||||||
|
void HandleRequest(const char* const request, char* response,
|
||||||
|
size_t* response_length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void HandleChannelSetup(const EcmgParameters& params, char* response,
|
||||||
|
size_t* response_length);
|
||||||
|
// TODO(user): HandleChannelTest()
|
||||||
|
void HandleChannelClose(const EcmgParameters& params, char* response,
|
||||||
|
size_t* response_length);
|
||||||
|
void HandleStreamSetup(const EcmgParameters& params, char* response,
|
||||||
|
size_t* response_length);
|
||||||
|
// TODO(user): HandleStreamTest()
|
||||||
|
void HandleStreamCloseRequest(const EcmgParameters& params, char* response,
|
||||||
|
size_t* response_length);
|
||||||
|
void HandleCwProvision(const EcmgParameters& params, char* response,
|
||||||
|
size_t* response_length);
|
||||||
|
void BuildEcmDatagram(const EcmgParameters& params, uint8_t* ecm_datagram);
|
||||||
|
|
||||||
|
EcmgConfig* ecmg_config_;
|
||||||
|
// Per spec, "There is always one (and only one) channel per TCP connection".
|
||||||
|
bool channel_id_set_;
|
||||||
|
uint16_t channel_id_;
|
||||||
|
// Map from ECM_stream_id to ECM_id.
|
||||||
|
std::unordered_map<uint16_t, uint16_t> streams_;
|
||||||
|
FixedKeyFetcher fixed_key_fetcher_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_
|
||||||
85
media_cas_packager_sdk/internal/ecmg_client_handler_test.cc
Normal file
85
media_cas_packager_sdk/internal/ecmg_client_handler_test.cc
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2018 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "example/test_ecmg_messages.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#define BUFFER_SIZE (1024)
|
||||||
|
|
||||||
|
class EcmgClientHandlerTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
EcmgClientHandlerTest() {
|
||||||
|
config_.delay_start = 200;
|
||||||
|
config_.delay_stop = 200;
|
||||||
|
config_.ecm_rep_period = 100;
|
||||||
|
config_.max_comp_time = 100;
|
||||||
|
config_.access_criteria_transfer_mode = 1;
|
||||||
|
client_handler_ = absl::make_unique<EcmgClientHandler>(&config_);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Helper function for debugging the tests.
|
||||||
|
void PrintMessage(const std::string &description, const char *const message,
|
||||||
|
size_t length) {
|
||||||
|
printf("%s ", description.c_str());
|
||||||
|
fflush(stdout);
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
printf("'\\x%02x', ", static_cast<uint16_t>(*(message + i)));
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
EcmgConfig config_;
|
||||||
|
std::unique_ptr<EcmgClientHandler> client_handler_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO(user): Add unit tests for error cases.
|
||||||
|
|
||||||
|
TEST_F(EcmgClientHandlerTest, SuccessSequence) {
|
||||||
|
char response[BUFFER_SIZE];
|
||||||
|
size_t response_length;
|
||||||
|
|
||||||
|
client_handler_->HandleRequest(kTestChannelSetup, response, &response_length);
|
||||||
|
EXPECT_EQ(62, response_length);
|
||||||
|
EXPECT_EQ(0, memcmp(kTestChannelStatus, response, response_length));
|
||||||
|
|
||||||
|
client_handler_->HandleRequest(kTestStreamSetup, response, &response_length);
|
||||||
|
EXPECT_EQ(28, response_length);
|
||||||
|
EXPECT_EQ(0, memcmp(kTestStreamStatus, response, response_length));
|
||||||
|
|
||||||
|
client_handler_->HandleRequest(kTestCwProvision, response, &response_length);
|
||||||
|
EXPECT_EQ(215, response_length);
|
||||||
|
// Only comparing the bytes in front of the ECM_datagram, because
|
||||||
|
// random wrapping IV is generated each time causing the ECM_datagram
|
||||||
|
// to be non-deterministic.
|
||||||
|
EXPECT_EQ(0, memcmp(kTestEcmResponse, response, 27));
|
||||||
|
|
||||||
|
client_handler_->HandleRequest(kTestStreamCloseRequest, response,
|
||||||
|
&response_length);
|
||||||
|
EXPECT_EQ(17, response_length);
|
||||||
|
EXPECT_EQ(0, memcmp(kTestStreamCloseResponse, response, response_length));
|
||||||
|
|
||||||
|
client_handler_->HandleRequest(kTestChannelClose, response, &response_length);
|
||||||
|
EXPECT_EQ(0, response_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
@@ -9,45 +9,95 @@
|
|||||||
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
|
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
|
||||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
|
||||||
|
|
||||||
// ECMG <> SCS
|
// ECMG <=> SCS protocol_version.
|
||||||
// Parameter_type values
|
#define ECMG_SCS_PROTOCOL_VERSION (0x03)
|
||||||
#define DVB_RESERVED 0x0000
|
|
||||||
#define SUPER_CAS_ID 0x0001
|
// ECMG message type values.
|
||||||
#define SECTION_TSPKT_FLAG 0x0002
|
// 0x0000 DVB reserved.
|
||||||
#define DELAY_START 0x0003
|
#define ECMG_CHANNEL_SETUP (0x0001)
|
||||||
#define DELAY_STOP 0x0004
|
#define ECMG_CHANNEL_TEST (0x0002)
|
||||||
#define TRANSITION_DELAY_START 0x0005
|
#define ECMG_CHANNEL_STATUS (0x0003)
|
||||||
#define TRANSITION_DELAY_STOP 0x0006
|
#define ECMG_CHANNEL_CLOSE (0x0004)
|
||||||
#define ECM_REP_PERIOD 0x0007
|
#define ECMG_CHANNEL_ERROR (0x0005)
|
||||||
#define MAX_STREAMS 0x0008
|
// 0x0016 - 0x0100 DVB reserved.
|
||||||
#define MIN_CP_DURATION 0x0009
|
#define ECMG_STREAM_SETUP (0x0101)
|
||||||
#define LEAD_CW 0x000A
|
#define ECMG_STREAM_TEST (0x0102)
|
||||||
#define CW_PER_MESSAGE 0x000B
|
#define ECMG_STREAM_STATUS (0x0103)
|
||||||
#define MAX_COMP_TIME 0x000C
|
#define ECMG_STREAM_CLOSE_REQUEST (0x0104)
|
||||||
#define ACCESS_CRITERIA 0x000D
|
#define ECMG_STREAM_CLOSE_RESPONSE (0x0105)
|
||||||
#define ECM_CHANNEL_ID 0x000E
|
#define ECMG_STREAM_ERROR (0x0106)
|
||||||
#define ECM_STREAM_ID 0x000F
|
// 0x0119 - 0x0200 DVB reserved.
|
||||||
#define NOMINAL_CP_DURATION 0x0010
|
#define ECMG_CW_PROVISION (0x0201)
|
||||||
#define ACCESS_CRITERIA_TRANSFER_MODE 0x0011
|
#define ECMG_ECM_RESPONSE (0x0202)
|
||||||
#define CP_NUMBER 0x0012
|
|
||||||
#define CP_DURATION 0x0013
|
// ECMG parameter type values.
|
||||||
#define CP_CW_COMBINATION 0x0014
|
#define DVB_RESERVED (0x0000)
|
||||||
#define ECM_DATAGRAM 0x0015
|
#define SUPER_CAS_ID (0x0001)
|
||||||
#define AC_DELAY_START 0x0016
|
#define SECTION_TSPKT_FLAG (0x0002)
|
||||||
#define AC_DELAY_STOP 0x0017
|
#define DELAY_START (0x0003)
|
||||||
#define CW_ENCRYPTION 0x0018
|
#define DELAY_STOP (0x0004)
|
||||||
#define ECM_ID 0x0019
|
#define TRANSITION_DELAY_START (0x0005)
|
||||||
#define ERROR_STATUS 0x7000
|
#define TRANSITION_DELAY_STOP (0x0006)
|
||||||
#define ERROR_INFORMATION 0x7001
|
#define ECM_REP_PERIOD (0x0007)
|
||||||
|
#define MAX_STREAMS (0x0008)
|
||||||
|
#define MIN_CP_DURATION (0x0009)
|
||||||
|
#define LEAD_CW (0x000A)
|
||||||
|
#define CW_PER_MESSAGE (0x000B)
|
||||||
|
#define MAX_COMP_TIME (0x000C)
|
||||||
|
#define ACCESS_CRITERIA (0x000D)
|
||||||
|
#define ECM_CHANNEL_ID (0x000E)
|
||||||
|
#define ECM_STREAM_ID (0x000F)
|
||||||
|
#define NOMINAL_CP_DURATION (0x0010)
|
||||||
|
#define ACCESS_CRITERIA_TRANSFER_MODE (0x0011)
|
||||||
|
#define CP_NUMBER (0x0012)
|
||||||
|
#define CP_DURATION (0x0013)
|
||||||
|
#define CP_CW_COMBINATION (0x0014)
|
||||||
|
#define ECM_DATAGRAM (0x0015)
|
||||||
|
#define AC_DELAY_START (0x0016)
|
||||||
|
#define AC_DELAY_STOP (0x0017)
|
||||||
|
#define CW_ENCRYPTION (0x0018)
|
||||||
|
#define ECM_ID (0x0019)
|
||||||
|
#define ERROR_STATUS (0x7000)
|
||||||
|
#define ERROR_INFORMATION (0x7001)
|
||||||
|
|
||||||
|
// ECMG protocol error values.
|
||||||
|
#define INVAID_MESSAGE (0x0001)
|
||||||
|
#define UNSUPPORTED_PROTOCOL_VERSION (0X0002)
|
||||||
|
#define UNKNOWN_MESSAGE_TYPE_VALUE (0X0003)
|
||||||
|
#define MESSAGE_TOO_LONG (0X0004)
|
||||||
|
#define UNKNOWN_SUPER_CAS_ID_VALUE (0X0005)
|
||||||
|
#define UNKNOWN_ECM_CHANNEL_ID_VALUE (0X0006)
|
||||||
|
#define UNKNOWN_ECM_STREAM_ID_VALUE (0X0007)
|
||||||
|
#define TOO_MANY_CHANNELS_ON_THIS_ECMG (0X0008)
|
||||||
|
#define TOO_MANY_ECM_STREAMS_ON_THIS_CHANNEL (0X0009)
|
||||||
|
#define TOO_MANY_ECM_STREAMS_ON_THIS_ECMG (0X000A)
|
||||||
|
#define NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM (0X000B)
|
||||||
|
#define ECMG_OUT_OF_STORAGE_CAPACITY (0X000C)
|
||||||
|
#define ECMG_OUT_OF_COMPUTATIONAL_RESOURCES (0X000D)
|
||||||
|
#define UNKNOWN_PARAMETER_TYPE_VALUE (0X000E)
|
||||||
|
#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0X000F)
|
||||||
|
#define MISSING_MANDATORY_DVB_PARAMETER (0X0010)
|
||||||
|
#define INVALID_VALUE_FOR_DVB_PARAMETER (0X0011)
|
||||||
|
#define UNKNOWN_ECM_ID_VALUE (0X0012)
|
||||||
|
#define ECM_CHANNEL_ID_VALUE_ALREADY_IN_USE (0X0013)
|
||||||
|
#define ECM_STREAM_ID_VALUE_ALREADY_IN_USE (0X0014)
|
||||||
|
#define ECM_ID_VALUE_ALREADY_IN_USE (0X0015)
|
||||||
|
#define UNKNOWN_ERROR (0X7000)
|
||||||
|
#define UNRECOVERABLE_ERROR (0X7001)
|
||||||
|
|
||||||
// Size (in # of bytes) of various fields.
|
// Size (in # of bytes) of various fields.
|
||||||
#define PARAMETER_TYPE_SIZE 2
|
#define PROTOCOL_VERSION_SIZE (1)
|
||||||
#define PARAMETER_LENGTH_SIZE 2
|
#define MESSAGE_TYPE_SIZE (2)
|
||||||
#define ECM_CHANNEL_ID_SIZE 2
|
#define MESSAGE_LENGTH_SIZE (2)
|
||||||
#define ECM_STREAM_ID_SIZE 2
|
#define PARAMETER_TYPE_SIZE (2)
|
||||||
#define NOMINAL_CP_DURATION_SIZE 2
|
#define PARAMETER_LENGTH_SIZE (2)
|
||||||
#define CP_NUMBER_SIZE 2
|
#define SUPER_CAS_ID_SIZE (4)
|
||||||
#define CP_DURATION_SIZE 2
|
#define ECM_CHANNEL_ID_SIZE (2)
|
||||||
#define CP_SIZE 2
|
#define ECM_ID_SIZE (2)
|
||||||
|
#define ECM_STREAM_ID_SIZE (2)
|
||||||
|
#define NOMINAL_CP_DURATION_SIZE (2)
|
||||||
|
#define CP_NUMBER_SIZE (2)
|
||||||
|
#define CP_DURATION_SIZE (2)
|
||||||
|
#define CP_SIZE (2)
|
||||||
|
|
||||||
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace cas {
|
namespace cas {
|
||||||
|
|
||||||
util::Status FixedKeyFetcher::RequestEntitlementKey(
|
Status FixedKeyFetcher::RequestEntitlementKey(const std::string& request_string,
|
||||||
const std::string& request_string, std::string* signed_response_string) {
|
std::string* signed_response_string) {
|
||||||
CasEncryptionRequest request;
|
CasEncryptionRequest request;
|
||||||
request.ParseFromString(request_string);
|
request.ParseFromString(request_string);
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ util::Status FixedKeyFetcher::RequestEntitlementKey(
|
|||||||
SignedCasEncryptionResponse signed_response;
|
SignedCasEncryptionResponse signed_response;
|
||||||
signed_response.set_response(response_string);
|
signed_response.set_response(response_string);
|
||||||
signed_response.SerializeToString(signed_response_string);
|
signed_response.SerializeToString(signed_response_string);
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ class FixedKeyFetcher : public KeyFetcher {
|
|||||||
// |signed_response_string| a serialized SignedCasEncryptionResponse
|
// |signed_response_string| a serialized SignedCasEncryptionResponse
|
||||||
// message. It should be passed into
|
// message. It should be passed into
|
||||||
// WvCasEcm::ProcessCasEncryptionResponse().
|
// WvCasEcm::ProcessCasEncryptionResponse().
|
||||||
util::Status RequestEntitlementKey(const std::string& request_string,
|
Status RequestEntitlementKey(const std::string& request_string,
|
||||||
std::string* signed_response_string) override;
|
std::string* signed_response_string) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string even_entitlement_key_id_;
|
std::string even_entitlement_key_id_;
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ class KeyFetcher {
|
|||||||
// |signed_response_string| a serialized SignedCasEncryptionResponse
|
// |signed_response_string| a serialized SignedCasEncryptionResponse
|
||||||
// message. It should be passed into
|
// message. It should be passed into
|
||||||
// WvCasEcm::ProcessCasEncryptionResponse().
|
// WvCasEcm::ProcessCasEncryptionResponse().
|
||||||
virtual util::Status RequestEntitlementKey(
|
virtual Status RequestEntitlementKey(const std::string& request_string,
|
||||||
const std::string& request_string, std::string* signed_response_string) = 0;
|
std::string* signed_response_string) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright 2018 Google LLC.
|
|
||||||
//
|
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
|
||||||
// widevine-licensing@google.com.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include "media_cas_packager_sdk/internal/simulcrypt.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "glog/logging.h"
|
|
||||||
#include "absl/strings/str_cat.h"
|
|
||||||
#include "common/status.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/ecmg.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/simulcrypt_constants.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/util.h"
|
|
||||||
|
|
||||||
namespace widevine {
|
|
||||||
namespace cas {
|
|
||||||
|
|
||||||
// TODO(user): Caller should check |message| is at lest 5 bytes long.
|
|
||||||
util::Status Simulcrypt::ProcessMessage(const char* message, std::string* response) {
|
|
||||||
DCHECK(message);
|
|
||||||
DCHECK(response);
|
|
||||||
|
|
||||||
uint8_t protocol_version;
|
|
||||||
uint16_t message_type;
|
|
||||||
uint16_t message_length;
|
|
||||||
// 'offset' is used to track where we are within |message|.
|
|
||||||
size_t offset = 0;
|
|
||||||
memcpy(&protocol_version, message, PROTOCOL_VERSION_SIZE);
|
|
||||||
if (protocol_version != EXPECTED_PROTOCOL_VERSION) {
|
|
||||||
return util::Status(
|
|
||||||
util::error::INVALID_ARGUMENT,
|
|
||||||
absl::StrCat("Invalid protocol version ", protocol_version));
|
|
||||||
}
|
|
||||||
offset += PROTOCOL_VERSION_SIZE;
|
|
||||||
BigEndianToHost16(&message_type, message + offset);
|
|
||||||
offset += MESSAGE_TYPE_SIZE;
|
|
||||||
BigEndianToHost16(&message_length, message + offset);
|
|
||||||
offset += MESSAGE_LENGTH_SIZE;
|
|
||||||
switch (message_type) {
|
|
||||||
case ECMG_STREAM_SETUP: {
|
|
||||||
return ecmg_.ProcessStreamSetupMessage(message + offset, message_length);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ECMG_CW_PROVISION: {
|
|
||||||
return ecmg_.ProcessCwProvisionMessage(message + offset, message_length,
|
|
||||||
response);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return util::Status(
|
|
||||||
util::error::UNIMPLEMENTED,
|
|
||||||
absl::StrCat("No implementation yet to process message of type ",
|
|
||||||
message_type));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return util::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace cas
|
|
||||||
} // namespace widevine
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright 2018 Google LLC.
|
|
||||||
//
|
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
|
||||||
// widevine-licensing@google.com.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_H_
|
|
||||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include "common/status.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/ecmg.h"
|
|
||||||
|
|
||||||
namespace widevine {
|
|
||||||
namespace cas {
|
|
||||||
|
|
||||||
// A class that handles Simulcrypt messages.
|
|
||||||
// The expected usage is by a TCP server that receives Simulcrypt message
|
|
||||||
// from the network then forward that message to an instance of this class
|
|
||||||
// for processing.
|
|
||||||
// This class is NOT thread-safe.
|
|
||||||
class Simulcrypt {
|
|
||||||
public:
|
|
||||||
Simulcrypt() = default;
|
|
||||||
Simulcrypt(const Simulcrypt&) = delete;
|
|
||||||
Simulcrypt& operator=(const Simulcrypt&) = delete;
|
|
||||||
virtual ~Simulcrypt() = default;
|
|
||||||
|
|
||||||
// Process a Simulcrypt |message|.
|
|
||||||
// If any response is generated, it would returned via |response|.
|
|
||||||
// Any error during processing would be turned via util::Status.
|
|
||||||
util::Status ProcessMessage(const char* message, std::string* response);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ecmg ecmg_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace cas
|
|
||||||
} // namespace widevine
|
|
||||||
|
|
||||||
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_H_
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright 2018 Google LLC.
|
|
||||||
//
|
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
|
||||||
// widevine-licensing@google.com.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_
|
|
||||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_
|
|
||||||
|
|
||||||
// Message_type Values
|
|
||||||
// 0x0000 DVB reserved.
|
|
||||||
#define ECMG_CHANNEL_SETUP 0x0001
|
|
||||||
#define ECMG_CHANNEL_TEST 0x0002
|
|
||||||
#define ECMG_CHANNEL_STATUS 0x0003
|
|
||||||
#define ECMG_CHANNEL_CLOSE 0x0004
|
|
||||||
#define ECMG_CHANNEL_ERROR 0x0005
|
|
||||||
// 0x0006 - 0x0010 DVB reserved.
|
|
||||||
#define EMMG_CHANNEL_SETUP 0x0011
|
|
||||||
#define EMMG_CHANNEL_TEST 0x0012
|
|
||||||
#define EMMG_CHANNEL_STATUS 0x0013
|
|
||||||
#define EMMG_CHANNEL_CLOSE 0x0014
|
|
||||||
#define EMMG_CHANNEL_ERROR 0x0015
|
|
||||||
// 0x0016 - 0x0100 DVB reserved.
|
|
||||||
#define ECMG_STREAM_SETUP 0x0101
|
|
||||||
#define ECMG_STREAM_TEST 0x0102
|
|
||||||
#define ECMG_STREAM_STATUS 0x0103
|
|
||||||
#define ECMG_STREAM_CLOSE_REQUEST 0x0104
|
|
||||||
#define ECMG_STREAM_CLOSE_RESPONSE 0x0105
|
|
||||||
#define ECMG_STREAM_ERROR 0x0106
|
|
||||||
// 0x0107 - 0x0110 DVB reserved.
|
|
||||||
#define EMMG_STREAM_SETUP 0x0111
|
|
||||||
#define EMMG_STREAM_TEST 0x0112
|
|
||||||
#define EMMG_STREAM_STATUS 0x0113
|
|
||||||
#define EMMG_STREAM_CLOSE_REQUEST 0x0114
|
|
||||||
#define EMMG_STREAM_CLOSE_RESPONSE 0x0115
|
|
||||||
#define EMMG_STREAM_ERROR 0x0116
|
|
||||||
#define EMMG_STREAM_BW_REQUEST 0x0117
|
|
||||||
#define EMMG_STREAM_BW_ALLOCATION 0x0118
|
|
||||||
// 0x0119 - 0x0200 DVB reserved.
|
|
||||||
#define ECMG_CW_PROVISION 0x0201
|
|
||||||
#define ECMG_ECM_RESPONSE 0x0202
|
|
||||||
// 0x0203 - 0x0210 DVB reserved.
|
|
||||||
#define EMMG_DATA_PROVISION 0x0211
|
|
||||||
// 0x0212 - 0x0300 DVB reserved.
|
|
||||||
|
|
||||||
#define EXPECTED_PROTOCOL_VERSION 0x01
|
|
||||||
|
|
||||||
// Size (in # of bytes) of various fields.
|
|
||||||
#define PROTOCOL_VERSION_SIZE 1
|
|
||||||
#define MESSAGE_TYPE_SIZE 2
|
|
||||||
#define MESSAGE_LENGTH_SIZE 2
|
|
||||||
|
|
||||||
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright 2018 Google LLC.
|
|
||||||
//
|
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
|
||||||
// widevine-licensing@google.com.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include "media_cas_packager_sdk/internal/simulcrypt.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "testing/gmock.h"
|
|
||||||
#include "testing/gunit.h"
|
|
||||||
#include "example/test_simulcrypt_messages.h"
|
|
||||||
|
|
||||||
namespace widevine {
|
|
||||||
namespace cas {
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class SimulcryptTest : public ::testing::Test {
|
|
||||||
protected:
|
|
||||||
SimulcryptTest() {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Simulcrypt simulcrypt_;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(SimulcryptTest, ProcessEcmgStreamSetupMessage) {
|
|
||||||
std::string response = "";
|
|
||||||
EXPECT_OK(simulcrypt_.ProcessMessage(kTestEcmgStreamSetupMessage, &response));
|
|
||||||
|
|
||||||
EXPECT_EQ("", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(SimulcryptTest, ProcessEcmgCwProvisionMessageWithOneCw) {
|
|
||||||
std::string response = "";
|
|
||||||
EXPECT_OK(simulcrypt_.ProcessMessage(kTestEcmgCwProvisionMessageWithOneCw,
|
|
||||||
&response));
|
|
||||||
|
|
||||||
EXPECT_EQ("", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(SimulcryptTest, ProcessEcmgCwProvisionMessageWithTwoCw) {
|
|
||||||
std::string response = "";
|
|
||||||
EXPECT_OK(simulcrypt_.ProcessMessage(kTestEcmgCwProvisionMessageWithTwoCw,
|
|
||||||
&response));
|
|
||||||
|
|
||||||
EXPECT_EQ("", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
} // namespace cas
|
|
||||||
} // namespace widevine
|
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace cas {
|
namespace cas {
|
||||||
|
|
||||||
util::Status TsPacket::Write(std::string* output) const {
|
Status TsPacket::Write(std::string* output) const {
|
||||||
DCHECK(output);
|
DCHECK(output);
|
||||||
output->resize(kTsPacketSize);
|
output->resize(kTsPacketSize);
|
||||||
|
|
||||||
@@ -32,8 +32,8 @@ util::Status TsPacket::Write(std::string* output) const {
|
|||||||
std::bitset<2> adaptation_field_control(adaptation_field_control_);
|
std::bitset<2> adaptation_field_control(adaptation_field_control_);
|
||||||
std::bitset<4> continuity_counter(continuity_counter_);
|
std::bitset<4> continuity_counter(continuity_counter_);
|
||||||
if (adaptation_field_control_ & kAdaptationFieldOnly) {
|
if (adaptation_field_control_ & kAdaptationFieldOnly) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
"TsPacket does NOT handle adaptation field yet");
|
"TsPacket does NOT handle adaptation field yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts header bitset to string.
|
// Converts header bitset to string.
|
||||||
@@ -43,29 +43,28 @@ util::Status TsPacket::Write(std::string* output) const {
|
|||||||
pid.to_string(), transport_scrambling_control.to_string(),
|
pid.to_string(), transport_scrambling_control.to_string(),
|
||||||
adaptation_field_control.to_string(), continuity_counter.to_string());
|
adaptation_field_control.to_string(), continuity_counter.to_string());
|
||||||
if (header_bitset.size() != 4 * 8) {
|
if (header_bitset.size() != 4 * 8) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
absl::StrCat("TS packet header bitset incorret size: ",
|
absl::StrCat("TS packet header bitset incorret size: ",
|
||||||
header_bitset.size()));
|
header_bitset.size()));
|
||||||
}
|
}
|
||||||
std::string serialized_header;
|
std::string serialized_header;
|
||||||
util::Status status = string_util::BitsetStringToBinaryString(
|
Status status = string_util::BitsetStringToBinaryString(header_bitset,
|
||||||
header_bitset, &serialized_header);
|
&serialized_header);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
"Failed to convert TS packet header bitset to std::string");
|
"Failed to convert TS packet header bitset to std::string");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write TsPacket payload.
|
// Write TsPacket payload.
|
||||||
if (payload_.size() != CalculatePayloadSize()) {
|
if (payload_.size() != CalculatePayloadSize()) {
|
||||||
return util::Status(
|
return Status(error::INVALID_ARGUMENT,
|
||||||
util::error::INVALID_ARGUMENT,
|
absl::StrCat("Incorrect payload size: ", payload_.size()));
|
||||||
absl::StrCat("Incorrect payload size: ", payload_.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return header + payload as a TS packet.
|
// Return header + payload as a TS packet.
|
||||||
*output = serialized_header + payload_;
|
*output = serialized_header + payload_;
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t TsPacket::CalculatePayloadSize() const {
|
int32_t TsPacket::CalculatePayloadSize() const {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class TsPacket {
|
|||||||
virtual ~TsPacket() = default;
|
virtual ~TsPacket() = default;
|
||||||
|
|
||||||
// Writes the packet into the provided output. Returns kOk on success.
|
// Writes the packet into the provided output. Returns kOk on success.
|
||||||
virtual util::Status Write(std::string* output) const;
|
virtual Status Write(std::string* output) const;
|
||||||
|
|
||||||
// Returns the size of payload data for the current TS packet configuration.
|
// Returns the size of payload data for the current TS packet configuration.
|
||||||
int32_t CalculatePayloadSize() const;
|
int32_t CalculatePayloadSize() const;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
#include "media_cas_packager_sdk/internal/util.h"
|
#include "media_cas_packager_sdk/internal/util.h"
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <stddef.h>
|
#include <cstddef>
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "media_cas_packager_sdk/internal/ts_packet.h"
|
#include "media_cas_packager_sdk/internal/ts_packet.h"
|
||||||
@@ -30,14 +30,36 @@ ContinuityCounter Increment(ContinuityCounter continuity_counter) {
|
|||||||
void BigEndianToHost16(uint16_t* destination, const void* source) {
|
void BigEndianToHost16(uint16_t* destination, const void* source) {
|
||||||
DCHECK(destination);
|
DCHECK(destination);
|
||||||
DCHECK(source);
|
DCHECK(source);
|
||||||
uint16_t big_endian_number;
|
uint16_t big_endian_number = 0;
|
||||||
memcpy(&big_endian_number, source, 2);
|
memcpy(&big_endian_number, source, 2);
|
||||||
*destination = ntohs(big_endian_number);
|
*destination = ntohs(big_endian_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
|
void BigEndianToHost32(uint32_t* destination, const void* source) {
|
||||||
uint8_t table_id, ContinuityCounter* cc,
|
DCHECK(destination);
|
||||||
uint8_t* buffer, ssize_t* bytes_modified) {
|
DCHECK(source);
|
||||||
|
uint32_t big_endian_number = 0;
|
||||||
|
memcpy(&big_endian_number, source, 4);
|
||||||
|
*destination = ntohl(big_endian_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host16ToBigEndian(void* destination, const uint16_t* source) {
|
||||||
|
DCHECK(destination);
|
||||||
|
DCHECK(source);
|
||||||
|
uint16_t big_endian_number = htons(*source);
|
||||||
|
memcpy(destination, &big_endian_number, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host16ToBigEndian(void* destination, const int16_t* source) {
|
||||||
|
DCHECK(destination);
|
||||||
|
DCHECK(source);
|
||||||
|
uint16_t big_endian_number = htons(static_cast<uint16_t>(*source));
|
||||||
|
memcpy(destination, &big_endian_number, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, uint8_t table_id,
|
||||||
|
ContinuityCounter* cc, uint8_t* buffer,
|
||||||
|
ssize_t* bytes_modified) {
|
||||||
DCHECK(cc);
|
DCHECK(cc);
|
||||||
DCHECK(buffer);
|
DCHECK(buffer);
|
||||||
DCHECK(bytes_modified);
|
DCHECK(bytes_modified);
|
||||||
@@ -75,7 +97,7 @@ util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
|
|||||||
|
|
||||||
// And write the packet.
|
// And write the packet.
|
||||||
std::string ecm_ts_packet;
|
std::string ecm_ts_packet;
|
||||||
util::Status status = ecm_packet.Write(&ecm_ts_packet);
|
Status status = ecm_packet.Write(&ecm_ts_packet);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -83,7 +105,7 @@ util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
|
|||||||
memcpy(buffer + *bytes_modified, ecm_ts_packet.data(), ecm_ts_packet.size());
|
memcpy(buffer + *bytes_modified, ecm_ts_packet.data(), ecm_ts_packet.size());
|
||||||
*bytes_modified += ecm_ts_packet.size();
|
*bytes_modified += ecm_ts_packet.size();
|
||||||
|
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
|
|||||||
@@ -24,6 +24,16 @@ namespace cas {
|
|||||||
// the result in |destination|.
|
// the result in |destination|.
|
||||||
void BigEndianToHost16(uint16_t* destination, const void* source);
|
void BigEndianToHost16(uint16_t* destination, const void* source);
|
||||||
|
|
||||||
|
// Read 32 bits (long int) from |source|, treat it as a big-endian number
|
||||||
|
// (network byte order), finally covert it to host endianness and return
|
||||||
|
// the result in |destination|.
|
||||||
|
void BigEndianToHost32(uint32_t* destination, const void* source);
|
||||||
|
|
||||||
|
// Covert |source| to big-endian (network byte order) and copy it to
|
||||||
|
// |destination|.
|
||||||
|
void Host16ToBigEndian(void* destination, const uint16_t* source);
|
||||||
|
void Host16ToBigEndian(void* destination, const int16_t* source);
|
||||||
|
|
||||||
// Packages an ECM as a TS packet and inserts it into a buffer.
|
// Packages an ECM as a TS packet and inserts it into a buffer.
|
||||||
// Args:
|
// Args:
|
||||||
// - |ecm| is the serialized ECM.
|
// - |ecm| is the serialized ECM.
|
||||||
@@ -42,9 +52,9 @@ void BigEndianToHost16(uint16_t* destination, const void* source);
|
|||||||
// the |buffer| and is used as an offset.
|
// the |buffer| and is used as an offset.
|
||||||
// |bytes_modified| will be incremented by 188 if insertion of ECM into
|
// |bytes_modified| will be incremented by 188 if insertion of ECM into
|
||||||
// |buffer| is successful.
|
// |buffer| is successful.
|
||||||
util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
|
Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, uint8_t table_id,
|
||||||
uint8_t table_id, ContinuityCounter* cc,
|
ContinuityCounter* cc, uint8_t* buffer,
|
||||||
uint8_t* buffer, ssize_t* bytes_modified);
|
ssize_t* bytes_modified);
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ constexpr char kExpectedEcmPacket[] = {
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace cas {
|
namespace cas {
|
||||||
|
|
||||||
|
// TODO(user): Add unit tests for BigEndianToHost16, BigEndianToHost32 and
|
||||||
|
// Host16ToBigEndian.
|
||||||
|
|
||||||
TEST(InsertEcmAsTsPacketTest, BasicHappyPath) {
|
TEST(InsertEcmAsTsPacketTest, BasicHappyPath) {
|
||||||
// declare variables used by InsertEcmAsTsPacket()
|
// declare variables used by InsertEcmAsTsPacket()
|
||||||
ContinuityCounter ecm_cc_ = 0;
|
ContinuityCounter ecm_cc_ = 0;
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ PUBLIC_COPTS = ["-fvisibility=default"]
|
|||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "binary_release_files",
|
name = "binary_release_files",
|
||||||
srcs = glob(["*.h"]),
|
srcs = glob(["*.h"]) + [
|
||||||
|
":wv_ecmg",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_binary(
|
cc_binary(
|
||||||
@@ -49,15 +51,6 @@ cc_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "simulcrypt_server",
|
|
||||||
srcs = ["simulcrypt_server.cc"],
|
|
||||||
deps = [
|
|
||||||
"//base",
|
|
||||||
"@abseil_repo//absl/base:core_headers",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "wv_cas_ca_descriptor",
|
name = "wv_cas_ca_descriptor",
|
||||||
srcs = ["wv_cas_ca_descriptor.cc"],
|
srcs = ["wv_cas_ca_descriptor.cc"],
|
||||||
@@ -97,8 +90,8 @@ cc_library(
|
|||||||
"@abseil_repo//absl/base:core_headers", # buildcleaner: keep
|
"@abseil_repo//absl/base:core_headers", # buildcleaner: keep
|
||||||
"@abseil_repo//absl/memory", # buildcleaner: keep
|
"@abseil_repo//absl/memory", # buildcleaner: keep
|
||||||
"@abseil_repo//absl/strings", # buildcleaner: keep
|
"@abseil_repo//absl/strings", # buildcleaner: keep
|
||||||
"//common:status",
|
|
||||||
"//common:crypto_util",
|
"//common:crypto_util",
|
||||||
|
"//common:status",
|
||||||
"//example:constants",
|
"//example:constants",
|
||||||
"//media_cas_packager_sdk/internal:ecm",
|
"//media_cas_packager_sdk/internal:ecm",
|
||||||
"//media_cas_packager_sdk/internal:ecm_generator",
|
"//media_cas_packager_sdk/internal:ecm_generator",
|
||||||
@@ -134,7 +127,6 @@ cc_library(
|
|||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@curl_repo//:curl",
|
"@curl_repo//:curl",
|
||||||
"//common:status",
|
|
||||||
"//common:signature_util",
|
"//common:signature_util",
|
||||||
"//media_cas_packager_sdk/internal:key_fetcher",
|
"//media_cas_packager_sdk/internal:key_fetcher",
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_proto",
|
||||||
@@ -153,7 +145,6 @@ cc_test(
|
|||||||
"//external:protobuf",
|
"//external:protobuf",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//common:status",
|
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -175,3 +166,14 @@ cc_test(
|
|||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "wv_ecmg",
|
||||||
|
srcs = ["wv_ecmg.cc"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"//media_cas_packager_sdk/internal:ecmg_client_handler",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright 2018 Google LLC.
|
|
||||||
//
|
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
|
||||||
// widevine-licensing@google.com.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Example server that listens on a port for Simulcrypt API messages.
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "gflags/gflags.h"
|
|
||||||
#include "glog/logging.h"
|
|
||||||
|
|
||||||
DEFINE_int32(port, 0, "Server port number");
|
|
||||||
|
|
||||||
constexpr uint32_t kBufferSize = 256;
|
|
||||||
constexpr uint32_t kLicenseBacklog = 5;
|
|
||||||
constexpr uint32_t kWriteChunkSize = 18;
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
|
||||||
CHECK(FLAGS_port != 0) << "need --port";
|
|
||||||
|
|
||||||
struct sockaddr_in server_address;
|
|
||||||
bzero(reinterpret_cast<char *>(&server_address), sizeof(server_address));
|
|
||||||
server_address.sin_family = AF_INET;
|
|
||||||
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
server_address.sin_port = htons(FLAGS_port);
|
|
||||||
|
|
||||||
int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);
|
|
||||||
CHECK(listen_socket_fd >= 0) << "failed to open socket";
|
|
||||||
CHECK(bind(listen_socket_fd, (struct sockaddr *)&server_address,
|
|
||||||
sizeof(server_address)) >= 0)
|
|
||||||
<< "error on binding";
|
|
||||||
std::cout << "Server listening ..." << std::endl << std::flush;
|
|
||||||
int return_val = listen(listen_socket_fd, kLicenseBacklog);
|
|
||||||
switch (return_val) {
|
|
||||||
case EADDRINUSE:
|
|
||||||
LOG(FATAL) << "Another socket is already listening on the same port.";
|
|
||||||
break;
|
|
||||||
case EBADF:
|
|
||||||
LOG(FATAL) << "The argument sockfd is not a valid descriptor.";
|
|
||||||
break;
|
|
||||||
case ENOTSOCK:
|
|
||||||
LOG(FATAL) << "The argument sockfd is not a socket.";
|
|
||||||
break;
|
|
||||||
case EOPNOTSUPP:
|
|
||||||
LOG(FATAL) << "The socket is not of a type that supports the listen() "
|
|
||||||
"operation.";
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in client_address;
|
|
||||||
socklen_t clilet_address_size = sizeof(client_address);
|
|
||||||
int client_socket_fd = accept(
|
|
||||||
listen_socket_fd, reinterpret_cast<struct sockaddr *>(&client_address),
|
|
||||||
&clilet_address_size);
|
|
||||||
CHECK(client_socket_fd >= 0) << "error on accept";
|
|
||||||
|
|
||||||
char buffer[kBufferSize];
|
|
||||||
bzero(buffer, kBufferSize);
|
|
||||||
if (read(client_socket_fd, buffer, kBufferSize - 1) < 0) {
|
|
||||||
LOG(FATAL) << "ERROR reading from socket";
|
|
||||||
}
|
|
||||||
printf("Here is the message: %s", buffer);
|
|
||||||
if (write(client_socket_fd, "I got your message", kWriteChunkSize) < 0) {
|
|
||||||
LOG(FATAL) << "ERROR writing to socket";
|
|
||||||
}
|
|
||||||
|
|
||||||
close(client_socket_fd);
|
|
||||||
close(listen_socket_fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -95,7 +95,7 @@ WvCasStatus WvCasCaDescriptor::GenerateCaDescriptor(
|
|||||||
return INTERNAL;
|
return INTERNAL;
|
||||||
}
|
}
|
||||||
std::string descriptor;
|
std::string descriptor;
|
||||||
util::Status status =
|
Status status =
|
||||||
string_util::BitsetStringToBinaryString(descriptor_bitset, &descriptor);
|
string_util::BitsetStringToBinaryString(descriptor_bitset, &descriptor);
|
||||||
|
|
||||||
*serialized_ca_desc = descriptor;
|
*serialized_ca_desc = descriptor;
|
||||||
|
|||||||
@@ -13,10 +13,10 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "common/status.h"
|
|
||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "common/crypto_util.h"
|
#include "common/crypto_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
#include "example/constants.h"
|
#include "example/constants.h"
|
||||||
#include "media_cas_packager_sdk/internal/ecm.h"
|
#include "media_cas_packager_sdk/internal/ecm.h"
|
||||||
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
||||||
@@ -154,7 +154,7 @@ WvCasStatus WvCasEcm::GenerateEcm(
|
|||||||
// TODO(user): When we want to retrieve entitlement key from License Server
|
// TODO(user): When we want to retrieve entitlement key from License Server
|
||||||
// we need to figure out a way to provide real 'content_id' and 'provder'
|
// we need to figure out a way to provide real 'content_id' and 'provder'
|
||||||
// to this function here.
|
// to this function here.
|
||||||
util::Status status;
|
Status status;
|
||||||
if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider,
|
if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider,
|
||||||
ecm_init_params, &entitlement_request))
|
ecm_init_params, &entitlement_request))
|
||||||
.ok()) {
|
.ok()) {
|
||||||
@@ -265,7 +265,7 @@ WvCasStatus WvCasEcm::GenerateSingleKeyEcm(
|
|||||||
// TODO(user): When we want to retrieve entitlement key from License Server
|
// TODO(user): When we want to retrieve entitlement key from License Server
|
||||||
// we need to figure out a way to provide real 'content_id' and 'provder'
|
// we need to figure out a way to provide real 'content_id' and 'provder'
|
||||||
// to this function here.
|
// to this function here.
|
||||||
util::Status status;
|
Status status;
|
||||||
if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider,
|
if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider,
|
||||||
ecm_init_params, &entitlement_request))
|
ecm_init_params, &entitlement_request))
|
||||||
.ok()) {
|
.ok()) {
|
||||||
@@ -319,8 +319,8 @@ WvCasStatus WvCasEcm::GenerateTsPacket(const std::string& ecm, uint16_t pid,
|
|||||||
uint8_t* continuity_counter,
|
uint8_t* continuity_counter,
|
||||||
uint8_t* packet) {
|
uint8_t* packet) {
|
||||||
ssize_t bytes_modified = 0;
|
ssize_t bytes_modified = 0;
|
||||||
util::Status status = InsertEcmAsTsPacket(
|
Status status = InsertEcmAsTsPacket(ecm, pid, table_id, continuity_counter,
|
||||||
ecm, pid, table_id, continuity_counter, packet, &bytes_modified);
|
packet, &bytes_modified);
|
||||||
if (!status.ok() || bytes_modified != kTsPacketSize) {
|
if (!status.ok() || bytes_modified != kTsPacketSize) {
|
||||||
memset(packet, 0, kTsPacketSize);
|
memset(packet, 0, kTsPacketSize);
|
||||||
LOG(ERROR) << "Failed to generate TS packet: " << status;
|
LOG(ERROR) << "Failed to generate TS packet: " << status;
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "curl/curl.h"
|
#include "curl/curl.h"
|
||||||
#include "curl/easy.h"
|
#include "curl/easy.h"
|
||||||
#include "common/status.h"
|
|
||||||
#include "common/signature_util.h"
|
#include "common/signature_util.h"
|
||||||
#include "protos/public/media_cas_encryption.pb.h"
|
#include "protos/public/media_cas_encryption.pb.h"
|
||||||
|
|
||||||
@@ -41,12 +40,12 @@ DEFINE_string(signing_iv, "",
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace cas {
|
namespace cas {
|
||||||
|
|
||||||
util::Status WvCasKeyFetcher::RequestEntitlementKey(
|
Status WvCasKeyFetcher::RequestEntitlementKey(const std::string& request_string,
|
||||||
const std::string& request_string, std::string* signed_response_string) {
|
std::string* signed_response_string) {
|
||||||
if (FLAGS_signing_provider.empty() || FLAGS_signing_key.empty() ||
|
if (FLAGS_signing_provider.empty() || FLAGS_signing_key.empty() ||
|
||||||
FLAGS_signing_iv.empty()) {
|
FLAGS_signing_iv.empty()) {
|
||||||
return util::Status(
|
return Status(
|
||||||
util::error::INVALID_ARGUMENT,
|
error::INVALID_ARGUMENT,
|
||||||
"Flag 'signing_provider', 'signing_key' or 'signing_iv' is empty");
|
"Flag 'signing_provider', 'signing_key' or 'signing_iv' is empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,8 +62,8 @@ util::Status WvCasKeyFetcher::RequestEntitlementKey(
|
|||||||
// NOTE: MessageToJsonString will automatically converts 'bytes' type fields
|
// NOTE: MessageToJsonString will automatically converts 'bytes' type fields
|
||||||
// to base64. For example content ID '21140844' becomes 'MjExNDA4NDQ='.
|
// to base64. For example content ID '21140844' becomes 'MjExNDA4NDQ='.
|
||||||
if (!MessageToJsonString(request, &request_json, print_options).ok()) {
|
if (!MessageToJsonString(request, &request_json, print_options).ok()) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
"Failed to convert request message to json.");
|
"Failed to convert request message to json.");
|
||||||
}
|
}
|
||||||
LOG(INFO) << "Json CasEncryptionRequest: " << request_json;
|
LOG(INFO) << "Json CasEncryptionRequest: " << request_json;
|
||||||
|
|
||||||
@@ -76,7 +75,7 @@ util::Status WvCasKeyFetcher::RequestEntitlementKey(
|
|||||||
request_json, absl::HexStringToBytes(FLAGS_signing_key),
|
request_json, absl::HexStringToBytes(FLAGS_signing_key),
|
||||||
absl::HexStringToBytes(FLAGS_signing_iv), &signature)
|
absl::HexStringToBytes(FLAGS_signing_iv), &signature)
|
||||||
.ok()) {
|
.ok()) {
|
||||||
return util::Status(util::error::INTERNAL, "Failed to sign the request.");
|
return Status(error::INTERNAL, "Failed to sign the request.");
|
||||||
}
|
}
|
||||||
signed_request.set_signature(signature);
|
signed_request.set_signature(signature);
|
||||||
signed_request.set_signer(FLAGS_signing_provider);
|
signed_request.set_signer(FLAGS_signing_provider);
|
||||||
@@ -85,23 +84,22 @@ util::Status WvCasKeyFetcher::RequestEntitlementKey(
|
|||||||
// 'signature' fields in SignedCasEncryptionRequest to base64, because they
|
// 'signature' fields in SignedCasEncryptionRequest to base64, because they
|
||||||
// are of type 'bytes'.
|
// are of type 'bytes'.
|
||||||
if (!MessageToJsonString(signed_request, &signed_request_json).ok()) {
|
if (!MessageToJsonString(signed_request, &signed_request_json).ok()) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
"Failed to convert signed request message to json.");
|
"Failed to convert signed request message to json.");
|
||||||
}
|
}
|
||||||
LOG(INFO) << "Json SignedCasEncryptionRequest: " << signed_request_json;
|
LOG(INFO) << "Json SignedCasEncryptionRequest: " << signed_request_json;
|
||||||
|
|
||||||
// Makes HTTP request against License Server.
|
// Makes HTTP request against License Server.
|
||||||
std::string http_response_json;
|
std::string http_response_json;
|
||||||
util::Status status =
|
Status status = MakeHttpRequest(signed_request_json, &http_response_json);
|
||||||
MakeHttpRequest(signed_request_json, &http_response_json);
|
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
LOG(INFO) << "Json HTTP response: " << http_response_json;
|
LOG(INFO) << "Json HTTP response: " << http_response_json;
|
||||||
HttpResponse http_response;
|
HttpResponse http_response;
|
||||||
if (!JsonStringToMessage(http_response_json, &http_response).ok()) {
|
if (!JsonStringToMessage(http_response_json, &http_response).ok()) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
"Failed to convert http response json to message.");
|
"Failed to convert http response json to message.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes signed response.
|
// Processes signed response.
|
||||||
@@ -110,13 +108,13 @@ util::Status WvCasKeyFetcher::RequestEntitlementKey(
|
|||||||
LOG(INFO) << "Json CasEncryptionResponse: " << http_response.response();
|
LOG(INFO) << "Json CasEncryptionResponse: " << http_response.response();
|
||||||
CasEncryptionResponse response;
|
CasEncryptionResponse response;
|
||||||
if (!JsonStringToMessage(http_response.response(), &response).ok()) {
|
if (!JsonStringToMessage(http_response.response(), &response).ok()) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL,
|
||||||
"Failed to convert response json to message.");
|
"Failed to convert response json to message.");
|
||||||
}
|
}
|
||||||
SignedCasEncryptionResponse signed_response;
|
SignedCasEncryptionResponse signed_response;
|
||||||
signed_response.set_response(response.SerializeAsString());
|
signed_response.set_response(response.SerializeAsString());
|
||||||
signed_response.SerializeToString(signed_response_string);
|
signed_response.SerializeToString(signed_response_string);
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AppendToString(void* ptr, size_t size, size_t count, std::string* output) {
|
size_t AppendToString(void* ptr, size_t size, size_t count, std::string* output) {
|
||||||
@@ -125,12 +123,11 @@ size_t AppendToString(void* ptr, size_t size, size_t count, std::string* output)
|
|||||||
return data.size();
|
return data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Status WvCasKeyFetcher::MakeHttpRequest(
|
Status WvCasKeyFetcher::MakeHttpRequest(const std::string& signed_request_json,
|
||||||
const std::string& signed_request_json, std::string* http_response_json) const {
|
std::string* http_response_json) const {
|
||||||
CHECK(http_response_json);
|
CHECK(http_response_json);
|
||||||
if (FLAGS_license_server.empty()) {
|
if (FLAGS_license_server.empty()) {
|
||||||
return util::Status(util::error::INVALID_ARGUMENT,
|
return Status(error::INVALID_ARGUMENT, "Flag 'license_server' is empty");
|
||||||
"Flag 'license_server' is empty");
|
|
||||||
}
|
}
|
||||||
CURL* curl;
|
CURL* curl;
|
||||||
CURLcode curl_code;
|
CURLcode curl_code;
|
||||||
@@ -145,15 +142,14 @@ util::Status WvCasKeyFetcher::MakeHttpRequest(
|
|||||||
(int64_t)strlen(signed_request_json.c_str()));
|
(int64_t)strlen(signed_request_json.c_str()));
|
||||||
curl_code = curl_easy_perform(curl);
|
curl_code = curl_easy_perform(curl);
|
||||||
if (curl_code != CURLE_OK) {
|
if (curl_code != CURLE_OK) {
|
||||||
return util::Status(util::error::INTERNAL,
|
return Status(error::INTERNAL, "curl_easy_perform() failed: " +
|
||||||
"curl_easy_perform() failed: " +
|
std::string(curl_easy_strerror(curl_code)));
|
||||||
std::string(curl_easy_strerror(curl_code)));
|
|
||||||
}
|
}
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
} else {
|
} else {
|
||||||
return util::Status(util::error::INTERNAL, "curl_easy_init() failed");
|
return Status(error::INTERNAL, "curl_easy_init() failed");
|
||||||
}
|
}
|
||||||
return util::OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "gflags/gflags.h"
|
#include "gflags/gflags.h"
|
||||||
#include "common/status.h"
|
|
||||||
#include "media_cas_packager_sdk/internal/key_fetcher.h"
|
#include "media_cas_packager_sdk/internal/key_fetcher.h"
|
||||||
|
|
||||||
DECLARE_string(license_server);
|
DECLARE_string(license_server);
|
||||||
@@ -41,15 +40,15 @@ class WvCasKeyFetcher : public KeyFetcher {
|
|||||||
// |signed_response_string| a serialized SignedCasEncryptionResponse
|
// |signed_response_string| a serialized SignedCasEncryptionResponse
|
||||||
// message. It should be passed into
|
// message. It should be passed into
|
||||||
// widevine::cas::Ecm::ProcessCasEncryptionResponse().
|
// widevine::cas::Ecm::ProcessCasEncryptionResponse().
|
||||||
virtual util::Status RequestEntitlementKey(const std::string& request_string,
|
Status RequestEntitlementKey(const std::string& request_string,
|
||||||
std::string* signed_response_string);
|
std::string* signed_response_string) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Makes a HTTP request to License Server for entitlement key(s).
|
// Makes a HTTP request to License Server for entitlement key(s).
|
||||||
// Returns the HTTP response in Json format in |http_response_json|.
|
// Returns the HTTP response in Json format in |http_response_json|.
|
||||||
// Protected visibility to support unit testing.
|
// Protected visibility to support unit testing.
|
||||||
virtual util::Status MakeHttpRequest(const std::string& signed_request_json,
|
virtual Status MakeHttpRequest(const std::string& signed_request_json,
|
||||||
std::string* http_response_json) const;
|
std::string* http_response_json) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "common/status.h"
|
|
||||||
#include "protos/public/media_cas_encryption.pb.h"
|
#include "protos/public/media_cas_encryption.pb.h"
|
||||||
|
|
||||||
using testing::_;
|
using testing::_;
|
||||||
@@ -55,9 +54,8 @@ class MockWvCasKeyFetcher : public WvCasKeyFetcher {
|
|||||||
public:
|
public:
|
||||||
MockWvCasKeyFetcher() : WvCasKeyFetcher() {}
|
MockWvCasKeyFetcher() : WvCasKeyFetcher() {}
|
||||||
~MockWvCasKeyFetcher() override {}
|
~MockWvCasKeyFetcher() override {}
|
||||||
MOCK_CONST_METHOD2(MakeHttpRequest,
|
MOCK_CONST_METHOD2(MakeHttpRequest, Status(const std::string& signed_request_json,
|
||||||
util::Status(const std::string& signed_request_json,
|
std::string* http_response_json));
|
||||||
std::string* http_response_json));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class WvCasKeyFetcherTest : public ::testing::Test {
|
class WvCasKeyFetcherTest : public ::testing::Test {
|
||||||
@@ -93,7 +91,7 @@ TEST_F(WvCasKeyFetcherTest, TestRequestEntitlementKey) {
|
|||||||
EXPECT_CALL(mock_key_fetcher_,
|
EXPECT_CALL(mock_key_fetcher_,
|
||||||
MakeHttpRequest(kSignedCasEncryptionRequest, _))
|
MakeHttpRequest(kSignedCasEncryptionRequest, _))
|
||||||
.WillOnce(DoAll(SetArgumentPointee<1>(std::string(kHttpResponse)),
|
.WillOnce(DoAll(SetArgumentPointee<1>(std::string(kHttpResponse)),
|
||||||
Return(util::OkStatus())));
|
Return(OkStatus())));
|
||||||
|
|
||||||
std::string actual_signed_response;
|
std::string actual_signed_response;
|
||||||
EXPECT_OK(mock_key_fetcher_.RequestEntitlementKey(
|
EXPECT_OK(mock_key_fetcher_.RequestEntitlementKey(
|
||||||
|
|||||||
190
media_cas_packager_sdk/public/wv_ecmg.cc
Normal file
190
media_cas_packager_sdk/public/wv_ecmg.cc
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2018 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Example server that listens on a port for Simulcrypt API messages.
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "gflags/gflags.h"
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
|
||||||
|
|
||||||
|
static constexpr int32_t kDefaultDelayStart = 200;
|
||||||
|
static constexpr int32_t kDefaultDelayStop = 200;
|
||||||
|
static constexpr int32_t kDefaultEcmRepPeriod = 100;
|
||||||
|
static constexpr int32_t kDefaultMaxCompTime = 100;
|
||||||
|
static constexpr int32_t kAccessCriteriaTransferMode = 1;
|
||||||
|
|
||||||
|
DEFINE_int32(port, 0, "Server port number");
|
||||||
|
|
||||||
|
// ECMG related flags.
|
||||||
|
// TODO(user): Consider adding flags 'ac_delay_start', 'ac_delay_stop',
|
||||||
|
// 'transition_delay_start', 'transition_delay_stop'.
|
||||||
|
DEFINE_int32(delay_start, kDefaultDelayStart,
|
||||||
|
absl::StrCat("This flag sets the DVB SimulCrypt delay_start "
|
||||||
|
"parameter, in milliseconds. Default: ",
|
||||||
|
kDefaultDelayStart, " ms")
|
||||||
|
.c_str());
|
||||||
|
DEFINE_int32(delay_stop, kDefaultDelayStop,
|
||||||
|
absl::StrCat("This flag sets the DVB SimulCrypt delay_stop "
|
||||||
|
"parameter, in milliseconds. Default: ",
|
||||||
|
kDefaultDelayStop, " ms")
|
||||||
|
.c_str());
|
||||||
|
DEFINE_int32(ecm_rep_period, kDefaultEcmRepPeriod,
|
||||||
|
absl::StrCat("It sets the DVB SimulCrypt parameter "
|
||||||
|
"ECM_rep_period, in milliseconds. Default: ",
|
||||||
|
kDefaultEcmRepPeriod, " ms")
|
||||||
|
.c_str());
|
||||||
|
DEFINE_int32(max_comp_time, kDefaultMaxCompTime,
|
||||||
|
absl::StrCat("It sets the DVB SimulCrypt parameter max_comp_time, "
|
||||||
|
"in milliseconds. Default: ",
|
||||||
|
kDefaultMaxCompTime, " ms")
|
||||||
|
.c_str());
|
||||||
|
DEFINE_int32(access_criteria_transfer_mode, kAccessCriteriaTransferMode,
|
||||||
|
absl::StrCat("It sets the DVB SimulCrypt parameter "
|
||||||
|
"access_criteria_transfer_mode. Default: ",
|
||||||
|
kAccessCriteriaTransferMode)
|
||||||
|
.c_str());
|
||||||
|
|
||||||
|
#define LISTEN_QUEUE_SIZE (20)
|
||||||
|
#define BUFFER_SIZE (1024)
|
||||||
|
|
||||||
|
using widevine::cas::EcmgClientHandler;
|
||||||
|
using widevine::cas::EcmgConfig;
|
||||||
|
|
||||||
|
void BuildEcmgConfig(EcmgConfig* config) {
|
||||||
|
DCHECK(config);
|
||||||
|
config->delay_start = FLAGS_delay_start;
|
||||||
|
config->delay_stop = FLAGS_delay_stop;
|
||||||
|
config->ecm_rep_period = FLAGS_ecm_rep_period;
|
||||||
|
config->max_comp_time = FLAGS_max_comp_time;
|
||||||
|
config->access_criteria_transfer_mode = FLAGS_access_criteria_transfer_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintMessage(const std::string& description, const char* const message,
|
||||||
|
size_t length) {
|
||||||
|
LOG(INFO) << description;
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
printf("'\\x%02x', ", static_cast<uint16_t>(*(message + i)));
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServeClient(int socket_fd, EcmgClientHandler* ecmg) {
|
||||||
|
DCHECK(ecmg);
|
||||||
|
char request[BUFFER_SIZE];
|
||||||
|
char response[BUFFER_SIZE];
|
||||||
|
while (true) {
|
||||||
|
bzero(request, BUFFER_SIZE);
|
||||||
|
bzero(response, BUFFER_SIZE);
|
||||||
|
size_t response_length = 0;
|
||||||
|
size_t request_length = recv(socket_fd, request, BUFFER_SIZE, 0);
|
||||||
|
if (request_length == 0) {
|
||||||
|
LOG(ERROR) << "No more request from client";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (request_length < 0) {
|
||||||
|
LOG(ERROR) << "Failed to receive request from client";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PrintMessage("Request", request, request_length);
|
||||||
|
ecmg->HandleRequest(request, response, &response_length);
|
||||||
|
PrintMessage("Response", response, response_length);
|
||||||
|
if (send(socket_fd, response, response_length, 0) < 0) {
|
||||||
|
LOG(INFO) << "Failed to send response to client";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
CHECK(FLAGS_port != 0) << "need --port";
|
||||||
|
|
||||||
|
EcmgConfig ecmg_config;
|
||||||
|
BuildEcmgConfig(&ecmg_config);
|
||||||
|
|
||||||
|
// Server address.
|
||||||
|
struct sockaddr_in server_address;
|
||||||
|
bzero(reinterpret_cast<char*>(&server_address), sizeof(server_address));
|
||||||
|
server_address.sin_family = AF_INET;
|
||||||
|
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
server_address.sin_port = htons(FLAGS_port);
|
||||||
|
|
||||||
|
// Create a listening socket.
|
||||||
|
int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);
|
||||||
|
CHECK(listen_socket_fd >= 0) << "Failed to open listening socket";
|
||||||
|
|
||||||
|
// Set SO_REUSEADDR on a socket to true (1).
|
||||||
|
int optval = 1;
|
||||||
|
setsockopt(listen_socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval,
|
||||||
|
sizeof(optval));
|
||||||
|
|
||||||
|
// Bind address.
|
||||||
|
CHECK(bind(listen_socket_fd, (struct sockaddr*)&server_address,
|
||||||
|
sizeof(server_address)) >= 0)
|
||||||
|
<< "Failed to bind on server socket";
|
||||||
|
|
||||||
|
// Listen for connection from clients.
|
||||||
|
std::cout << "Server listening ..." << std::endl << std::flush;
|
||||||
|
int return_val = listen(listen_socket_fd, LISTEN_QUEUE_SIZE);
|
||||||
|
switch (return_val) {
|
||||||
|
case EADDRINUSE:
|
||||||
|
LOG(FATAL) << "Another socket is already listening on the same port.";
|
||||||
|
break;
|
||||||
|
case EBADF:
|
||||||
|
LOG(FATAL) << "The argument sockfd is not a valid descriptor.";
|
||||||
|
break;
|
||||||
|
case ENOTSOCK:
|
||||||
|
LOG(FATAL) << "The argument sockfd is not a socket.";
|
||||||
|
break;
|
||||||
|
case EOPNOTSUPP:
|
||||||
|
LOG(FATAL) << "The socket is not of a type that supports the listen() "
|
||||||
|
"operation.";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A single client handler, allow only 1 TCP connection / 1 channel at a time.
|
||||||
|
EcmgClientHandler client_handler(&ecmg_config);
|
||||||
|
|
||||||
|
// While loop to serve different client connections.
|
||||||
|
while (true) {
|
||||||
|
struct sockaddr_in client_address;
|
||||||
|
socklen_t client_address_size = sizeof(client_address);
|
||||||
|
int client_socket_fd = accept(
|
||||||
|
listen_socket_fd, reinterpret_cast<struct sockaddr*>(&client_address),
|
||||||
|
&client_address_size);
|
||||||
|
LOG(INFO) << "\nTCP connection start\n";
|
||||||
|
if (client_socket_fd < 0) {
|
||||||
|
LOG(ERROR) << "Failed to accept connection request from client";
|
||||||
|
} else {
|
||||||
|
// TODO(user): Support multi-threading of serving concurrent clients.
|
||||||
|
// TODO(user): Per jfore@ suggestion, look into using
|
||||||
|
// http://man7.org/linux/man-pages/man7/epoll.7.html
|
||||||
|
ServeClient(client_socket_fd, &client_handler);
|
||||||
|
}
|
||||||
|
LOG(INFO) << "\nTCP connection closed\n";
|
||||||
|
close(client_socket_fd);
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close listening socket.
|
||||||
|
close(listen_socket_fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -11,13 +11,6 @@ syntax = "proto2";
|
|||||||
|
|
||||||
package widevine.cas;
|
package widevine.cas;
|
||||||
|
|
||||||
// Encrypt/decrypt mode.
|
|
||||||
enum CasCryptoMode {
|
|
||||||
CRYPTO_MODE_UNSPECIFIED = 0;
|
|
||||||
CTR = 1;
|
|
||||||
CBC = 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Widevine private data in the CA descriptor.
|
// Widevine private data in the CA descriptor.
|
||||||
message CaDescriptorPrivateData {
|
message CaDescriptorPrivateData {
|
||||||
// Provider name.
|
// Provider name.
|
||||||
|
|||||||
@@ -11,9 +11,7 @@
|
|||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#define EXPECT_OK(expression) \
|
#define EXPECT_OK(expression) EXPECT_EQ(error::OK, expression.error_code())
|
||||||
EXPECT_EQ(util::error::OK, expression.error_code())
|
#define ASSERT_OK(expression) ASSERT_EQ(error::OK, expression.error_code())
|
||||||
#define ASSERT_OK(expression) \
|
|
||||||
ASSERT_EQ(util::error::OK, expression.error_code())
|
|
||||||
|
|
||||||
#endif // TESTING_GUNIT_H_
|
#endif // TESTING_GUNIT_H_
|
||||||
|
|||||||
48
zlib.BUILD
48
zlib.BUILD
@@ -1,48 +0,0 @@
|
|||||||
################################################################################
|
|
||||||
# Copyright 2018 Google LLC.
|
|
||||||
#
|
|
||||||
# This software is licensed under the terms defined in the Widevine Master
|
|
||||||
# License Agreement. For a copy of this agreement, please contact
|
|
||||||
# widevine-licensing@google.com.
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
# Build file for zlib.
|
|
||||||
|
|
||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "zlib",
|
|
||||||
srcs = [
|
|
||||||
"adler32.c",
|
|
||||||
"compress.c",
|
|
||||||
"crc32.c",
|
|
||||||
"crc32.h",
|
|
||||||
"deflate.c",
|
|
||||||
"deflate.h",
|
|
||||||
"gzclose.c",
|
|
||||||
"gzguts.h",
|
|
||||||
"gzlib.c",
|
|
||||||
"gzread.c",
|
|
||||||
"gzwrite.c",
|
|
||||||
"infback.c",
|
|
||||||
"inffast.c",
|
|
||||||
"inffast.h",
|
|
||||||
"inffixed.h",
|
|
||||||
"inflate.c",
|
|
||||||
"inflate.h",
|
|
||||||
"inftrees.c",
|
|
||||||
"inftrees.h",
|
|
||||||
"trees.c",
|
|
||||||
"trees.h",
|
|
||||||
"uncompr.c",
|
|
||||||
"zconf.h",
|
|
||||||
"zutil.c",
|
|
||||||
"zutil.h",
|
|
||||||
],
|
|
||||||
hdrs = ["zlib.h"],
|
|
||||||
copts = [
|
|
||||||
"-Wno-shift-negative-value",
|
|
||||||
"-Wno-implicit-function-declaration",
|
|
||||||
],
|
|
||||||
includes = ["."],
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user