diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..fe9863c --- /dev/null +++ b/BUILD @@ -0,0 +1,30 @@ +################################################################################ +# 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 MediaCas Proxy SDK tar package. + +load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") + +pkg_tar( + name = "media_cas_proxy_sdk_files", + strip_prefix = "/", + files = [ + "//common:binary_release_files", + "//media_cas_proxy_sdk/external/common/wvpl:binary_release_files", + "//sdk/external/common/wvpl:binary_release_files", + "//util:binary_release_files", + ], + mode = "0644", +) + +pkg_tar( + name = "media_cas_proxy_sdk-bin", + deps = [":media_cas_proxy_sdk_files"], + files = ["//media_cas_proxy_sdk/external/common/wvpl:libwvpl_cas_proxy.so"], + mode = "0755", +) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fe743e5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,4 @@ +Copyright 2017 Google, Inc. + +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. diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..eee1f2b --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,89 @@ +workspace(name = "media_cas_proxy_sdk") + +# CCTZ (Time-zone framework), needed by abseil. +git_repository( + name = "com_googlesource_code_cctz", + remote = "https://github.com/google/cctz.git", + tag = "v2.2", +) + +git_repository( + name = "abseil_repo", + commit = "475d64f2de7403a01b1b36c487328ed41d29c20c", #2018-04-10 + remote = "https://github.com/abseil/abseil-cpp.git", +) + +# Name com_google_protobuf instead of protobuf_repo because Bazel's proto rules +# implicitly depend on @com_google_protobuf. See +# https://bazel.build/blog/2017/02/27/protocol-buffers.html. +git_repository( + name = "com_google_protobuf", + remote = "https://github.com/google/protobuf.git", + tag = "v3.5.0", +) + +git_repository( + name = "boringssl_repo", + commit = "27ae6cadd74fd054208730827a8de3fe9bc648f0", # 2017-12-07 + remote = "https://github.com/google/boringssl.git", +) + +git_repository( + name = "gflags_repo", + commit = "fe57e5af4db74ab298523f06d2c43aa895ba9f98", # 2016-07-20 + remote = "https://github.com/gflags/gflags.git", +) + +git_repository( + name = "googletest_repo", + commit = "9816b96a6ddc0430671693df90192bbee57108b6", # 2017-08-11 + remote = "https://github.com/google/googletest.git", +) + +maven_jar( + name = "junit_junit", + artifact = "junit:junit:4.11", + sha1 = "4e031bb61df09069aeb2bffb4019e7a5034a4ee0", +) + +new_git_repository( + name = "glog_repo", + build_file = "glog.BUILD", + commit = "0472b91c5defdf90cff7292e3bf7bd86770a9a0a", # 2016-07-13 + remote = "https://github.com/google/glog.git", +) + +bind( + name = "protobuf", + actual = "@com_google_protobuf//:protobuf", +) + +bind( + name = "protobuf_java", + actual = "@com_google_protobuf//:protobuf_java", +) + +bind( + name = "openssl", + actual = "@boringssl_repo//:crypto", +) + +bind( + name = "gflags", + actual = "@gflags_repo//:gflags", +) + +bind( + name = "gtest", + actual = "@googletest_repo//:gtest", +) + +bind( + name = "gtest_main", + actual = "@googletest_repo//:gtest_main", +) + +bind( + name = "glog", + actual = "@glog_repo//:glog", +) diff --git a/base/BUILD b/base/BUILD new file mode 100644 index 0000000..0612c2d --- /dev/null +++ b/base/BUILD @@ -0,0 +1,25 @@ +################################################################################ +# Copyright 2016 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. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "base", + hdrs = [ + "macros.h", + "thread_annotations.h", + "timestamp.h", + ], + deps = [ + "@abseil_repo//absl/base", + "//external:gflags", + "//external:glog", + ], +) diff --git a/base/macros.h b/base/macros.h new file mode 100644 index 0000000..e2ec936 --- /dev/null +++ b/base/macros.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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 BASE_MACROS_H_ +#define BASE_MACROS_H_ + +#include "absl/base/macros.h" + +// DISALLOW_COPY_AND_ASSIGN disallows the copy constructor and copy assignment +// operator. DISALLOW_IMPLICIT_CONSTRUCTORS is like DISALLOW_COPY_AND_ASSIGN, +// but also disallows the default constructor, intended to help make a +// class uninstantiable. +// +// These must be placed in the private: declarations for a class. +// +// Note: New code should prefer static_assert over COMPILE_ASSERT. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName& operator=(const TypeName&) = delete +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +#define arraysize(array) ABSL_ARRAYSIZE(array) + +#endif // BASE_MACROS_H_ diff --git a/base/thread_annotations.h b/base/thread_annotations.h new file mode 100644 index 0000000..ce3535a --- /dev/null +++ b/base/thread_annotations.h @@ -0,0 +1,14 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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 BASE_THREAD_ANNOTATIONS_H_ +#define BASE_THREAD_ANNOTATIONS_H_ + +#include "absl/base/thread_annotations.h" + +#endif // BASE_THREAD_ANNOTATIONS_H_ diff --git a/base/timestamp.h b/base/timestamp.h new file mode 100644 index 0000000..81b1a77 --- /dev/null +++ b/base/timestamp.h @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef BASE_TIMESTAMP_H_ +#define BASE_TIMESTAMP_H_ + +namespace widevine { + +class BuildData { + public: + static const char* Timestamp() { return __DATE__ " " __TIME__; } +}; + +} // namespace widevine + +#endif // BASE_TIMESTAMP_H_ diff --git a/common/BUILD b/common/BUILD new file mode 100644 index 0000000..28b097e --- /dev/null +++ b/common/BUILD @@ -0,0 +1,577 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ +# +# Constants, data structures, util classes for Widevine libraries. + +package( + default_visibility = ["//visibility:public"], +) + +filegroup( + name = "binary_release_files", + srcs = [ + "certificate_type.h", + ], +) + +cc_library( + name = "widevine_system_id", + srcs = ["widevine_system_id.cc"], + hdrs = ["widevine_system_id.h"], + deps = ["//base"], +) + +cc_library( + name = "certificate_type", + hdrs = ["certificate_type.h"], +) + +cc_library( + name = "drm_root_certificate", + srcs = ["drm_root_certificate.cc"], + hdrs = ["drm_root_certificate.h"], + deps = [ + ":certificate_type", + ":error_space", + ":rsa_key", + "//base", + "@abseil_repo//absl/strings", + "//external:openssl", + "//util:status", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_test( + name = "drm_root_certificate_test", + timeout = "short", + srcs = ["drm_root_certificate_test.cc"], + deps = [ + ":drm_root_certificate", + ":rsa_key", + ":rsa_test_keys", + "//base", + "//testing:gunit_main", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_library( + name = "certificate_util", + srcs = ["certificate_util.cc"], + hdrs = ["certificate_util.h"], + deps = [ + ":certificate_type", + ":drm_root_certificate", + ":drm_service_certificate", + ":verified_media_pipeline", + ":vmp_checker", + "//base", + "//util:status", + "//license_server_sdk/internal:sdk", + ], +) + +cc_library( + name = "client_id_util", + srcs = ["client_id_util.cc"], + hdrs = ["client_id_util.h"], + deps = [ + ":aes_cbc_util", + ":drm_service_certificate", + ":error_space", + "//base", + "@abseil_repo//absl/strings", + "//util:status", + "//protos/public:client_identification_proto", + "//protos/public:errors_proto", + ], +) + +cc_library( + name = "rsa_util", + srcs = ["rsa_util.cc"], + hdrs = ["rsa_util.h"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "rsa_util_test", + size = "medium", + timeout = "short", + srcs = ["rsa_util_test.cc"], + deps = [ + ":rsa_test_keys", + ":rsa_util", + "//base", + "//testing:gunit", + "//testing:gunit_main", + "//external:openssl", + ], +) + +cc_library( + name = "openssl_util", + hdrs = ["openssl_util.h"], + deps = [ + "//external:openssl", + ], +) + +cc_library( + name = "rsa_key", + srcs = ["rsa_key.cc"], + hdrs = ["rsa_key.h"], + deps = [ + ":rsa_util", + ":sha_util", + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "rsa_key_test", + size = "medium", + timeout = "short", + srcs = ["rsa_key_test.cc"], + deps = [ + ":rsa_key", + ":rsa_test_keys", + ":rsa_util", + "//testing:gunit", + "//testing:gunit_main", + ], +) + +cc_library( + name = "rsa_test_keys", + testonly = 1, + srcs = ["rsa_test_keys.cc"], + hdrs = ["rsa_test_keys.h"], + deps = [ + "//base", + ], +) + +cc_library( + name = "mock_rsa_key", + testonly = 1, + hdrs = ["mock_rsa_key.h"], + deps = [ + ":rsa_key", + "//testing:gunit", + ], +) + +cc_library( + name = "aes_cbc_util", + srcs = ["aes_cbc_util.cc"], + hdrs = ["aes_cbc_util.h"], + visibility = ["//visibility:public"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "aes_cbc_util_test", + srcs = ["aes_cbc_util_test.cc"], + deps = [ + ":aes_cbc_util", + "//testing:gunit_main", + ], +) + +cc_library( + name = "crypto_util", + srcs = ["crypto_util.cc"], + hdrs = ["crypto_util.h"], + visibility = ["//visibility:public"], + deps = [ + "//base", + "@abseil_repo//absl/strings", + "//external:openssl", + "//util/endian", + ], +) + +cc_test( + name = "crypto_util_test", + size = "medium", + srcs = ["crypto_util_test.cc"], + deps = [ + ":crypto_util", + "//testing:gunit", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "ecb_util", + srcs = ["ecb_util.cc"], + hdrs = ["ecb_util.h"], + visibility = ["//visibility:public"], + deps = [ + "//base", + "@abseil_repo//absl/strings", + "//external:openssl", + ], +) + +cc_test( + name = "ecb_util_test", + size = "small", + srcs = ["ecb_util_test.cc"], + deps = [ + ":ecb_util", + "//testing:gunit", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "file_util", + srcs = ["file_util.cc"], + hdrs = ["file_util.h"], + deps = [ + "//base", + ], +) + +cc_test( + name = "file_util_test", + srcs = ["file_util_test.cc"], + deps = [ + ":file_util", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "random_util", + srcs = ["random_util.cc"], + hdrs = ["random_util.h"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "random_util_test", + srcs = ["random_util_test.cc"], + deps = [ + ":random_util", + "//testing:gunit_main", + ], +) + +cc_library( + name = "sha_util", + srcs = ["sha_util.cc"], + hdrs = ["sha_util.h"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "sha_util_test", + srcs = ["sha_util_test.cc"], + deps = [ + ":sha_util", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "signature_util", + srcs = ["signature_util.cc"], + hdrs = ["signature_util.h"], + deps = [ + ":aes_cbc_util", + ":rsa_key", + ":sha_util", + "//base", + "//util:status", + ], +) + +cc_library( + name = "signing_key_util", + srcs = ["signing_key_util.cc"], + hdrs = ["signing_key_util.h"], + deps = [ + ":crypto_util", + "//base", + "//protos/public:license_protocol_proto", + ], +) + +cc_test( + name = "signing_key_util_test", + size = "small", + srcs = ["signing_key_util_test.cc"], + deps = [ + ":crypto_util", + ":signing_key_util", + "//testing:gunit", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//protos/public:license_protocol_proto", + ], +) + +cc_library( + name = "test_certificates", + testonly = 1, + srcs = ["test_certificates.cc"], + hdrs = ["test_certificates.h"], + deps = [ + "//base", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "wvm_token_handler", + srcs = ["wvm_token_handler.cc"], + hdrs = ["wvm_token_handler.h"], + deps = [ + ":aes_cbc_util", + ":ecb_util", + ":sha_util", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util/endian", + "//util/gtl:map_util", + "//util:status", + ], +) + +cc_test( + name = "wvm_token_handler_test", + size = "small", + srcs = ["wvm_token_handler_test.cc"], + deps = [ + ":wvm_test_keys", + ":wvm_token_handler", + "//testing:gunit", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "wvm_test_keys", + testonly = 1, + srcs = ["wvm_test_keys.cc"], + hdrs = ["wvm_test_keys.h"], + deps = [ + ":wvm_token_handler", + "//base", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "error_space", + srcs = ["error_space.cc"], + hdrs = ["error_space.h"], + deps = [ + "//util:status", + "//util:proto_status", + "//protos/public:errors_proto", + ], +) + +cc_library( + name = "remote_attestation_verifier", + srcs = ["remote_attestation_verifier.cc"], + hdrs = ["remote_attestation_verifier.h"], + deps = [ + ":client_id_util", + ":drm_service_certificate", + ":error_space", + ":rsa_key", + ":x509_cert", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util:status", + "//protos/public:client_identification_proto", + "//protos/public:errors_proto", + "//protos/public:remote_attestation_proto", + ], +) + +cc_library( + name = "drm_service_certificate", + srcs = ["drm_service_certificate.cc"], + hdrs = ["drm_service_certificate.h"], + deps = [ + ":aes_cbc_util", + ":certificate_type", + ":drm_root_certificate", + ":error_space", + ":rsa_key", + ":rsa_util", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util/gtl:map_util", + "//util:status", + "//protos/public:client_identification_proto", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_test( + name = "drm_service_certificate_test", + timeout = "short", + srcs = ["drm_service_certificate_test.cc"], + deps = [ + ":aes_cbc_util", + ":drm_service_certificate", + ":rsa_key", + ":rsa_test_keys", + ":rsa_util", + ":test_certificates", + "//base", + "//external:protobuf", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//protos/public:client_identification_proto", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:license_server_sdk_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_library( + name = "verified_media_pipeline", + srcs = ["verified_media_pipeline.cc"], + hdrs = ["verified_media_pipeline.h"], + deps = [ + ":vmp_checker", + "//base", + "@abseil_repo//absl/strings", + "//util:status", + "//protos/public:license_protocol_proto", + ], +) + +cc_library( + name = "x509_cert", + srcs = ["x509_cert.cc"], + hdrs = ["x509_cert.h"], + deps = [ + ":openssl_util", + ":rsa_key", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//external:openssl", + "//util:status", + ], +) + +cc_library( + name = "test_utils", + testonly = 1, + srcs = ["test_utils.cc"], + hdrs = ["test_utils.h"], + deps = [ + "//base", + "//external:openssl", + "//util:status", + ], +) + +cc_test( + name = "x509_cert_test", + timeout = "short", + srcs = ["x509_cert_test.cc"], + deps = [ + ":rsa_key", + ":test_utils", + ":x509_cert", + "//base", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "vmp_checker", + srcs = ["vmp_checker.cc"], + hdrs = ["vmp_checker.h"], + deps = [ + ":certificate_type", + ":error_space", + ":rsa_key", + ":x509_cert", + "//base", + "//util:status", + "//protos/public:errors_proto", + "//protos/public:verified_media_pipeline_proto", + ], +) + +cc_test( + name = "vmp_checker_test", + timeout = "short", + srcs = ["vmp_checker_test.cc"], + deps = [ + ":rsa_key", + ":vmp_checker", + "//base", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//protos/public:errors_proto", + "//protos/public:verified_media_pipeline_proto", + ], +) + +cc_library( + name = "string_util", + srcs = ["string_util.cc"], + hdrs = ["string_util.h"], + deps = [ + "//base", + "//util:status", + ], +) + +cc_test( + name = "string_util_test", + srcs = ["string_util_test.cc"], + deps = [ + ":string_util", + "//base", + "//testing:gunit_main", + ], +) diff --git a/common/aes_cbc_util.cc b/common/aes_cbc_util.cc new file mode 100644 index 0000000..66e6552 --- /dev/null +++ b/common/aes_cbc_util.cc @@ -0,0 +1,132 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/aes_cbc_util.h" + +#include + +#include +#include "glog/logging.h" +#include "openssl/aes.h" + +namespace widevine { +namespace crypto_util { + +// Encrypts the provided plantext std::string using AES-CBC encryption. +std::string EncryptAesCbc(const std::string& key, const std::string& iv, + const std::string& plaintext) { + const size_t num_padding_bytes = + AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE); + std::string padded_text = plaintext; + padded_text.append(num_padding_bytes, static_cast(num_padding_bytes)); + return EncryptAesCbcNoPad(key, iv, padded_text); +} + +std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& plaintext) { + if (iv.size() != AES_BLOCK_SIZE) { + LOG(WARNING) << "Invalid CBC IV size: " << iv.size(); + return std::string(); + } + if (plaintext.size() % AES_BLOCK_SIZE) { + LOG(WARNING) << "Invalid AEC-CBC plaintext size: " << plaintext.size(); + return std::string(); + } + + AES_KEY aes_key; + if (AES_set_encrypt_key(reinterpret_cast(&key[0]), + key.size() * 8, &aes_key) != 0) { + LOG(WARNING) << "Invalid AES key."; + return std::string(); + } + + std::string encrypted(plaintext.size(), 0); + std::vector local_iv(iv.begin(), iv.end()); + AES_cbc_encrypt(reinterpret_cast(plaintext.data()), + reinterpret_cast(&encrypted[0]), plaintext.size(), + &aes_key, &local_iv[0], AES_ENCRYPT); + return encrypted; +} + +// Decrypts the AES-CBC encrypted text. Returns an empty std::string on error or +// the plaintext on success. +std::string DecryptAesCbc(const std::string& key, const std::string& iv, + const std::string& ciphertext) { + if (ciphertext.empty()) { + LOG(WARNING) << "Empty ciphertext."; + return std::string(); + } + if (iv.size() != AES_BLOCK_SIZE) { + LOG(WARNING) << "Invalid CBC IV size: " << iv.size(); + return std::string(); + } + if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) { + LOG(WARNING) << "Ciphertext not a multiple of AES block size: " + << ciphertext.size(); + return std::string(); + } + + AES_KEY aes_key; + if (AES_set_decrypt_key(reinterpret_cast(&key[0]), + key.size() * 8, &aes_key) != 0) { + LOG(WARNING) << "Invalid AES key."; + return std::string(); + } + + std::string cleartext(ciphertext.size(), 0); + std::vector local_iv(iv.begin(), iv.end()); + AES_cbc_encrypt(reinterpret_cast(ciphertext.data()), + reinterpret_cast(&cleartext[0]), ciphertext.size(), + &aes_key, &local_iv[0], AES_DECRYPT); + + const uint8_t num_padding_bytes = cleartext[cleartext.size() - 1]; + if (num_padding_bytes > AES_BLOCK_SIZE) { + LOG(WARNING) << "AES padding too long: " << num_padding_bytes; + return std::string(); + } + for (uint8_t i = 0; i < num_padding_bytes; ++i) { + if (cleartext[cleartext.size() - 1 - i] != num_padding_bytes) { + LOG(WARNING) << "Padding verification failure."; + return std::string(); + } + } + cleartext.resize(cleartext.size() - num_padding_bytes); + return cleartext; +} + +std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& ciphertext) { + std::vector local_iv(iv.begin(), iv.end()); + if (local_iv.empty()) { + local_iv.resize(AES_BLOCK_SIZE, '\0'); + } else if (local_iv.size() != AES_BLOCK_SIZE) { + LOG(WARNING) << "Invalid CBC IV size: " << iv.size(); + return std::string(); + } + if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) { + LOG(WARNING) << "Ciphertext not a multiple of AES block size: " + << ciphertext.size(); + return std::string(); + } + + AES_KEY aes_key; + if (AES_set_decrypt_key(reinterpret_cast(&key[0]), + key.size() * 8, &aes_key) != 0) { + LOG(WARNING) << "Invalid AES key."; + return std::string(); + } + + std::string cleartext(ciphertext.size(), 0); + AES_cbc_encrypt(reinterpret_cast(ciphertext.data()), + reinterpret_cast(&cleartext[0]), ciphertext.size(), + &aes_key, &local_iv[0], AES_DECRYPT); + return cleartext; +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/aes_cbc_util.h b/common/aes_cbc_util.h new file mode 100644 index 0000000..a1ad59b --- /dev/null +++ b/common/aes_cbc_util.h @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_AES_CBC_UTIL_H_ +#define COMMON_AES_CBC_UTIL_H_ + +#include + +namespace widevine { +namespace crypto_util { + +// Helper for wrapping AES CBC encryption. Uses PKCS7 padding. +std::string EncryptAesCbc(const std::string& key, const std::string& iv, + const std::string& plaintext); + +// Helper for wrapping AES CBC encryption. Adds no padding, so the input +// must be an multiple of the 16-byte AES block size. Returns empty std::string +// on error. +std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& plaintext); + +// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an +// empty std::string on error or the plaintext on success. Expects PKCS7 padding. +std::string DecryptAesCbc(const std::string& key, const std::string& iv, + const std::string& ciphertext); + +// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an +// empty std::string on error or the plaintext on success. +// Uses no padding; fails if the ciphertext is not a multiple of 16 bytes. +// This is used to decrypt the encrypted blob in the WVM keyboxes, with +// a zero iv. +std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& ciphertext); + +} // namespace crypto_util +} // namespace widevine + +#endif // COMMON_AES_CBC_UTIL_H_ diff --git a/common/aes_cbc_util_test.cc b/common/aes_cbc_util_test.cc new file mode 100644 index 0000000..1eded76 --- /dev/null +++ b/common/aes_cbc_util_test.cc @@ -0,0 +1,161 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/aes_cbc_util.h" +#include "testing/gmock.h" +#include "testing/gunit.h" + +namespace { + +const uint8_t kKey[] = { + 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e, + 0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b, +}; + +const uint8_t kIv[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, +}; + +} // namespace + +namespace widevine { +namespace crypto_util { + +TEST(CryptoUtilTest, EncryptAndDecryptAesCbc) { + std::string plain_text("Foo"); + std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), plain_text); + std::string expected_ciphertext( + "\xCF\x1A\x3\x1C\x9C\x8C\xB9Z\xEC\xC0\x17\xDCRxX\xD7"); + ASSERT_EQ(0, ciphertext.size() % 16); + ASSERT_GT(ciphertext.size(), plain_text.size()); + ASSERT_EQ(expected_ciphertext, ciphertext); + + std::string decrypted = DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), ciphertext); + ASSERT_EQ(plain_text, decrypted); +} + +TEST(CryptoUtilTest, DecryptAesCbcNoPad) { + const uint8_t kKey[] = { + 0xdd, 0x71, 0x39, 0xea, 0xfa, 0xce, 0xed, 0x7c, + 0xda, 0x9f, 0x25, 0xda, 0x8a, 0xa9, 0x15, 0xea, + }; + const uint8_t kIv[] = { + 0x5d, 0x16, 0x44, 0xea, 0xec, 0x11, 0xf9, 0x83, + 0x14, 0x75, 0x41, 0xe4, 0x6e, 0xeb, 0x27, 0x74, + }; + const uint8_t kCiphertext[] = { + 0x6d, 0xa6, 0xda, 0xe4, 0xee, 0x40, 0x09, 0x17, + 0x54, 0x7b, 0xba, 0xa5, 0x27, 0xb8, 0x82, 0x1b, + }; + const uint8_t kExpectedPlaintext[] = { + 0xb3, 0x49, 0xd4, 0x80, 0x9e, 0x91, 0x06, 0x87, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10, + }; + const uint8_t kExpectedPlaintextEmptyIv[] = { + 0xee, 0x5f, 0x90, 0x6a, 0x72, 0x80, 0xff, 0x04, + 0x14, 0x75, 0x41, 0xa4, 0x6e, 0xeb, 0x27, 0x64, + }; + + std::string decrypted = DecryptAesCbcNoPad( + std::string(kKey, kKey + sizeof(kKey)), std::string(kIv, kIv + sizeof(kIv)), + std::string(kCiphertext, kCiphertext + sizeof(kCiphertext))); + ASSERT_EQ(std::string(kExpectedPlaintext, + kExpectedPlaintext + sizeof(kExpectedPlaintext)), + decrypted); + + std::string dummy_iv; + decrypted = DecryptAesCbcNoPad( + std::string(kKey, kKey + sizeof(kKey)), dummy_iv, + std::string(kCiphertext, kCiphertext + sizeof(kCiphertext))); + ASSERT_EQ( + std::string(kExpectedPlaintextEmptyIv, + kExpectedPlaintextEmptyIv + sizeof(kExpectedPlaintextEmptyIv)), + decrypted); +} + +TEST(CryptoUtilTest, TestFailedEncrypt) { + // Test with bogus initialization vector. + std::string plain_text("Foo"); + std::string bogus_iv("bogus"); + std::string ciphertext = + EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, plain_text); + ASSERT_EQ(ciphertext.size(), 0); + + // Test with bogus key. + std::string bogus_key("bogus"); + ciphertext = + EncryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), plain_text); + ASSERT_EQ(ciphertext.size(), 0); +} + +TEST(CryptoUtilTest, TestFailedEncryptNoPad) { + std::string plaintext("0123456789abcdef"); + std::string key(kKey, kKey + sizeof(kKey)); + std::string iv(kIv, kIv + sizeof(kIv)); + + // Control. + std::string ciphertext = EncryptAesCbcNoPad(key, iv, plaintext); + ASSERT_EQ(plaintext.size(), ciphertext.size()); + + // Bogus key. + std::string bogus_key("bogus"); + ciphertext = EncryptAesCbcNoPad(bogus_key, iv, plaintext); + EXPECT_EQ(ciphertext.size(), 0); + + // Bogus IV. + std::string bogus_iv("bogus"); + ciphertext = EncryptAesCbcNoPad(key, bogus_iv, plaintext); + EXPECT_EQ(ciphertext.size(), 0); + + // Incorrectly-sized plaintext. + std::string bad_plaintext("Foo"); + ciphertext = EncryptAesCbcNoPad(key, iv, bad_plaintext); + EXPECT_EQ(ciphertext.size(), 0); +} + +TEST(CryptoUtilTest, TestFailedDecrypt) { + // First, encrypt the data. + std::string plain_text("Foo"); + std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), plain_text); + ASSERT_NE(ciphertext.size(), 0); + + // Test Decrypt with bogus iv. + std::string bogus_iv("bogus"); + plain_text = + DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, ciphertext); + ASSERT_EQ(plain_text.size(), 0); + + // Test Decrypt with bogus key. + std::string bogus_key("bogus"); + plain_text = + DecryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), ciphertext); + ASSERT_EQ(plain_text.size(), 0); +} + +TEST(CryptoUtilTest, TestEmptyEncrypt) { + EXPECT_EQ("\xDBx\xD9\x91\xE8\x1D\xD9\x19\x80r\x12\x89\xD7Kp\xEB", + EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), "")); +} + +TEST(CryptoUtilTest, TestEmptyDecryptAesCbc) { + EXPECT_EQ("", DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), "")); +} + +TEST(CryptoUtilTest, TestEmptyDecryptAesCbcNoPad) { + EXPECT_EQ("", DecryptAesCbcNoPad(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), "")); +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/certificate_type.h b/common/certificate_type.h new file mode 100644 index 0000000..b63a639 --- /dev/null +++ b/common/certificate_type.h @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_CERTIFICATE_TYPE_H_ +#define COMMON_CERTIFICATE_TYPE_H_ + +namespace widevine { + +enum CertificateType { + kCertificateTypeTesting, + kCertificateTypeDevelopment, + kCertificateTypeProduction, +}; + +} // namespace widevine + +#endif // COMMON_CERTIFICATE_TYPE_H_ diff --git a/common/certificate_util.cc b/common/certificate_util.cc new file mode 100644 index 0000000..356ff69 --- /dev/null +++ b/common/certificate_util.cc @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/certificate_util.h" + +#include "common/certificate_type.h" +#include "common/drm_root_certificate.h" +#include "common/drm_service_certificate.h" +#include "common/verified_media_pipeline.h" +#include "common/vmp_checker.h" +#include "license_server_sdk/internal/client_cert.h" +#include "license_server_sdk/internal/device_status_list.h" + +namespace widevine { +util::Status SetCertificateStatusList( + CertificateType cert_type, const std::string& signed_certificate_status_list, + uint32_t expiration_period_seconds, bool allow_unknown_devices) { + util::Status status = + VmpChecker::Instance()->SelectDrmCertificateType(cert_type); + if (!status.ok()) return status; + + std::unique_ptr root_cert; + status = DrmRootCertificate::CreateByType(cert_type, &root_cert); + if (!status.ok()) { + return status; + } + status = CertificateClientCert::SetDrmRootCertificatePublicKey( + root_cert->public_key()); + if (!status.ok()) { + return status; + } + DeviceStatusList::Instance()->set_allow_unknown_devices( + allow_unknown_devices); + return DeviceStatusList::Instance()->UpdateStatusList( + root_cert->public_key(), signed_certificate_status_list, + expiration_period_seconds); +} + +util::Status AddDrmServiceCertificate( + CertificateType cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + util::Status status = + VmpChecker::Instance()->SelectDrmCertificateType(cert_type); + if (!status.ok()) return status; + return DrmServiceCertificate::AddDrmServiceCertificate( + cert_type, service_certificate, service_private_key, + service_private_key_passphrase); +} +} // namespace widevine diff --git a/common/certificate_util.h b/common/certificate_util.h new file mode 100644 index 0000000..b6fe1d0 --- /dev/null +++ b/common/certificate_util.h @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_CERTIFICATE_UTIL_H_ +#define COMMON_CERTIFICATE_UTIL_H_ + +#include + +#include "util/status.h" +#include "common/certificate_type.h" + +namespace widevine { +// Set the certificate status list system-wide. |cert_type| specifies +// whether to use development or production root certificates. +// |expiration_period| is the number of seconds until the +// certificate_status_list expires after its creation time +// (creation_time_seconds). If |allow_unknown_devices| is false, an error is +// returned if the device does not appear in the certificate_status_list. +util::Status SetCertificateStatusList(CertificateType cert_type, + const std::string& certificate_status_list, + uint32_t expiration_period_seconds, + bool allow_unknown_devices); + +// Add a service certificate system-wide. |cert_type| indicates the type of +// root certificate used to sign the service certificate; +// |service_certificate| is a Google-generated certificate used to +// authenticate the service provider for purposes of device privacy; +// |service_private_key| is the encrypted PKCS#8 private RSA key corresponding +// to the service certificate; and |service_private_key_passphrase| is the +// password required to decrypt |service_private_key|. +util::Status AddDrmServiceCertificate( + CertificateType cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); +} // namespace widevine + +#endif // COMMON_CERTIFICATE_UTIL_H_ diff --git a/common/client_id_util.cc b/common/client_id_util.cc new file mode 100644 index 0000000..35013a2 --- /dev/null +++ b/common/client_id_util.cc @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/client_id_util.h" + +#include "glog/logging.h" +#include "common/aes_cbc_util.h" +#include "common/drm_service_certificate.h" +#include "common/error_space.h" +#include "protos/public/errors.pb.h" + +namespace widevine { + +void AddClientInfo(ClientIdentification* client_id, absl::string_view name, + absl::string_view value) { + ClientIdentification_NameValue* nv = client_id->add_client_info(); + nv->set_name(std::string(name)); + nv->set_value(std::string(value)); +} + +bool SetClientInfo(ClientIdentification* client_id, absl::string_view name, + absl::string_view value) { + int n = client_id->client_info_size(); + for (int i = 0; i < n; i++) { + if (client_id->client_info(i).name() == name) { + client_id->mutable_client_info(i)->set_value(std::string(value)); + return true; + } + } + AddClientInfo(client_id, name, value); + return false; +} + +std::string GetClientInfo(const ClientIdentification& client_id, + absl::string_view name) { + return GetClientInfo(client_id, name, std::string()); +} + +std::string GetClientInfo(const ClientIdentification& client_id, + absl::string_view name, const std::string& default_value) { + for (const auto& nv : client_id.client_info()) { + if (nv.name() == name) { + return nv.value(); + } + } + return default_value; +} + +util::Status DecryptEncryptedClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id) { + return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id, + client_id); +} + +util::Status DecryptEncryptedClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + const std::string& privacy_key, ClientIdentification* client_id) { + DCHECK(client_id); + if (!encrypted_client_id.has_encrypted_client_id() || + encrypted_client_id.encrypted_client_id().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id"); + } + if (!encrypted_client_id.has_encrypted_client_id_iv() || + encrypted_client_id.encrypted_client_id_iv().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id-iv"); + } + std::string serialized_client_id(crypto_util::DecryptAesCbc( + privacy_key, encrypted_client_id.encrypted_client_id_iv(), + encrypted_client_id.encrypted_client_id())); + if (serialized_client_id.empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-decryption-failed"); + } + if (!client_id->ParseFromString(serialized_client_id)) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-parse-failed"); + } + return util::OkStatus(); +} + +} // namespace widevine diff --git a/common/client_id_util.h b/common/client_id_util.h new file mode 100644 index 0000000..1f0e8b1 --- /dev/null +++ b/common/client_id_util.h @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// Utilities for manipulating the ClientIdentification proto. +// ClientIdentification.client_info() contains a sequence of +// arbitrary name-value pairs; this code consolidates the +// accessors for them in one place. +#ifndef COMMON_CLIENT_ID_UTIL_H_ +#define COMMON_CLIENT_ID_UTIL_H_ + +#include "absl/strings/string_view.h" +#include "util/status.h" +#include "protos/public/client_identification.pb.h" + +namespace widevine { + +// Append the given name/value pair to client_id->client_info(). Does not +// check for duplicates. +void AddClientInfo(ClientIdentification* client_id, absl::string_view name, + absl::string_view value); + +// Append the given name/value pair to client_id->client_info(). If the +// given name already had a value, replaces it and returns true. +bool SetClientInfo(ClientIdentification* client_id, absl::string_view name, + absl::string_view value); + +// Return the value from client_id.client_info() matching the given name, +// or the empty std::string if not found. +std::string GetClientInfo(const ClientIdentification& client_id, + absl::string_view name); + +// Return the value from client_id.client_info() matching the given name, +// or the given default value if not found. +std::string GetClientInfo(const ClientIdentification& client_id, + absl::string_view name, const std::string& default_value); + +// Decrypts the encrypted client identification in |encrypted_client_id| into +// |client_id| using the private key for the service certificate which was +// used to encrypt the information. +// |client_id| is owned by caller. +// Returns util::Status::OK, if successful, else an error. +util::Status DecryptEncryptedClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id); + +// Decrypts the encrypted client identification in |encrypted_client_id| into +// |client_id| using |privacy_key|. +// |client_id| is owned by caller. +// Returns util::Status::OK, if successful, else an error. +util::Status DecryptEncryptedClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + const std::string& privacy_key, ClientIdentification* client_id); + +} // namespace widevine + +#endif // COMMON_CLIENT_ID_UTIL_H_ diff --git a/common/crypto_util.cc b/common/crypto_util.cc new file mode 100644 index 0000000..901b991 --- /dev/null +++ b/common/crypto_util.cc @@ -0,0 +1,181 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Implementation of Common crypto utilities used by Widevine services. + +#include "common/crypto_util.h" + +#include "glog/logging.h" +#include "absl/strings/string_view.h" +#include "openssl/aes.h" +#include "openssl/cmac.h" +#include "openssl/evp.h" +#include "openssl/hmac.h" +#include "openssl/sha.h" +#include "util/endian/endian.h" + +namespace widevine { +namespace crypto_util { + +const char kEncryptionKeyLabel[] = "ENCRYPTION"; +const int kEncryptionKeySizeBits = 128; +const char kSigningKeyLabel[] = "AUTHENTICATION"; +const int kSigningKeySizeBits = 256; +const size_t kSigningKeySizeBytes = 32; +const char kIvMasterKey[] = "1234567890123456"; +const char kIvLabel[] = "IV_ENCRYPTION"; +const int kIvSizeBits = 128; +const char kKeyIdMasterKey[] = "0123456789abcdef"; +const char kKeyIdLabel[] = "KEY_ID_ENCRYPTION"; +const int kKeyIdSizeBits = 128; +const char kGroupKeyLabel[] = "GROUP_ENCRYPTION"; +// TODO(user): This is a temporary key for development. Replace this with +// a real group master key in keystore. +// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy +// like VerifySignatureHmacSha1. +const char kPhonyGroupMasterKey[] = "fedcba9876543210"; +const int kAes128KeySizeBits = 128; +const int kAes128KeySizeBytes = 16; + +const uint32_t kCENCSchemeID = 0x63656E63; // 'cenc' (AES-CTR): 0x63656E63 +const uint32_t kCBC1SchemeID = 0x63626331; // 'cbc1' (AES-CBC): 0x63626331 +const uint32_t kCENSSchemeID = + 0x63656E73; // 'cens' (AES-CTR subsample): 0x63656E73 +const uint32_t kCBCSSchemeID = + 0x63626373; // 'cbcs' (AES-CBC subsample): 0x63626373 + +// Creates a SHA-256 HMAC signature for the given message. +std::string CreateSignatureHmacSha256(absl::string_view key, + absl::string_view message) { + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init(&ctx, key.data(), key.size(), EVP_sha256()); + HMAC_Update(&ctx, reinterpret_cast(message.data()), + message.size()); + unsigned char digest[SHA256_DIGEST_LENGTH]; + unsigned int digest_len; + HMAC_Final(&ctx, digest, &digest_len); + HMAC_CTX_cleanup(&ctx); + std::string s(reinterpret_cast(digest), digest_len); + return s; +} + +// Compares the SHA-256 HMAC against the provided signature. +bool VerifySignatureHmacSha256(absl::string_view key, + absl::string_view signature, + absl::string_view message) { + return CreateSignatureHmacSha256(key, message) == signature; +} + +// Creates a SHA-1 HMAC signature for the given message. +std::string CreateSignatureHmacSha1(absl::string_view key, + absl::string_view message) { + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init(&ctx, key.data(), key.size(), EVP_sha1()); + HMAC_Update(&ctx, reinterpret_cast(message.data()), + message.size()); + unsigned char digest[SHA_DIGEST_LENGTH]; + unsigned int digest_len; + HMAC_Final(&ctx, digest, &digest_len); + HMAC_CTX_cleanup(&ctx); + std::string s(reinterpret_cast(digest), digest_len); + return s; +} + +// Compares the SHA-1 HMAC against the provided signature. +bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature, + absl::string_view message) { + return CreateSignatureHmacSha1(key, message) == signature; +} + +// Derives an AES 128 key from the provided key and additional info. +std::string DeriveKey(absl::string_view key, absl::string_view label, + absl::string_view context, const uint32_t size_bits) { + if (key.size() != kAes128KeySizeBytes) return ""; + + // We only handle even multiples of 16 bytes (128 bits) right now. + if ((size_bits % 128) || (size_bits > (128 * 255))) { + return ""; + } + + std::string result; + + const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + + for (unsigned char counter = 1; counter <= (size_bits / 128); counter++) { + if (CMAC_Init(cmac_ctx, key.data(), key.size(), cipher, 0)) { + std::string message; + message.append(1, counter); + message.append(label.data(), label.size()); + message.append(1, '\0'); + message.append(context.data(), context.size()); + char size_string[4]; + BigEndian::Store32(&size_string, size_bits); + message.append(&size_string[0], &size_string[0] + 4); + if (CMAC_Update(cmac_ctx, reinterpret_cast(message.data()), + message.size())) { + size_t reslen; + unsigned char res[AES_BLOCK_SIZE]; + if (CMAC_Final(cmac_ctx, res, &reslen)) { + result.append(reinterpret_cast(res), reslen); + } + DCHECK(reslen == AES_BLOCK_SIZE); + } + } + } + + CMAC_CTX_free(cmac_ctx); + return result; +} + +// Derives an IV from the provided info. +std::string DeriveIv(absl::string_view context) { + return DeriveKey(kIvMasterKey, kIvLabel, context, kIvSizeBits); +} + +// Derives a key ID from the provided info. +std::string DeriveKeyId(absl::string_view context) { + return DeriveKey(kKeyIdMasterKey, kKeyIdLabel, context, kKeyIdSizeBits); +} + +std::string DeriveGroupSessionKey(absl::string_view context, + const uint32_t size_bits) { + return DeriveKey(kPhonyGroupMasterKey, kGroupKeyLabel, context, size_bits); +} + +std::string DeriveSigningKey(absl::string_view key, absl::string_view context, + const uint32_t size_bits) { + return DeriveKey(key, kSigningKeyLabel, context, size_bits); +} + +bool FourCCEncryptionSchemeIDFromString(const std::string& requested, + uint32_t* four_cc_code) { + if (requested.size() != 4 || four_cc_code == nullptr) return false; + + uint32_t result = 0; + for (auto i = 0; i < 4; ++i) { + result <<= 8; + result |= requested[i]; + } + + switch (result) { + case kCENCSchemeID: + case kCBC1SchemeID: + case kCENSSchemeID: + case kCBCSSchemeID: + *four_cc_code = result; + return true; + default: + return false; + } +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/crypto_util.h b/common/crypto_util.h new file mode 100644 index 0000000..dc00f4e --- /dev/null +++ b/common/crypto_util.h @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// Contains common crypto routines for widevine protocols. These routines are +// used as part of licensing and provisioning request handling. + +#ifndef COMMON_CRYPTO_UTIL_H_ +#define COMMON_CRYPTO_UTIL_H_ + +#include + +#include "base/macros.h" +#include "absl/strings/string_view.h" + +namespace widevine { +namespace crypto_util { + +// Default constants used for key derivation for encryption and signing. +// TODO(user): These are duplicated in session.cc in the sdk. de-dup. +extern const char kEncryptionKeyLabel[]; +extern const int kEncryptionKeySizeBits; +extern const char kSigningKeyLabel[]; +extern const int kSigningKeySizeBits; +extern const size_t kSigningKeySizeBytes; +extern const char kIvMasterKey[]; +extern const char kIvLabel[]; +extern const int kIvSizeBits; +extern const int kAes128KeySizeBits; +extern const int kAes128KeySizeBytes; + +extern const uint32_t kCENCSchemeID; // 'cenc' (AES-CTR): 0x63656E63 +extern const uint32_t kCBC1SchemeID; // 'cbc1' (AES-CBC): 0x63626331 +extern const uint32_t kCENSSchemeID; // 'cens' (AES-CTR subsample): 0x63656E73 +extern const uint32_t kCBCSSchemeID; // 'cbcs' (AES-CBC subsample): 0x63626373 + +// DeriveKey uses the NIST 800-108 KDF recommendation, using AES-CMAC PRF. +// NIST 800-108: +// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf +// AES-CMAC: +// http://tools.ietf.org/html/rfc4493 +std::string DeriveKey(absl::string_view key, absl::string_view label, + absl::string_view context, const uint32_t size_bits); + +// Derives an IV from the provided |context|. +std::string DeriveIv(absl::string_view context); + +// Derives a key ID from the provided |context|. +std::string DeriveKeyId(absl::string_view context); + +// Helper function to derive a key using the group master key and context. +std::string DeriveGroupSessionKey(absl::string_view context, const uint32_t size_bits); + +// Helper function to derive a signing key for from the signing context. +std::string DeriveSigningKey(absl::string_view key, absl::string_view context, + const uint32_t size_bits); + +// Helper function to create a SHA-256 HMAC signature for the given message. +std::string CreateSignatureHmacSha256(absl::string_view key, + absl::string_view message); + +// Helper function which compares the SHA-256 HMAC against the provided +// signature. +bool VerifySignatureHmacSha256(absl::string_view key, + absl::string_view signature, + absl::string_view message); + +// Helper function to create a SHA-1 HMAC signature for the given message. +std::string CreateSignatureHmacSha1(absl::string_view key, + absl::string_view message); + +// Helper function which compares the SHA-1 HMAC against the provided +// signature. +bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature, + absl::string_view message); + +// Converts a requested 4CC encryption scheme ID from a std::string to a uint32_t and +// verifies it is a correct value. +bool FourCCEncryptionSchemeIDFromString(const std::string& requested, + uint32_t* four_cc_code); + +} // namespace crypto_util +} // namespace widevine + +#endif // COMMON_CRYPTO_UTIL_H_ diff --git a/common/crypto_util_test.cc b/common/crypto_util_test.cc new file mode 100644 index 0000000..b939609 --- /dev/null +++ b/common/crypto_util_test.cc @@ -0,0 +1,236 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// Unit tests for the crypto_util helper functions. + +#include + +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "common/crypto_util.h" + +namespace widevine { +namespace crypto_util { + +const char kCENCStr[] = "cenc"; +const char kCBC1Str[] = "cbc1"; +const char kCENSStr[] = "cens"; +const char kCBCSStr[] = "cbcs"; + +static unsigned char key_data[] = + { 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e, + 0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b }; + +static std::string key_str(key_data, key_data + sizeof(key_data)); + +static unsigned char iv_data[] = + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + +static std::string iv_str(iv_data, iv_data + sizeof(iv_data)); + +TEST(CryptoUtilTest, DeriveAes128KeyTest) { + unsigned char label[] = { 0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1, + 0x92, 0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08, + 0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60, + 0xd1, 0xcf, 0x6a, 0x4d, 0x40, 0xa1, 0xe3, 0xfe, + 0xd3, 0xe9, 0xa6, 0x58, 0x4c, 0xd4, 0xad, 0xa4, + 0xa2 }; + + unsigned char context[] = { 0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d, 0x6d, + 0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5, 0x5b, 0x80, + 0x81, 0x91, 0xff }; + + unsigned char output0[] = { 0xd5, 0xad, 0x2d, 0xb1, 0x5a, 0x06, 0xcb, 0x50, + 0xf2, 0x59, 0x5a, 0xb2, 0xb2, 0x0d, 0x44, 0x4e }; + + unsigned char output1[] = { 0xdf, 0x38, 0x45, 0x97, 0x5d, 0x7a, 0x81, 0xb4, + 0x94, 0x86, 0xaf, 0x0c, 0xdc, 0x4d, 0xeb, 0x62, + 0x31, 0x39, 0x67, 0x8f, 0xff, 0x5d, 0x68, 0x35, + 0xdc, 0x89, 0x5f, 0x47, 0xca, 0xe0, 0x2d, 0x3a, + 0x10, 0x24, 0xf8, 0x7e, 0x5b, 0x70, 0xe1, 0xa3, + 0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22 }; + + std::string label_str(label, label + sizeof(label)); + std::string key_str(key_data, key_data + sizeof(key_data)); + std::string context_str(context, context + sizeof(context)); + std::string result = DeriveKey(key_str, label_str, context_str, 128); + + std::string output_128(output0, output0 + sizeof(output0)); + + ASSERT_EQ(result, output_128); + + result = DeriveKey(key_str, label_str, context_str, 384); + + std::string output_384(output1, output1 + sizeof(output1)); + + ASSERT_EQ(result, output_384); +} + +TEST(CryptoUtilTest, DeriveGroupSesionKey) { + unsigned char output[] = { 0x92, 0x6c, 0x2f, 0x5, 0xa6, 0x4f, 0xff, 0xb1, + 0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1 }; + std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128); + EXPECT_EQ(crypto_util::kAes128KeySizeBytes, group_session_key.size()); + const std::string output_128(output, output + sizeof(output)); + ASSERT_EQ(output_128, group_session_key); +} + +TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) { + unsigned char message_data[] = { + 0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, + 0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2, + 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91, + 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, + 0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, + 0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5, + 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54, + 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda }; + + std::string message(message_data, message_data + sizeof(message_data)); + std::string signature(CreateSignatureHmacSha256(key_str, message)); + + ASSERT_EQ(signature.size(), 32); + + ASSERT_TRUE(VerifySignatureHmacSha256(key_str, signature, message)); +} + +TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) { + unsigned char message_data[] = { + 0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, + 0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2, + 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91, + 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, + 0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, + 0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5, + 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54, + 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda }; + + std::string message(message_data, message_data + sizeof(message_data)); + // Test with bogus key; + std::string bogus_key("bogus"); + std::string signature(CreateSignatureHmacSha256(bogus_key, message)); + + // This should still produce an hmac signature. + ASSERT_EQ(signature.size(), 32); + + // Create valid signature to compare. + signature = CreateSignatureHmacSha256(key_str, message); + + // Test with bogus key. + ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message)); + + // Test with munged signature. + signature[0] = 0xFF; + ASSERT_FALSE(VerifySignatureHmacSha256(key_str, signature, message)); + + // Test with bogus signature. + ASSERT_FALSE(VerifySignatureHmacSha256(key_str, "bogus", message)); +} + +TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) { + unsigned char message_data[] = { + 0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, + 0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2, + 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91, + 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, + 0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, + 0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5, + 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54, + 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda }; + + std::string message(message_data, message_data + sizeof(message_data)); + std::string signature(CreateSignatureHmacSha1(key_str, message)); + + ASSERT_EQ(20, signature.size()); + ASSERT_TRUE(VerifySignatureHmacSha1(key_str, signature, message)); +} + +TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) { + unsigned char message_data[] = { + 0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, + 0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2, + 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91, + 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, + 0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, + 0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5, + 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54, + 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda }; + + std::string message(message_data, message_data + sizeof(message_data)); + // Test with bogus key; + std::string bogus_key("bogus"); + std::string signature(CreateSignatureHmacSha1(bogus_key, message)); + + // This should still produce an hmac signature. + ASSERT_EQ(20, signature.size()); + // Create valid signature to compare. + signature = CreateSignatureHmacSha1(key_str, message); + // Test with bogus key. + ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message)); + // Test with munged signature. + signature[0] = 0xFF; + ASSERT_FALSE(VerifySignatureHmacSha1(key_str, signature, message)); + // Test with bogus signature. + ASSERT_FALSE(VerifySignatureHmacSha1(key_str, "bogus", message)); +} + +TEST(CryptoUtilTest, DeriveIv) { + // First value in the pair is the key_id, second value is the expected IV. + std::pair id_iv_pairs[] = { + {"1234567890123456", "3278234c7682d1a2e153af4912975f5f"}, + {"0987654321098765", "cf09abd30f04b60544910791a6b904cf"}}; + for (const auto& id_iv_pair : id_iv_pairs) { + SCOPED_TRACE(absl::StrCat("test case:", id_iv_pair.first)); + EXPECT_EQ(id_iv_pair.second, + absl::BytesToHexString(DeriveIv(id_iv_pair.first))); + // Repeat same call to verify derivied result is repeatable. + EXPECT_EQ(id_iv_pair.second, + absl::BytesToHexString(DeriveIv(id_iv_pair.first))); + } +} + +TEST(CryptoUtilTest, DeriveKeyId) { + // First value in the pair is the context, second value is the expected id. + std::pair context_id_pairs[] = { + {"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"}, + {"0987654321098765", "084fc6bece9688ccce6b1672d9b47e22"}}; + for (const auto& context_id_pair : context_id_pairs) { + SCOPED_TRACE(absl::StrCat("test case:", context_id_pair.first)); + EXPECT_EQ(context_id_pair.second, + absl::BytesToHexString(DeriveKeyId(context_id_pair.first))); + // Repeat same call to verify derivied result is repeatable. + EXPECT_EQ(context_id_pair.second, + absl::BytesToHexString(DeriveKeyId(context_id_pair.first))); + } +} + +TEST(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) { + uint32_t cc_code; + ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code)); + ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code)); + ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code)); +} + +TEST(CryptoUtilTest, Verify4CCEncryptionIDFromString) { + uint32_t cc_code = 0; + ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code)); + ASSERT_EQ(kCENCSchemeID, cc_code); + ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBC1Str, &cc_code)); + ASSERT_EQ(kCBC1SchemeID, cc_code); + ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENSStr, &cc_code)); + ASSERT_EQ(kCENSSchemeID, cc_code); + ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBCSStr, &cc_code)); + ASSERT_EQ(kCBCSSchemeID, cc_code); +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/drm_root_certificate.cc b/common/drm_root_certificate.cc new file mode 100644 index 0000000..dc42f66 --- /dev/null +++ b/common/drm_root_certificate.cc @@ -0,0 +1,320 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/drm_root_certificate.h" + +#include + +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "openssl/sha.h" +#include "common/certificate_type.h" +#include "common/error_space.h" +#include "common/rsa_key.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +// From common::TestCertificates. +// TODO(user): common::test_certificates is a testonly target, consider +// how to use instead of dupliciating the test cert here. +static const unsigned char kTestRootCertificate[] = { + 0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22, + 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5, + 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, + 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f, + 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, + 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b, + 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, + 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01, + 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, + 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14, + 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, + 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6, + 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, + 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31, + 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, + 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90, + 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, + 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f, + 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, + 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17, + 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, + 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf, + 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, + 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42, + 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, + 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4, + 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, + 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72, + 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, + 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04, + 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, + 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33, + 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, + 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a, + 0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35, + 0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe, + 0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49, + 0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6, + 0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58, + 0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce, + 0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27, + 0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5, + 0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c, + 0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e, + 0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12, + 0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54, + 0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8, + 0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1, + 0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1, + 0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8, + 0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4, + 0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9, + 0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd, + 0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9, + 0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f, + 0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92, + 0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15, + 0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0, + 0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9, + 0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1, + 0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2, + 0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c, + 0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36, + 0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35, + 0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7, + 0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44}; + +static const unsigned char kDevRootCertificate[] = { + 0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xc3, 0x94, 0x88, + 0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, + 0x81, 0x00, 0xc0, 0x00, 0x36, 0x6f, 0x8e, 0xe9, 0xcf, 0x86, 0xdb, 0xcd, + 0xdd, 0x4e, 0xfd, 0xcd, 0x45, 0xbf, 0x6d, 0x96, 0x05, 0x00, 0xb8, 0x72, + 0xff, 0x9c, 0xb4, 0x39, 0xa8, 0xd8, 0xc0, 0x09, 0x73, 0xc0, 0x24, 0x6a, + 0x39, 0x4d, 0x36, 0x3f, 0x9a, 0xe4, 0xb8, 0x76, 0xdc, 0x34, 0xe3, 0xee, + 0x5f, 0xdd, 0x13, 0x20, 0x08, 0xdc, 0x4e, 0x6f, 0x4e, 0x9f, 0xc0, 0x36, + 0xf9, 0xce, 0xc6, 0xb7, 0xdb, 0xe0, 0x51, 0x2d, 0x30, 0x0b, 0xae, 0x0a, + 0x20, 0xd2, 0x29, 0x3c, 0x2c, 0x1d, 0x87, 0x65, 0xeb, 0x5f, 0x93, 0xd7, + 0x3f, 0x12, 0x08, 0x50, 0x0e, 0x55, 0xf3, 0xf1, 0x19, 0xee, 0x18, 0x21, + 0x6e, 0xea, 0xb6, 0x0a, 0x4a, 0x0b, 0x9c, 0x72, 0x37, 0xeb, 0x0b, 0x68, + 0xfc, 0x52, 0x46, 0x62, 0xd0, 0xa2, 0x99, 0x66, 0xe2, 0x2b, 0x74, 0xdd, + 0x5c, 0xaf, 0x9a, 0x03, 0xc4, 0x5d, 0x93, 0xfb, 0xcd, 0x45, 0x9a, 0xee, + 0xfb, 0x7b, 0x18, 0x94, 0xc1, 0x8c, 0x82, 0x34, 0x7f, 0x02, 0x12, 0x21, + 0xfc, 0x40, 0xc1, 0x50, 0xc9, 0xf4, 0x7c, 0xd5, 0x96, 0xbe, 0x55, 0x7f, + 0x3c, 0x1d, 0x70, 0x34, 0xb4, 0xa2, 0x03, 0xc4, 0x3f, 0x89, 0x60, 0xe4, + 0x24, 0x09, 0x1a, 0x74, 0xc4, 0xb6, 0x39, 0xf0, 0x34, 0x60, 0x8e, 0xa7, + 0x5f, 0x02, 0x7f, 0xb9, 0x2a, 0xc5, 0xaa, 0xb2, 0x4c, 0x34, 0xd3, 0x5a, + 0x5d, 0xfa, 0x07, 0xf2, 0xb9, 0xb3, 0xc1, 0xba, 0xab, 0xbe, 0x89, 0x99, + 0xe3, 0x6d, 0x9b, 0xa9, 0xd3, 0xaf, 0x2a, 0x08, 0x76, 0xf3, 0x0e, 0xc9, + 0xe0, 0xb3, 0xbf, 0x51, 0x0c, 0xc5, 0xf4, 0xf3, 0x15, 0x7b, 0x08, 0x11, + 0x8f, 0x61, 0x1f, 0x61, 0x64, 0xdb, 0x15, 0x84, 0x5b, 0x8a, 0xd1, 0x28, + 0x40, 0xde, 0xc5, 0x32, 0xb5, 0xad, 0xad, 0x65, 0x4c, 0xf5, 0xf7, 0xd1, + 0x90, 0x14, 0x5d, 0xc2, 0x85, 0x98, 0xcc, 0xe9, 0xe6, 0x95, 0x42, 0xe1, + 0x3e, 0xfc, 0x7f, 0xc4, 0x49, 0xed, 0x9c, 0xe4, 0x49, 0x3f, 0x03, 0x1b, + 0x0d, 0xa0, 0xfb, 0xf5, 0x38, 0x49, 0xd2, 0xdf, 0xa3, 0x88, 0xb2, 0x76, + 0x93, 0x08, 0x20, 0x18, 0xfe, 0xdc, 0x72, 0x6c, 0x6e, 0xbf, 0x61, 0x37, + 0x03, 0xdb, 0xe5, 0x72, 0x68, 0xe0, 0x99, 0x2f, 0xb9, 0xe0, 0x2e, 0xbb, + 0x9f, 0x96, 0x36, 0x61, 0xaa, 0x2d, 0xa4, 0x93, 0xe8, 0x50, 0x58, 0xe6, + 0x61, 0xe1, 0x14, 0xcf, 0xac, 0x86, 0x98, 0x7f, 0x3c, 0x67, 0x16, 0xce, + 0xb8, 0x70, 0x90, 0x3a, 0x5a, 0xd4, 0xe1, 0xe2, 0x35, 0x98, 0xbf, 0x93, + 0x41, 0x11, 0xb2, 0x44, 0xb2, 0x64, 0xc2, 0xe7, 0x09, 0x45, 0xb7, 0x6f, + 0xb0, 0xbd, 0x6e, 0xe8, 0x67, 0xfa, 0x8d, 0xd4, 0xfa, 0x4b, 0xef, 0xa8, + 0x9d, 0x8a, 0x0a, 0xd9, 0x14, 0x77, 0x09, 0x11, 0x9e, 0xc3, 0x50, 0x14, + 0x6c, 0x45, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x17, 0x01, + 0x60, 0x24, 0xe1, 0xfd, 0x75, 0x60, 0x17, 0x5c, 0x5e, 0x6f, 0x9f, 0x7f, + 0xdf, 0xee, 0xf0, 0xf7, 0x7d, 0xb2, 0x50, 0x65, 0x36, 0x26, 0x14, 0x19, + 0x01, 0x5e, 0x98, 0x94, 0x65, 0x97, 0x83, 0xaa, 0x4a, 0x2b, 0x98, 0x2e, + 0x02, 0xf3, 0xb2, 0xc9, 0xb2, 0xed, 0xd3, 0x1b, 0x20, 0x27, 0x9e, 0xe1, + 0x25, 0xc7, 0x86, 0xf0, 0x66, 0x68, 0x5d, 0xd2, 0x3d, 0xa7, 0xbb, 0xbc, + 0x22, 0xfc, 0x29, 0xfa, 0x17, 0x16, 0xf4, 0xa2, 0x00, 0x10, 0x87, 0xb4, + 0x5d, 0x51, 0x45, 0x6b, 0xc8, 0xf4, 0x6b, 0xcc, 0x92, 0x91, 0xe7, 0xa7, + 0x93, 0xbc, 0xc7, 0x2e, 0xdc, 0xac, 0x82, 0x2b, 0x85, 0x56, 0x7b, 0xae, + 0xf2, 0xd8, 0xda, 0xa6, 0xd7, 0xfa, 0x6d, 0x70, 0x2a, 0x2e, 0xcf, 0x69, + 0xef, 0x57, 0x91, 0xa7, 0xaa, 0x40, 0x15, 0x4a, 0x49, 0x1b, 0xbc, 0x36, + 0xbb, 0x1c, 0x94, 0x33, 0x36, 0x61, 0x22, 0x9d, 0x22, 0x66, 0xf0, 0x88, + 0x5e, 0x7c, 0x3c, 0xa5, 0xff, 0x81, 0xcf, 0x1a, 0x44, 0xa1, 0x2b, 0xdf, + 0xc9, 0x3d, 0xd5, 0xc7, 0xc7, 0x3a, 0x75, 0xac, 0x29, 0xfa, 0xfd, 0x5b, + 0xda, 0xf5, 0x8f, 0xd9, 0xdf, 0x08, 0xa4, 0x8d, 0x19, 0x4a, 0xa4, 0x79, + 0x6e, 0x47, 0xf6, 0x07, 0xe0, 0xbd, 0xbf, 0x30, 0x3a, 0xf9, 0xf5, 0xc0, + 0x90, 0x6d, 0x70, 0x27, 0x44, 0xa8, 0x5e, 0x70, 0xcd, 0x43, 0x3e, 0xaf, + 0xf0, 0xd7, 0x20, 0xd3, 0x5e, 0x97, 0x2d, 0x32, 0x1a, 0x3d, 0x2d, 0x0f, + 0x0f, 0xcf, 0xac, 0x4e, 0x88, 0x75, 0x98, 0x6c, 0xfa, 0xe8, 0x42, 0x58, + 0x99, 0xaa, 0x45, 0x0c, 0x41, 0x0c, 0x6e, 0x27, 0x58, 0x57, 0xd2, 0x5b, + 0x82, 0x3d, 0x75, 0x2f, 0x9e, 0xf3, 0xe4, 0x00, 0xcf, 0x91, 0x48, 0x25, + 0xca, 0x98, 0xf2, 0x91, 0x6b, 0x41, 0xa5, 0xe8, 0xcd, 0x64, 0xa7, 0x2e, + 0x78, 0xc7, 0x76, 0x82, 0x3f, 0xf8, 0x57, 0x8a, 0x9d, 0x78, 0x25, 0xad, + 0xf3, 0x1a, 0x8b, 0xfc, 0x83, 0x9a, 0x98, 0x87, 0xe4, 0x55, 0x3e, 0x1c, + 0xa7, 0x80, 0x8f, 0xd6, 0x76, 0xab, 0x03, 0xc7, 0x05, 0x66, 0xc3, 0xa0, + 0x4c, 0x33, 0x1f, 0x39, 0x74, 0x1b, 0x2a, 0xbf, 0xe6, 0xb0, 0x9f, 0x6b, + 0xc1, 0xd6, 0xd3, 0xf4, 0x46, 0x9b, 0xf3, 0xab, 0xca, 0x2e, 0x88, 0x3d, + 0x84, 0x5f, 0xc9, 0x9b, 0x47, 0xbb, 0x57, 0x64, 0x08, 0x0e, 0x18, 0x74, + 0x83, 0x44, 0xd4, 0xc3, 0x18, 0x97, 0xcf, 0x89, 0x6a, 0x49, 0x51, 0xc6, + 0xff, 0x8d, 0x39, 0xc5, 0x23, 0xf9, 0xd5, 0x01, 0xd7, 0x2f, 0xa9, 0xa5, + 0x5d, 0xa9, 0xf3, 0xc9, 0xfd, 0xc4, 0x52, 0x19, 0x7d, 0xf6, 0xa4, 0x2c, + 0x0c, 0xa0, 0x07, 0xdf, 0x7b, 0x44, 0xd7, 0xe5, 0xbf, 0x57, 0x87, 0xc9, + 0x8c, 0xfe, 0x30, 0xb2, 0x89, 0x5d, 0x00, 0x03, 0x3b, 0xe5}; + +static const unsigned char kProdRootCertificate[] = { + 0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xdd, 0x94, 0x88, + 0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, + 0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, + 0x97, 0x09, 0xe8, 0x68, 0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, + 0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0, + 0x57, 0x03, 0x53, 0x4c, 0xf6, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, + 0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe, + 0x66, 0xdf, 0xc5, 0x21, 0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, + 0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, + 0xbe, 0x34, 0x23, 0x8b, 0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, + 0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, + 0x0f, 0x92, 0xd6, 0x4c, 0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, + 0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, + 0x9a, 0x97, 0xeb, 0x84, 0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, + 0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, + 0x72, 0xa0, 0xfa, 0xc1, 0xbd, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, + 0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc, + 0xfd, 0x82, 0x48, 0x2b, 0xb7, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, + 0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e, + 0x65, 0xf5, 0xc8, 0x6c, 0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, + 0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98, + 0x79, 0x52, 0x5e, 0x45, 0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, + 0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, + 0x4f, 0x5e, 0x48, 0x77, 0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, + 0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, + 0xe4, 0x29, 0x06, 0x5e, 0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, + 0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, + 0xec, 0x1f, 0x11, 0xb3, 0x24, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, + 0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, + 0x68, 0x15, 0x84, 0xff, 0xa5, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4, + 0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12, + 0xd2, 0x88, 0x6d, 0x41, 0x3d, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4, + 0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5, + 0xce, 0xa6, 0x96, 0x55, 0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, + 0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x58, 0xf1, + 0xd6, 0x4d, 0x04, 0x09, 0x7b, 0xdf, 0xd7, 0xef, 0x5d, 0x3b, 0x02, 0x39, + 0x17, 0xfa, 0x14, 0x36, 0x75, 0x4a, 0x38, 0x67, 0x85, 0x57, 0x12, 0xa7, + 0x14, 0xee, 0x35, 0x16, 0xd5, 0x3d, 0xbf, 0x42, 0x86, 0xf6, 0x69, 0x00, + 0x76, 0xcd, 0x93, 0xf4, 0x7c, 0xb2, 0xdf, 0x9e, 0x44, 0xcd, 0x4c, 0xd4, + 0xae, 0x09, 0x18, 0x53, 0x44, 0x32, 0xec, 0xe0, 0x61, 0x1b, 0xe5, 0xda, + 0x13, 0xd3, 0x55, 0xc5, 0xdd, 0x1a, 0xcb, 0x90, 0x1e, 0x7e, 0x5b, 0xc6, + 0xe9, 0x0f, 0x22, 0x9f, 0xbe, 0x85, 0x02, 0xfe, 0x90, 0x31, 0xcc, 0x6b, + 0x03, 0x84, 0xbd, 0x22, 0xc4, 0x55, 0xfa, 0xf5, 0xf2, 0x08, 0xcd, 0x65, + 0x41, 0x58, 0xe8, 0x7d, 0x29, 0xda, 0x04, 0x58, 0x82, 0xf5, 0x37, 0x69, + 0xbc, 0xf3, 0x5a, 0x57, 0x84, 0x17, 0x7b, 0x32, 0x87, 0x70, 0xb2, 0xb0, + 0x76, 0x9c, 0xb2, 0xc3, 0x15, 0xd1, 0x11, 0x26, 0x2a, 0x23, 0x75, 0x99, + 0x3e, 0xb9, 0x77, 0x22, 0x32, 0x0d, 0xbc, 0x1a, 0x19, 0xc1, 0xd5, 0x65, + 0x90, 0x76, 0x55, 0x74, 0x0f, 0x0e, 0x69, 0x4d, 0x5f, 0x4d, 0x8f, 0x19, + 0xaf, 0xdf, 0xd6, 0x16, 0x31, 0x94, 0xa8, 0x92, 0x5f, 0x4f, 0xbc, 0x7a, + 0x31, 0xf8, 0xae, 0x8e, 0xad, 0x33, 0xb7, 0xe9, 0x30, 0xd0, 0x8c, 0x0a, + 0x8a, 0x6c, 0x83, 0x35, 0xf8, 0x8a, 0x81, 0xb2, 0xfe, 0x1c, 0x88, 0xac, + 0x2a, 0x66, 0xc5, 0xff, 0xbd, 0xe6, 0x17, 0xd0, 0x62, 0x0b, 0xdc, 0x8a, + 0x45, 0xf7, 0xb0, 0x3e, 0x5a, 0xc8, 0x1e, 0x4a, 0x24, 0x2f, 0x6c, 0xa5, + 0xe3, 0x1c, 0x88, 0x14, 0x83, 0xd5, 0xc5, 0xef, 0x5e, 0x9f, 0x3d, 0x85, + 0x45, 0x73, 0xe2, 0x6b, 0x50, 0x52, 0x57, 0x4c, 0xfb, 0x92, 0x6c, 0x66, + 0x75, 0x8a, 0xd6, 0x0d, 0x1b, 0xae, 0xf3, 0xec, 0xaf, 0x51, 0x22, 0x03, + 0x5d, 0x0a, 0x2e, 0x63, 0x93, 0x9c, 0x0b, 0x01, 0x20, 0xa8, 0xa9, 0x84, + 0x2e, 0x17, 0xca, 0xae, 0x73, 0xec, 0x22, 0x1b, 0x79, 0xae, 0xf6, 0xa0, + 0x72, 0x2c, 0xdf, 0x07, 0x47, 0xdb, 0x88, 0x86, 0x30, 0x14, 0x78, 0x21, + 0x11, 0x22, 0x88, 0xac, 0xd7, 0x54, 0x74, 0xf9, 0xf3, 0x26, 0xc2, 0xa5, + 0x56, 0xc8, 0x56, 0x4f, 0x00, 0x29, 0x1d, 0x08, 0x7b, 0x7a, 0xfb, 0x95, + 0x89, 0xc3, 0xee, 0x98, 0x54, 0x9e, 0x3c, 0x6b, 0x94, 0x05, 0x13, 0x12, + 0xf6, 0x71, 0xb9, 0xab, 0x13, 0xc3, 0x0c, 0x9b, 0x46, 0x08, 0x7b, 0x3d, + 0x32, 0x6a, 0x68, 0xca, 0x1e, 0x9c, 0x90, 0x62, 0xc5, 0xed, 0x10, 0xb9, + 0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd, 0xc4, 0x46, 0xf5, 0xa3, + 0x62, 0x13, 0x74, 0x02, 0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf, + 0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5, 0x02, 0x0c}; + +util::Status DrmRootCertificate::Create( + const std::string& signed_drm_certificate, + std::unique_ptr* cert) { + CHECK(cert); + + SignedDrmCertificate signed_root_cert; + if (!signed_root_cert.ParseFromString(signed_drm_certificate)) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "signed-root-cert-deserialize-fail"); + } + DrmCertificate root_cert; + if (!signed_root_cert.has_drm_certificate()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-root-device-certificate"); + } + if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "root-cert-deserialize-fail"); + } + if (!root_cert.has_public_key()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-root-cert-public-key"); + } + if (!signed_root_cert.has_signature()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-root-certificate-signature"); + } + std::unique_ptr public_key( + RsaPublicKey::Create(root_cert.public_key())); + if (!public_key) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-root-public-key"); + } + if (!public_key->VerifySignature(signed_root_cert.drm_certificate(), + signed_root_cert.signature())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-root-certificate-signature"); + } + cert->reset(new DrmRootCertificate(root_cert.public_key())); + return util::OkStatus(); +} + +util::Status DrmRootCertificate::CreateByType( + CertificateType cert_type, std::unique_ptr* cert) { + CHECK(cert); + return Create(GetDrmRootCertificate(cert_type), cert); +} + +std::string DrmRootCertificate::GetDrmRootCertificate(CertificateType cert_type) { + std::string root_cert; + switch (cert_type) { + case kCertificateTypeProduction: { + root_cert.assign(kProdRootCertificate, + kProdRootCertificate + sizeof(kProdRootCertificate)); + break; + } + case kCertificateTypeDevelopment: { + root_cert.assign(kDevRootCertificate, + kDevRootCertificate + sizeof(kDevRootCertificate)); + break; + } + case kCertificateTypeTesting: { + root_cert.assign(kTestRootCertificate, + kTestRootCertificate + sizeof(kTestRootCertificate)); + break; + } + default: + // TODO(user): Consider returning util::Status indicating unsupported + // cert type. + break; + } + return root_cert; +} + +std::string DrmRootCertificate::GetDigest(CertificateType cert_type) { + std::string cert(GetDrmRootCertificate(cert_type)); + if (cert.empty()) { + return std::string(); + } + std::string hash(SHA256_DIGEST_LENGTH, 0); + SHA256(reinterpret_cast(cert.data()), cert.size(), + reinterpret_cast(&hash[0])); + return absl::BytesToHexString(hash); +} + +} // namespace widevine diff --git a/common/drm_root_certificate.h b/common/drm_root_certificate.h new file mode 100644 index 0000000..fdea567 --- /dev/null +++ b/common/drm_root_certificate.h @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Root device certificate holder class which deserializes, validates, +// and extracts the root certificate public key. + +#ifndef COMMON_DRM_ROOT_CERTIFICATE_H_ +#define COMMON_DRM_ROOT_CERTIFICATE_H_ + +#include +#include + +#include "base/macros.h" +#include "util/status.h" + +#include "common/certificate_type.h" + +namespace widevine { + +class DrmRootCertificate { + public: + virtual ~DrmRootCertificate() {} + // Creates a DrmRootCertificate object given a certificate type. + // |cert| may not be nullptr, and it points to a + // std::unique_ptr which will be used to return a newly + // created DrmRootCertificate* if successful. The caller assumes ownership of + // the new DrmRootCertificate. This method returns util::Status::OK on + // success, or appropriate error status otherwise. + static util::Status CreateByType(CertificateType cert_type, + std::unique_ptr* cert); + // Returns the hex-encoded SHA-256 digest for the specified root certificate. + static std::string GetDigest(CertificateType cert_type); + // Given |cert_type|, the appropiate root certificate is returned as + // a serialized SignedDrmCertificates. + static std::string GetDrmRootCertificate(CertificateType cert_type); + const std::string& public_key() const { return public_key_; } + + // Verifies a DRM certificate. + private: + friend class DrmRootCertificateTest; + + // Creates a DrmRootCertificate object given a serialized + // SignedDrmCertificate. |cert| may not be nullptr, and it points to a + // std::unique_ptr which will be used to return a newly + // created DrmRootCertificate* if successful. The caller assumes ownership of + // the new DrmRootCertificate. This method returns util::Status::OK on + // success, or appropriate error status otherwise. + // TODO(user): Consider moving to private. + static util::Status Create(const std::string& signed_drm_certificate, + std::unique_ptr* cert); + explicit DrmRootCertificate(const std::string& public_key) + : public_key_(public_key) {} + + std::string public_key_; + DISALLOW_IMPLICIT_CONSTRUCTORS(DrmRootCertificate); +}; + +} // namespace widevine + +#endif // COMMON_DRM_ROOT_CERTIFICATE_H_ diff --git a/common/drm_root_certificate_test.cc b/common/drm_root_certificate_test.cc new file mode 100644 index 0000000..f5029d9 --- /dev/null +++ b/common/drm_root_certificate_test.cc @@ -0,0 +1,100 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Unit tests for drm_root_certificate.cc + +#include + +#include "testing/gunit.h" +#include "common/drm_root_certificate.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +class DrmRootCertificateTest : public testing::Test { + protected: + DrmRootCertificateTest() {} + util::Status DrmRootCertificateCreate( + const std::string& signed_drm_certificate, + std::unique_ptr* cert) { + return DrmRootCertificate::Create(signed_drm_certificate, cert); + } +}; + +TEST_F(DrmRootCertificateTest, DrmRootCertificateCreation) { + RsaTestKeys test_keys; + std::unique_ptr root_cert; + + // First, invalid serialized cert. Should fail. + EXPECT_EQ(INVALID_DRM_CERTIFICATE, + DrmRootCertificateCreate("bad_cert", &root_cert).error_code()); + SignedDrmCertificate signed_cert; + std::string serialized; + // Serialized empty cert. Should fail. + ASSERT_TRUE(signed_cert.SerializeToString(&serialized)); + EXPECT_NE(util::OkStatus(), + DrmRootCertificateCreate(serialized, &root_cert)); + // Add public key. Should still fail. + DrmCertificate drm_cert; + drm_cert.set_public_key(test_keys.public_test_key_1_3072_bits()); + ASSERT_TRUE( + drm_cert.SerializeToString(signed_cert.mutable_drm_certificate())); + ASSERT_TRUE(signed_cert.SerializeToString(&serialized)); + EXPECT_EQ(INVALID_DRM_CERTIFICATE, + DrmRootCertificateCreate(serialized, &root_cert).error_code()); + // Now self-sign the cert. Should succeed. + std::unique_ptr private_key( + RsaPrivateKey::Create(test_keys.private_test_key_1_3072_bits())); + ASSERT_TRUE(private_key.get()); + ASSERT_TRUE(private_key->GenerateSignature(signed_cert.drm_certificate(), + signed_cert.mutable_signature())); + ASSERT_TRUE(signed_cert.SerializeToString(&serialized)); + EXPECT_EQ(util::OkStatus(), + DrmRootCertificateCreate(serialized, &root_cert)); + ASSERT_TRUE(root_cert); + // Verify the public key. + EXPECT_EQ(test_keys.public_test_key_1_3072_bits(), root_cert->public_key()); +} + +TEST_F(DrmRootCertificateTest, DrmRootCertificateCreationByType) { + std::unique_ptr root_cert; + EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeTesting, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeDevelopment, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeProduction, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); +} + +TEST_F(DrmRootCertificateTest, DrmRootCertificateDigest) { + const std::string test_cert_hash( + "49f917b1bdfed78002a58e799a58e940" + "1fffaaed9d8d80752782b066757e2c8c"); + const std::string dev_cert_hash( + "0e25ee95476a770f30b98ac5ef778b3f" + "137b66c29385b84f547a361b4724b17d"); + const std::string prod_cert_hash( + "d62fdabc9286648a81f7d3bedaf2f5a5" + "27bbad39bc38da034ba98a21569adb9b"); + EXPECT_EQ(test_cert_hash, + DrmRootCertificate::GetDigest(kCertificateTypeTesting)); + EXPECT_EQ(dev_cert_hash, + DrmRootCertificate::GetDigest(kCertificateTypeDevelopment)); + EXPECT_EQ(prod_cert_hash, + DrmRootCertificate::GetDigest(kCertificateTypeProduction)); +} + +} // namespace widevine diff --git a/common/drm_service_certificate.cc b/common/drm_service_certificate.cc new file mode 100644 index 0000000..75c0047 --- /dev/null +++ b/common/drm_service_certificate.cc @@ -0,0 +1,341 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/drm_service_certificate.h" + +#include +#include +#include + +#include "glog/logging.h" +#include "base/thread_annotations.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" +#include "util/gtl/map_util.h" +#include "common/aes_cbc_util.h" +#include "common/certificate_type.h" +#include "common/drm_root_certificate.h" +#include "common/error_space.h" +#include "common/rsa_util.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +namespace { + +// Class used to hold global service certificate map. +class DrmServiceCertificateMap { + public: + DrmServiceCertificateMap(); + ~DrmServiceCertificateMap(); + + DrmServiceCertificateMap(const DrmServiceCertificateMap&) = delete; + DrmServiceCertificateMap& operator=(const DrmServiceCertificateMap&) = delete; + + void Reset(); + void AddCert(std::unique_ptr new_cert); + void ClearDefaultDrmServiceCertificate(); + const DrmServiceCertificate* GetDefaultCert(); + const DrmServiceCertificate* GetCert(const std::string& serial_number); + + static DrmServiceCertificateMap* GetInstance(); + + private: + absl::Mutex mutex_; + // Certificate serial number to certificate map. + std::map> map_ + GUARDED_BY(mutex_); + DrmServiceCertificate* default_cert_ GUARDED_BY(mutex_); +}; + +DrmServiceCertificateMap::DrmServiceCertificateMap() : default_cert_(nullptr) {} +DrmServiceCertificateMap::~DrmServiceCertificateMap() { Reset(); } + +void DrmServiceCertificateMap::Reset() { + absl::WriterMutexLock lock(&mutex_); + map_.clear(); + default_cert_ = nullptr; +} + +void DrmServiceCertificateMap::AddCert( + std::unique_ptr new_cert) { + absl::WriterMutexLock lock(&mutex_); + + std::unique_ptr* previous_cert = + gtl::FindOrNull(map_, new_cert->serial_number()); + if (previous_cert != nullptr) { + if (default_cert_ == previous_cert->get()) { + default_cert_ = nullptr; + } + } + + if (default_cert_ == nullptr) { + default_cert_ = new_cert.get(); + } + const std::string& serial_number = new_cert->serial_number(); + map_[serial_number] = std::move(new_cert); +} + +void DrmServiceCertificateMap::ClearDefaultDrmServiceCertificate() { + absl::WriterMutexLock lock(&mutex_); + default_cert_ = nullptr; +} + +const DrmServiceCertificate* DrmServiceCertificateMap::GetDefaultCert() { + absl::ReaderMutexLock lock(&mutex_); + return default_cert_; +} + +const DrmServiceCertificate* DrmServiceCertificateMap::GetCert( + const std::string& serial_number) { + absl::ReaderMutexLock lock(&mutex_); + return map_[serial_number].get(); +} + +DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() { + static auto* const kInstance = new DrmServiceCertificateMap(); + return kInstance; +} + +} // namespace + +util::Status DrmServiceCertificate::AddDrmServiceCertificate( + const std::string& root_public_key, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + std::unique_ptr root_key(RsaPublicKey::Create(root_public_key)); + if (root_key == nullptr) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "root-certificate-rsa-public-key-failed"); + } + SignedDrmCertificate signed_cert; + if (!signed_cert.ParseFromString(service_certificate)) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "signed-certificate-parse-failed"); + } + if (!root_key->VerifySignature(signed_cert.drm_certificate(), + signed_cert.signature())) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "certificate-signature-verification-failed"); + } + DrmCertificate drm_cert; + if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "certificate-parse-failed"); + } + if (drm_cert.type() != DrmCertificate::SERVICE) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "not-service-certificate"); + } + if (drm_cert.serial_number().empty()) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing-certificate-serial-number"); + } + if (drm_cert.provider_id().empty()) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing-certificate-service-id"); + } + if (!drm_cert.has_creation_time_seconds()) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing-certificate-creation-time"); + } + if (drm_cert.public_key().empty()) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing-certificate-public-key"); + } + std::unique_ptr public_key( + RsaPublicKey::Create(drm_cert.public_key())); + if (!public_key) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "invalid-certificate-public-key"); + } + std::string pkcs1_key; + if (!rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey( + service_private_key, service_private_key_passphrase, &pkcs1_key)) { + return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY, + "key-decryption-failed"); + } + std::unique_ptr private_key(RsaPrivateKey::Create(pkcs1_key)); + if (private_key == nullptr) { + return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY, + "invalid-private-key"); + } + + std::unique_ptr new_cert(new DrmServiceCertificate( + service_certificate, drm_cert.provider_id(), drm_cert.serial_number(), + drm_cert.creation_time_seconds(), std::move(public_key), + std::move(private_key))); + DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert)); + + return util::OkStatus(); +} + +util::Status DrmServiceCertificate::AddDrmServiceCertificate( + CertificateType root_cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + std::unique_ptr root_cert; + util::Status status = + DrmRootCertificate::CreateByType(root_cert_type, &root_cert); + if (!status.ok()) { + return status; + } + return AddDrmServiceCertificate(root_cert->public_key(), service_certificate, + service_private_key, + service_private_key_passphrase); +} + +const DrmServiceCertificate* +DrmServiceCertificate::GetDefaultDrmServiceCertificate() { + return DrmServiceCertificateMap::GetInstance()->GetDefaultCert(); +} + +const DrmServiceCertificate* +DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() { + const DrmServiceCertificate* default_cert = + DrmServiceCertificateMap::GetInstance()->GetDefaultCert(); + CHECK(default_cert) << "Service Certificate not set!"; + return default_cert; +} + +const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate( + const std::string& serial_number) { + return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number); +} + +util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate( + const std::string& root_public_key, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + DrmServiceCertificateMap::GetInstance()->ClearDefaultDrmServiceCertificate(); + return AddDrmServiceCertificate(root_public_key, service_certificate, + service_private_key, + service_private_key_passphrase); +} + +util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate( + CertificateType root_cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + std::unique_ptr root_cert; + util::Status status = + DrmRootCertificate::CreateByType(root_cert_type, &root_cert); + if (!status.ok()) { + return status; + } + return SetDefaultDrmServiceCertificate( + root_cert->public_key(), service_certificate, service_private_key, + service_private_key_passphrase); +} + +util::Status DrmServiceCertificate::DecryptClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id) { + DCHECK(client_id); + if (encrypted_client_id.service_certificate_serial_number().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-service-certificate-serial-number"); + } + if (encrypted_client_id.provider_id().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-service-id"); + } + if (encrypted_client_id.encrypted_client_id().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id"); + } + if (encrypted_client_id.encrypted_client_id_iv().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id-iv"); + } + if (encrypted_client_id.encrypted_privacy_key().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-privacy-key"); + } + std::string privacy_key; + std::string provider_id; + const DrmServiceCertificate* cert = GetDrmServiceCertificate( + encrypted_client_id.service_certificate_serial_number()); + if (!cert) { + return util::Status( + error_space, SERVICE_CERTIFICATE_NOT_FOUND, + "service-certificate-not-found (SN " + + absl::BytesToHexString( + encrypted_client_id.service_certificate_serial_number()) + + ")"); + } + if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(), + &privacy_key)) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "privacy-key-decryption-failed"); + } + if (cert->provider_id() != encrypted_client_id.provider_id()) { + return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND, + std::string("provider-id-mismatch (") + cert->provider_id() + + " / " + encrypted_client_id.provider_id() + ")"); + } + std::string serialized_client_id(crypto_util::DecryptAesCbc( + privacy_key, encrypted_client_id.encrypted_client_id_iv(), + encrypted_client_id.encrypted_client_id())); + if (serialized_client_id.empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-decryption-failed"); + } + if (!client_id->ParseFromString(serialized_client_id)) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-parse-failed"); + } + return util::OkStatus(); +} + +void DrmServiceCertificate::ResetServiceCertificates() { + DrmServiceCertificateMap::GetInstance()->Reset(); +} + +util::Status DrmServiceCertificate::ValidateDrmServiceCertificate() { + const DrmServiceCertificate* service_certificate = + GetDefaultDrmServiceCertificate(); + if (!service_certificate) { + return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND, + "drm service certificate is not found."); + } + SignedDrmCertificate signed_cert; + if (!signed_cert.ParseFromString(service_certificate->certificate())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "signed drm service certificate is failed to parse."); + } + DrmCertificate drm_cert; + if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "Drm service certificate is failed to parse."); + } + if (!drm_cert.has_creation_time_seconds()) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing certificate creation time"); + } + // TODO(user): Check creation_time_seconds field in DrmCertificate and also + // export the absl/time dependency through moe. + return util::OkStatus(); +} + +DrmServiceCertificate::DrmServiceCertificate( + const std::string& service_certificate, const std::string& provider_id, + const std::string& serial_number, const uint32_t creation_time_seconds, + std::unique_ptr public_key, + std::unique_ptr private_key) + : certificate_(service_certificate), + provider_id_(provider_id), + serial_number_(serial_number), + creation_time_seconds_(creation_time_seconds), + public_key_(std::move(public_key)), + private_key_(std::move(private_key)) {} + +} // namespace widevine diff --git a/common/drm_service_certificate.h b/common/drm_service_certificate.h new file mode 100644 index 0000000..88a2161 --- /dev/null +++ b/common/drm_service_certificate.h @@ -0,0 +1,129 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Service certificate holder used to decrypt encrypted client credentials. + +#ifndef COMMON_DRM_SERVICE_CERTIFICATE_H_ +#define COMMON_DRM_SERVICE_CERTIFICATE_H_ + +#include +#include +#include + +#include +#include "base/macros.h" +#include "util/status.h" +#include "common/certificate_type.h" +#include "common/rsa_key.h" + +namespace widevine { +class RequestInspectorTest; +} // namespace widevine + +namespace widevine { + +class ClientIdentification; +class EncryptedClientIdentification; + +class DrmServiceCertificate { + public: + // Create a new DrmServiceCertificate object and add it to the list of valid + // service certificates. |root_cert_type| indicates which root public key to + // use to verify |service_certificate|, |service_certificate| is a + // Google-generated certificate used to authenticate the service provider for + // purposes of device privacy, |service_private_key| is the encrypted PKCS#8 + // private RSA key corresponding to the service certificate, + // |service_private_key_passphrase| is the password required to decrypt + // |service_private_key|. + // Returns status::OK if successful, or appropriate error code otherwise. + // If the default service certificate is not set, this certificate will be + // used as the default service certificate. + // This method is thread-safe. + static util::Status AddDrmServiceCertificate( + CertificateType root_cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + // Same as AddDrmServiceCertificate(), but will clear the default service + // certificate if it's set. This will result in this service certificate + // being set as the default service certificate. + static util::Status SetDefaultDrmServiceCertificate( + CertificateType root_cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + // Returns the default service certificate. Will return null if no default + // Service Certificate is set. This method is thread-safe. + static const DrmServiceCertificate* GetDefaultDrmServiceCertificate(); + + // Returns the default service certificate. Will abort if no default Service + // Certificate is set. This method is thread-safe. + static const DrmServiceCertificate* GetDefaultDrmServiceCertificateOrDie(); + + // Returns the service certificate with the given serial number if found, or + // null otherwise. + static const DrmServiceCertificate* GetDrmServiceCertificate( + const std::string& cert_serial_number); + + // Decrypts the EncryptedClientIdentification message passed in + // |encrypted_client_id| into |client_id| using the private key for the + // certificate which was used to encrypt the information. |client_id| must + // not be NULL. Returns status::OK if successful, or an appropriate error + // otherwise. This method is thread-safe. + static util::Status DecryptClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id); + + const std::string& certificate() const { return certificate_; } + const std::string& provider_id() const { return provider_id_; } + const std::string& serial_number() const { return serial_number_; } + const RsaPrivateKey* const private_key() const { return private_key_.get(); } + const RsaPublicKey* const public_key() const { return public_key_.get(); } + + // Returns the validation result of drm service certificate. Returns + // status::OK if successful, or in case of error, contact + // widevine-tam@google.com to get the next valid service certificate renewed + // via get deviceCertificate StatusList. + static util::Status ValidateDrmServiceCertificate(); + + private: + friend class DrmServiceCertificateTest; + friend class widevine::RequestInspectorTest; + + static util::Status AddDrmServiceCertificate( + const std::string& root_public_key, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + static util::Status SetDefaultDrmServiceCertificate( + const std::string& root_public_key, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + DrmServiceCertificate(const std::string& service_certificate, + const std::string& provider_id, const std::string& serial_number, + const uint32_t creation_time_seconds, + std::unique_ptr public_key, + std::unique_ptr private_key); + + static void ResetServiceCertificates(); + + std::string certificate_; + std::string provider_id_; + std::string serial_number_; + uint32_t creation_time_seconds_; + std::unique_ptr public_key_; + std::unique_ptr private_key_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DrmServiceCertificate); +}; + +} // namespace widevine + +#endif // COMMON_DRM_SERVICE_CERTIFICATE_H_ diff --git a/common/drm_service_certificate_test.cc b/common/drm_service_certificate_test.cc new file mode 100644 index 0000000..fbd02bb --- /dev/null +++ b/common/drm_service_certificate_test.cc @@ -0,0 +1,368 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 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 + +#include "glog/logging.h" +#include "google/protobuf/util/message_differencer.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "common/aes_cbc_util.h" +#include "common/drm_service_certificate.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" +#include "common/test_certificates.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" // IWYU pragma: keep +#include "protos/public/license_server_sdk.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +const char kPrivacyKey[] = "f7538b38acc78ec68c732ac665c55c65"; +const char kIv[] = "09e9cda133ff5140bd2793173a04b5a3"; +const char kPassphrase[] = "passphrase"; + +class DrmServiceCertificateTest : public ::testing::Test { + public: + DrmServiceCertificateTest() + : privacy_key_(absl::HexStringToBytes(kPrivacyKey)), + iv_(absl::HexStringToBytes(kIv)), + root_private_key_( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())) { + CHECK(root_private_key_ != nullptr); + client_id_.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE); + client_id_.set_token(test_certs_.test_user_device_certificate()); + } + + void SetUp() override { DrmServiceCertificate::ResetServiceCertificates(); } + + protected: + std::string GenerateDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds, + const std::string& public_key) { + DrmCertificate cert; + cert.set_type(DrmCertificate::SERVICE); + cert.set_serial_number(serial_number); + cert.set_provider_id(provider_id); + cert.set_public_key(public_key); + cert.set_creation_time_seconds(creation_time_seconds); + SignedDrmCertificate signed_cert; + cert.SerializeToString(signed_cert.mutable_drm_certificate()); + root_private_key_->GenerateSignature(signed_cert.drm_certificate(), + signed_cert.mutable_signature()); + std::string serialized_cert; + signed_cert.SerializeToString(&serialized_cert); + return serialized_cert; + } + + util::Status SetDefaultDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds) { + std::string signed_cert(GenerateDrmServiceCertificate( + serial_number, provider_id, creation_time_seconds, + test_keys_.public_test_key_2_2048_bits())); + std::string encrypted_private_key; + if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), kPassphrase, + &encrypted_private_key)) { + return util::Status(util::error::INTERNAL, ""); + } + return DrmServiceCertificate::SetDefaultDrmServiceCertificate( + test_keys_.public_test_key_1_3072_bits(), signed_cert, + encrypted_private_key, kPassphrase); + } + + util::Status AddDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds) { + std::string signed_cert(GenerateDrmServiceCertificate( + serial_number, provider_id, creation_time_seconds, + test_keys_.public_test_key_2_2048_bits())); + std::string encrypted_private_key; + if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), kPassphrase, + &encrypted_private_key)) { + return util::Status(util::error::INTERNAL, ""); + } + return DrmServiceCertificate::AddDrmServiceCertificate( + test_keys_.public_test_key_1_3072_bits(), signed_cert, + encrypted_private_key, kPassphrase); + } + + void EncryptClientIdentification( + const std::string& serial_number, const std::string& provider_id, + const std::string& public_key, + EncryptedClientIdentification* encrypted_client_id) { + CHECK(encrypted_client_id); + encrypted_client_id->set_provider_id(provider_id); + encrypted_client_id->set_service_certificate_serial_number(serial_number); + std::string serial_client_id; + client_id_.SerializeToString(&serial_client_id); + encrypted_client_id->set_encrypted_client_id( + crypto_util::EncryptAesCbc(privacy_key_, iv_, serial_client_id)); + encrypted_client_id->set_encrypted_client_id_iv(iv_); + std::unique_ptr rsa_key(RsaPublicKey::Create(public_key)); + ASSERT_TRUE(rsa_key.get()); + rsa_key->Encrypt(privacy_key_, + encrypted_client_id->mutable_encrypted_privacy_key()); + } + + RsaTestKeys test_keys_; + TestCertificates test_certs_; + std::string privacy_key_; + std::string iv_; + std::unique_ptr root_private_key_; + ClientIdentification client_id_; +}; + +TEST_F(DrmServiceCertificateTest, BasicClientIdDecrypt) { + std::string serial_number("serial_number"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + EXPECT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); +} + +TEST_F(DrmServiceCertificateTest, NoDefaultDrmServiceCertificate) { + ASSERT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate()); + const auto& get_default_sc_or_die = []() { + DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie(); + }; + EXPECT_DEATH(get_default_sc_or_die(), "Service Certificate not set!"); +} + +TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) { + std::string serial_number1("serial_number1"); + std::string provider_id1("someservice.com"); + uint32_t creation_time_seconds1(1234); + std::string serial_number2("serial_number2"); + uint32_t creation_time_seconds2(1234); + std::string bogus_serial_number("bogus-serial-number2"); + + EXPECT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate()); + + EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id1, + creation_time_seconds1)); + + // Expect this to pass because the serial number is allowed to change as long + // as the service Id is the same as before. + EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id1, + creation_time_seconds2)); + + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number1, provider_id1, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptClientIdentification(serial_number2, provider_id1, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptClientIdentification(bogus_serial_number, provider_id1, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND, + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id) + .error_code()); +} + +TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) { + std::string serial_number1("serial_number1"); + std::string serial_number2("serial_number2"); + std::string serial_number3("serial_number3"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id, + creation_time_seconds)); + EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id, + creation_time_seconds + 1)); + EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id, + creation_time_seconds - 1)); + + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number1, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + EXPECT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptClientIdentification(serial_number2, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + EXPECT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptClientIdentification(serial_number3, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + EXPECT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + const DrmServiceCertificate* default_cert( + DrmServiceCertificate::GetDefaultDrmServiceCertificate()); + ASSERT_TRUE(default_cert); + SignedDrmCertificate signed_cert; + ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate())); + DrmCertificate drm_cert; + ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate())); + EXPECT_EQ(serial_number1, drm_cert.serial_number()); + + EXPECT_OK(SetDefaultDrmServiceCertificate(serial_number2, provider_id, + creation_time_seconds)); + default_cert = DrmServiceCertificate::GetDefaultDrmServiceCertificate(); + ASSERT_TRUE(default_cert); + ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate())); + ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate())); + EXPECT_EQ(serial_number2, drm_cert.serial_number()); +} + +TEST_F(DrmServiceCertificateTest, DrmServiceCertificateNotFound) { + std::string serial_number("serial_number"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification("invalid_serial_number", provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND, + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id) + .error_code()); +} + +TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) { + std::string serial_number("serial_number"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + ASSERT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptedClientIdentification invalid; + invalid = encrypted_client_id; + invalid.clear_encrypted_privacy_key(); + EXPECT_EQ( + "Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: " + "missing-encrypted-privacy-key", + DrmServiceCertificate::DecryptClientIdentification(invalid, + &decrypted_client_id) + .ToString()); + + invalid = encrypted_client_id; + ++(*invalid.mutable_encrypted_client_id_iv())[4]; + EXPECT_NE(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + invalid, &decrypted_client_id)); + + invalid.clear_encrypted_client_id_iv(); + EXPECT_EQ( + "Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: " + "missing-encrypted-client-id-iv", + DrmServiceCertificate::DecryptClientIdentification(invalid, + &decrypted_client_id) + .ToString()); + + invalid = encrypted_client_id; + ++(*invalid.mutable_encrypted_client_id())[0]; + EXPECT_NE(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + invalid, &decrypted_client_id)); + + invalid.clear_encrypted_client_id(); + EXPECT_EQ( + "Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: " + "missing-encrypted-client-id", + DrmServiceCertificate::DecryptClientIdentification(invalid, + &decrypted_client_id) + .ToString()); +} + +TEST_F(DrmServiceCertificateTest, PrivateKeyDecryptError) { + std::string serial_number("serial_number"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + ASSERT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptedClientIdentification corrupted; + corrupted = encrypted_client_id; + ++(*corrupted.mutable_encrypted_privacy_key())[20]; + EXPECT_EQ( + "Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: " + "privacy-key-decryption-failed", + DrmServiceCertificate::DecryptClientIdentification(corrupted, + &decrypted_client_id) + .ToString()); +} + +// TODO(user): Add more unit tests for various fail cases (bad keys having +// to do with bad keys and bad certs). + +} // namespace widevine diff --git a/common/ecb_util.cc b/common/ecb_util.cc new file mode 100644 index 0000000..de7377d --- /dev/null +++ b/common/ecb_util.cc @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Implementation of ecb crypto routines used by Widevine services. + +#include "common/ecb_util.h" + +#include "glog/logging.h" +#include "absl/strings/string_view.h" +#include "openssl/aes.h" +#include "openssl/des.h" + +namespace widevine { +namespace crypto_util { + +const int kWvmDESKeySizeBytes = 16; + +static bool EncryptOrDecrypt3DesCbc(absl::string_view key, + absl::string_view src, std::string* dst, + bool encrypt) { + CHECK(dst); + if (key.size() != kWvmDESKeySizeBytes) { + LOG(WARNING) << "Invalid 3DES key size (" << key.size() << "!=16)."; + dst->clear(); + return false; + } + const int data_size = src.size(); + if (data_size % DES_KEY_SZ != 0) { + // Data must be a multiple of block size + LOG(WARNING) << "3DES data is not a multiple of 8 bytes."; + dst->clear(); + return false; + } + const int data_size_blocks = data_size / DES_KEY_SZ; + DES_key_schedule schedule[2]; + // const_DES_cblock (the type of the first argument to DES_ecb3_encrypt) isn't + // actually const, so we have to cast away const. + DES_cblock* keyblock = + const_cast(reinterpret_cast(key.data())); + DES_set_key(keyblock + 0, schedule + 1); + DES_set_key(keyblock + 1, schedule + 0); + DES_cblock* srcblock = + const_cast(reinterpret_cast(src.data())); + dst->resize(data_size); + DES_cblock* dstblock = reinterpret_cast(&*dst->begin()); + for (int i = 0; i < data_size_blocks; i++) { + DES_ecb3_encrypt(srcblock + i, dstblock + i, schedule + 0, schedule + 1, + schedule + 0, encrypt); + } + return true; +} + +bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) { + return EncryptOrDecrypt3DesCbc(key, src, dst, DES_ENCRYPT); +} + +bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) { + return EncryptOrDecrypt3DesCbc(key, src, dst, DES_DECRYPT); +} + +bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) { + CHECK(dst); + dst->clear(); + if (src.size() % 16 != 0) { + LOG(WARNING) << "AES-ECB data is not a multiple of 16 bytes."; + return false; + } + int num_bits = key.size() * 8; + AES_KEY aes_key; + int aes_result = AES_set_encrypt_key( + reinterpret_cast(key.data()), num_bits, &aes_key); + if (aes_result != 0) { + LOG(WARNING) << "AES result is not zero."; + return false; + } + dst->resize(src.size()); + for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) { + AES_encrypt(reinterpret_cast(src.data() + i), + reinterpret_cast(&(*dst)[i]), &aes_key); + } + return true; +} + +bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) { + CHECK(dst); + dst->clear(); + if (src.size() % 16 != 0) { + LOG(WARNING) << "AES-ECB data is not a multiple of 16 bytes."; + return false; + } + int num_bits = key.size() * 8; + AES_KEY aes_key; + int aes_result = AES_set_decrypt_key( + reinterpret_cast(key.data()), num_bits, &aes_key); + if (aes_result != 0) { + LOG(WARNING) << "AES result is not zero."; + return false; + } + dst->resize(src.size()); + for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) { + AES_decrypt(reinterpret_cast(src.data() + i), + reinterpret_cast(&(*dst)[i]), &aes_key); + } + return true; +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/ecb_util.h b/common/ecb_util.h new file mode 100644 index 0000000..0b5e82b --- /dev/null +++ b/common/ecb_util.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// Contains ecb crypto routines for widevine protocols. These routines are used +// as part of licensing and provisioning request handling. + +#ifndef COMMON_ECB_UTIL_H_ +#define COMMON_ECB_UTIL_H_ + +#include + +#include "absl/strings/string_view.h" + +namespace widevine { +namespace crypto_util { + +// Encrypts |src| into |dst| using 3DES ECB2 mode with the given key. This is +// used for protecting content keys on some older Widevine keyboxes, and is not +// intended for any other purpose. Returns false and sets *|dst|="" if +// unsuccessful. Key should be 16 bytes. The first 8 are key2 of the 3DES key +// bundle, and the last 8 bytes are key1 and key3. |src| must be a multiple of +// 8 bytes. +bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst); + +// Decrypts |src| into |dst| using 3DES ECB2 mode with the given (16-byte) +// key. This is used for protecting content keys on some older Widevine +// keyboxes, and is not intended for any other purpose. +// Returns false and sets *|dst|="" if unsuccessful. Note that it can only +// fail if invalid key or data sizes are passed in. +// Key should be 16 bytes. The first 8 are key2 of the 3DES key bundle, +// and the last 8 bytes are key1 and key3. |src| must be a multiple of +// 8 bytes. +bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst); + +// Encrypts |src| into |dst| using AES ECB mode with the given +// key. This is used for protecting content keys on Widevine devices, +// and is not intended for any other purpose. +// Returns false and sets *|dst|="" if unsuccessful. Note that it can only +// fail if invalid key or data sizes are passed in. +// Key must be 16 bytes, and src must be a multiple of 16 bytes. +bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst); + +// Decrypts |src| into |dst| using AES ECB mode with the given +// key. This is used for protecting content keys on Widevine devices, +// and is not intended for any other purpose. +// Returns false and sets *|dst|="" if unsuccessful. Note that it can only +// fail if invalid key or data sizes are passed in. +// Key must be 16 bytes, and src must be a multiple of 16 bytes. +bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst); + +} // namespace crypto_util +} // namespace widevine + +#endif // COMMON_ECB_UTIL_H_ diff --git a/common/ecb_util_test.cc b/common/ecb_util_test.cc new file mode 100644 index 0000000..40285b8 --- /dev/null +++ b/common/ecb_util_test.cc @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// Unit tests for the ecb_util functions. + +#include + +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "common/ecb_util.h" + +namespace widevine { +namespace crypto_util { + +TEST(CryptoUtilTest, TestEncrypt3DesEcb) { + // Test vector generated by (Python): + // c = M2Crypto.EVP.Cipher('des_ede3_ecb', '89abcdef0123456789abcdef', + // iv='', op=1, padding=False) + // (c.update('This is a test message. ')+c.final()).encode('hex') + // TODO(user): find some Widevine test vectors and use those + std::string key = "0123456789abcdef"; + std::string plain = "This is a test message. "; + std::string cipher; + std::string decrypted; + EXPECT_TRUE(Encrypt3DesEcb(key, plain, &cipher)); + EXPECT_EQ("ae7c7accaf99a973e7f89bb7f6dc61a9aa9e226a5ba17376", + absl::BytesToHexString(cipher)); + EXPECT_TRUE(Decrypt3DesEcb(key, cipher, &decrypted)); + EXPECT_EQ(plain, decrypted); +} + +TEST(CryptoUtilTest, TestEncrypt3DesEcbFail) { + // Verify that 3DES fails with invalid key or data size + std::string badkey = "0123456789abcde"; // 15 bytes + std::string badplain = "This is a test message."; // 23 bytes + std::string goodkey = "0123456789abcdef"; // 16 bytes + std::string goodplain = "This is a test message. "; // 24 bytes + + // Encryption failure should leave 'decrypted' as empty std::string + std::string out = "Not empty"; + EXPECT_FALSE(Encrypt3DesEcb(badkey, goodplain, &out)); + EXPECT_TRUE(out.empty()); + + out = "Not empty"; + EXPECT_FALSE(Decrypt3DesEcb(goodkey, badplain, &out)); + EXPECT_TRUE(out.empty()); +} + +TEST(CryptoUtilTest, TestEncryptAesEcb) { + // Test vector generated by (Python): + // c = M2Crypto.EVP.Cipher('aes_128_ecb', key, '', 1, padding=False) + // encrypted = c.update(data) + c.final(); + // TODO(user): find some Widevine test vectors and use those + std::string key = "0123456789abcdef"; + std::string plaintext = "This message has 32 bytes in it."; + std::string encrypted; + std::string decrypted; + EXPECT_TRUE(EncryptAesEcb(key, plaintext, &encrypted)); + EXPECT_EQ("1f6a7d63e0645de25c56c6b39ba7723d640129d65f41e96b87be812bc94ad8a9", + absl::BytesToHexString(encrypted)); + EXPECT_TRUE(DecryptAesEcb(key, encrypted, &decrypted)); + EXPECT_EQ(plaintext, decrypted); +} + +TEST(CryptoUtilTest, TestEncryptAesEcbFail) { + // Verify that EncryptAesEcb fails with invalid key or data size + std::string badkey = "0123456789abcde"; // 15 bytes + std::string badplain = "This message has 31 bytes in it"; + std::string goodkey = "0123456789abcdef"; // 16 bytes + std::string goodplain = "This message has 32 bytes in it."; + + // Encryption failure should leave 'decrypted' as empty std::string + std::string out = "Not empty"; + EXPECT_FALSE(EncryptAesEcb(badkey, goodplain, &out)); + EXPECT_TRUE(out.empty()); + + out = "Not empty"; + EXPECT_FALSE(EncryptAesEcb(goodkey, badplain, &out)); + EXPECT_TRUE(out.empty()); +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/error_space.cc b/common/error_space.cc new file mode 100644 index 0000000..2e9742b --- /dev/null +++ b/common/error_space.cc @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/error_space.h" + +#include "util/proto_status.h" +#include "protos/public/errors.pb.h" + +namespace widevine { + +const util::ErrorSpace* error_space = + util::ProtoEnumErrorSpace::Get(); + +} // namespace widevine diff --git a/common/error_space.h b/common/error_space.h new file mode 100644 index 0000000..1a4ab78 --- /dev/null +++ b/common/error_space.h @@ -0,0 +1,20 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_ERROR_SPACE_H_ +#define COMMON_ERROR_SPACE_H_ + +#include "util/error_space.h" + +namespace widevine { + +extern const util::ErrorSpace* error_space; + +} // namespace widevine + +#endif // COMMON_ERROR_SPACE_H_ diff --git a/common/file_util.cc b/common/file_util.cc new file mode 100644 index 0000000..49147f4 --- /dev/null +++ b/common/file_util.cc @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/file_util.h" + +#include +#include + +#include "glog/logging.h" + +namespace widevine { + +bool GetContents(const std::string& file_name, std::string* contents) { + if (file_name.empty()) { + LOG(WARNING) << "File name is empty."; + return false; + } + FILE* file = fopen(file_name.c_str(), "r"); + if (!file) { + LOG(WARNING) << "Unable to open file " << file_name; + return false; + } + contents->clear(); + const size_t kReadSize = 0x1000; + char buffer[kReadSize]; + while (true) { + size_t size_read = fread(buffer, sizeof(char), kReadSize, file); + if (size_read == 0) break; + contents->append(buffer, size_read); + } + const bool eof = feof(file); + fclose(file); + if (!eof) { + LOG(WARNING) << "Failed to read all file contents."; + return false; + } + return true; +} + +bool SetContents(const std::string& file_name, const std::string& contents) { + if (file_name.empty()) { + LOG(WARNING) << "File name is empty."; + return false; + } + FILE* file = fopen(file_name.c_str(), "w"); + if (!file) { + LOG(WARNING) << "Unable to open file " << file_name; + return false; + } + const size_t size_written = + fwrite(contents.data(), sizeof(char), contents.size(), file); + if (size_written != contents.size()) + LOG(WARNING) << "Failed to write to " << file_name; + fclose(file); + return size_written == contents.size(); +} + +} // namespace widevine diff --git a/common/file_util.h b/common/file_util.h new file mode 100644 index 0000000..42c03aa --- /dev/null +++ b/common/file_util.h @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// File util wrapper to be used in partner sdks. Implemented using generic file +// apis. + +#ifndef COMMON_FILE_UTIL_H_ +#define COMMON_FILE_UTIL_H_ + +#include + +namespace widevine { + +// Read file to string. +bool GetContents(const std::string& file_name, std::string* contents); + +// Write file. +bool SetContents(const std::string& file_name, const std::string& contents); + +} // namespace widevine + +#endif // COMMON_FILE_UTIL_H_ diff --git a/common/file_util_test.cc b/common/file_util_test.cc new file mode 100644 index 0000000..33399c9 --- /dev/null +++ b/common/file_util_test.cc @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/file_util.h" +#include "testing/gunit.h" +#include "absl/strings/str_cat.h" + +namespace widevine { + +TEST(FileUtilTest, EmptyFileName) { + std::string contents; + EXPECT_FALSE(GetContents("", &contents)); + EXPECT_FALSE(SetContents("", "test content")); +} + +TEST(FileUtilTest, BasicTest) { + const std::string file_path = absl::StrCat("/tmp", "/file_util_test"); + EXPECT_TRUE(SetContents(file_path, "test content")); + std::string contents; + EXPECT_TRUE(GetContents(file_path, &contents)); + EXPECT_EQ("test content", contents); +} + +} // namespace widevine diff --git a/common/mock_rsa_key.h b/common/mock_rsa_key.h new file mode 100644 index 0000000..dd8fa9a --- /dev/null +++ b/common/mock_rsa_key.h @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_MOCK_RSA_KEY_H_ +#define COMMON_MOCK_RSA_KEY_H_ + +#include +#include "testing/gmock.h" +#include "common/rsa_key.h" + +namespace widevine { + +class MockRsaPrivateKey : public RsaPrivateKey { + public: + MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {} + ~MockRsaPrivateKey() override {} + + MOCK_CONST_METHOD2(Decrypt, bool(const std::string& encrypted_message, + std::string* decrypted_message)); + MOCK_CONST_METHOD2(GenerateSignature, + bool(const std::string& message, std::string* signature)); + MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key)); + MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key)); + + private: + MockRsaPrivateKey(const MockRsaPrivateKey&) = delete; + MockRsaPrivateKey& operator=(const MockRsaPrivateKey&) = delete; +}; + +class MockRsaPublicKey : public RsaPublicKey { + public: + MockRsaPublicKey() : RsaPublicKey(RSA_new()) {} + ~MockRsaPublicKey() override {} + + MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message, + std::string* encrypted_message)); + MOCK_CONST_METHOD2(VerifySignature, + bool(const std::string& message, const std::string& signature)); + MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key)); + MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key)); + + private: + MockRsaPublicKey(const MockRsaPublicKey&) = delete; + MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete; +}; + +class MockRsaKeyFactory : public RsaKeyFactory { + public: + MockRsaKeyFactory() {} + ~MockRsaKeyFactory() override {} + + MOCK_METHOD1(CreateFromPkcs1PrivateKey, + std::unique_ptr(const std::string& private_key)); + MOCK_METHOD2( + CreateFromPkcs8PrivateKey, + std::unique_ptr(const std::string& private_key, + const std::string& private_key_passphrase)); + MOCK_METHOD1(CreateFromPkcs1PublicKey, + std::unique_ptr(const std::string& public_key)); + + private: + MockRsaKeyFactory(const MockRsaKeyFactory&) = delete; + MockRsaKeyFactory& operator=(const MockRsaKeyFactory&) = delete; +}; + +} // namespace widevine + +#endif // COMMON_MOCK_RSA_KEY_H_ diff --git a/common/openssl_util.h b/common/openssl_util.h new file mode 100644 index 0000000..b715c8b --- /dev/null +++ b/common/openssl_util.h @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// +// +// RAII wrapper classes for cleaning up various OpenSSL dynamically allocated +// structures. + +#ifndef COMMON_OPENSSL_UTIL_H_ +#define COMMON_OPENSSL_UTIL_H_ + +#include "openssl/bio.h" +#include "openssl/evp.h" +#include "openssl/rsa.h" +#include "openssl/x509v3.h" + +template +struct OpenSSLDeleter { + void operator()(T *obj) { func(obj); } +}; + +template +struct OpenSSLStackDeleter { + void operator()(StackType *obj) { + sk_pop_free(reinterpret_cast<_STACK *>(obj), + reinterpret_cast(func)); + } +}; + +template +struct OpenSSLStackOnlyDeleter { + void operator()(StackType *obj) { sk_free(reinterpret_cast<_STACK *>(obj)); } +}; + +template +using ScopedOpenSSLType = std::unique_ptr>; +template +using ScopedOpenSSLStack = + std::unique_ptr>; +template +using ScopedOpenSSLStackOnly = + std::unique_ptr>; + +using ScopedBIGNUM = ScopedOpenSSLType; +using ScopedBIO = ScopedOpenSSLType; +using ScopedPKEY = ScopedOpenSSLType; +using ScopedRSA = ScopedOpenSSLType; +using ScopedX509 = ScopedOpenSSLType; +using ScopedX509Extension = + ScopedOpenSSLType; +using ScopedX509Name = ScopedOpenSSLType; +using ScopedX509NameEntry = + ScopedOpenSSLType; +using ScopedX509Store = ScopedOpenSSLType; +using ScopedX509StoreCtx = + ScopedOpenSSLType; +using ScopedX509Req = ScopedOpenSSLType; +using ScopedAsn1UtcTime = ScopedOpenSSLType; +using ScopedAsn1Utc8String = + ScopedOpenSSLType; +using ScopedAsn1Integer = ScopedOpenSSLType; +using ScopedAsn1Object = ScopedOpenSSLType; +using ScopedAsn1OctetString = + ScopedOpenSSLType; + +// XxxStack deallocates the stack and its members while XxxStackOnly deallocates +// the stack only. +using ScopedX509Stack = ScopedOpenSSLStack; +using ScopedX509StackOnly = ScopedOpenSSLStackOnly; +using ScopedX509InfoStack = + ScopedOpenSSLStack; +using ScopedX509InfoStackOnly = ScopedOpenSSLStackOnly; + +#endif // COMMON_OPENSSL_UTIL_H_ diff --git a/common/random_util.cc b/common/random_util.cc new file mode 100644 index 0000000..286ab3d --- /dev/null +++ b/common/random_util.cc @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/random_util.h" + +#include "glog/logging.h" +#include "openssl/rand.h" + +namespace widevine { + +bool RandomBytes(size_t num_bytes, std::string* output) { + DCHECK(output); + output->resize(num_bytes); + return RAND_bytes(reinterpret_cast(&(*output)[0]), num_bytes); +} + +std::string Random16Bytes() { + std::string output; + CHECK(RandomBytes(16u, &output)); + return output; +} +} // namespace widevine diff --git a/common/random_util.h b/common/random_util.h new file mode 100644 index 0000000..90eb37e --- /dev/null +++ b/common/random_util.h @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_RANDOM_UTIL_H_ +#define COMMON_RANDOM_UTIL_H_ + +#include + +namespace widevine { + +// Generates a random string. +// Returns true on success, false otherwise. +bool RandomBytes(size_t num_bytes, std::string* output); + +// Returns a 16-byte std::string suitable for use as an AES key +std::string Random16Bytes(); + +} // namespace widevine + +#endif // COMMON_RANDOM_UTIL_H_ diff --git a/common/random_util_test.cc b/common/random_util_test.cc new file mode 100644 index 0000000..1a52d6f --- /dev/null +++ b/common/random_util_test.cc @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/random_util.h" +#include "testing/gunit.h" + +namespace widevine { + +TEST(RandomUtilTest, Test) { + std::string output; + ASSERT_TRUE(RandomBytes(16u, &output)); + EXPECT_EQ(16u, output.size()); + + std::string output2; + ASSERT_TRUE(RandomBytes(16u, &output2)); + EXPECT_EQ(16u, output2.size()); + EXPECT_NE(output, output2); + + ASSERT_TRUE(RandomBytes(10u, &output2)); + EXPECT_EQ(10u, output2.size()); +} + +} // namespace widevine diff --git a/common/remote_attestation_verifier.cc b/common/remote_attestation_verifier.cc new file mode 100644 index 0000000..b8cb252 --- /dev/null +++ b/common/remote_attestation_verifier.cc @@ -0,0 +1,261 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/remote_attestation_verifier.h" + +#include +#include + +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/synchronization/mutex.h" +#include "common/client_id_util.h" +#include "common/drm_service_certificate.h" +#include "common/error_space.h" +#include "common/rsa_key.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/errors.pb.h" + +namespace widevine { + +const char kTestRootCaDerCert[] = + "30820403308202eba003020102020900a24f94af7ae6831f300d06092a86" + "4886f70d0101050500308197310b30090603550406130255533113301106" + "035504080c0a57617368696e67746f6e3111300f06035504070c084b6972" + "6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f" + "060355040b0c085769646576696e653115301306035504030c0c54657374" + "20526f6f742043413121301f06092a864886f70d010901161274696e736b" + "697040676f6f676c652e636f6d301e170d3133303831363030353731305a" + "170d3333303831353030353731305a308197310b30090603550406130255" + "533113301106035504080c0a57617368696e67746f6e3111300f06035504" + "070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049" + "6e633111300f060355040b0c085769646576696e65311530130603550403" + "0c0c5465737420526f6f742043413121301f06092a864886f70d01090116" + "1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648" + "86f70d01010105000382010f003082010a0282010100c6eee629d99f7736" + "2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c" + "e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319" + "0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3" + "f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93" + "52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7" + "e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f" + "d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20" + "79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7" + "3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30" + "ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680" + "144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405" + "30030101ff300d06092a864886f70d01010505000382010100779e9b98d3" + "ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee" + "79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124" + "ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436" + "026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140" + "ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053" + "0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258" + "e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82" + "d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4" + "c270d87191c13aefdd177b"; + +const char kProdRootCaDerCert[] = + "30820408308202f0a003020102020101300d06092a864886f70d01010505" + "00307d311830160603550403130f5072697661637920434120526f6f7431" + "123010060355040b13094368726f6d65204f5331133011060355040a130a" + "476f6f676c6520496e63311630140603550407130d4d6f756e7461696e20" + "56696577311330110603550408130a43616c69666f726e6961310b300906" + "0355040613025553301e170d3133303231383130313334325a170d333330" + "3231333130313334325a307d311830160603550403130f50726976616379" + "20434120526f6f7431123010060355040b13094368726f6d65204f533113" + "3011060355040a130a476f6f676c6520496e63311630140603550407130d" + "4d6f756e7461696e2056696577311330110603550408130a43616c69666f" + "726e6961310b300906035504061302555330820122300d06092a864886f7" + "0d01010105000382010f003082010a0282010100e10ea6819d3d066b421d" + "d7612de3eef9599f5d9a2a24bfd09caab543511cf22f615e29f989425a65" + "7396bf33603747719cfb0b4240cd682c7c558fec0176b4793be440752246" + "83648f5b12d02a838a2a8e55a4b645ed0a4a52b19252a23d34bf64a17ac7" + "11fe93a889086d943211b17d670f96442c9f367d38026000da79664e600e" + "e9259348f4fd74108e973d561e624e9f5eda77a085a6eb15fadb2cc7787c" + "7f30ef3b196f2a416a76fa9eb30d65753f5039d97bea70e82431d2962396" + "a34864f33b74d60707fea794c03c82e547abc2407fa7bad67bd09cdab49b" + "26e68754994d12a3845dbeceffe18de0d51fc6fa78676d89ea1e0fcff931" + "59bfb809519b0203010001a3819230818f30290603551d0e042204204b1d" + "148aa5380938812ed6a763f5dc2c318610d5fa9604d609cb2e0d8cec3289" + "302b0603551d230424302280204b1d148aa5380938812ed6a763f5dc2c31" + "8610d5fa9604d609cb2e0d8cec3289300e0603551d0f0101ff0404030201" + "06300f0603551d130101ff040530030101ff30140603551d200101ff040a" + "300830060604551d2000300d06092a864886f70d01010505000382010100" + "c40d84bc8d609b1b68b3caa7e841021838d7e392557d40debab3e0685e72" + "80541092dc913b0aa6150228d8fe5ab08cceefbac56952fa00ba614294d1" + "ba4fa170c86b27f9bf58666c46940f740c4be2795501b25e40b9702af07c" + "884926bd8beed036c503e5e42a223ff36271404ca4360a93dec92a02fd8d" + "ae8f756fc68aaa647e2159f0a7a95d1446e92362bd512f59daec02c5d152" + "c301b9807db998ba70c616364762a0a497aaa92eb7d92f3635169d3f74c6" + "40c738941759a8ab43677b80329d015bdcf8922b779a80f85f1e4a677659" + "c60de80152e8c526a7de46cac143a75af58f0806de81e15c97f616e1bffa" + "1c1c6b0d2438543bdfb2a21bd9bc7ae4"; + +const char kServiceIdFieldName[] = "OU"; +const char kDeviceModeFieldName[] = "O"; +const char kExpectedDeviceMode[] = "Chrome Device Content Protection"; + +RemoteAttestationVerifier& RemoteAttestationVerifier::get() { + static RemoteAttestationVerifier instance; + return instance; +} + +void RemoteAttestationVerifier::EnableTestCertificates(bool enable) { + absl::WriterMutexLock lock(&ca_mutex_); + enable_test_certificates_ = enable; + ca_.reset(); +} + +util::Status RemoteAttestationVerifier::VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + std::string* remote_attestation_cert_sn) { + DCHECK(remote_attestation_cert_sn); + + // Sanity check RemoteAttestation. + if (!remote_attestation.has_certificate()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-certificate-missing")); + } + if (!remote_attestation.has_salt()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-salt-missing")); + } + if (!remote_attestation.has_signature()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-signature-missing")); + } + // Decrypt ClientIdentification containing remote attestation certificate. + // A service cert would be looked up first, then that cert will be used + // to decrypt the ClientIdentification. + ClientIdentification client_id; + util::Status status = DrmServiceCertificate::DecryptClientIdentification( + remote_attestation.certificate(), &client_id); + if (!status.ok()) return status; + + if (client_id.type() != + ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) { + return (util::Status(error_space, INVALID_MESSAGE, + std::string("remote-attestation-invalid-client-id-type (") + + absl::StrCat(client_id.type()) + ")")); + } + return VerifyRemoteAttestation(message, remote_attestation, client_id, + remote_attestation_cert_sn); +} + +util::Status RemoteAttestationVerifier::VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + const std::string& privacy_key) { + // Sanity check RemoteAttestation. + if (!remote_attestation.has_certificate()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-certificate-missing")); + } + if (!remote_attestation.has_salt()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-salt-missing")); + } + if (!remote_attestation.has_signature()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-signature-missing")); + } + // Decrypt ClientIdentification containing remote attestation certificate, + // directly using an explicitly provided key |privacy_key|. + ClientIdentification client_id; + util::Status status = DecryptEncryptedClientIdentification( + remote_attestation.certificate(), privacy_key, &client_id); + if (!status.ok()) return status; + + if (client_id.type() != + ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) { + return (util::Status(error_space, INVALID_MESSAGE, + std::string("remote-attestation-invalid-client-id-type (") + + absl::StrCat(client_id.type()) + ")")); + } + std::string remote_attestation_cert_sn; + return VerifyRemoteAttestation(message, remote_attestation, client_id, + &remote_attestation_cert_sn); +} + +util::Status RemoteAttestationVerifier::VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) { + if (!client_id.has_token()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-token-missing")); + } + // Load and verify the certificate chain. + std::unique_ptr cert_chain(new X509CertChain); + util::Status status = cert_chain->LoadPem(client_id.token()); + if (!status.ok()) return status; + + if (cert_chain->GetNumCerts() < 1) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-empty-certificate-chain")); + } + std::string device_mode_string = + cert_chain->GetCert(0)->GetSubjectNameField(kDeviceModeFieldName); + if (device_mode_string != kExpectedDeviceMode) { + return (util::Status(error_space, REMOTE_ATTESTATION_FAILED, + std::string("remote-attestation-device-not-verified (") + + device_mode_string + " / " + kDeviceModeFieldName + + ")")); + } + ca_mutex_.ReaderLock(); + if (ca_ == NULL) { + ca_mutex_.ReaderUnlock(); + status = LoadCa(); + if (!status.ok()) return status; + ca_mutex_.ReaderLock(); + } + status = ca_->VerifyCertChain(*cert_chain); + ca_mutex_.ReaderUnlock(); + if (!status.ok()) { + return (util::Status( + error_space, REMOTE_ATTESTATION_FAILED, + std::string("remote-attestation-cert-chain-validation-failed: ") + + status.error_message())); + } + // Verify the remote attestation signature. + std::unique_ptr leaf_key; + std::string message_with_salt = message + remote_attestation.salt(); + for (size_t idx = 0; idx < cert_chain->GetNumCerts(); ++idx) { + if (!cert_chain->GetCert(idx)->IsCaCertificate()) { + leaf_key = cert_chain->GetCert(idx)->GetRsaPublicKey(); + break; + } + } + if (!leaf_key) { + return util::Status(error_space, REMOTE_ATTESTATION_FAILED, + "remote-attestation-cert-chain-no-leaf"); + } + + if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt, + remote_attestation.signature())) { + return (util::Status(error_space, REMOTE_ATTESTATION_FAILED, + "remote-attestation-signature-verification-failed: ")); + } + + *remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber(); + return util::OkStatus(); +} + +util::Status RemoteAttestationVerifier::LoadCa() { + absl::WriterMutexLock lock(&ca_mutex_); + std::unique_ptr ca_cert(new X509Cert); + util::Status status = ca_cert->LoadDer(absl::HexStringToBytes( + enable_test_certificates_ ? kTestRootCaDerCert : kProdRootCaDerCert)); + if (!status.ok()) { + return status; + } + ca_.reset(new X509CA(ca_cert.release())); + return util::OkStatus(); +} + +} // namespace widevine diff --git a/common/remote_attestation_verifier.h b/common/remote_attestation_verifier.h new file mode 100644 index 0000000..4ea7d2c --- /dev/null +++ b/common/remote_attestation_verifier.h @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Functionality used to verifier ChromeOS remote attestation. + +#ifndef COMMON_REMOTE_ATTESTATION_VERIFIER_H_ +#define COMMON_REMOTE_ATTESTATION_VERIFIER_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "util/status.h" +#include "common/x509_cert.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/remote_attestation.pb.h" + +namespace widevine { + +// Singleton class used to do remote attestation. Access singleton instance via +// the get() method. +// TODO(user): This class is tested as part of the Session unit tests, but +// finer unit tests should be implemented for the failure cases. +class RemoteAttestationVerifier { + public: + RemoteAttestationVerifier() : enable_test_certificates_(false) {} + virtual ~RemoteAttestationVerifier() {} + + // Singleton accessor. + static RemoteAttestationVerifier& get(); + + // Call to use the test (non-production) remote attestation root certificate. + // This method is thread-safe. + void EnableTestCertificates(bool enable); + + // Call to verify a RemoteAttestation challenge response, used in licensing + // protocol. + // |message| is the challenge message, + // |remote_attestation| is the remote attestation response to verify, + // |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 + // certificate. + // This method is thread-safe. + util::Status VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + std::string* remote_attestation_cert_sn); + + // Call to verify a RemoteAttestation challenge response, used in certificate + // provisioning protocol. + // |message| is the challenge message, + // |remote_attestation| is the remote attestation response to verify, + // |privacy_key| is used to decrypt the EncryptedClientIdentification within + // the |remote_attestation| message. + // This method is thread-safe. + util::Status VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + const std::string& privacy_key); + + private: + // Common subroutine to perform the verification. + // |message| is the challenge message, + // |remote_attestation| is the remote attestation response to verify, + // |client_id| is the decrypted client identification carrying the token, + // |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 + // certificate. + util::Status VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + const ClientIdentification& client_id, + std::string* remote_attestation_cert_sn); + + util::Status LoadCa(); + + bool enable_test_certificates_; + absl::Mutex ca_mutex_; + std::unique_ptr ca_ GUARDED_BY(ca_mutex_); + + DISALLOW_COPY_AND_ASSIGN(RemoteAttestationVerifier); +}; + +} // namespace widevine + +#endif // COMMON_REMOTE_ATTESTATION_VERIFIER_H_ diff --git a/common/rsa_key.cc b/common/rsa_key.cc new file mode 100644 index 0000000..3f17744 --- /dev/null +++ b/common/rsa_key.cc @@ -0,0 +1,313 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// Definition of classes representing RSA private and public keys used +// for message signing, signature verification, encryption and decryption. +// +// RSA signature details: +// Algorithm: RSASSA-PSS +// Hash algorithm: SHA1 +// Mask generation function: mgf1SHA1 +// Salt length: 20 bytes +// Trailer field: 0xbc +// +// RSA encryption details: +// Algorithm: RSA-OAEP +// Mask generation function: mgf1SHA1 +// Label (encoding paramter): empty std::string + +#include "common/rsa_key.h" + +#include "glog/logging.h" +#include "openssl/bn.h" +#include "openssl/err.h" +#include "openssl/evp.h" +#include "openssl/rsa.h" +#include "openssl/sha.h" +#include "common/rsa_util.h" +#include "common/sha_util.h" + +static const int kPssSaltLength = 20; + +namespace { + +// Check if two RSA keys match. If matches, they are either a public-private key +// pair or the same public key or the same private key. +bool RsaKeyMatch(const RSA* key1, const RSA* key2) { + if (!key1 || !key2) return false; + return BN_cmp(key1->n, key2->n) == 0; +} + +std::string OpenSSLErrorString(uint32_t error) { + char buf[ERR_ERROR_STRING_BUF_LEN]; + ERR_error_string_n(error, buf, sizeof(buf)); + return buf; +} + +} // namespace + +namespace widevine { + +RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); } + +RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key) + : key_(RSAPrivateKey_dup(rsa_key.key_)) { + CHECK(key_ != nullptr); +} + +RsaPrivateKey::~RsaPrivateKey() { RSA_free(key_); } + +RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) { + RSA* key; + if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key)) return nullptr; + if (RSA_check_key(key) != 1) { + LOG(ERROR) << "Invalid private RSA key: " + << OpenSSLErrorString(ERR_get_error()); + RSA_free(key); + } + return new RsaPrivateKey(key); +} + +bool RsaPrivateKey::Decrypt(const std::string& encrypted_message, + std::string* decrypted_message) const { + DCHECK(decrypted_message); + + size_t rsa_size = RSA_size(key_); + if (encrypted_message.size() != rsa_size) { + LOG(ERROR) << "Encrypted RSA message has the wrong size (expected " + << rsa_size << ", actual " << encrypted_message.size() << ")"; + return false; + } + decrypted_message->assign(rsa_size, 0); + int decrypted_size = RSA_private_decrypt( + rsa_size, + const_cast( + reinterpret_cast(encrypted_message.data())), + reinterpret_cast(&(*decrypted_message)[0]), key_, + RSA_PKCS1_OAEP_PADDING); + if (decrypted_size == -1) { + LOG(ERROR) << "RSA private decrypt failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + decrypted_message->resize(decrypted_size); + return true; +} + +bool RsaPrivateKey::GenerateSignature(const std::string& message, + std::string* signature) const { + DCHECK(signature); + + if (message.empty()) { + LOG(ERROR) << "Message to be signed is empty"; + return false; + } + // Hash the message using SHA1. + std::string message_digest = Sha1_Hash(message); + + // Add PSS padding. + size_t rsa_size = RSA_size(key_); + std::string padded_digest(rsa_size, 0); + if (!RSA_padding_add_PKCS1_PSS_mgf1( + key_, reinterpret_cast(&padded_digest[0]), + reinterpret_cast(&message_digest[0]), EVP_sha1(), + EVP_sha1(), kPssSaltLength)) { + LOG(ERROR) << "RSA padding failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + // Encrypt PSS padded digest. + signature->assign(rsa_size, 0); + if (RSA_private_encrypt(padded_digest.size(), + reinterpret_cast(&padded_digest[0]), + reinterpret_cast(&(*signature)[0]), + key_, RSA_NO_PADDING) != + static_cast(signature->size())) { + LOG(ERROR) << "RSA private encrypt failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + return true; +} + +bool RsaPrivateKey::GenerateSignatureSha256Pkcs7(const std::string& message, + std::string* signature) const { + DCHECK(signature); + if (message.empty()) { + LOG(ERROR) << "Empty signature verification message"; + return false; + } + + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast(message.data()), message.size(), + digest); + unsigned int sig_len = RSA_size(key_); + signature->resize(sig_len); + return RSA_sign(NID_sha256, digest, sizeof(digest), + reinterpret_cast(&(*signature)[0]), &sig_len, + key_) == 1; +} + +bool RsaPrivateKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const { + return RsaKeyMatch(key(), private_key.key()); +} + +bool RsaPrivateKey::MatchesPublicKey(const RsaPublicKey& public_key) const { + return RsaKeyMatch(key(), public_key.key()); +} + +uint32_t RsaPrivateKey::KeySize() const { return RSA_size(key_); } + +RsaPublicKey::RsaPublicKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); } + +RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key) + : key_(RSAPublicKey_dup(rsa_key.key_)) { + CHECK(key_ != nullptr); +} + +RsaPublicKey::~RsaPublicKey() { RSA_free(key_); } + +RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) { + RSA* key; + if (!rsa_util::DeserializeRsaPublicKey(serialized_key, &key)) return nullptr; + if (RSA_size(key) == 0) { + LOG(ERROR) << "Invalid public RSA key: " + << OpenSSLErrorString(ERR_get_error()); + RSA_free(key); + } + return new RsaPublicKey(key); +} + +bool RsaPublicKey::Encrypt(const std::string& clear_message, + std::string* encrypted_message) const { + DCHECK(encrypted_message); + + if (clear_message.empty()) { + LOG(ERROR) << "Message to be encrypted is empty"; + return false; + } + size_t rsa_size = RSA_size(key_); + encrypted_message->assign(rsa_size, 0); + if (RSA_public_encrypt( + clear_message.size(), + const_cast( + reinterpret_cast(clear_message.data())), + reinterpret_cast(&(*encrypted_message)[0]), key_, + RSA_PKCS1_OAEP_PADDING) != static_cast(rsa_size)) { + LOG(ERROR) << "RSA public encrypt failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + return true; +} + +bool RsaPublicKey::VerifySignature(const std::string& message, + const std::string& signature) const { + if (message.empty()) { + LOG(ERROR) << "Signed message is empty"; + return false; + } + size_t rsa_size = RSA_size(key_); + if (signature.size() != rsa_size) { + LOG(ERROR) << "Message signature is of the wrong size (expected " + << rsa_size << ", actual " << signature.size() << ")"; + return false; + } + // Decrypt the signature. + std::string padded_digest(signature.size(), 0); + if (RSA_public_decrypt( + signature.size(), + const_cast( + reinterpret_cast(signature.data())), + reinterpret_cast(&padded_digest[0]), key_, + RSA_NO_PADDING) != static_cast(rsa_size)) { + LOG(ERROR) << "RSA public decrypt failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + // Hash the message using SHA1. + std::string message_digest = Sha1_Hash(message); + + // Verify PSS padding. + if (RSA_verify_PKCS1_PSS_mgf1( + key_, reinterpret_cast(&message_digest[0]), + EVP_sha1(), EVP_sha1(), + reinterpret_cast(&padded_digest[0]), + kPssSaltLength) == 0) { + LOG(ERROR) << "RSA Verify PSS padding failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + return true; +} + +bool RsaPublicKey::VerifySignatureSha256Pkcs7(const std::string& message, + const std::string& signature) const { + if (message.empty()) { + LOG(ERROR) << "Empty signature verification message"; + return false; + } + if (signature.empty()) { + LOG(ERROR) << "Empty signature"; + return false; + } + + if (signature.size() != RSA_size(key_)) { + LOG(ERROR) << "RSA signature has the wrong size"; + return false; + } + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast(message.data()), message.size(), + digest); + return RSA_verify(NID_sha256, digest, sizeof(digest), + reinterpret_cast(signature.data()), + signature.size(), key_) == 1; +} + +bool RsaPublicKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const { + return RsaKeyMatch(key(), private_key.key()); +} + +bool RsaPublicKey::MatchesPublicKey(const RsaPublicKey& public_key) const { + return RsaKeyMatch(key(), public_key.key()); +} + +uint32_t RsaPublicKey::KeySize() const { return RSA_size(key_); } + +RsaKeyFactory::RsaKeyFactory() {} + +RsaKeyFactory::~RsaKeyFactory() {} + +std::unique_ptr RsaKeyFactory::CreateFromPkcs1PrivateKey( + const std::string& private_key) { + return std::unique_ptr(RsaPrivateKey::Create(private_key)); +} + +std::unique_ptr RsaKeyFactory::CreateFromPkcs8PrivateKey( + const std::string& private_key, const std::string& private_key_passphrase) { + std::string pkcs1_key; + const bool result = + private_key_passphrase.empty() + ? rsa_util::PrivateKeyInfoToRsaPrivateKey(private_key, &pkcs1_key) + : rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey( + private_key, private_key_passphrase, &pkcs1_key); + if (!result) { + LOG(WARNING) << "Failed to get pkcs1_key."; + return std::unique_ptr(); + } + return std::unique_ptr(RsaPrivateKey::Create(pkcs1_key)); +} + +std::unique_ptr RsaKeyFactory::CreateFromPkcs1PublicKey( + const std::string& public_key) { + return std::unique_ptr(RsaPublicKey::Create(public_key)); +} + +} // namespace widevine diff --git a/common/rsa_key.h b/common/rsa_key.h new file mode 100644 index 0000000..30913a8 --- /dev/null +++ b/common/rsa_key.h @@ -0,0 +1,150 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// Declaration of classes representing RSA private and public keys used +// for message signing, signature verification, encryption and decryption. + +#ifndef COMMON_RSA_KEY_H_ +#define COMMON_RSA_KEY_H_ + +#include +#include + +#include +#include "base/macros.h" +#include "openssl/rsa.h" + +namespace widevine { + +class RsaPublicKey; + +class RsaPrivateKey { + public: + explicit RsaPrivateKey(RSA* key); + RsaPrivateKey(const RsaPrivateKey&); + virtual ~RsaPrivateKey(); + + // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. + // Returns NULL on failure. + static RsaPrivateKey* Create(const std::string& serialized_key); + + // Decrypt a message using RSA-OAEP. Caller retains ownership of all + // parameters. Returns true if successful, false otherwise. + virtual bool Decrypt(const std::string& encrypted_message, + std::string* decrypted_message) const; + + // Generate RSSASSA-PSS signature. Caller retains ownership of all parameters. + // Returns true if successful, false otherwise. + virtual bool GenerateSignature(const std::string& message, + std::string* signature) const; + + // Generate SHA256 digest, PKCS#7 padded signature. Caller retains ownership + // of all parameters. Returns true if successful, false otherwise. + virtual bool GenerateSignatureSha256Pkcs7(const std::string& message, + std::string* signature) const; + + // Return true if the underlying key matches with |private_key|. + virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const; + + // Return true if the underlying key is a public-private key pair with + // |public_key|. + virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const; + + // Returns the RSA key size (modulus) in bytes. + virtual uint32_t KeySize() const; + + const RSA* key() const { return key_; } + + private: + RSA* key_; + +// SWIG appears to think this declaration is a syntax error. Excluding it for +// python SWIG wrapping. +#ifndef SWIG + // Disallow assignment operator. + RsaPrivateKey& operator=(const RsaPrivateKey&) = delete; +#endif // SWIG +}; + +class RsaPublicKey { + public: + explicit RsaPublicKey(RSA* key); + RsaPublicKey(const RsaPublicKey&); + virtual ~RsaPublicKey(); + + // Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey. + // Returns NULL on failure. + static RsaPublicKey* Create(const std::string& serialized_key); + + // Encrypt a message using RSA-OAEP. Caller retains ownership of all + // parameters. Returns true if successful, false otherwise. + virtual bool Encrypt(const std::string& clear_message, + std::string* encrypted_message) const; + + // Verify RSSASSA-PSS signature. Caller retains ownership of all parameters. + // Returns true if validation succeeds, false otherwise. + virtual bool VerifySignature(const std::string& message, + const std::string& signature) const; + + // Verify a signature. This method takes two parameters: |message| which is a + // std::string containing the data which was signed, and |signature| which is a + // std::string containing the message SHA256 digest signature with PKCS#7 + // padding. Returns true if verification succeeds, false otherwise. + virtual bool VerifySignatureSha256Pkcs7(const std::string& message, + const std::string& signature) const; + + // Return true if the underlying key is a public-private key pair with + // |private_key|. + virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const; + + // Return true if the underlying key matches with |public_key|. + virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const; + + // Returns the RSA key size (modulus) in bytes. + virtual uint32_t KeySize() const; + + const RSA* key() const { return key_; } + + private: + RSA* key_; + +// SWIG appears to think this declaration is a syntax error. Excluding it for +// python SWIG wrapping. +#ifndef SWIG + // Disallow assignment operator. + RsaPublicKey& operator=(const RsaPublicKey&) = delete; +#endif // SWIG +}; + +class RsaKeyFactory { + public: + RsaKeyFactory(); + virtual ~RsaKeyFactory(); + + // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. + virtual std::unique_ptr CreateFromPkcs1PrivateKey( + const std::string& private_key); + + // Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or + // EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty). + virtual std::unique_ptr CreateFromPkcs8PrivateKey( + const std::string& private_key, const std::string& private_key_passphrase); + + // Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey. + virtual std::unique_ptr CreateFromPkcs1PublicKey( + const std::string& public_key); + + private: + DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory); +}; + +} // namespace widevine + +#endif // COMMON_RSA_KEY_H_ diff --git a/common/rsa_key_test.cc b/common/rsa_key_test.cc new file mode 100644 index 0000000..f4b716a --- /dev/null +++ b/common/rsa_key_test.cc @@ -0,0 +1,255 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// Unit test for rsa_key RSA encryption and signing. + +#include + +#include "testing/gunit.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" + +namespace widevine { + +static const char kTestMessage[] = + "A fool thinks himself to be wise, but a wise man knows himself to be a " + "fool."; + +class RsaKeyTest : public ::testing::Test { + protected: + void TestEncryption(std::unique_ptr private_key, + std::unique_ptr public_key); + + void TestSigning(std::unique_ptr private_key, + std::unique_ptr public_key); + + void TestSigningSha256Pkcs7(std::unique_ptr private_key, + std::unique_ptr public_key); + + RsaTestKeys test_keys_; + RsaKeyFactory factory_; +}; + +TEST_F(RsaKeyTest, CopyConstructor) { + std::unique_ptr private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + std::unique_ptr public_key( + RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); + + std::unique_ptr private_key_copy( + new RsaPrivateKey(*private_key)); + std::unique_ptr public_key_copy( + new RsaPublicKey(*public_key)); + + EXPECT_TRUE(public_key_copy->MatchesPublicKey(*public_key)); + EXPECT_TRUE(public_key_copy->MatchesPrivateKey(*private_key)); + + EXPECT_TRUE(private_key_copy->MatchesPublicKey(*public_key)); + EXPECT_TRUE(private_key_copy->MatchesPrivateKey(*private_key)); +} + +void RsaKeyTest::TestEncryption(std::unique_ptr private_key, + std::unique_ptr public_key) { + ASSERT_TRUE(private_key); + ASSERT_TRUE(public_key); + std::string encrypted_message; + EXPECT_TRUE(public_key->Encrypt(kTestMessage, &encrypted_message)); + std::string decrypted_message; + EXPECT_TRUE(private_key->Decrypt(encrypted_message, &decrypted_message)); + EXPECT_EQ(kTestMessage, decrypted_message); + // Add a byte to the encrypted message. + std::string bad_enc_message(encrypted_message); + bad_enc_message += '\0'; + EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message)); + // Remove a byte from the encrypted message. + bad_enc_message = encrypted_message.substr(0, encrypted_message.size() - 1); + EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message)); + // Change a byte in the encrypted message. + bad_enc_message = encrypted_message; + bad_enc_message[128] ^= 0x55; + EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message)); +} + +void RsaKeyTest::TestSigning(std::unique_ptr private_key, + std::unique_ptr public_key) { + ASSERT_TRUE(private_key); + ASSERT_TRUE(public_key); + std::string signature; + EXPECT_TRUE(private_key->GenerateSignature(kTestMessage, &signature)); + EXPECT_TRUE(public_key->VerifySignature(kTestMessage, signature)); + // Add a byte to the signature. + std::string bad_signature(signature); + bad_signature += '\0'; + EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature)); + // Remove a byte from the signature. + bad_signature = signature.substr(0, signature.size() - 1); + EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature)); + // Change a byte in the signature. + bad_signature = signature; + bad_signature[32] ^= 0x55; + EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature)); +} + +void RsaKeyTest::TestSigningSha256Pkcs7( + std::unique_ptr private_key, + std::unique_ptr public_key) { + ASSERT_TRUE(private_key); + ASSERT_TRUE(public_key); + std::string signature; + EXPECT_TRUE( + private_key->GenerateSignatureSha256Pkcs7(kTestMessage, &signature)); + EXPECT_TRUE(public_key->VerifySignatureSha256Pkcs7(kTestMessage, signature)); + // Add a byte to the signature. + std::string bad_signature(signature); + bad_signature += '\0'; + EXPECT_FALSE( + public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature)); + // Remove a byte from the signature. + bad_signature = signature.substr(0, signature.size() - 1); + EXPECT_FALSE( + public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature)); + // Change a byte in the signature. + bad_signature = signature; + bad_signature[32] ^= 0x55; + EXPECT_FALSE( + public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature)); +} + +TEST_F(RsaKeyTest, BadKey) { + std::unique_ptr private_key( + RsaPrivateKey::Create("bad_private_key")); + EXPECT_TRUE(!private_key); + std::unique_ptr public_key( + RsaPublicKey::Create("bad_public_key")); + EXPECT_TRUE(!public_key); +} + +TEST_F(RsaKeyTest, EncryptAndDecrypt_3072) { + const std::string& private_key = test_keys_.private_test_key_1_3072_bits(); + const std::string& public_key = test_keys_.public_test_key_1_3072_bits(); + TestEncryption( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + TestEncryption(factory_.CreateFromPkcs1PrivateKey(private_key), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, EncryptAndDecrypt_2048) { + const std::string& private_key = test_keys_.private_test_key_2_2048_bits(); + const std::string& public_key = test_keys_.public_test_key_2_2048_bits(); + TestEncryption( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + + std::string pkcs8_key; + std::string passphrase("passphrase"); + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + private_key, passphrase, &pkcs8_key)); + TestEncryption(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase), + factory_.CreateFromPkcs1PublicKey(public_key)); + + ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key)); + TestEncryption(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerify_3072) { + const std::string& private_key = test_keys_.private_test_key_1_3072_bits(); + const std::string& public_key = test_keys_.public_test_key_1_3072_bits(); + TestSigning( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + TestSigning(factory_.CreateFromPkcs1PrivateKey(private_key), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerify_2048) { + const std::string& private_key = test_keys_.private_test_key_2_2048_bits(); + const std::string& public_key = test_keys_.public_test_key_2_2048_bits(); + TestSigning( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + + std::string pkcs8_key; + std::string passphrase("passphrase"); + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + private_key, passphrase, &pkcs8_key)); + TestSigning(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase), + factory_.CreateFromPkcs1PublicKey(public_key)); + + ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key)); + TestSigning(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_3072) { + const std::string& private_key = test_keys_.private_test_key_1_3072_bits(); + const std::string& public_key = test_keys_.public_test_key_1_3072_bits(); + TestSigningSha256Pkcs7( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + TestSigningSha256Pkcs7(factory_.CreateFromPkcs1PrivateKey(private_key), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_2048) { + const std::string& private_key = test_keys_.private_test_key_2_2048_bits(); + const std::string& public_key = test_keys_.public_test_key_2_2048_bits(); + TestSigningSha256Pkcs7( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + + std::string pkcs8_key; + std::string passphrase("passphrase"); + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + private_key, passphrase, &pkcs8_key)); + TestSigningSha256Pkcs7( + factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase), + factory_.CreateFromPkcs1PublicKey(public_key)); + + ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key)); + TestSigningSha256Pkcs7( + factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, KeySize) { + std::unique_ptr private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + std::unique_ptr public_key( + RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); + + EXPECT_EQ(256, private_key->KeySize()); + EXPECT_EQ(256, public_key->KeySize()); +} + +TEST_F(RsaKeyTest, RsaKeyMatch) { + std::unique_ptr private_key2( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + std::unique_ptr private_key3( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + std::unique_ptr public_key2( + RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); + std::unique_ptr public_key3( + RsaPublicKey::Create(test_keys_.public_test_key_3_2048_bits())); + + EXPECT_TRUE(public_key2->MatchesPublicKey(*public_key2)); + EXPECT_FALSE(public_key2->MatchesPublicKey(*public_key3)); + EXPECT_TRUE(public_key2->MatchesPrivateKey(*private_key2)); + EXPECT_FALSE(public_key2->MatchesPrivateKey(*private_key3)); + + EXPECT_TRUE(private_key2->MatchesPublicKey(*public_key2)); + EXPECT_FALSE(private_key2->MatchesPublicKey(*public_key3)); + EXPECT_TRUE(private_key2->MatchesPrivateKey(*private_key2)); + EXPECT_FALSE(private_key2->MatchesPrivateKey(*private_key3)); +} + +} // namespace widevine diff --git a/common/rsa_test_keys.cc b/common/rsa_test_keys.cc new file mode 100644 index 0000000..315ae45 --- /dev/null +++ b/common/rsa_test_keys.cc @@ -0,0 +1,786 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// RSA keys generated using fake_prng for purposes of testing. + +#include "common/rsa_test_keys.h" + +namespace widevine { + +static const unsigned char kTestRsaPrivateKey1_3072[] = { + 0x30, 0x82, 0x06, 0xe3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x81, 0x00, + 0xa5, 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, + 0x78, 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, + 0x0f, 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, + 0xca, 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, + 0x1b, 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, + 0x46, 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, + 0x01, 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, + 0x5c, 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, + 0x14, 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, + 0xcd, 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, + 0xb6, 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, + 0xde, 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, + 0x31, 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, + 0x3a, 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, + 0x90, 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, + 0x49, 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, + 0x5f, 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, + 0xda, 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, + 0x17, 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, + 0x30, 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, + 0xaf, 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, + 0x16, 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, + 0x42, 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, + 0xf9, 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, + 0xe4, 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, + 0x4a, 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, + 0x72, 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, + 0xde, 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, + 0x04, 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, + 0x1b, 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, + 0x33, 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, + 0x67, 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, + 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x80, 0x5a, 0x09, 0x3f, + 0x9e, 0x2e, 0x4d, 0x26, 0x50, 0x7b, 0x70, 0x21, 0xb0, 0x0c, 0x25, 0x21, + 0x1f, 0xd9, 0x89, 0x5a, 0xca, 0x35, 0x23, 0x0b, 0x58, 0xa9, 0x7d, 0xf6, + 0x19, 0xc4, 0x29, 0x87, 0xc7, 0xd4, 0x94, 0x85, 0xb4, 0x2c, 0xaf, 0x62, + 0xb1, 0xe8, 0x62, 0x5b, 0xda, 0xdb, 0x70, 0x40, 0x37, 0xb1, 0x4e, 0x0c, + 0xc8, 0x62, 0xee, 0xa2, 0xfc, 0x3c, 0xd2, 0x39, 0x90, 0x15, 0x2c, 0xba, + 0x20, 0x50, 0xb7, 0x82, 0x2a, 0xa0, 0x76, 0x83, 0x20, 0x7f, 0x56, 0x73, + 0x43, 0x8a, 0x9b, 0xa7, 0x6c, 0x63, 0xb6, 0xad, 0x56, 0xb2, 0x8a, 0xb2, + 0xbc, 0x8f, 0xe2, 0xef, 0x83, 0x9d, 0x98, 0x0b, 0xc7, 0x62, 0x0e, 0x51, + 0x6e, 0x57, 0x1d, 0x1b, 0x0e, 0x3a, 0xea, 0x3b, 0x76, 0x63, 0x35, 0xd0, + 0xd1, 0xcf, 0xbe, 0xad, 0xbb, 0x1d, 0xde, 0x0f, 0x05, 0x48, 0x55, 0x29, + 0xc1, 0xbc, 0x21, 0xc7, 0x87, 0xf2, 0x75, 0x12, 0x7d, 0x92, 0x9e, 0xbf, + 0xad, 0x04, 0x68, 0xc4, 0xc9, 0x9d, 0x35, 0xd6, 0xa8, 0x62, 0xc1, 0x69, + 0x6a, 0xb6, 0x41, 0xb7, 0x37, 0x66, 0xdf, 0xb2, 0xb9, 0x8c, 0x8b, 0x15, + 0x08, 0x4c, 0x3d, 0xf1, 0xed, 0x82, 0x0f, 0xe3, 0xd5, 0xff, 0x46, 0xbd, + 0xf7, 0x85, 0x43, 0xc0, 0x8b, 0xba, 0x47, 0xf1, 0x41, 0x57, 0xc3, 0x7f, + 0x8b, 0x0d, 0x48, 0xea, 0xc2, 0xed, 0xc0, 0x69, 0x84, 0xb6, 0x32, 0x08, + 0x49, 0x74, 0x14, 0x84, 0xa4, 0x1b, 0x48, 0x5b, 0xec, 0xd3, 0x0b, 0x12, + 0x2b, 0x4c, 0x9e, 0x5c, 0x01, 0x60, 0xad, 0xef, 0xcb, 0x2b, 0x56, 0x84, + 0x07, 0xfa, 0x62, 0xc6, 0x08, 0x92, 0x98, 0x70, 0xc9, 0x5b, 0x18, 0xc8, + 0xfa, 0x27, 0x0c, 0xe2, 0xbd, 0xfb, 0x3e, 0x43, 0xa5, 0xb7, 0x06, 0x2c, + 0x4e, 0xf1, 0x07, 0x5d, 0x8d, 0xdd, 0x53, 0xc5, 0x8c, 0x4a, 0xf2, 0x2f, + 0x8e, 0x80, 0x96, 0x16, 0xc0, 0xfc, 0xf9, 0x20, 0x4f, 0x35, 0xc7, 0x53, + 0x8b, 0x2d, 0x37, 0x43, 0x93, 0x3d, 0x74, 0x3f, 0x63, 0xf7, 0x0b, 0xbd, + 0x46, 0xe4, 0x51, 0x67, 0x33, 0x57, 0x15, 0xf5, 0x59, 0x27, 0x66, 0xe8, + 0xe2, 0x4b, 0xa3, 0x93, 0x03, 0x8a, 0x9c, 0x05, 0x13, 0xf2, 0xcb, 0xf7, + 0x9c, 0x68, 0xe7, 0x16, 0x4b, 0x8e, 0x59, 0x71, 0x2b, 0x73, 0x9b, 0xb9, + 0xae, 0x50, 0xfa, 0xd7, 0xd3, 0x34, 0x17, 0x1d, 0x62, 0x88, 0xbd, 0x8c, + 0xba, 0x5a, 0x6b, 0x6a, 0x5e, 0xb3, 0xa5, 0x80, 0xca, 0xbb, 0xb9, 0xb5, + 0xa8, 0x2e, 0xb1, 0x61, 0x6e, 0xd5, 0xd6, 0x62, 0x98, 0x4a, 0xb0, 0xb8, + 0x76, 0xa9, 0x19, 0x5c, 0xe2, 0xbe, 0xb3, 0x9b, 0x4a, 0x39, 0xf5, 0xe6, + 0xbb, 0x11, 0x6e, 0x13, 0x13, 0x38, 0xb8, 0x1f, 0x21, 0x19, 0xf5, 0xa7, + 0x76, 0x93, 0xb3, 0x56, 0xfa, 0xcc, 0x74, 0xbc, 0x19, 0x02, 0x81, 0xc1, + 0x00, 0xd1, 0xd1, 0x72, 0x57, 0xe5, 0xb0, 0x1c, 0x09, 0x05, 0xbb, 0x55, + 0x89, 0x3c, 0x4a, 0x81, 0x90, 0x9a, 0xf9, 0x32, 0x63, 0x41, 0xad, 0x6a, + 0x5f, 0x65, 0x94, 0x92, 0xcc, 0xf7, 0xc7, 0x53, 0x93, 0xa0, 0xf7, 0xbe, + 0x48, 0x82, 0x63, 0x31, 0x7b, 0xd0, 0x82, 0x09, 0xbb, 0x0a, 0xbc, 0x60, + 0xc9, 0x4d, 0x83, 0xe4, 0x5d, 0x50, 0xe6, 0x5f, 0x8b, 0x47, 0x07, 0xa3, + 0x3a, 0x36, 0x97, 0xaa, 0x21, 0x70, 0x7f, 0xd5, 0x6c, 0xb0, 0x56, 0xf5, + 0x5c, 0x48, 0x74, 0x2a, 0xdd, 0xfe, 0x94, 0x83, 0x05, 0xe0, 0x3d, 0x5d, + 0xdd, 0x5a, 0x05, 0xcb, 0x47, 0xd7, 0xf9, 0x89, 0x55, 0xaa, 0x0b, 0x21, + 0xc0, 0x71, 0x5d, 0xe1, 0x4c, 0x6a, 0x45, 0x86, 0x86, 0xf2, 0xb9, 0x38, + 0x6a, 0x56, 0x51, 0x0d, 0x7d, 0xac, 0x30, 0x31, 0xca, 0x2d, 0xaa, 0xaa, + 0xba, 0xcc, 0x12, 0x40, 0xc1, 0x0d, 0xa6, 0xc1, 0x7d, 0x22, 0xec, 0xb6, + 0x51, 0x45, 0xfe, 0x4e, 0xbb, 0x4a, 0xd2, 0xba, 0x9b, 0xa2, 0xcc, 0x28, + 0x2b, 0x01, 0x53, 0x53, 0xf3, 0xa9, 0x5a, 0x8f, 0xeb, 0xb7, 0xb8, 0x62, + 0x6b, 0x8a, 0x79, 0x24, 0xcc, 0x86, 0x34, 0x45, 0xe2, 0xad, 0x1d, 0xd0, + 0x4c, 0xc9, 0x77, 0x2a, 0xf9, 0x1a, 0xe8, 0x58, 0x78, 0x51, 0x8a, 0xea, + 0x3f, 0x90, 0x36, 0x46, 0x2a, 0xc0, 0x71, 0x41, 0x83, 0x2c, 0x48, 0xee, + 0xc5, 0x02, 0x81, 0xc1, 0x00, 0xc9, 0xc8, 0xce, 0xc4, 0x50, 0xb2, 0x26, + 0xcb, 0x35, 0x78, 0x55, 0x3c, 0xcc, 0xf0, 0x7e, 0xba, 0xad, 0xeb, 0x58, + 0xe9, 0xb5, 0x78, 0x2f, 0x43, 0x5f, 0x07, 0x47, 0x56, 0x05, 0x41, 0x38, + 0x71, 0xe1, 0x58, 0x62, 0xb1, 0x8e, 0xbc, 0xf9, 0x80, 0x04, 0x22, 0x39, + 0x22, 0x24, 0x28, 0x86, 0x9c, 0x00, 0x44, 0x5f, 0xc4, 0x97, 0xe6, 0x71, + 0x5f, 0x1f, 0x58, 0xea, 0x75, 0x18, 0x0c, 0x23, 0x63, 0x09, 0xc5, 0x98, + 0xc4, 0x6d, 0x23, 0xc2, 0x2c, 0x93, 0x6a, 0x26, 0xe4, 0x3d, 0x8d, 0xa1, + 0x39, 0x70, 0x34, 0x25, 0xcd, 0xbc, 0x82, 0x78, 0x2b, 0xf3, 0x7e, 0x81, + 0xb6, 0x5f, 0xc5, 0x69, 0xd0, 0x81, 0x69, 0x50, 0x2f, 0x17, 0x0c, 0x17, + 0x3c, 0x0b, 0x45, 0x38, 0xce, 0xe3, 0xbf, 0x8a, 0x50, 0x0a, 0x00, 0x74, + 0x7e, 0x7a, 0xd8, 0x55, 0x52, 0x6b, 0x82, 0xfb, 0x34, 0x15, 0x73, 0x6a, + 0xf4, 0x51, 0x9b, 0x9f, 0xa0, 0x45, 0xb9, 0x76, 0xe5, 0xd3, 0xd5, 0xf4, + 0xa9, 0xa4, 0xcd, 0x42, 0x2f, 0x29, 0x89, 0xec, 0x28, 0x5f, 0x03, 0x45, + 0x27, 0xaf, 0x8c, 0x39, 0x3e, 0x59, 0x9d, 0xaf, 0x27, 0x5d, 0x17, 0x53, + 0x17, 0xeb, 0x8d, 0x7f, 0x3d, 0xb8, 0x2a, 0x50, 0x1e, 0xb5, 0xc5, 0x04, + 0xab, 0x9c, 0xa7, 0xaa, 0x86, 0x41, 0xb9, 0x36, 0x29, 0x9e, 0xd2, 0xd8, + 0xde, 0x5f, 0xde, 0x80, 0xdb, 0x02, 0x81, 0xc0, 0x03, 0xf3, 0x5f, 0xa5, + 0xcc, 0x0b, 0x5e, 0xdb, 0xc4, 0xa1, 0xdc, 0x60, 0x73, 0x24, 0x2c, 0x00, + 0x5f, 0x0a, 0xa6, 0x2a, 0x3c, 0x48, 0x59, 0xa2, 0x66, 0x35, 0x3f, 0xf6, + 0x60, 0x0b, 0xfe, 0xc4, 0xde, 0xd9, 0x0b, 0x5a, 0x2e, 0x2a, 0x53, 0xfa, + 0x32, 0xd8, 0xdf, 0xfa, 0x07, 0x9f, 0xb8, 0x6a, 0xd1, 0xec, 0xd3, 0xd5, + 0xf5, 0xfa, 0x00, 0x7e, 0x8c, 0xdd, 0xd5, 0xf2, 0xf8, 0xa8, 0x2e, 0x69, + 0xe6, 0xc6, 0x61, 0x6c, 0x64, 0x7d, 0x9e, 0xad, 0x18, 0x28, 0x27, 0xce, + 0x7a, 0x46, 0xad, 0x98, 0xe4, 0xba, 0x03, 0x14, 0x71, 0xe7, 0x7e, 0x06, + 0x62, 0x48, 0xae, 0x8f, 0x50, 0x5e, 0x59, 0x4a, 0x58, 0x58, 0x1e, 0x2f, + 0xe4, 0x28, 0x5e, 0xfa, 0x17, 0x83, 0xe9, 0x4e, 0x07, 0x46, 0x0b, 0x6c, + 0xfc, 0x5b, 0x03, 0xf4, 0xfc, 0x9b, 0x24, 0x0f, 0xd4, 0x5b, 0xdb, 0xa0, + 0x46, 0xf3, 0x86, 0xdd, 0x26, 0x55, 0x32, 0xb1, 0xa1, 0x11, 0xc2, 0xc5, + 0xc0, 0x08, 0xeb, 0xbe, 0x96, 0x78, 0x25, 0xa1, 0x79, 0xaa, 0xe9, 0xff, + 0xc2, 0x86, 0x94, 0x03, 0x2a, 0x38, 0x6c, 0x91, 0xfd, 0xcf, 0x7e, 0x23, + 0xe3, 0xbb, 0x04, 0x3d, 0xda, 0x68, 0x9f, 0x4d, 0x72, 0xd5, 0xad, 0x97, + 0x77, 0x2c, 0x3c, 0xce, 0x37, 0x2a, 0xd8, 0x72, 0x4d, 0xf2, 0xd7, 0xab, + 0x62, 0x68, 0x3f, 0x85, 0x8a, 0xc5, 0xec, 0xc9, 0x02, 0x81, 0xc1, 0x00, + 0x92, 0x43, 0x0c, 0x1d, 0x20, 0xa1, 0x01, 0x9d, 0xaa, 0x54, 0x5e, 0xf4, + 0x83, 0x58, 0x8f, 0x83, 0xa1, 0x2d, 0x46, 0x75, 0xa1, 0x24, 0x4c, 0x9d, + 0xf8, 0xf3, 0xbd, 0xb1, 0x8c, 0x7d, 0x89, 0xfc, 0x81, 0xeb, 0x1f, 0x1e, + 0xb4, 0xe8, 0x25, 0xb1, 0xb5, 0x4d, 0x59, 0x3c, 0x76, 0x19, 0x29, 0xf9, + 0x49, 0xf8, 0x45, 0xb2, 0xaa, 0xa8, 0x4e, 0xe5, 0x34, 0x43, 0xaf, 0x2e, + 0xd1, 0x0f, 0x7b, 0x56, 0xfe, 0x6e, 0x4c, 0x1d, 0x95, 0x3e, 0xa6, 0x30, + 0xc9, 0x69, 0xd8, 0x66, 0xf8, 0x77, 0x00, 0xb6, 0x31, 0xae, 0x9a, 0xf8, + 0x55, 0xfb, 0xfc, 0x3f, 0x5f, 0x70, 0x03, 0x75, 0xbe, 0x55, 0xca, 0x2d, + 0x68, 0xa0, 0x7d, 0x8e, 0xa4, 0x96, 0x0f, 0x01, 0x66, 0xe9, 0xf6, 0x13, + 0x80, 0xe2, 0x05, 0xcf, 0x9e, 0x70, 0x56, 0x00, 0x97, 0xea, 0xd7, 0x6d, + 0xb6, 0xa0, 0x6a, 0x95, 0x86, 0x36, 0xf2, 0xff, 0xc5, 0x67, 0x98, 0x7d, + 0x04, 0x0d, 0x3b, 0x31, 0xbc, 0x2b, 0x09, 0xfd, 0x2d, 0x87, 0xda, 0xc1, + 0x74, 0xca, 0x94, 0x73, 0x6e, 0xeb, 0x5f, 0xe5, 0x34, 0x49, 0xdf, 0xf4, + 0x61, 0xe0, 0xfa, 0x64, 0xfe, 0x05, 0x3a, 0x25, 0xcc, 0x87, 0xf4, 0x03, + 0x38, 0xca, 0xf2, 0xe8, 0x4f, 0xb9, 0x4f, 0x79, 0x55, 0x43, 0xf3, 0x46, + 0xfd, 0xbc, 0xd2, 0x95, 0xb8, 0x99, 0xfc, 0xb8, 0xb3, 0xa5, 0x04, 0xa1, + 0x02, 0x81, 0xc0, 0x47, 0xc6, 0x9c, 0x18, 0x54, 0xe5, 0xbb, 0xf9, 0xf4, + 0x38, 0xd2, 0xc0, 0xd1, 0x1a, 0xcc, 0xdb, 0x06, 0x87, 0x75, 0x1f, 0x13, + 0xa2, 0x7f, 0x8b, 0x45, 0x54, 0xcb, 0x43, 0xf8, 0xbb, 0x94, 0xd6, 0x2e, + 0x56, 0x5c, 0x69, 0x6d, 0x83, 0xb5, 0x45, 0x46, 0x68, 0x5c, 0x76, 0x1e, + 0x6c, 0x0c, 0x53, 0x59, 0xcc, 0x19, 0xc7, 0x81, 0x62, 0x66, 0x92, 0x02, + 0x8f, 0xa6, 0xdb, 0x50, 0x1c, 0x67, 0xfc, 0x82, 0x56, 0x2b, 0x4b, 0x1f, + 0x97, 0x87, 0xc4, 0x7d, 0x20, 0xda, 0xd3, 0x3f, 0x28, 0xf9, 0x55, 0xfe, + 0x84, 0x50, 0xc5, 0x3b, 0xd4, 0xaf, 0xf5, 0x3d, 0x43, 0xce, 0xdc, 0x55, + 0x11, 0x87, 0xdb, 0x72, 0x66, 0xcc, 0x83, 0xc4, 0x8b, 0x20, 0xae, 0x59, + 0x4d, 0xeb, 0xac, 0xb5, 0x4a, 0xec, 0x66, 0x09, 0x37, 0x55, 0x14, 0x21, + 0x57, 0xff, 0x0a, 0xac, 0xda, 0xb1, 0xae, 0x31, 0xab, 0x41, 0x30, 0x65, + 0x02, 0x83, 0xd1, 0xdb, 0x65, 0xb7, 0x52, 0xa7, 0x21, 0x9f, 0x1f, 0x8f, + 0x69, 0x23, 0x3b, 0xb8, 0xf9, 0x6d, 0xe7, 0xc1, 0x53, 0x9f, 0x8f, 0x67, + 0xfc, 0x6e, 0x20, 0x18, 0x31, 0x89, 0xe7, 0xbb, 0xd4, 0xc1, 0x03, 0x67, + 0xd6, 0xa5, 0x76, 0xc9, 0xea, 0x97, 0x93, 0x02, 0xca, 0x44, 0x52, 0x55, + 0x0f, 0xed, 0x55, 0xb5, 0x49, 0xd6, 0x94, 0x59, 0xee, 0xcc, 0x1b, 0x5a, + 0x00, 0x3d, 0xcd}; + +static const unsigned char kTestRsaPublicKey1_3072[] = { + 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5, 0x62, 0x07, + 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, 0x76, 0xbe, + 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f, 0xe9, 0x61, + 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, 0xe4, 0xdd, + 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b, 0x83, 0xbe, + 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, 0xc2, 0x65, + 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01, 0xb5, 0x11, + 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, 0xb4, 0x7a, + 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14, 0xf1, 0x22, + 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, 0xa0, 0x0a, + 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6, 0x0e, 0x85, + 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, 0xf2, 0x59, + 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31, 0xa0, 0xee, + 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, 0xae, 0xb2, + 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90, 0x97, 0x2c, + 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, 0x94, 0x53, + 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f, 0x07, 0x53, + 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, 0xea, 0x1e, + 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17, 0xe8, 0xc9, + 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, 0xb7, 0xf5, + 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf, 0x39, 0xf3, + 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, 0x9c, 0xee, + 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42, 0x72, 0x85, + 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, 0xa6, 0x82, + 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4, 0x3a, 0x3b, + 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, 0xe9, 0x4c, + 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72, 0x71, 0x04, + 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, 0x8f, 0xe1, + 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04, 0xb5, 0xd7, + 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, 0x38, 0xff, + 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33, 0x61, 0x53, + 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, 0xd5, 0xf0, + 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02, 0x03, 0x01, + 0x00, 0x01}; + +static const unsigned char kTestRsaPrivateKey2_2048[] = { + 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4, + 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, + 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e, + 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, + 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7, + 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, + 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68, + 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, + 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, + 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, + 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e, + 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, + 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88, + 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, + 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8, + 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, + 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e, + 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, + 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29, + 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, + 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, + 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x00, 0x5e, 0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, 0x45, 0x0f, + 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, 0xd5, 0xde, 0x33, 0x63, 0xd8, 0xb8, + 0xac, 0x97, 0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, 0x3b, 0x5c, + 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, 0x46, 0xf5, 0xca, 0x2d, 0x8b, 0x3a, + 0x7e, 0xdc, 0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, 0x5e, 0x79, + 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, 0xc1, 0x6b, 0x78, 0x04, 0x4e, 0x8e, + 0x79, 0xf9, 0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, 0x68, 0x7b, + 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, 0xa7, 0x79, 0x5c, 0x7a, 0x81, 0xd1, + 0x71, 0xe7, 0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, 0xbe, 0x09, + 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, 0x0e, 0x97, 0x8d, 0x89, 0x6e, 0xf1, + 0xe8, 0x88, 0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, 0x25, 0x0b, + 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, 0xb6, 0xda, 0xc6, 0x24, 0x5a, 0xd0, + 0x37, 0x14, 0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, 0xde, 0x00, + 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, 0x68, 0x71, 0x17, 0x66, 0x12, 0x1a, + 0x87, 0x27, 0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, 0x6f, 0x35, + 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, 0x13, 0x86, 0x9c, 0x79, 0xd0, 0xb7, + 0xb6, 0x64, 0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, 0x1f, 0x51, + 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, 0x70, 0x98, 0x0f, 0xee, 0xa8, 0x96, + 0x07, 0x5f, 0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, 0xf6, 0x06, + 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, 0x96, 0xa9, 0x03, 0x17, 0xbb, 0x4e, + 0xc9, 0x21, 0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, 0xb2, 0x51, + 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, 0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c, + 0xbe, 0x6d, 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, 0x78, 0xcc, + 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85, + 0xab, 0x04, 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, 0x84, 0x68, + 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86, + 0x7b, 0xd0, 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, 0x3a, 0x83, + 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7, + 0x1e, 0x64, 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, 0x18, 0x6c, + 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a, + 0xcc, 0x48, 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, 0x3c, 0x2d, + 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, 0x26, 0x60, 0x48, 0x16, + 0xcb, 0xef, 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, 0x15, 0x43, + 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49, + 0xc2, 0x19, 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, 0xcb, 0x43, + 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17, + 0x68, 0x78, 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, 0xa7, 0x5b, + 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d, + 0xeb, 0x3f, 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, 0xc1, 0xee, + 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d, + 0x8e, 0x87, 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, 0x0d, 0x6c, + 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, 0xed, 0x83, 0x51, 0x67, + 0xa9, 0x55, 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, 0x53, 0xa8, + 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, 0x67, 0x9c, 0x32, 0x83, + 0x39, 0x57, 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, 0x0a, 0x2d, + 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b, + 0x1b, 0x43, 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, 0xeb, 0xdb, + 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25, + 0x92, 0xe4, 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, 0x37, 0x43, + 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d, + 0x45, 0xdc, 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, 0xf9, 0xca, + 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71, + 0x16, 0xd8, 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, 0xb1, 0x31, + 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, 0x41, 0x29, 0x40, 0x19, + 0x83, 0x35, 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, 0xcc, 0x3b, + 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, 0x63, 0x94, 0x49, 0x4c, + 0xad, 0x10, 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, 0x6a, 0xab, + 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec, + 0x5e, 0x61, 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, 0x5f, 0xdf, + 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22, + 0xfa, 0x34, 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, 0x77, 0xe6, + 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a, + 0x2d, 0x0c, 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, 0xd9, 0xbe, + 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96, + 0x76, 0xe8, 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, 0xac, 0x21, + 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6, + 0x69, 0x1d, 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, 0x28, 0x66, + 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9, + 0xe4, 0xc7, 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, 0x70, 0xcf, + 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6, + 0x2b, 0xad, 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, 0x50, 0xcb, + 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7, + 0xab, 0x30, 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, 0xa2, 0x0d, + 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae, + 0x84, 0x6b, 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, 0x72, 0x2c, + 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, + 0xdb, 0x03}; + +static const unsigned char kTestRsaPublicKey2_2048[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, + 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, + 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, + 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, + 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, + 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, + 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, + 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, + 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, + 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, + 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, + 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, + 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, + 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, + 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, + 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, + 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, + 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, + 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, + 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, + 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, + 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, + 0x05, 0x02, 0x03, 0x01, 0x00, 0x01}; + +static const unsigned char kTestRsaPrivateKey3_2048[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40, + 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83, 0xa7, + 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44, + 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a, 0x91, + 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08, + 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74, 0x10, + 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d, + 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47, 0x94, + 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d, + 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03, 0x68, + 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55, + 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c, 0xe8, + 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, + 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0, 0x35, + 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06, + 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a, 0x21, + 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae, + 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87, 0x3c, + 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84, + 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f, 0x87, + 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b, + 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x00, 0x43, 0x8f, 0x19, 0x83, 0xb1, 0x27, 0x4e, 0xee, 0x98, 0xba, 0xcb, + 0x54, 0xa0, 0x77, 0x11, 0x6d, 0xd4, 0x25, 0x31, 0x8c, 0xb0, 0x01, 0xcf, + 0xe6, 0x80, 0x83, 0x14, 0x40, 0x67, 0x39, 0x33, 0x67, 0x03, 0x1e, 0xa0, + 0x8b, 0xd1, 0x1d, 0xfd, 0x80, 0xa4, 0xb9, 0xe7, 0x57, 0x5e, 0xc8, 0x8e, + 0x79, 0x71, 0xd5, 0x6b, 0x09, 0xe9, 0x2b, 0x41, 0xa0, 0x33, 0x64, 0xc9, + 0x66, 0x33, 0xa1, 0xb1, 0x55, 0x07, 0x55, 0x98, 0x53, 0x10, 0xe6, 0xc0, + 0x39, 0x6d, 0x61, 0xd9, 0xe8, 0x16, 0x52, 0x28, 0xe4, 0x2b, 0xda, 0x27, + 0x01, 0xaf, 0x21, 0x4a, 0xe8, 0x55, 0x1d, 0x0b, 0xd1, 0x1c, 0xdc, 0xfd, + 0xb3, 0x0b, 0xa6, 0x5c, 0xcc, 0x6e, 0x77, 0xb8, 0xe0, 0xd1, 0x4e, 0x0a, + 0xd7, 0x7a, 0x5e, 0x18, 0xc3, 0xfb, 0xe9, 0xa1, 0x9c, 0xc3, 0x9c, 0xd4, + 0x4a, 0x7e, 0x70, 0x72, 0x11, 0x18, 0x24, 0x56, 0x24, 0xdf, 0xf8, 0xba, + 0xac, 0x5b, 0x54, 0xd3, 0xc4, 0x65, 0x69, 0xc8, 0x79, 0x94, 0x16, 0x88, + 0x9a, 0x68, 0x1c, 0xbc, 0xd4, 0xca, 0xec, 0x5e, 0x07, 0x4a, 0xc9, 0x54, + 0x7a, 0x4b, 0xdb, 0x19, 0x88, 0xf6, 0xbe, 0x50, 0x9d, 0x9e, 0x9d, 0x88, + 0x5b, 0x4a, 0x23, 0x86, 0x2b, 0xa9, 0xa6, 0x6c, 0x70, 0x7d, 0xe1, 0x11, + 0xba, 0xbf, 0x03, 0x2e, 0xf1, 0x46, 0x7e, 0x1b, 0xed, 0x06, 0x11, 0x57, + 0xad, 0x4a, 0xcb, 0xe5, 0xb1, 0x11, 0x05, 0x0a, 0x30, 0xb1, 0x73, 0x79, + 0xcd, 0x7a, 0x04, 0xcc, 0x70, 0xe9, 0x95, 0xe4, 0x27, 0xc2, 0xd5, 0x2d, + 0x92, 0x44, 0xdf, 0xb4, 0x94, 0xa8, 0x73, 0xa1, 0x4a, 0xc3, 0xcc, 0xc4, + 0x0e, 0x8d, 0xa1, 0x6a, 0xc2, 0xd8, 0x03, 0x7f, 0xfa, 0xa7, 0x76, 0x0d, + 0xad, 0x87, 0x88, 0xa0, 0x77, 0xaf, 0x3b, 0x23, 0xd1, 0x66, 0x0b, 0x31, + 0x2b, 0xaf, 0xef, 0xd5, 0x41, 0x02, 0x81, 0x81, 0x00, 0xdb, 0xc1, 0xe7, + 0xdd, 0xba, 0x3c, 0x1f, 0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb, 0xd2, 0x47, + 0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5, 0xda, 0xc2, 0x25, 0x64, 0x0a, 0x02, + 0xbc, 0x7d, 0x7f, 0x50, 0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d, 0x77, 0x6d, + 0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd, 0x18, 0xe9, 0x53, 0x0b, 0x5c, 0xa2, + 0x71, 0x14, 0x02, 0xfd, 0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c, 0x2a, 0x4e, + 0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26, 0xd0, 0xd8, 0x3d, 0xcf, 0x8c, 0x89, + 0x65, 0x6c, 0x35, 0x19, 0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49, 0xcd, 0x6d, + 0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9, 0xba, 0x43, 0xe5, 0xdd, 0x39, 0x3a, + 0x78, 0x8f, 0x07, 0xb8, 0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8, 0x07, 0x02, + 0x9b, 0x79, 0x26, 0x32, 0x22, 0x38, 0x91, 0x01, 0x90, 0x81, 0x29, 0x94, + 0xad, 0x77, 0xeb, 0x86, 0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x29, 0x88, + 0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77, 0x5d, 0x32, 0x12, 0x5e, 0xdf, 0x28, + 0x0c, 0x96, 0x0d, 0xa8, 0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0, 0x41, 0xcb, + 0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2, 0xfb, 0xd2, 0xf3, 0xf2, 0x95, 0xff, + 0xd8, 0x33, 0x3f, 0x8c, 0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe, 0x32, 0x66, + 0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe, 0x05, 0xb1, 0xbc, 0xbe, 0x44, 0x09, + 0xb4, 0xfe, 0x95, 0x06, 0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d, 0x22, 0xa0, + 0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09, 0x2c, 0xb8, 0xcc, 0xeb, 0x10, 0x4d, + 0xa7, 0xd0, 0x4b, 0x46, 0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47, 0x55, 0xd3, + 0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e, 0xf9, 0x5b, 0x7b, 0xae, 0x6d, 0x08, + 0x9f, 0x0c, 0x2a, 0xe1, 0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55, 0xa3, 0x6d, + 0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02, 0x81, 0x80, 0x61, 0x73, 0x3d, 0x64, + 0xff, 0xdf, 0x05, 0x8d, 0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d, 0x7d, 0x53, + 0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4, 0x72, 0x8f, 0xae, 0x2b, 0x1a, 0x47, + 0x87, 0xc7, 0x5b, 0x78, 0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3, 0x8c, 0x0c, + 0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a, 0x88, 0x7d, 0x40, 0xfe, 0x95, 0x32, + 0x5b, 0xd3, 0x6f, 0x90, 0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d, 0xc5, 0xc7, + 0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b, 0xcb, 0x78, 0xad, 0x7e, 0xfe, 0xb6, + 0xb1, 0x23, 0x63, 0x01, 0x94, 0x9a, 0x99, 0x05, 0x63, 0xda, 0xea, 0xf1, + 0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b, 0x35, 0xec, 0xcb, 0x0b, 0x43, 0xb8, + 0x8e, 0x84, 0xb8, 0x09, 0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c, 0x7c, 0x95, + 0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08, 0x09, 0x8f, 0x91, 0x17, 0x65, 0x4c, + 0xff, 0x6e, 0xbc, 0x51, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d, 0x9d, 0xd8, + 0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f, 0x85, 0xd7, 0xce, 0x72, 0x02, 0x38, + 0xf2, 0x0f, 0x9c, 0x27, 0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d, 0x02, 0x19, + 0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb, 0x61, 0x8e, 0xe6, 0xb8, 0xfc, 0x07, + 0x3c, 0xd1, 0x1b, 0x16, 0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26, 0xb2, 0xbd, + 0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11, 0xc5, 0xdd, 0xf9, 0xdb, 0x4e, 0x48, + 0x52, 0xd8, 0xe6, 0x4b, 0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1, 0x7c, 0xd3, + 0x51, 0x6b, 0x45, 0x46, 0x54, 0x11, 0x1a, 0x71, 0xd3, 0xcd, 0x6b, 0x8f, + 0x79, 0x22, 0x83, 0x02, 0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed, 0x32, 0xd8, + 0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec, 0x2c, 0x7e, 0xa4, 0x89, 0xdc, 0xbf, + 0xf9, 0x0d, 0x32, 0xc8, 0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc, 0x70, 0x4e, + 0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81, 0x81, 0x00, 0xd3, 0x35, 0x1b, 0x19, + 0x75, 0x3f, 0x61, 0xf2, 0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf, 0x0c, 0x0a, + 0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b, 0x13, 0x4d, 0x1f, 0x86, 0x58, 0x4f, + 0xd8, 0xee, 0xfa, 0x76, 0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2, 0xd2, 0x62, + 0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08, 0xa9, 0x83, 0x07, 0xac, 0x8c, 0xa3, + 0x7e, 0x18, 0xc0, 0x1c, 0x57, 0x63, 0x8d, 0x05, 0x17, 0x47, 0x1b, 0xd3, + 0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8, 0x1a, 0x43, 0x04, 0x36, 0xc8, 0x19, + 0xbe, 0xdc, 0xa6, 0xe5, 0x0f, 0x25, 0x62, 0x24, 0x96, 0x92, 0xb6, 0xb3, + 0x97, 0xad, 0x57, 0x9a, 0x90, 0x37, 0x4e, 0x31, 0x44, 0x74, 0xfa, 0x7c, + 0xb4, 0xea, 0xfc, 0x15, 0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e, 0xed, 0x5b, + 0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b, 0x21, 0xdc, 0x86, 0x17, + 0x6e, 0xc8, 0xee, 0x24}; + +static const unsigned char kTestRsaPublicKey3_2048[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, + 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, + 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, + 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, + 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, + 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, + 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, + 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, + 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47, 0x94, 0x40, 0x70, 0xac, + 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, + 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, + 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, + 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, + 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, + 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, + 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, + 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, + 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, + 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, + 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, + 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, + 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, + 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01}; + +unsigned char kTestRsaPrivateKey2CarmichaelTotient_2048[] = { + 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4, + 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, + 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e, + 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, + 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7, + 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, + 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68, + 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, + 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, + 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, + 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e, + 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, + 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88, + 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, + 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8, + 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, + 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e, + 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, + 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29, + 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, + 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, + 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x00, 0x0a, 0xf9, 0x4a, 0x19, 0x72, 0x88, 0x1b, 0x4e, 0xd8, 0x2f, 0xef, + 0x99, 0x93, 0x32, 0xda, 0x51, 0x21, 0x2e, 0x14, 0x06, 0xf4, 0xe9, 0x65, + 0x1c, 0xf9, 0xd4, 0xcf, 0x1a, 0x51, 0x53, 0xcd, 0x48, 0x33, 0x8c, 0x30, + 0xed, 0xdd, 0x53, 0x6f, 0x29, 0x82, 0xf9, 0xe0, 0x74, 0xde, 0xb1, 0x13, + 0x01, 0x88, 0x8f, 0xce, 0x14, 0xc1, 0x3b, 0x90, 0xb7, 0xcc, 0x6c, 0xdf, + 0x35, 0xa1, 0xf2, 0x1a, 0x3d, 0xbe, 0x19, 0xd7, 0x0a, 0xe4, 0x67, 0x75, + 0xbb, 0xfa, 0x87, 0xf4, 0x03, 0xb5, 0x7f, 0x69, 0xe4, 0x0b, 0x6a, 0xdc, + 0x92, 0x82, 0x54, 0x64, 0x1a, 0x94, 0x2d, 0xe4, 0x63, 0x40, 0xb2, 0xb4, + 0x85, 0x6b, 0xc8, 0x34, 0xba, 0xa2, 0x14, 0x30, 0x47, 0x1a, 0xeb, 0x90, + 0x62, 0x30, 0x43, 0x44, 0x02, 0xc7, 0x0c, 0x30, 0xc0, 0x7f, 0xa9, 0x47, + 0xae, 0xde, 0x68, 0x27, 0x92, 0xaa, 0x11, 0x95, 0xf5, 0x6f, 0xfc, 0x19, + 0x8b, 0x49, 0xa0, 0x77, 0x9d, 0xc6, 0x13, 0x5d, 0x73, 0xff, 0x45, 0xa2, + 0x4c, 0x3b, 0xf3, 0xe1, 0x2d, 0xd7, 0xc4, 0x70, 0xe2, 0x6c, 0x37, 0x99, + 0x4c, 0x7a, 0xa9, 0x27, 0xf8, 0x3a, 0xd6, 0xfd, 0xc5, 0xd8, 0xfa, 0x2d, + 0x0e, 0x71, 0x4b, 0x85, 0x7e, 0xce, 0xcb, 0x1c, 0x79, 0x71, 0xbd, 0xff, + 0x63, 0x03, 0x6b, 0x58, 0x68, 0xe0, 0x14, 0xca, 0x5e, 0x85, 0xfd, 0xd0, + 0xb7, 0xe0, 0x68, 0x14, 0xff, 0x2c, 0x82, 0x22, 0x26, 0x8a, 0x3f, 0xbf, + 0xb0, 0x2a, 0x90, 0xff, 0xc7, 0x72, 0xfc, 0x66, 0x51, 0x3e, 0x51, 0x9f, + 0x82, 0x68, 0x0e, 0xf3, 0x65, 0x74, 0x88, 0xab, 0xb7, 0xe5, 0x97, 0x5f, + 0x0f, 0x3e, 0xe5, 0x3a, 0xbc, 0xa4, 0xa1, 0x50, 0xdd, 0x5c, 0x94, 0x4b, + 0x0c, 0x70, 0x71, 0x48, 0x4e, 0xd0, 0xec, 0x46, 0x8f, 0xdf, 0xa2, 0x9a, + 0xfe, 0xd8, 0x35, 0x1a, 0x2f, 0x02, 0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c, + 0xbe, 0x6d, 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, 0x78, 0xcc, + 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85, + 0xab, 0x04, 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, 0x84, 0x68, + 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86, + 0x7b, 0xd0, 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, 0x3a, 0x83, + 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7, + 0x1e, 0x64, 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, 0x18, 0x6c, + 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a, + 0xcc, 0x48, 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, 0x3c, 0x2d, + 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, 0x26, 0x60, 0x48, 0x16, + 0xcb, 0xef, 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, 0x15, 0x43, + 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49, + 0xc2, 0x19, 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, 0xcb, 0x43, + 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17, + 0x68, 0x78, 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, 0xa7, 0x5b, + 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d, + 0xeb, 0x3f, 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, 0xc1, 0xee, + 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d, + 0x8e, 0x87, 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, 0x0d, 0x6c, + 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, 0xed, 0x83, 0x51, 0x67, + 0xa9, 0x55, 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, 0x53, 0xa8, + 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, 0x67, 0x9c, 0x32, 0x83, + 0x39, 0x57, 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, 0x0a, 0x2d, + 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b, + 0x1b, 0x43, 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, 0xeb, 0xdb, + 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25, + 0x92, 0xe4, 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, 0x37, 0x43, + 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d, + 0x45, 0xdc, 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, 0xf9, 0xca, + 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71, + 0x16, 0xd8, 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, 0xb1, 0x31, + 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, 0x41, 0x29, 0x40, 0x19, + 0x83, 0x35, 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, 0xcc, 0x3b, + 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, 0x63, 0x94, 0x49, 0x4c, + 0xad, 0x10, 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, 0x6a, 0xab, + 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec, + 0x5e, 0x61, 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, 0x5f, 0xdf, + 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22, + 0xfa, 0x34, 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, 0x77, 0xe6, + 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a, + 0x2d, 0x0c, 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, 0xd9, 0xbe, + 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96, + 0x76, 0xe8, 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, 0xac, 0x21, + 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6, + 0x69, 0x1d, 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, 0x28, 0x66, + 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9, + 0xe4, 0xc7, 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, 0x70, 0xcf, + 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6, + 0x2b, 0xad, 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, 0x50, 0xcb, + 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7, + 0xab, 0x30, 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, 0xa2, 0x0d, + 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae, + 0x84, 0x6b, 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, 0x72, 0x2c, + 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, + 0xdb, 0x03}; + +static const unsigned char kTestRsaPrivateKey3CarmichaelTotient_2048[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40, + 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83, 0xa7, + 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44, + 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a, 0x91, + 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08, + 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74, 0x10, + 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d, + 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47, 0x94, + 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d, + 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03, 0x68, + 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55, + 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c, 0xe8, + 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, + 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0, 0x35, + 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06, + 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a, 0x21, + 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae, + 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87, 0x3c, + 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84, + 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f, 0x87, + 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b, + 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x00, 0x1a, 0x1a, 0xe3, 0xb4, 0x2d, 0x9b, 0xd0, 0x1d, 0xc4, 0x54, 0x50, + 0xc4, 0x98, 0xeb, 0xae, 0xf4, 0xab, 0x95, 0x72, 0x78, 0x60, 0xbe, 0x2e, + 0xfc, 0x88, 0x59, 0xc3, 0xff, 0x5f, 0xb4, 0x01, 0xfd, 0x2c, 0x06, 0x52, + 0xfa, 0xa4, 0x5b, 0xfc, 0x29, 0xdf, 0x82, 0x67, 0x14, 0x52, 0x39, 0x67, + 0xd5, 0x31, 0xc1, 0x41, 0x02, 0x76, 0x03, 0x5d, 0xd2, 0xc5, 0x74, 0x2c, + 0x24, 0x26, 0xfe, 0xed, 0x46, 0x65, 0x97, 0x22, 0x74, 0xe7, 0xff, 0x63, + 0x35, 0x3b, 0xd8, 0xad, 0x88, 0x2c, 0xe2, 0x50, 0xf3, 0x76, 0x14, 0xbe, + 0x3a, 0x37, 0x1b, 0xf0, 0x21, 0x91, 0x8e, 0xdd, 0x43, 0xed, 0xb7, 0xab, + 0xcd, 0xfb, 0x8a, 0x31, 0xa6, 0x26, 0xb4, 0xd5, 0x4b, 0x2c, 0x80, 0x7f, + 0xfc, 0x39, 0x24, 0x33, 0x7d, 0x6d, 0xf3, 0x1c, 0x06, 0xdd, 0x21, 0x53, + 0x70, 0x78, 0xe4, 0x07, 0x60, 0x2a, 0x3f, 0xb8, 0xd0, 0x47, 0xf6, 0x0e, + 0xbd, 0xde, 0x31, 0xf3, 0x66, 0xfe, 0x6e, 0x4b, 0x50, 0x75, 0x0b, 0x49, + 0x3a, 0x96, 0xeb, 0x63, 0xb9, 0x24, 0xbb, 0xfc, 0xcd, 0xf5, 0x49, 0x12, + 0xa9, 0x6d, 0x39, 0xd4, 0x18, 0x15, 0x14, 0x50, 0x82, 0xa9, 0x75, 0xeb, + 0x9f, 0x1a, 0xaa, 0x52, 0x1d, 0x0d, 0x55, 0x74, 0x30, 0x45, 0x3b, 0xd2, + 0xd3, 0xe1, 0xdb, 0x8d, 0xec, 0x38, 0x2b, 0xb0, 0xdd, 0xda, 0x10, 0xe3, + 0x40, 0x87, 0x27, 0xbe, 0x0b, 0xbf, 0x08, 0x9e, 0x25, 0x95, 0x14, 0xf4, + 0xd7, 0xfe, 0x8c, 0x4f, 0x23, 0xfd, 0x1b, 0xad, 0x83, 0x6b, 0x05, 0x3a, + 0x83, 0xfa, 0x65, 0x1e, 0x65, 0x12, 0xe3, 0x9f, 0xea, 0x52, 0xd7, 0xed, + 0x01, 0x7d, 0xc5, 0xf1, 0x96, 0x2d, 0xf5, 0x4a, 0xa3, 0xcb, 0x69, 0x6c, + 0x9a, 0x48, 0xe9, 0xf5, 0x01, 0xef, 0x1d, 0x2e, 0x90, 0x64, 0x6c, 0x0b, + 0x79, 0xe0, 0xeb, 0x64, 0x29, 0x02, 0x81, 0x81, 0x00, 0xdb, 0xc1, 0xe7, + 0xdd, 0xba, 0x3c, 0x1f, 0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb, 0xd2, 0x47, + 0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5, 0xda, 0xc2, 0x25, 0x64, 0x0a, 0x02, + 0xbc, 0x7d, 0x7f, 0x50, 0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d, 0x77, 0x6d, + 0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd, 0x18, 0xe9, 0x53, 0x0b, 0x5c, 0xa2, + 0x71, 0x14, 0x02, 0xfd, 0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c, 0x2a, 0x4e, + 0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26, 0xd0, 0xd8, 0x3d, 0xcf, 0x8c, 0x89, + 0x65, 0x6c, 0x35, 0x19, 0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49, 0xcd, 0x6d, + 0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9, 0xba, 0x43, 0xe5, 0xdd, 0x39, 0x3a, + 0x78, 0x8f, 0x07, 0xb8, 0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8, 0x07, 0x02, + 0x9b, 0x79, 0x26, 0x32, 0x22, 0x38, 0x91, 0x01, 0x90, 0x81, 0x29, 0x94, + 0xad, 0x77, 0xeb, 0x86, 0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x29, 0x88, + 0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77, 0x5d, 0x32, 0x12, 0x5e, 0xdf, 0x28, + 0x0c, 0x96, 0x0d, 0xa8, 0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0, 0x41, 0xcb, + 0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2, 0xfb, 0xd2, 0xf3, 0xf2, 0x95, 0xff, + 0xd8, 0x33, 0x3f, 0x8c, 0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe, 0x32, 0x66, + 0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe, 0x05, 0xb1, 0xbc, 0xbe, 0x44, 0x09, + 0xb4, 0xfe, 0x95, 0x06, 0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d, 0x22, 0xa0, + 0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09, 0x2c, 0xb8, 0xcc, 0xeb, 0x10, 0x4d, + 0xa7, 0xd0, 0x4b, 0x46, 0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47, 0x55, 0xd3, + 0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e, 0xf9, 0x5b, 0x7b, 0xae, 0x6d, 0x08, + 0x9f, 0x0c, 0x2a, 0xe1, 0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55, 0xa3, 0x6d, + 0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02, 0x81, 0x80, 0x61, 0x73, 0x3d, 0x64, + 0xff, 0xdf, 0x05, 0x8d, 0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d, 0x7d, 0x53, + 0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4, 0x72, 0x8f, 0xae, 0x2b, 0x1a, 0x47, + 0x87, 0xc7, 0x5b, 0x78, 0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3, 0x8c, 0x0c, + 0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a, 0x88, 0x7d, 0x40, 0xfe, 0x95, 0x32, + 0x5b, 0xd3, 0x6f, 0x90, 0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d, 0xc5, 0xc7, + 0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b, 0xcb, 0x78, 0xad, 0x7e, 0xfe, 0xb6, + 0xb1, 0x23, 0x63, 0x01, 0x94, 0x9a, 0x99, 0x05, 0x63, 0xda, 0xea, 0xf1, + 0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b, 0x35, 0xec, 0xcb, 0x0b, 0x43, 0xb8, + 0x8e, 0x84, 0xb8, 0x09, 0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c, 0x7c, 0x95, + 0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08, 0x09, 0x8f, 0x91, 0x17, 0x65, 0x4c, + 0xff, 0x6e, 0xbc, 0x51, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d, 0x9d, 0xd8, + 0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f, 0x85, 0xd7, 0xce, 0x72, 0x02, 0x38, + 0xf2, 0x0f, 0x9c, 0x27, 0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d, 0x02, 0x19, + 0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb, 0x61, 0x8e, 0xe6, 0xb8, 0xfc, 0x07, + 0x3c, 0xd1, 0x1b, 0x16, 0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26, 0xb2, 0xbd, + 0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11, 0xc5, 0xdd, 0xf9, 0xdb, 0x4e, 0x48, + 0x52, 0xd8, 0xe6, 0x4b, 0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1, 0x7c, 0xd3, + 0x51, 0x6b, 0x45, 0x46, 0x54, 0x11, 0x1a, 0x71, 0xd3, 0xcd, 0x6b, 0x8f, + 0x79, 0x22, 0x83, 0x02, 0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed, 0x32, 0xd8, + 0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec, 0x2c, 0x7e, 0xa4, 0x89, 0xdc, 0xbf, + 0xf9, 0x0d, 0x32, 0xc8, 0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc, 0x70, 0x4e, + 0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81, 0x81, 0x00, 0xd3, 0x35, 0x1b, 0x19, + 0x75, 0x3f, 0x61, 0xf2, 0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf, 0x0c, 0x0a, + 0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b, 0x13, 0x4d, 0x1f, 0x86, 0x58, 0x4f, + 0xd8, 0xee, 0xfa, 0x76, 0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2, 0xd2, 0x62, + 0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08, 0xa9, 0x83, 0x07, 0xac, 0x8c, 0xa3, + 0x7e, 0x18, 0xc0, 0x1c, 0x57, 0x63, 0x8d, 0x05, 0x17, 0x47, 0x1b, 0xd3, + 0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8, 0x1a, 0x43, 0x04, 0x36, 0xc8, 0x19, + 0xbe, 0xdc, 0xa6, 0xe5, 0x0f, 0x25, 0x62, 0x24, 0x96, 0x92, 0xb6, 0xb3, + 0x97, 0xad, 0x57, 0x9a, 0x90, 0x37, 0x4e, 0x31, 0x44, 0x74, 0xfa, 0x7c, + 0xb4, 0xea, 0xfc, 0x15, 0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e, 0xed, 0x5b, + 0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b, 0x21, 0xdc, 0x86, 0x17, + 0x6e, 0xc8, 0xee, 0x24}; + +static const unsigned char kTestRsaPrivateKey4CarmichaelTotient_2048[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xb5, 0xed, 0x90, 0x77, 0xfe, 0xb4, 0xac, 0x2c, 0x7a, 0x53, 0x0a, 0xe0, + 0x94, 0x87, 0xfe, 0xc7, 0xc4, 0xa8, 0x3f, 0x09, 0x5b, 0xdd, 0xa7, 0x6e, + 0x70, 0xc9, 0xf0, 0xef, 0x9e, 0x47, 0xad, 0xcf, 0xbe, 0xdc, 0x9b, 0x89, + 0xc7, 0xbd, 0xb5, 0x2f, 0xa9, 0x43, 0x97, 0xd5, 0x5b, 0xae, 0x73, 0x46, + 0xa9, 0x10, 0x47, 0x67, 0x35, 0xa0, 0x6e, 0xfc, 0xfb, 0x45, 0xf8, 0x85, + 0xb0, 0xf1, 0xd3, 0x67, 0x84, 0x91, 0x03, 0x1b, 0x90, 0xb1, 0x18, 0x12, + 0xa4, 0x15, 0x7a, 0x65, 0xfa, 0x74, 0x4f, 0x73, 0x34, 0x70, 0x9b, 0x64, + 0x91, 0x1f, 0x89, 0x4f, 0x40, 0x2d, 0x37, 0x2f, 0xbf, 0x2b, 0x1e, 0x54, + 0x95, 0x7c, 0x16, 0x14, 0xd3, 0x60, 0xf4, 0xd7, 0x86, 0xf7, 0x0c, 0x2c, + 0xbb, 0x3f, 0xe7, 0x49, 0xdf, 0x74, 0x8b, 0xc9, 0x8f, 0x7c, 0x22, 0x23, + 0xd9, 0xbd, 0x2a, 0x57, 0xfe, 0xfb, 0x7a, 0x77, 0x91, 0xce, 0xa9, 0xd8, + 0xa7, 0xfb, 0xf5, 0xe7, 0x39, 0xbe, 0xfb, 0xf5, 0x47, 0x57, 0x48, 0xe7, + 0x71, 0x37, 0xda, 0x53, 0xe7, 0x89, 0xd3, 0x37, 0x31, 0x98, 0xba, 0xd8, + 0xd0, 0xd9, 0x74, 0xb2, 0xda, 0x06, 0x99, 0x3b, 0x97, 0x87, 0xc4, 0x22, + 0x3c, 0xf7, 0xa3, 0x13, 0x84, 0x49, 0x27, 0x74, 0x50, 0x03, 0x51, 0xea, + 0xc9, 0x57, 0xeb, 0x16, 0xa9, 0x7a, 0xea, 0x3c, 0x26, 0xda, 0xcb, 0xc6, + 0x86, 0x85, 0xfc, 0xd0, 0x73, 0x26, 0xce, 0xe8, 0xd9, 0x92, 0x09, 0xaf, + 0x68, 0x0c, 0x01, 0x8d, 0x95, 0xfd, 0x8d, 0x01, 0xde, 0x26, 0x0f, 0x25, + 0xb9, 0xd5, 0x80, 0x99, 0x62, 0xb1, 0x71, 0xcd, 0xa1, 0x5f, 0x52, 0xab, + 0x97, 0x97, 0x3f, 0xa7, 0x93, 0x40, 0x89, 0x4f, 0xbc, 0x7d, 0x99, 0x7a, + 0x7e, 0x35, 0xa5, 0xfc, 0x74, 0xfb, 0xe4, 0x5a, 0x0b, 0xed, 0xf0, 0xef, + 0x65, 0xed, 0x22, 0x95, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x00, 0x35, 0x4a, 0x9d, 0x36, 0xa1, 0x3c, 0x50, 0x71, 0x08, 0xf6, 0x19, + 0x6a, 0x16, 0xe8, 0x4d, 0x68, 0x3c, 0x41, 0xa2, 0x91, 0x7d, 0xc1, 0x0c, + 0xa2, 0x2b, 0xd4, 0xe3, 0xc8, 0x75, 0x3f, 0x7e, 0xf9, 0x2b, 0x6a, 0x18, + 0xff, 0xbf, 0xac, 0x61, 0x0e, 0x50, 0x91, 0x55, 0xc1, 0x30, 0x85, 0x86, + 0x0c, 0x0d, 0x4b, 0x10, 0xf7, 0x79, 0x3c, 0x81, 0x36, 0x86, 0xee, 0x84, + 0xb1, 0xb8, 0xd6, 0xe5, 0xbb, 0xdd, 0x97, 0xd2, 0xe6, 0xb8, 0xb8, 0x3f, + 0x9a, 0x7a, 0x49, 0x36, 0x5c, 0xf8, 0x04, 0x29, 0x1f, 0xd0, 0x9d, 0x29, + 0xcf, 0xc8, 0x39, 0x0a, 0x32, 0x56, 0x54, 0xc8, 0x65, 0x2a, 0xa5, 0x19, + 0x51, 0xe2, 0xa6, 0x02, 0x1b, 0xe0, 0x9d, 0x76, 0xab, 0x49, 0xc4, 0x45, + 0x63, 0x37, 0x08, 0xad, 0x9a, 0x34, 0xa4, 0x41, 0xac, 0x6d, 0xe5, 0x09, + 0x65, 0x22, 0x0b, 0xa9, 0x03, 0x34, 0xd4, 0x7a, 0x97, 0x5c, 0x5d, 0xdc, + 0xa3, 0xae, 0xfd, 0xe8, 0xd6, 0xe8, 0xf8, 0x52, 0x44, 0x9c, 0xf6, 0x00, + 0xe3, 0x8e, 0xad, 0xee, 0xa3, 0x82, 0x8a, 0x85, 0x07, 0x8b, 0x46, 0x12, + 0x0f, 0xa1, 0xca, 0x07, 0x0d, 0x50, 0x02, 0x96, 0x71, 0x2f, 0x1b, 0x2b, + 0x59, 0x17, 0x95, 0xec, 0x90, 0xa6, 0xb9, 0x05, 0xff, 0xa2, 0x25, 0x75, + 0xc5, 0x57, 0x42, 0x37, 0x12, 0xa0, 0xb6, 0x09, 0xa1, 0xe0, 0xef, 0x70, + 0x85, 0xa7, 0x39, 0x47, 0xb1, 0x72, 0x78, 0x0d, 0xf4, 0x4a, 0xa7, 0xca, + 0x92, 0x68, 0x2c, 0xe8, 0x53, 0x54, 0x52, 0xe2, 0x21, 0xce, 0xaf, 0x7e, + 0x56, 0x05, 0x74, 0xe1, 0xb7, 0x18, 0xed, 0x7f, 0x83, 0xf6, 0x7d, 0x99, + 0x33, 0xf9, 0x60, 0x24, 0xae, 0x59, 0x09, 0xc0, 0x95, 0x90, 0x5a, 0x19, + 0x25, 0xb0, 0x79, 0xfd, 0x0b, 0x2c, 0x73, 0x85, 0xc5, 0xa0, 0xe5, 0x7b, + 0x3a, 0x47, 0xce, 0x8d, 0x03, 0x02, 0x81, 0x81, 0x00, 0xe2, 0xd7, 0xeb, + 0x7f, 0xc1, 0x1f, 0x13, 0x8f, 0x52, 0x49, 0xa2, 0x83, 0x22, 0xb7, 0x3d, + 0x93, 0xfe, 0xe7, 0x20, 0x03, 0xf6, 0x6c, 0xd8, 0x95, 0x25, 0xef, 0x5f, + 0xa4, 0xd4, 0x8f, 0xb1, 0x38, 0x39, 0x7c, 0xe0, 0xdd, 0xff, 0x56, 0xa7, + 0xdf, 0x59, 0xe1, 0x26, 0x36, 0x72, 0x78, 0xdf, 0xd4, 0x19, 0xc0, 0x37, + 0x58, 0x75, 0x85, 0xdd, 0x8b, 0x2e, 0x73, 0xfa, 0xf5, 0x65, 0x93, 0xc7, + 0x36, 0x3d, 0xb7, 0x8e, 0xa9, 0x5e, 0x41, 0x71, 0x55, 0x1d, 0x22, 0x83, + 0x13, 0x91, 0x76, 0xa4, 0x80, 0xa0, 0xac, 0xf6, 0xd5, 0xbb, 0x6a, 0xf3, + 0x47, 0x01, 0x81, 0x3d, 0x34, 0xd6, 0x32, 0x49, 0xb2, 0xc9, 0xa7, 0xb3, + 0x93, 0x64, 0x47, 0x6d, 0x30, 0x8b, 0x23, 0xbd, 0xf0, 0xe8, 0x3c, 0xb5, + 0x4e, 0x33, 0x74, 0x4b, 0x60, 0x38, 0x25, 0x89, 0xaf, 0xf8, 0x86, 0x53, + 0x73, 0x62, 0x74, 0x35, 0xb7, 0x02, 0x81, 0x81, 0x00, 0xcd, 0x4f, 0xbd, + 0xe1, 0x43, 0xb4, 0xfe, 0x9a, 0x50, 0xb8, 0xeb, 0xf3, 0xd6, 0x3f, 0x2a, + 0xbe, 0x19, 0x7a, 0x2c, 0xf4, 0x63, 0x35, 0x29, 0x47, 0x39, 0x17, 0xb4, + 0xc7, 0x58, 0xc0, 0x5a, 0xbf, 0x5c, 0xa4, 0xcf, 0x9e, 0x4b, 0xad, 0x5a, + 0x95, 0xfd, 0xfa, 0x50, 0xbe, 0x54, 0x12, 0xbe, 0xd8, 0x8e, 0xaf, 0x8b, + 0xb2, 0x15, 0xa1, 0xb5, 0x78, 0x03, 0xa6, 0x3b, 0x18, 0x13, 0x1d, 0xfe, + 0x38, 0xb7, 0x65, 0x1e, 0x20, 0x39, 0xa9, 0x25, 0x37, 0xed, 0xe8, 0x5b, + 0xc7, 0xd0, 0x60, 0xe2, 0x1f, 0xf2, 0x06, 0x5d, 0x3c, 0x5c, 0x36, 0xe0, + 0xc6, 0x83, 0x52, 0xd3, 0xd3, 0x3f, 0x7c, 0x43, 0x82, 0x4a, 0x13, 0xf8, + 0x8d, 0x48, 0x03, 0x4f, 0xb8, 0xad, 0x64, 0x85, 0xa9, 0xee, 0xde, 0x3d, + 0x0c, 0x23, 0xb6, 0xe9, 0x8d, 0xad, 0x02, 0x62, 0x32, 0x58, 0x39, 0xbf, + 0x5f, 0x3c, 0x6c, 0x0a, 0x13, 0x02, 0x81, 0x80, 0x7f, 0x87, 0x3c, 0x12, + 0x3a, 0x94, 0x29, 0xfe, 0xed, 0x18, 0x10, 0x91, 0x00, 0xb7, 0x5b, 0x9b, + 0x14, 0x37, 0x03, 0xbc, 0xb6, 0x91, 0x42, 0xc1, 0xb6, 0xed, 0xf8, 0x2b, + 0x46, 0x84, 0xf1, 0xf0, 0xd6, 0x00, 0xea, 0xba, 0x63, 0x8e, 0x68, 0x1a, + 0x1d, 0x01, 0x82, 0xe6, 0x21, 0x3c, 0xeb, 0x38, 0xe6, 0xb1, 0x35, 0x6c, + 0x39, 0xc5, 0xe4, 0x63, 0x16, 0xde, 0x85, 0x3b, 0xe8, 0xbb, 0x47, 0xc7, + 0xaa, 0xb2, 0xc3, 0x35, 0x5c, 0x94, 0x16, 0x0e, 0xef, 0xae, 0x33, 0x5a, + 0x90, 0xf0, 0xce, 0x52, 0xb2, 0x02, 0x0b, 0x52, 0xe5, 0x66, 0x9f, 0x16, + 0x50, 0x36, 0x44, 0x1e, 0xac, 0x3c, 0xe1, 0x49, 0xee, 0x2c, 0xa5, 0xbc, + 0x3b, 0x28, 0x1c, 0xae, 0xa9, 0xca, 0x92, 0x42, 0x19, 0x8d, 0xe7, 0xaf, + 0xeb, 0x25, 0x7a, 0x2a, 0xc1, 0xe5, 0xf8, 0x9e, 0x41, 0x6d, 0xe3, 0x04, + 0x7f, 0x59, 0x2d, 0xc9, 0x02, 0x81, 0x81, 0x00, 0xbe, 0xc3, 0x51, 0xdd, + 0x25, 0x48, 0xdd, 0xab, 0xca, 0x47, 0x17, 0xcd, 0x57, 0x0b, 0x18, 0x0e, + 0xcb, 0xa3, 0x4e, 0x73, 0xc0, 0x5e, 0x1b, 0xbd, 0x76, 0x99, 0xc5, 0x39, + 0xd8, 0x07, 0xda, 0x09, 0xa5, 0xed, 0xe8, 0x8e, 0xdf, 0x27, 0xf2, 0x5c, + 0x1d, 0x40, 0xe0, 0x97, 0x07, 0x8c, 0xe7, 0x50, 0x55, 0xbb, 0x5c, 0x24, + 0x1a, 0x9f, 0x46, 0xfa, 0x7d, 0x01, 0x8e, 0x34, 0xbf, 0x46, 0x85, 0xf8, + 0x72, 0xc6, 0x7c, 0x68, 0x5a, 0xcb, 0x03, 0xae, 0xe4, 0xd9, 0x99, 0xb5, + 0x9d, 0xb2, 0xf7, 0x47, 0xd1, 0x5c, 0x02, 0x73, 0x5c, 0x07, 0x0d, 0x70, + 0xc5, 0x82, 0x47, 0x19, 0x28, 0x0a, 0xb0, 0xbb, 0x35, 0x53, 0x3b, 0x05, + 0x22, 0x9d, 0x19, 0x0c, 0xb1, 0xe7, 0x0d, 0x9e, 0xa8, 0x38, 0x4c, 0x26, + 0xa4, 0x64, 0x86, 0xbb, 0x41, 0xbe, 0x4e, 0x39, 0x12, 0xea, 0x8d, 0x1a, + 0xd3, 0x0c, 0x5b, 0x8b, 0x02, 0x81, 0x81, 0x00, 0xa9, 0x2d, 0x4b, 0x43, + 0x55, 0x7f, 0x90, 0xb4, 0xea, 0xf9, 0xc2, 0x4c, 0x8c, 0x4d, 0xee, 0x62, + 0x85, 0xd2, 0x58, 0x71, 0xc7, 0xb3, 0xfb, 0x80, 0x37, 0xd9, 0xc5, 0x20, + 0x61, 0xeb, 0x39, 0x7f, 0x4d, 0x09, 0x7c, 0x32, 0x46, 0x8b, 0x49, 0xa4, + 0xd6, 0x99, 0xb4, 0xdc, 0xb6, 0xe3, 0x2a, 0x77, 0x53, 0xa3, 0xee, 0xdb, + 0x4d, 0xf9, 0x59, 0x20, 0x8a, 0x83, 0x94, 0x29, 0x72, 0x96, 0x76, 0x36, + 0x63, 0xbe, 0xa1, 0xe8, 0x94, 0xf9, 0x50, 0x71, 0x56, 0xaa, 0xeb, 0x90, + 0x06, 0xf2, 0x05, 0xb1, 0x19, 0xf0, 0x13, 0x89, 0x2d, 0x7b, 0x94, 0xb7, + 0xf2, 0x18, 0xfa, 0x52, 0x09, 0x82, 0x99, 0x20, 0xe9, 0x7a, 0x1c, 0x68, + 0xfe, 0x44, 0xe5, 0xf8, 0xc5, 0xd3, 0xc6, 0x66, 0x3d, 0xe3, 0x43, 0xc3, + 0x63, 0x81, 0xff, 0x78, 0x91, 0x5c, 0x48, 0x16, 0xbe, 0x92, 0x90, 0x2f, + 0x10, 0x9f, 0x2d, 0x17}; + +RsaTestKeys::RsaTestKeys() + : private_key_1_3072_bits_(std::begin(kTestRsaPrivateKey1_3072), + std::end(kTestRsaPrivateKey1_3072)), + public_key_1_3072_bits_(std::begin(kTestRsaPublicKey1_3072), + std::end(kTestRsaPublicKey1_3072)), + private_key_2_2048_bits_(std::begin(kTestRsaPrivateKey2_2048), + std::end(kTestRsaPrivateKey2_2048)), + public_key_2_2048_bits_(std::begin(kTestRsaPublicKey2_2048), + std::end(kTestRsaPublicKey2_2048)), + private_key_3_2048_bits_(std::begin(kTestRsaPrivateKey3_2048), + std::end(kTestRsaPrivateKey3_2048)), + public_key_3_2048_bits_(std::begin(kTestRsaPublicKey3_2048), + std::end(kTestRsaPublicKey3_2048)), + private_key_2_carmichael_totient_2048_bits_( + std::begin(kTestRsaPrivateKey2CarmichaelTotient_2048), + std::end(kTestRsaPrivateKey2CarmichaelTotient_2048)), + private_key_3_carmichael_totient_2048_bits_( + std::begin(kTestRsaPrivateKey3CarmichaelTotient_2048), + std::end(kTestRsaPrivateKey3CarmichaelTotient_2048)), + private_key_4_carmichael_totient_2048_bits_( + std::begin(kTestRsaPrivateKey4CarmichaelTotient_2048), + std::end(kTestRsaPrivateKey4CarmichaelTotient_2048)) {} + +} // namespace widevine diff --git a/common/rsa_test_keys.h b/common/rsa_test_keys.h new file mode 100644 index 0000000..68d98a4 --- /dev/null +++ b/common/rsa_test_keys.h @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// RSA keys generated using fake_prng for purposes of testing. + +#ifndef COMMON_RSA_TEST_KEYS_H_ +#define COMMON_RSA_TEST_KEYS_H_ + +#include + +namespace widevine { + +// Container for test RSA keys +// +// Except for the keys noted below, these keys were generated using Euler's +// Totient. +// +class RsaTestKeys { + public: + RsaTestKeys(); + + // Returns 3072-bit private RSA test key 1 + const std::string& private_test_key_1_3072_bits() const { + return private_key_1_3072_bits_; + } + // Returns 3072-bit public RSA test key 1 + const std::string& public_test_key_1_3072_bits() const { + return public_key_1_3072_bits_; + } + // Returns 2048-bit private RSA test key 2 + const std::string& private_test_key_2_2048_bits() const { + return private_key_2_2048_bits_; + } + // Returns 2048-bit public RSA test key 2 + const std::string& public_test_key_2_2048_bits() const { + return public_key_2_2048_bits_; + } + // Returns 2048-bit private RSA test key 3 + const std::string& private_test_key_3_2048_bits() const { + return private_key_3_2048_bits_; + } + // Returns 2048-bit public RSA test key 3 + const std::string& public_test_key_3_2048_bits() const { + return public_key_3_2048_bits_; + } + + // This key was converted to a Carmichael totient version from the original + // private_key_2_2048_bits_. + const std::string& private_test_key_2_carmichael_totient_2048_bits() const { + return private_key_2_carmichael_totient_2048_bits_; + } + + // This key was converted to a Carmichael totient version from the original + // private_key_3_2048_bits_. + const std::string& private_test_key_3_carmichael_totient_2048_bits() const { + return private_key_3_carmichael_totient_2048_bits_; + } + + // This key has been created using the Carmichael totient. This is + // useful for testing with some RSA implementations that had challenges + // with keys generated this way. + const std::string& private_test_key_4_carmichael_totient_2048_bits() const { + return private_key_4_carmichael_totient_2048_bits_; + } + + private: + RsaTestKeys(const RsaTestKeys&) = delete; + RsaTestKeys& operator=(const RsaTestKeys&) = delete; + + std::string private_key_1_3072_bits_; + std::string public_key_1_3072_bits_; + std::string private_key_2_2048_bits_; + std::string public_key_2_2048_bits_; + std::string private_key_3_2048_bits_; + std::string public_key_3_2048_bits_; + + // Tests keys that use the Carmichael totient to calculate d. + std::string private_key_2_carmichael_totient_2048_bits_; + std::string private_key_3_carmichael_totient_2048_bits_; + std::string private_key_4_carmichael_totient_2048_bits_; +}; + +} // namespace widevine + +#endif // COMMON_RSA_TEST_KEYS_H_ diff --git a/common/rsa_util.cc b/common/rsa_util.cc new file mode 100644 index 0000000..d9a4b5c --- /dev/null +++ b/common/rsa_util.cc @@ -0,0 +1,516 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// RSA utility functions for serializing and deserializing RSA keys, +// encryption, and signing. + +#include "common/rsa_util.h" + +#include +#include +#include + +#include "glog/logging.h" +#include "openssl/pem.h" +#include "openssl/x509.h" + +namespace { +int BigNumGreaterThanPow2(const BIGNUM* b, int n) { + if (BN_is_negative(b) || n == INT_MAX) { + return 0; + } + int b_bits = BN_num_bits(b); + return b_bits > n + 1 || (b_bits == n + 1 && !BN_is_pow2(b)); +} +} // anonymous namespace + +namespace widevine { +namespace rsa_util { + +static bool SerializeRsaKey(const RSA* key, std::string* serialized_key, + bool serialize_private_key) { + if (key == nullptr) { + LOG(ERROR) << (serialize_private_key ? "Private" : "Public") + << " RSA key is nullptr."; + return false; + } + if (serialized_key == nullptr) { + LOG(ERROR) << "Pointer to hold serialized RSA" + << (serialize_private_key ? "Private" : "Public") + << "Key is nullptr."; + return false; + } + BIO* bio = BIO_new(BIO_s_mem()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new returned nullptr"; + return false; + } + bool success = false; + if ((serialize_private_key + ? i2d_RSAPrivateKey_bio(bio, const_cast(key)) + : i2d_RSAPublicKey_bio(bio, const_cast(key))) != 0) { + int serialized_size = BIO_pending(bio); + serialized_key->assign(serialized_size, 0); + if (BIO_read(bio, &(*serialized_key)[0], serialized_size) == + serialized_size) { + success = true; + } else { + LOG(ERROR) << "BIO_read failure"; + } + } else { + LOG(ERROR) << (serialize_private_key ? "Private" : "Public") + << " key serialization failure"; + } + BIO_free(bio); + return success; +} + +static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key, + bool deserialize_private_key) { + if (serialized_key.empty()) { + LOG(ERROR) << "Serialized RSA" + << (deserialize_private_key ? "Private" : "Public") + << "Key is empty."; + return false; + } + if (key == nullptr) { + LOG(ERROR) << "Pointer to hold new RSA " + << (deserialize_private_key ? "private" : "public") + << " key is nullptr."; + return false; + } + BIO* bio = BIO_new_mem_buf(const_cast(serialized_key.data()), + serialized_key.size()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new_mem_buf returned nullptr"; + return false; + } + *key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, nullptr) + : d2i_RSAPublicKey_bio(bio, nullptr); + BIO_free(bio); + if (*key == nullptr) { + LOG(ERROR) << (deserialize_private_key ? "Private" : "Public") + << " RSA key deserialization failure"; + } + return *key != nullptr; +} + +bool SerializeRsaPrivateKey(const RSA* private_key, + std::string* serialized_private_key) { + return SerializeRsaKey(private_key, serialized_private_key, true); +} + +bool DeserializeRsaPrivateKey(const std::string& serialized_private_key, + RSA** private_key) { + return DeserializeRsaKey(serialized_private_key, private_key, true); +} + +bool SerializeRsaPublicKey(const RSA* public_key, + std::string* serialized_public_key) { + return SerializeRsaKey(public_key, serialized_public_key, false); +} + +bool DeserializeRsaPublicKey(const std::string& serialized_public_key, + RSA** public_key) { + return DeserializeRsaKey(serialized_public_key, public_key, false); +} + +bool SerializePrivateKeyInfo(const RSA* private_key, + std::string* serialized_private_key) { + if (private_key == nullptr) { + LOG(ERROR) << "Private RSA key is nullptr."; + return false; + } + if (serialized_private_key == nullptr) { + LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is nullptr."; + return false; + } + // The following method of serializing a PKCS#8 PrivateKeyInfo object + // was obtained from analyzing the openssl utility code, as the official + // mechanism via i2d_PKCS8PrivateKey_bio is broken in the current openssl + // version (1.0.0c). Please refer to b/8560683. + EVP_PKEY* evp = EVP_PKEY_new(); + if (evp == nullptr) { + LOG(ERROR) << "EVP_PKEY_new returned nullptr."; + return false; + } + bool success = false; + PKCS8_PRIV_KEY_INFO* pkcs8_pki = nullptr; + BIO* bio = nullptr; + if (EVP_PKEY_set1_RSA(evp, const_cast(private_key)) == 0) { + LOG(ERROR) << "EVP_PKEY_set1_RSA failed."; + goto cleanup; + } + pkcs8_pki = EVP_PKEY2PKCS8(evp); + if (pkcs8_pki == nullptr) { + LOG(ERROR) << "EVP_PKEY2PKCS8 returned nullptr."; + goto cleanup; + } + bio = BIO_new(BIO_s_mem()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new returned nullptr."; + goto cleanup; + } + if (i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8_pki) == 0) { + LOG(ERROR) << "i2d_PKCS8_PRIV_KEY_INFO_bio failed."; + goto cleanup; + } + { + int serialized_size = BIO_pending(bio); + serialized_private_key->assign(serialized_size, 0); + if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) != + serialized_size) { + LOG(ERROR) << "BIO_read failed."; + goto cleanup; + } + } + success = true; + +cleanup: + if (bio != nullptr) { + BIO_free(bio); + } + if (pkcs8_pki != nullptr) { + PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); + } + EVP_PKEY_free(evp); + return success; +} + +bool DeserializePrivateKeyInfo(const std::string& serialized_private_key, + RSA** private_key) { + if (serialized_private_key.empty()) { + LOG(ERROR) << "Serialized PrivateKeyInfo is empty."; + return false; + } + if (private_key == nullptr) { + LOG(ERROR) << "Pointer to hold new RSA private key is nullptr."; + return false; + } + // The following method of deserializing a PKCS#8 PrivateKeyInfo object + // was obtained from analyzing the openssl utility code, as the official + // mechanism via d2i_PKCS8PrivateKey_bio is broken in the current openssl + // version (1.0.0c). Please refer to b/8560683. + BIO* bio = BIO_new_mem_buf(const_cast(serialized_private_key.data()), + serialized_private_key.size()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new_mem_buf returned nullptr"; + return false; + } + bool success = false; + EVP_PKEY* evp = nullptr; + PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr); + if (pkcs8_pki == nullptr) { + LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr."; + goto cleanup; + } + evp = EVP_PKCS82PKEY(pkcs8_pki); + if (evp == nullptr) { + LOG(ERROR) << "EVP_PKCS82PKEY returned nullptr."; + goto cleanup; + } + *private_key = EVP_PKEY_get1_RSA(evp); + if (*private_key == nullptr) { + LOG(ERROR) << "PrivateKeyInfo did not contain an RSA key."; + goto cleanup; + } + success = true; + +cleanup: + if (evp != nullptr) { + EVP_PKEY_free(evp); + } + if (pkcs8_pki != nullptr) { + PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); + } + BIO_free(bio); + return success; +} + +bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key, + std::string* private_key_info) { + RSA* key = nullptr; + if (DeserializeRsaPrivateKey(rsa_private_key, &key)) { + bool success = SerializePrivateKeyInfo(key, private_key_info); + RSA_free(key); + return success; + } + return false; +} + +bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + std::string* rsa_private_key) { + RSA* key = nullptr; + if (DeserializePrivateKeyInfo(private_key_info, &key)) { + bool success = SerializeRsaPrivateKey(key, rsa_private_key); + RSA_free(key); + return success; + } + return false; +} + +bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key, + const std::string& passphrase, + std::string* serialized_private_key) { + if (private_key == nullptr) { + LOG(ERROR) << "Private RSA key is nullptr."; + return false; + } + if (passphrase.empty()) { + LOG(ERROR) << "Passphrase for RSA key encryption is empty."; + return false; + } + if (serialized_private_key == nullptr) { + LOG(ERROR) + << "Pointer to hold serialized EncryptedPrivateKeyInfo is nullptr."; + return false; + } + EVP_PKEY* evp = EVP_PKEY_new(); + if (evp == nullptr) { + LOG(ERROR) << "EVP_PKEY_new returned nullptr."; + return false; + } + bool success = false; + BIO* bio = nullptr; + if (EVP_PKEY_set1_RSA(evp, const_cast(private_key)) == 0) { + LOG(ERROR) << "EVP_PKEY_set1_RSA failed."; + goto cleanup; + } + bio = BIO_new(BIO_s_mem()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new returned nullptr."; + goto cleanup; + } + if (i2d_PKCS8PrivateKey_bio(bio, evp, EVP_aes_256_cbc(), + const_cast(passphrase.data()), + passphrase.size(), nullptr, nullptr) == 0) { + LOG(ERROR) << "i2d_PKCS8PrivateKey_bio failed."; + goto cleanup; + } + { + int serialized_size = BIO_pending(bio); + serialized_private_key->assign(serialized_size, 0); + if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) != + serialized_size) { + LOG(ERROR) << "BIO_read failed."; + goto cleanup; + } + } + success = true; + +cleanup: + if (bio != nullptr) { + BIO_free(bio); + } + EVP_PKEY_free(evp); + return success; +} + +namespace { +// Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below. +int get_password(char* buf, int size, int rwflag, void* u) { + CHECK(buf); + CHECK(u); + const std::string* pass(static_cast(u)); + if (!pass->empty() && size >= static_cast(pass->size())) { + memcpy(buf, pass->data(), pass->size()); + return pass->size(); + } + return 0; +} +} // namespace + +bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key, + const std::string& passphrase, + RSA** private_key) { + if (serialized_private_key.empty()) { + LOG(ERROR) << "Serialized RSAEncryptedPrivateKeyInfo is empty."; + return false; + } + if (passphrase.empty()) { + LOG(ERROR) << "Passphrase for RSA key decryption is empty."; + return false; + } + if (private_key == nullptr) { + LOG(ERROR) << "Pointer to hold new RSA private key is nullptr."; + return false; + } + BIO* bio = BIO_new_mem_buf(const_cast(serialized_private_key.data()), + serialized_private_key.size()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new_mem_buf returned nullptr"; + return false; + } + bool success = false; + EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, nullptr, get_password, + const_cast(&passphrase)); + if (evp == nullptr) { + LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned nullptr."; + goto cleanup; + } + *private_key = EVP_PKEY_get1_RSA(evp); + if (*private_key == nullptr) { + LOG(ERROR) << "EncryptedPrivateKeyInfo did not contain an RSA key."; + goto cleanup; + } + success = true; + +cleanup: + if (evp != nullptr) { + EVP_PKEY_free(evp); + } + BIO_free(bio); + return success; +} + +bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key, + const std::string& passphrase, + std::string* private_key_info) { + RSA* key = nullptr; + if (DeserializeRsaPrivateKey(rsa_private_key, &key)) { + bool success = + SerializeEncryptedPrivateKeyInfo(key, passphrase, private_key_info); + RSA_free(key); + return success; + } + return false; +} + +bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + const std::string& passphrase, + std::string* rsa_private_key) { + RSA* key = nullptr; + if (DeserializeEncryptedPrivateKeyInfo(private_key_info, passphrase, &key)) { + bool success = SerializeRsaPrivateKey(key, rsa_private_key); + RSA_free(key); + return success; + } + return false; +} + +bool ConvertToEulerTotient(RSA* rsa) { + // RSA key generation requires computing e (public exponent) and d (private + // exponent), such that m^(e * d) = 1 (mod n) for all m coprime to n. + // BoringSSL previously computed this by taking the inverse of e modulo the + // Euler totient, (p - 1) * (q - 1). However, it now uses the Carmichael + // totient, lcm(p - 1, q - 1). These two methods produce equivalent RSA keys. + // + // This breaks some vendors' RSA code which use a custom RSA format (rather + // than the standard one in RFC 8017) which omits most of the required + // parameters. They then attempt to recover those parameters, but their + // recovery algorithm breaks when using the Carmichael totient. To work around + // this bug, re-compute the private exponent against the Euler totient. + const BIGNUM *e, *p, *q; + RSA_get0_key(rsa, nullptr /* n */, &e, nullptr /* d */); + RSA_get0_factors(rsa, &p, &q); + + bssl::UniquePtr ctx(BN_CTX_new()); + bssl::UniquePtr pm1(BN_new()); + bssl::UniquePtr qm1(BN_new()); + bssl::UniquePtr totient(BN_new()); + bssl::UniquePtr d(BN_new()); + if (!ctx || !pm1 || !qm1 || !totient || !d || + !BN_sub(pm1.get(), p, BN_value_one()) || + !BN_sub(qm1.get(), q, BN_value_one()) || + !BN_mul(totient.get(), pm1.get(), qm1.get(), ctx.get()) || + !BN_mod_inverse(d.get(), e, totient.get(), ctx.get())) { + return false; + } + + // Perform a sanity check that d is still valid after conversion. + // d > 2 ^ (nlen / 2) per Appendix B 3.1 in FIPS 186/4. + int prime_bits = (8 * RSA_size(rsa)) / 2; + if (!BigNumGreaterThanPow2(d.get(), prime_bits)) { + return false; + } + + if (!RSA_set0_key(rsa, nullptr /* n */, nullptr /* e */, d.get())) { + return false; + } + d.release(); // RSA_set0_key takes ownership on success. + + if (!RSA_check_key(rsa)) { + return false; + } + + return true; +} + +bool ConvertToEulerTotient(const std::string& private_key, + std::string* euler_private_key) { + CHECK(euler_private_key); + RSA* rsa_ptr; + if (!rsa_util::DeserializeRsaPrivateKey(private_key, &rsa_ptr)) { + return false; + } + bssl::UniquePtr rsa(rsa_ptr); + if (!rsa_util::ConvertToEulerTotient(rsa.get()) || + !rsa_util::SerializeRsaPrivateKey(rsa.get(), euler_private_key)) { + return false; + } + + return true; +} + +bool ConvertToCarmichaelTotient(RSA* rsa) { + bssl::UniquePtr ctx(BN_CTX_new()); + bssl::UniquePtr pm1(BN_new()); + bssl::UniquePtr qm1(BN_new()); + bssl::UniquePtr gcd(BN_new()); + bssl::UniquePtr totient(BN_new()); + bssl::UniquePtr d(BN_new()); + // This calculates d = e^-1 (mod lcm(p-1, q-1)). + // This is equivalent to what is used in RSA_generate_key in BoringSSL. + if (!BN_sub(pm1.get(), rsa->p, BN_value_one()) || + !BN_sub(qm1.get(), rsa->q, BN_value_one()) || + !BN_mul(totient.get(), pm1.get(), qm1.get(), ctx.get()) || + !BN_gcd(gcd.get(), pm1.get(), qm1.get(), ctx.get()) || + !BN_div(totient.get(), nullptr, totient.get(), gcd.get(), ctx.get()) || + !BN_mod_inverse(d.get(), rsa->e, totient.get(), ctx.get())) { + return false; + } + + // Perform a sanity check that d is still valid after conversion. + // d > 2 ^ (nlen / 2) per Appendix B 3.1 in FIPS 186/4. + int prime_bits = (8 * RSA_size(rsa)) / 2; + if (!BigNumGreaterThanPow2(d.get(), prime_bits)) { + return false; + } + + // TODO(user): Replace this with |RSA_set0_key| once BoringSSL has + // finished transitioning to the OpenSSL 1.1.0 API. + BN_free(rsa->d); + rsa->d = d.release(); + + if (!RSA_check_key(rsa)) { + return false; + } + + return true; +} + +bool ConvertToCarmichaelTotient(const std::string& private_key, + std::string* carmichael_private_key) { + CHECK(carmichael_private_key); + RSA* rsa_ptr; + if (!rsa_util::DeserializeRsaPrivateKey(private_key, &rsa_ptr)) { + return false; + } + bssl::UniquePtr rsa(rsa_ptr); + if (!rsa_util::ConvertToCarmichaelTotient(rsa.get()) || + !rsa_util::SerializeRsaPrivateKey(rsa.get(), carmichael_private_key)) { + return false; + } + + return true; +} + +} // namespace rsa_util +} // namespace widevine diff --git a/common/rsa_util.h b/common/rsa_util.h new file mode 100644 index 0000000..63e91f7 --- /dev/null +++ b/common/rsa_util.h @@ -0,0 +1,201 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// RSA utility functions for serializing and deserializing RSA keys, +// encryption, and signing. + +#ifndef COMMON_RSA_UTIL_H_ +#define COMMON_RSA_UTIL_H_ + +#include + +#include "openssl/rsa.h" + +namespace widevine { +namespace rsa_util { + +// Serialize RSA private key into DER encoded PKCS#1 RSAPrivateKey. +// - private_key is the RSA key to be serialized, which must not be NULL. +// - serialized_private_key is a pointer to the std::string to hold the serialized +// PKCS#1 RSAPrivateKey object. Caller retains ownership of the string. This +// parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializeRsaPrivateKey(const RSA* private_key, + std::string* serialized_private_key); + +// Deserialize RSA private key from DER encoded PKCS#1 RSAPrivateKey. +// - serialized_private_key is the DER-encoded PKCS#1 RSAPrivateKey to be +// deserialized. +// - private_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializeRsaPrivateKey(const std::string& serialized_private_key, + RSA** private_key); + +// Serialize RSA key into DER encoded PKCS#1 RSAPublicKey. +// - public_key is the RSA key to be serialized, which must not be NULL. +// - serialized_public_key is a pointer to the std::string to hold the serialized +// PKCS#1 RSAPublicKey object. Caller retains ownership of the string. This +// parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializeRsaPublicKey(const RSA* public_key, + std::string* serialized_public_key); + +// Deserialize RSA public key from DER encoded PKCS#1 RSAPublicKey. +// - serialized_public_key is the DER-encoded PKCS#1 RSAPublicKey to be +// deserialized. +// - public_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializeRsaPublicKey(const std::string& serialized_public_key, + RSA** public_key); + +// Serialize RSA private key into DER encoded PKCS#8 PrivateKeyInfo. +// - private_key is the RSA key to be serialized, which must not be NULL. +// - serialized_private_key is a pointer to the std::string to hold the serialized +// PKCS#8 PrivateKeyInfo object. Caller retains ownership of the string. This +// parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializePrivateKeyInfo(const RSA* private_key, + std::string* serialized_private_key); + +// Deserialize RSA private key from DER encoded PKCS#8 PrivateKeyInfo. +// - serialized_private_key is the DER-encoded PKCS#8 PrivateKeyInfo to be +// deserialized. +// - private_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializePrivateKeyInfo(const std::string& serialized_private_key, + RSA** private_key); + +// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8 +// PrivateKeyInfo. +// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted. +// - private_key_info is a pointer to std::string to hold the PKCS#8 PrivateKeyInfo. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key, + std::string* private_key_info); + +// Convert DER-encoded PKCS#8 PrivateKeyInfo to DER-encoded PKCS#1 +// RSAPrivateKey. +// - private_key_info is the PKCS#8 PrivateKeyInfo to be converted. +// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + std::string* rsa_private_key); + +// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo. +// - private_key is the RSA key to be serialized, which must not be NULL. +// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption. +// - serialized_private_key is a pointer to the std::string to hold the serialized +// PKCS#8 EncryptedPrivateKeyInfo object. Caller retains ownership of the +// string. This parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key, + const std::string& passphrase, + std::string* serialized_private_key); + +// Deserialize RSA private key from DER encoded PKCS#8 EncryptedPrivateKeyInfo. +// - serialized_private_key is the DER-encoded PKCS#8 EncryptedPrivateKeyInfo to +// be deserialized. +// - passphrase is the password to use for key decryption. +// - private_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key, + const std::string& passphrase, + RSA** private_key); + +// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8 +// EncryptedPrivateKeyInfo. +// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted. +// - passphrase is the password to use for PKCS#5 v2.1 AES-256-CBC encryption. +// - private_key_info is a pointer to std::string to hold the PKCS#8 +// EncryptedPrivateKeyInfo. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key, + const std::string& passphrase, + std::string* private_key_info); + +// Convert DER-encoded PKCS#8 EncryptedPrivateKeyInfo to DER-encoded PKCS#1 +// RSAPrivateKey. +// - private_key_info is the PKCS#8 EncryptedPrivateKeyInfo to be converted. +// - passphrase is the password to use for key decryption. +// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + const std::string& passphrase, + std::string* rsa_private_key); + +// This method changes |rsa| to use the more common, but not FIPS 186-4 +// compliant, computation for the RSA private exponent (d). This used to be the +// form produced by the BoringSSL method RSA_generate_key. This changed in this +// CL: https://boringssl-review.googlesource.com/c/boringssl/+/15944 +// +// This method is used to produce a "backward-compatible" version. Some vendor +// RSA implementations do not handle the new computation properly. +// +// - rsa is the openssl RSA structure. Must not be null. Caller retains +// ownership. +// +// Returns true if the key was successfully updated, false otherwise. +bool ConvertToEulerTotient(RSA* rsa); + +// This is wrapper to the other SwitchToEulerTotient that supports deserializing +// and serializing from and to a DER encoded PKCS#1 RSAPrivateKey. +// +// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be +// deserialized and converted. +// - euler_private_key is a pointer to the std::string to hold the converted and +// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the +// string. This parameter must not be NULL. +// Returns true if the key was successfully updated, false otherwise. +bool ConvertToEulerTotient(const std::string& private_key, + std::string* euler_private_key); + +// This method changes |rsa| to use the FIPS 186-4 compliant computation for d. +// This uses the Carmichael totient. This is equivalent to the way the key +// is generated in BoringSSL as of this change: +// https://boringssl-review.googlesource.com/c/boringssl/+/15944 +// +// This method is mostly used for testing. It allows tests to convert back and +// forth between forms and verify the output. +// +// Returns true if the key can be successfully updated, false otherwise. +bool ConvertToCarmichaelTotient(RSA* rsa); + +// This is wrapper to the other SwitchToCarmichaelTotient that supports +// deserializing and serializing from and to a DER encoded PKCS#1 RSAPrivateKey. +// +// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be +// deserialized and converted. +// - carmichael_private_key is a pointer to the std::string to hold the converted and +// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the +// string. This parameter must not be NULL. +// Returns true if the key was successfully updated, false otherwise. +bool ConvertToCarmichaelTotient(const std::string& private_key, + std::string* carmichael_private_key); + +} // namespace rsa_util +} // namespace widevine + +#endif // COMMON_RSA_UTIL_H_ diff --git a/common/rsa_util_test.cc b/common/rsa_util_test.cc new file mode 100644 index 0000000..23cdf4b --- /dev/null +++ b/common/rsa_util_test.cc @@ -0,0 +1,353 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// Unit test for rsa_util RSA utilties library. + +#include +#include + +#include +#include "glog/logging.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "openssl/bn.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" + +using ::testing::NotNull; + +namespace { +const uint32_t kRsaPublicExponent = 65537; +const int kTestRsaBits = 2048; +} // anonymous namespace + +namespace widevine { +namespace rsa_util { + +class RsaUtilTest : public ::testing::Test { + protected: + RsaTestKeys test_keys_; +}; + +TEST_F(RsaUtilTest, SerializeDeserializePrivateKey) { + RSA* private_key; + std::string serialized_private_key; + // Key 1 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_1_3072_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key)); + EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_1_3072_bits()); + EXPECT_EQ(RSA_check_key(private_key), 1); + RSA_free(private_key); + // Key 2 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key)); + EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_2_2048_bits()); + EXPECT_EQ(RSA_check_key(private_key), 1); + RSA_free(private_key); + // Key 3 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_3_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key)); + EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_3_2048_bits()); + EXPECT_EQ(RSA_check_key(private_key), 1); + RSA_free(private_key); + // Invalid key + EXPECT_FALSE(DeserializeRsaPrivateKey("this is a bad key", &private_key)); +} + +TEST_F(RsaUtilTest, SerializeDeserializePublicKey) { + RSA* public_key; + std::string serialized_public_key; + // Key 1 + EXPECT_TRUE(DeserializeRsaPublicKey( + test_keys_.public_test_key_1_3072_bits(), &public_key)); + ASSERT_TRUE(public_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits()); + RSA_free(public_key); + // Key 2 + EXPECT_TRUE(DeserializeRsaPublicKey( + test_keys_.public_test_key_2_2048_bits(), &public_key)); + ASSERT_TRUE(public_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits()); + RSA_free(public_key); + // Key 3 + EXPECT_TRUE(DeserializeRsaPublicKey( + test_keys_.public_test_key_3_2048_bits(), &public_key)); + ASSERT_TRUE(public_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits()); + RSA_free(public_key); + // Invalid key + EXPECT_FALSE(DeserializeRsaPublicKey("this is a bad key", &public_key)); +} + +TEST_F(RsaUtilTest, PublicKeyExtraction) { + RSA* private_key; + std::string serialized_public_key; + // Key 1 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_1_3072_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits()); + RSA_free(private_key); + // Key 2 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits()); + RSA_free(private_key); + // Key 3 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_3_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits()); + RSA_free(private_key); +} + +TEST_F(RsaUtilTest, Pkcs8PrivateKeyInfo) { + // The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8 + // serialization/deserialization functionality , so we test those. + std::string serialized_pkcs8; + std::string serialized_pkcs1; + // Key 1 + EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo( + test_keys_.private_test_key_1_3072_bits(), &serialized_pkcs8)); + EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1); + // Key 2 + EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), &serialized_pkcs8)); + EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1); + // Key 3 + EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo( + test_keys_.private_test_key_3_2048_bits(), &serialized_pkcs8)); + EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1); +} + +TEST_F(RsaUtilTest, Pkcs8EncryptedPrivateKeyInfo) { + // The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8 + // serialization/deserialization functionality , so we test those. + std::string serialized_pkcs8; + std::string serialized_pkcs1; + std::string passphrase("passphrase"); + // Key 1 + EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_1_3072_bits(), passphrase, + &serialized_pkcs8)); + EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + passphrase, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1); + // Key 2 + EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), passphrase, + &serialized_pkcs8)); + EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + passphrase, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1); + // Key 3 + EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_3_2048_bits(), passphrase, + &serialized_pkcs8)); + EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + passphrase, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1); +} + +TEST_F(RsaUtilTest, FailOnInvalidParams) { + RSA* test_input_key = NULL; + RSA* test_output_key = NULL; + std::string test_string; + std::string pass("password"); + ASSERT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &test_input_key)); + ASSERT_TRUE(test_input_key != NULL); + EXPECT_FALSE(SerializeRsaPrivateKey(NULL, &test_string)); + EXPECT_FALSE(SerializeRsaPrivateKey(test_input_key, NULL)); + EXPECT_FALSE(SerializeRsaPublicKey(NULL, &test_string)); + EXPECT_FALSE(SerializeRsaPublicKey(test_input_key, NULL)); + EXPECT_FALSE(SerializePrivateKeyInfo(NULL, &test_string)); + EXPECT_FALSE(SerializePrivateKeyInfo(test_input_key, NULL)); + EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(NULL, pass, &test_string)); + EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass, NULL)); + EXPECT_FALSE(DeserializeRsaPrivateKey("", &test_output_key)); + EXPECT_FALSE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), NULL)); + EXPECT_FALSE(DeserializeRsaPublicKey("", &test_output_key)); + EXPECT_FALSE(DeserializeRsaPublicKey( + test_keys_.public_test_key_2_2048_bits(), NULL)); + EXPECT_FALSE(DeserializePrivateKeyInfo("", &test_output_key)); + EXPECT_FALSE(DeserializePrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), NULL)); + EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo("", pass, &test_output_key)); + EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), pass, NULL)); + RSA_free(test_input_key); +} + +TEST_F(RsaUtilTest, Pkcs8FailOnInvalidPassword) { + RSA* test_input_key = NULL; + RSA* test_output_key = NULL; + std::string serialized_pkcs8; + std::string pass("password"); + ASSERT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &test_input_key)); + EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(test_input_key, "", + &serialized_pkcs8)); + ASSERT_TRUE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass, + &serialized_pkcs8)); + EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass + "a", + &test_output_key)); + EXPECT_TRUE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass, + &test_output_key)); + RSA_free(test_input_key); + RSA_free(test_output_key); +} + +TEST_F(RsaUtilTest, ConvertToCarmichaelTotient_ExistingKey_Success) { + bssl::UniquePtr original_private_key; + RSA* original_key_ptr = nullptr; + + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &original_key_ptr)); + original_private_key.reset(original_key_ptr); + ASSERT_THAT(original_private_key, NotNull()); + + bssl::UniquePtr private_key( + RSAPrivateKey_dup(original_private_key.get())); + ASSERT_THAT(private_key, NotNull()); + EXPECT_TRUE(ConvertToCarmichaelTotient(private_key.get())); + + // Confirm that the key is valid and has changed from the original. + EXPECT_EQ(1, RSA_check_key(private_key.get())); + std::string serialized_carmichael_private_key; + EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(), + &serialized_carmichael_private_key)); + EXPECT_NE(serialized_carmichael_private_key, + test_keys_.private_test_key_2_2048_bits()); + + // Convert back and make sure the serialized key matches the original. + EXPECT_TRUE(ConvertToEulerTotient(private_key.get())); + EXPECT_EQ(1, RSA_check_key(private_key.get())); + std::string serialized_euler_private_key; + EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(), + &serialized_euler_private_key)); + EXPECT_EQ(serialized_euler_private_key, + test_keys_.private_test_key_2_2048_bits()); +} + +TEST_F(RsaUtilTest, ConvertToEulerTotient_NewKey_Success) { + bssl::UniquePtr rsa; + bssl::UniquePtr private_key; + bssl::UniquePtr exponent(BN_new()); + ASSERT_TRUE(BN_set_word(exponent.get(), kRsaPublicExponent)); + + // It is possible that sometimes, the d value generated using carmichael + // and euler is the same. For this test, find a key where they are not the + // same (max 100 tries). + bool found_distinct_keys = false; + for (int i = 0; i < 100; i++) { + rsa.reset(RSA_new()); + ASSERT_TRUE(RSA_generate_key_ex(rsa.get(), kTestRsaBits, + exponent.get(), nullptr)); + + private_key.reset(RSAPrivateKey_dup(rsa.get())); + EXPECT_TRUE(ConvertToEulerTotient(private_key.get())); + EXPECT_EQ(1, RSA_check_key(private_key.get())); + + // If the values are different, break. + if (BN_cmp(private_key->d, rsa->d) != 0) { + found_distinct_keys = true; + break; + } + + LOG(INFO) << "Euler and Carmichael d values are the same. Count: " << i; + } + + ASSERT_TRUE(found_distinct_keys) + << "Reached maximum attempts, but did not generate distinct keys"; + EXPECT_EQ(1, RSA_check_key(private_key.get())); + + // Sanity check that the serialized keys are distinct. + std::string serialized_carmichael_private_key; + std::string serialized_private_key; + EXPECT_TRUE(SerializeRsaPrivateKey(rsa.get(), + &serialized_carmichael_private_key)); + + EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(), + &serialized_private_key)); + EXPECT_NE(serialized_carmichael_private_key, serialized_private_key); + + // Convert back to Carmichael, validate, and confirm that the keys are the + // same. + EXPECT_TRUE(ConvertToCarmichaelTotient(private_key.get())); + EXPECT_EQ(1, RSA_check_key(private_key.get())); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(), + &serialized_private_key)); + EXPECT_EQ(serialized_carmichael_private_key, serialized_private_key); +} + +TEST_F(RsaUtilTest, ConvertToSerializedCarmichaelTotient_Success) { + std::string private_key; + EXPECT_TRUE(ConvertToCarmichaelTotient( + test_keys_.private_test_key_2_2048_bits(), &private_key)); + EXPECT_EQ(test_keys_.private_test_key_2_carmichael_totient_2048_bits(), + private_key); +} + +TEST_F(RsaUtilTest, ConvertToSerializedEulerTotient_Success) { + std::string private_key; + EXPECT_TRUE(ConvertToEulerTotient( + test_keys_.private_test_key_2_carmichael_totient_2048_bits(), + &private_key)); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), private_key); +} + +TEST_F(RsaUtilTest, ConvertToEulerTotient_Idempotent_Success) { + std::string private_key; + EXPECT_TRUE(ConvertToEulerTotient(test_keys_.private_test_key_2_2048_bits(), + &private_key)); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), private_key); +} + +TEST_F(RsaUtilTest, ConvertToCarmichaelTotient_Idempotent_Success) { + std::string private_key; + EXPECT_TRUE(ConvertToCarmichaelTotient( + test_keys_.private_test_key_2_carmichael_totient_2048_bits(), + &private_key)); + EXPECT_EQ(test_keys_.private_test_key_2_carmichael_totient_2048_bits(), + private_key); +} + +} // namespace rsa_util +} // namespace widevine diff --git a/common/sha_util.cc b/common/sha_util.cc new file mode 100644 index 0000000..1ad68fc --- /dev/null +++ b/common/sha_util.cc @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/sha_util.h" + +#include +#include "openssl/sha.h" + +namespace widevine { + +std::string Sha1_Hash(const std::string& message) { + std::string digest; + digest.resize(SHA_DIGEST_LENGTH); + SHA1(reinterpret_cast(message.data()), message.size(), + reinterpret_cast(&digest[0])); + return digest; +} + +std::string Sha256_Hash(const std::string& message) { + std::string digest; + digest.resize(SHA256_DIGEST_LENGTH); + SHA256(reinterpret_cast(message.data()), message.size(), + reinterpret_cast(&digest[0])); + return digest; +} + +std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name) { + // X.667 14 Setting the fields of a name-based UUID. + // - Allocate a UUID to use as a "name space identifier" for all UUIDs + // generated from names in that name space. + // - Compute the 16-octet hash value of the name space identifier concatenated + // with the name. + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, name_space.data(), name_space.length()); + SHA1_Update(&ctx, name.data(), name.length()); + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA1_Final(hash, &ctx); + std::string hash_str = + std::string(reinterpret_cast(hash), SHA_DIGEST_LENGTH); + + // - For a SHA-1 hash function, the "hash value" referenced in 14.1 shall be + // octets zero to 15. + std::string uuid = hash_str.substr(0, 16); + + // - Overwrite the four most significant bits (bits 15 through 12) of the + // "VersionAndTimeHigh" field with the four-bit version number from Table 3 + // of 12.2 for the hash function that was used. [Name-based SHA-1 is 5] + (uuid[6] &= 0xF) |= 0x50; + + // - Overwrite the two most significant bits (bits 7 and 6) of the + // "VariantAndClockSeqHigh" field with 1 and 0, respectively. + (uuid[8] &= 0x3F) |= 0x80; + + return uuid; +} + +} // namespace widevine diff --git a/common/sha_util.h b/common/sha_util.h new file mode 100644 index 0000000..bc9390d --- /dev/null +++ b/common/sha_util.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_SHA_UTIL_H_ +#define COMMON_SHA_UTIL_H_ + +#include + +#include + +namespace widevine { + +// Calculates SHA1 hash. +std::string Sha1_Hash(const std::string& message); + +// Calculates SHA256 hash. +std::string Sha256_Hash(const std::string& message); + +// 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 +// name in the namespace. +std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name); + +} // namespace widevine + +#endif // COMMON_SHA_UTIL_H_ diff --git a/common/sha_util_test.cc b/common/sha_util_test.cc new file mode 100644 index 0000000..6adc7ca --- /dev/null +++ b/common/sha_util_test.cc @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/sha_util.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" + +namespace widevine { + +TEST(ShaUtilTest, Sha1Empty) { + const uint8_t kExpected[] = { + 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, + 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha1_Hash("")); +} + +TEST(ShaUtilTest, Sha256Empty) { + const uint8_t kExpected[] = { + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, + 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, + 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha256_Hash("")); +} + +TEST(ShaUtilTest, Sha1) { + const uint8_t kExpected[] = { + 0x6b, 0x63, 0xa5, 0xe3, 0x6b, 0xfe, 0x54, 0x19, 0x4e, 0xfe, + 0x9f, 0xe0, 0x29, 0x7e, 0xd8, 0x11, 0x0f, 0x10, 0x0d, 0x1d, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), + Sha1_Hash("random data")); +} + +TEST(ShaUtilTest, Sha256) { + const uint8_t kExpected[] = { + 0x62, 0x4b, 0x56, 0xb3, 0x38, 0x1b, 0xbc, 0xe0, 0x4f, 0x58, 0x88, + 0x83, 0xb4, 0x2f, 0x4e, 0x27, 0xfe, 0xc0, 0x95, 0x56, 0xf8, 0x61, + 0xcf, 0x94, 0x49, 0xe6, 0x5f, 0x26, 0xea, 0x70, 0xad, 0x88, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), + Sha256_Hash("random data")); +} + +TEST(ShaUtilTest, GenerateSha1Uuid) { + std::string name_space = + absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774"); + std::string name = "some seed value"; + EXPECT_EQ("730621a55c675c4086be38e72aefa03e", + absl::BytesToHexString(GenerateSha1Uuid(name_space, name))); +} + +} // namespace widevine diff --git a/common/signature_util.cc b/common/signature_util.cc new file mode 100644 index 0000000..b223233 --- /dev/null +++ b/common/signature_util.cc @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/signature_util.h" + +#include +#include + +#include "util/status.h" +#include "common/aes_cbc_util.h" +#include "common/rsa_key.h" +#include "common/sha_util.h" + +namespace widevine { +namespace signature_util { + +util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key, + const std::string& aes_iv, std::string* signature) { + if (signature == nullptr) { + return util::Status(util::error::INVALID_ARGUMENT, "signature is nullptr"); + } + std::string hash = Sha1_Hash(message); + if (hash.empty()) { + return util::Status(util::error::INTERNAL, "Computed hash is empty"); + } + std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash); + if (sig.empty()) { + return util::Status(util::error::INTERNAL, + "Computed AES signature is empty"); + } + *signature = sig; + return util::OkStatus(); +} + +util::Status GenerateRsaSignature(const std::string& message, + const std::string& private_key, + std::string* signature) { + if (signature == nullptr) { + return util::Status(util::error::INVALID_ARGUMENT, "signature is nullptr"); + } + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(private_key)); + if (rsa_private_key == nullptr) { + return util::Status(util::error::INTERNAL, + "Failed to construct a RsaPrivateKey"); + } + std::string sig; + if (!rsa_private_key->GenerateSignature(message, &sig)) { + return util::Status(util::error::INTERNAL, + "Failed to generate a RSA signature"); + } + if (sig.empty()) { + return util::Status(util::error::INTERNAL, + "Computed RSA signature is empty"); + } + *signature = sig; + return util::OkStatus(); +} + +} // namespace signature_util +} // namespace widevine diff --git a/common/signature_util.h b/common/signature_util.h new file mode 100644 index 0000000..f66693f --- /dev/null +++ b/common/signature_util.h @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_SIGNATURE_UTIL_H_ +#define COMMON_SIGNATURE_UTIL_H_ + +#include + +#include "util/status.h" + +namespace widevine { +namespace signature_util { + +// Generates an AES signature of |message| using |aes_key| and |aes_iv|. +// Signature is returned via |signature| if generation was successful. +// Returns a Status that carries the details of error if generation failed. +util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key, + const std::string& aes_iv, std::string* signature); + +// Generates a RSA signature of |message| using |private_key|. +// Signature is returned via |sigature| if generation was successful. +// Returns a Status that carries the details of error if generation failed. +util::Status GenerateRsaSignature(const std::string& message, + const std::string& private_key, std::string* signature); + +} // namespace signature_util +} // namespace widevine + +#endif // COMMON_SIGNATURE_UTIL_H_ diff --git a/common/signing_key_util.cc b/common/signing_key_util.cc new file mode 100644 index 0000000..959f437 --- /dev/null +++ b/common/signing_key_util.cc @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/signing_key_util.h" + +#include "glog/logging.h" +#include "common/crypto_util.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +using crypto_util::kSigningKeySizeBits; + +uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version) { + if (protocol_version <= VERSION_2_0) { + return kSigningKeySizeBits; + } else { + return kSigningKeySizeBits * 2; + } +} + +using crypto_util::kSigningKeySizeBytes; +std::string GetClientSigningKey(const std::string& derived_key, + ProtocolVersion protocol_version) { + if (protocol_version == VERSION_2_0) { + DCHECK(derived_key.size() >= kSigningKeySizeBytes); + return derived_key.substr(0, kSigningKeySizeBytes); + } else { + DCHECK(derived_key.size() >= kSigningKeySizeBytes * 2); + return derived_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes); + } +} + +std::string GetServerSigningKey(const std::string& derived_key) { + DCHECK(derived_key.size() >= kSigningKeySizeBytes); + return derived_key.substr(0, kSigningKeySizeBytes); +} + +} // namespace widevine diff --git a/common/signing_key_util.h b/common/signing_key_util.h new file mode 100644 index 0000000..eb71297 --- /dev/null +++ b/common/signing_key_util.h @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// Contains functions that are used to create and parse derived keys created +// using the NIST 800-108 KDF recommendation, using AES-CMAC PRF. +// NIST 800-108: +// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf +// AES-CMAC: +// http://tools.ietf.org/html/rfc4493 +// +// Example usage: +// using video::widevine::common::crypto_util::DeriveKey; +// using widevine_server::sdk::VERSION_2_1; +// +// std::string derived_key = DeriveKey(key_str, +// label, +// context, +// SigningKeyMaterialSize(VERSION_2_1)); +// std::string server_derived_key = GetServerSigningKey(derived_key); +// std::string client_derived_key = GetClientSigninKey(derived_key); +#ifndef COMMON_SIGNING_KEY_UTIL_H_ +#define COMMON_SIGNING_KEY_UTIL_H_ + +#include + +#include "base/macros.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +// 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 +// keys for version 2.1 have a length of 512. +uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version); + +// 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 +// are assumed to be version 2.1 keys. The last 256 bits of those +// keys are returned. Keys that are 256 bits in length are returned +// in there entirety, version 2.0 keys. +std::string GetClientSigningKey(const std::string& derived_key, + ProtocolVersion protocol_version); + +// Returns the server portion of the derived_key. The server portion +// is the first 256 bits of the key. +std::string GetServerSigningKey(const std::string& derived_key); + +} // namespace widevine + +#endif // COMMON_SIGNING_KEY_UTIL_H_ diff --git a/common/signing_key_util_test.cc b/common/signing_key_util_test.cc new file mode 100644 index 0000000..ae017a8 --- /dev/null +++ b/common/signing_key_util_test.cc @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/signing_key_util.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "common/crypto_util.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { +namespace signing_key_util { +namespace { +const char* kFrontKeyHex = + "0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b"; +const char* kBackKeyHex = + "0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d"; + +std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) { + if (protocol_version == widevine::VERSION_2_0) { + return absl::HexStringToBytes(kFrontKeyHex); + } else { + return absl::HexStringToBytes(kFrontKeyHex) + + absl::HexStringToBytes(kBackKeyHex); + } +} +} // namespace + +TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_0) { + ASSERT_EQ(crypto_util::kSigningKeySizeBits, + SigningKeyMaterialSize(VERSION_2_0)); +} + +TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_1) { + ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2, + SigningKeyMaterialSize(VERSION_2_1)); +} + +TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) { + ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey( + GenerateDerivedKey(VERSION_2_1)))); +} + +TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_1) { + ASSERT_EQ(kBackKeyHex, absl::BytesToHexString(GetClientSigningKey( + GenerateDerivedKey(VERSION_2_1), VERSION_2_1))); +} + +TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_0) { + ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey( + GenerateDerivedKey(VERSION_2_0)))); +} + +TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_0) { + ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetClientSigningKey( + GenerateDerivedKey(VERSION_2_0), VERSION_2_0))); +} + +} // namespace signing_key_util +} // namespace widevine diff --git a/common/string_util.cc b/common/string_util.cc new file mode 100644 index 0000000..e2d580b --- /dev/null +++ b/common/string_util.cc @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/string_util.h" + +#include +#include +#include +#include "util/status.h" + +namespace widevine { +namespace string_util { + +util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) { + if (output == nullptr) { + return util::Status(util::error::INTERNAL, "output is nullptr."); + } + + std::stringstream sstream(bitset); + for (size_t i = 0; i < bitset.size(); i += 8) { + std::bitset<8> bits; + sstream >> bits; + char c = static_cast(bits.to_ulong()); + *output += c; + } + + return util::OkStatus(); +} + +} // namespace string_util +} // namespace widevine diff --git a/common/string_util.h b/common/string_util.h new file mode 100644 index 0000000..57b3b6f --- /dev/null +++ b/common/string_util.h @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_STRING_UTIL_H_ +#define COMMON_STRING_UTIL_H_ + +#include +#include "util/status.h" + +namespace widevine { +namespace string_util { + +// Converts std::string representation of a bitset to its binary equivalent string. +// For example, converts "01110100011001010111001101110100" to "test". +util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output); + +} // namespace string_util +} // namespace widevine + +#endif // COMMON_STRING_UTIL_H_ diff --git a/common/string_util_test.cc b/common/string_util_test.cc new file mode 100644 index 0000000..9354571 --- /dev/null +++ b/common/string_util_test.cc @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/string_util.h" +#include "testing/gmock.h" +#include "testing/gunit.h" + +namespace widevine { +namespace string_util { + +TEST(StringUtilTest, BitsetStringToBinaryString) { + std::string input = "01110100011001010111001101110100"; + std::string output; + + EXPECT_OK(BitsetStringToBinaryString(input, &output)); + + EXPECT_EQ("test", output); +} + +} // namespace string_util +} // namespace widevine diff --git a/common/test_certificates.cc b/common/test_certificates.cc new file mode 100644 index 0000000..b9f53d8 --- /dev/null +++ b/common/test_certificates.cc @@ -0,0 +1,328 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// + +#include "common/test_certificates.h" + +namespace widevine { + +static const unsigned char kTestRootCertificate[] = { + 0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22, + 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5, + 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, + 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f, + 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, + 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b, + 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, + 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01, + 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, + 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14, + 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, + 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6, + 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, + 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31, + 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, + 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90, + 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, + 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f, + 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, + 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17, + 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, + 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf, + 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, + 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42, + 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, + 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4, + 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, + 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72, + 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, + 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04, + 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, + 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33, + 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, + 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a, + 0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35, + 0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe, + 0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49, + 0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6, + 0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58, + 0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce, + 0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27, + 0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5, + 0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c, + 0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e, + 0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12, + 0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54, + 0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8, + 0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1, + 0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1, + 0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8, + 0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4, + 0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9, + 0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd, + 0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9, + 0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f, + 0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92, + 0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15, + 0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0, + 0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9, + 0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1, + 0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2, + 0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c, + 0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36, + 0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35, + 0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7, + 0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44}; + +const unsigned char kTestIntermediateCertificate[] = { + 0x0a, 0xaf, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18, + 0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, + 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, + 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, + 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, + 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, + 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, + 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, + 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, + 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, + 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, + 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, + 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, + 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, + 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, + 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, + 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, + 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, + 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, + 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, + 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, + 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, + 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40, + 0xa8, 0xd0, 0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf, + 0xc9, 0x26, 0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4, + 0x33, 0x8e, 0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b, + 0xc3, 0x4f, 0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51, + 0xce, 0x45, 0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4, + 0x5a, 0x02, 0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9, + 0xee, 0xc9, 0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb, + 0x1f, 0x7a, 0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9, + 0x26, 0xbe, 0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25, + 0xdb, 0x85, 0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba, + 0xb9, 0x3d, 0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80, + 0x12, 0x72, 0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98, + 0xab, 0x73, 0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1, + 0x75, 0x79, 0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98, + 0xe8, 0x40, 0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f, + 0x6f, 0x23, 0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30, + 0xaa, 0xcf, 0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b, + 0x7f, 0x91, 0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a, + 0x98, 0xad, 0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e, + 0x6e, 0x9d, 0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04, + 0x4f, 0x32, 0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52, + 0xfc, 0xcc, 0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa, + 0xb3, 0x77, 0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d, + 0x3c, 0x74, 0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f, + 0x13, 0x97, 0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94, + 0x3b, 0x31, 0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b, + 0x65, 0xe9, 0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d, + 0x0a, 0xfd, 0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4, + 0x83, 0xcf, 0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16, + 0x7b, 0x66, 0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d, + 0x94, 0x5d, 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, + 0x55, 0xf8, 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a}; + +const unsigned char kTestUserDrmCertificate[] = { + 0x0a, 0xc1, 0x02, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42, + 0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18, + 0x91, 0xab, 0x4b, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51, + 0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, + 0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c, + 0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, + 0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7, + 0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, + 0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, + 0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, + 0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93, + 0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, + 0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc, + 0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, + 0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, + 0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, + 0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec, + 0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, + 0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e, + 0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, + 0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8, + 0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, + 0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd, + 0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x12, 0x80, 0x02, 0x62, 0xd5, 0x8b, 0xb6, 0x52, 0x94, 0xcb, 0x25, 0xba, + 0x68, 0x44, 0xdc, 0x6f, 0x03, 0xf6, 0x24, 0xc5, 0xba, 0x46, 0xd1, 0xa1, + 0x83, 0xe7, 0xaf, 0x94, 0x96, 0x8b, 0x57, 0x89, 0xd6, 0xa0, 0x99, 0x6f, + 0xed, 0xac, 0xfe, 0x6c, 0x9d, 0x80, 0x1c, 0xae, 0x34, 0xda, 0x49, 0x4f, + 0x10, 0x22, 0x3c, 0xdd, 0x77, 0xa0, 0x9a, 0x79, 0x73, 0x68, 0x66, 0xa4, + 0x6d, 0x1e, 0x82, 0xbf, 0xce, 0x06, 0x1a, 0x83, 0xcd, 0xa3, 0xed, 0x91, + 0xbe, 0xb1, 0xfe, 0xf3, 0xde, 0x63, 0x96, 0xd5, 0x24, 0x44, 0x46, 0x94, + 0x7f, 0xc2, 0x14, 0x19, 0x42, 0x08, 0x64, 0xef, 0x93, 0x81, 0x7a, 0x54, + 0x8b, 0x6e, 0xd9, 0xf5, 0x14, 0x88, 0x6c, 0x39, 0x6f, 0x0f, 0x70, 0x91, + 0x97, 0xd4, 0x24, 0x73, 0x9d, 0x12, 0x7a, 0xc8, 0x83, 0xd7, 0x2b, 0xc7, + 0xb7, 0xe1, 0x20, 0x6c, 0x28, 0x11, 0x6f, 0x56, 0x82, 0xf6, 0x1c, 0x4f, + 0x2d, 0x51, 0x0f, 0xd6, 0xd4, 0x14, 0xea, 0xac, 0x28, 0x66, 0xeb, 0x37, + 0xca, 0x00, 0x49, 0xff, 0xed, 0x8e, 0x8c, 0x3e, 0x4b, 0x9b, 0x12, 0x0e, + 0xbf, 0xcd, 0xb7, 0xe6, 0xed, 0xd6, 0x1f, 0x88, 0xe8, 0x99, 0x68, 0x1a, + 0xf8, 0xbb, 0xa2, 0x33, 0xfa, 0xb6, 0x21, 0xdf, 0xba, 0x24, 0x5c, 0x19, + 0xa2, 0xe7, 0x6f, 0x61, 0x90, 0x78, 0x21, 0xca, 0x2f, 0x84, 0xab, 0x9f, + 0xff, 0x37, 0x14, 0x33, 0x83, 0x43, 0x98, 0xeb, 0xa9, 0x88, 0xde, 0xad, + 0x3a, 0xd9, 0xe2, 0x5c, 0x26, 0xd3, 0x95, 0x72, 0xba, 0x8c, 0x77, 0xdf, + 0x90, 0x67, 0x4e, 0xbc, 0xda, 0x83, 0x09, 0x22, 0x70, 0x51, 0x84, 0x70, + 0x31, 0x25, 0x8b, 0xae, 0x5e, 0x19, 0xba, 0x97, 0xd7, 0x1f, 0x6a, 0xd7, + 0x95, 0xcf, 0xde, 0x8f, 0x93, 0x69, 0x88, 0x11, 0xbe, 0x8c, 0x6a, 0xfb, + 0x3c, 0x13, 0x87, 0x0e, 0x6c, 0xa5, 0xa0, 0x1a, 0xb5, 0x05, 0x0a, 0xaf, + 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18, 0xb2, 0x92, + 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, + 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, + 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, + 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, + 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, + 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, + 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, + 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, + 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, + 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, + 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, + 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, + 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, + 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, + 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, + 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, + 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, + 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, + 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, + 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, + 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, + 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x28, 0xd2, + 0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40, 0xa8, 0xd0, + 0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf, 0xc9, 0x26, + 0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4, 0x33, 0x8e, + 0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b, 0xc3, 0x4f, + 0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51, 0xce, 0x45, + 0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4, 0x5a, 0x02, + 0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9, 0xee, 0xc9, + 0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb, 0x1f, 0x7a, + 0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9, 0x26, 0xbe, + 0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25, 0xdb, 0x85, + 0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba, 0xb9, 0x3d, + 0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80, 0x12, 0x72, + 0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98, 0xab, 0x73, + 0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1, 0x75, 0x79, + 0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98, 0xe8, 0x40, + 0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f, 0x6f, 0x23, + 0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30, 0xaa, 0xcf, + 0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b, 0x7f, 0x91, + 0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a, 0x98, 0xad, + 0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e, 0x6e, 0x9d, + 0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04, 0x4f, 0x32, + 0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52, 0xfc, 0xcc, + 0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa, 0xb3, 0x77, + 0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d, 0x3c, 0x74, + 0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f, 0x13, 0x97, + 0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94, 0x3b, 0x31, + 0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b, 0x65, 0xe9, + 0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d, 0x0a, 0xfd, + 0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4, 0x83, 0xcf, + 0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16, 0x7b, 0x66, + 0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d, 0x94, 0x5d, + 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, 0x55, 0xf8, + 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a}; + +const unsigned char kTestDrmServiceCertificate[] = { + 0x0a, 0xbc, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32, + 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18, + 0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, + 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, + 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, + 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, + 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, + 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, + 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, + 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, + 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, + 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, + 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, + 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, + 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, + 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, + 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, + 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, + 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, + 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, + 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, + 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, + 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, + 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0x6e, 0xc3, + 0x5a, 0x17, 0xa8, 0xf9, 0xef, 0xee, 0x67, 0x4d, 0x0a, 0xef, 0x57, 0x5e, + 0xbc, 0x59, 0x3d, 0x22, 0x84, 0xa0, 0x0a, 0xf5, 0x84, 0x26, 0xb7, 0x8b, + 0xab, 0x91, 0x3e, 0x4b, 0xb9, 0x91, 0x3c, 0x50, 0xc9, 0x08, 0x2f, 0x97, + 0x0a, 0x91, 0xb5, 0x48, 0xe4, 0xba, 0xfd, 0x7b, 0xbd, 0xf0, 0xba, 0x08, + 0xb3, 0x29, 0xb4, 0x23, 0x74, 0xaf, 0x3f, 0xe9, 0x77, 0x78, 0x3f, 0xdc, + 0x3d, 0x8a, 0x37, 0xec, 0x1c, 0x3a, 0xff, 0x60, 0x8e, 0x10, 0x72, 0xaa, + 0x97, 0x98, 0x56, 0xa0, 0x35, 0xa9, 0xbf, 0x43, 0x21, 0x6a, 0x15, 0x88, + 0xba, 0xc0, 0x68, 0x01, 0x7b, 0xd7, 0x88, 0x2f, 0x1a, 0xc5, 0x1f, 0x54, + 0xf0, 0xea, 0x36, 0xb7, 0xed, 0x49, 0x78, 0x09, 0xb1, 0x07, 0x46, 0xfe, + 0xf4, 0xfa, 0x16, 0x0c, 0x46, 0x91, 0xe2, 0xa9, 0xe0, 0x8e, 0x97, 0xe5, + 0xea, 0x2f, 0xd9, 0x94, 0x1e, 0xe7, 0xba, 0x28, 0x98, 0x92, 0xae, 0xb8, + 0xb6, 0x6e, 0xf6, 0xd2, 0x50, 0xd3, 0x5b, 0x25, 0x12, 0x68, 0x5e, 0x07, + 0x82, 0x64, 0x27, 0xfe, 0x1a, 0xcd, 0x38, 0xa8, 0x00, 0x53, 0x8c, 0x69, + 0x51, 0x75, 0x71, 0xc2, 0x6a, 0x5f, 0x05, 0x13, 0x77, 0x2b, 0xc8, 0x6c, + 0xab, 0xd2, 0x64, 0x27, 0xbd, 0x21, 0xfc, 0x33, 0x0a, 0x3a, 0x53, 0xa6, + 0x28, 0x1c, 0x2a, 0xad, 0x23, 0x0a, 0x95, 0xe4, 0x38, 0x6b, 0x9b, 0x3e, + 0x77, 0x7d, 0x96, 0x20, 0x42, 0xf5, 0x18, 0xbe, 0xb0, 0x78, 0xe4, 0xf0, + 0x95, 0x6c, 0xd5, 0x30, 0xd6, 0xfc, 0x04, 0xe2, 0xf7, 0xff, 0x06, 0x6b, + 0xaf, 0xf1, 0x9c, 0x10, 0xa6, 0xdb, 0xed, 0x4a, 0x18, 0x68, 0x87, 0xda, + 0x43, 0x2c, 0x60, 0xc6, 0x0a, 0x72, 0x1e, 0x9f, 0x4b, 0x05, 0x80, 0x15, + 0x17, 0x84, 0xf1, 0xee, 0xcc, 0x80, 0x25, 0x33, 0x87, 0x74, 0x02, 0x8c, + 0xa1, 0xbb, 0xd9, 0x29, 0x33, 0x97, 0xbd, 0x5b, 0x1c, 0xed, 0xcc, 0x47, + 0xda, 0x73, 0xae, 0xb1, 0x75, 0xac, 0xf7, 0x39, 0xbe, 0x67, 0xc3, 0xaf, + 0x60, 0x07, 0xf5, 0xba, 0x81, 0xf4, 0x42, 0xad, 0x28, 0x8d, 0xe6, 0x63, + 0xea, 0x8a, 0x0e, 0x71, 0x53, 0x6e, 0x62, 0x8a, 0x23, 0x4f, 0xad, 0x2a, + 0x9a, 0xf6, 0xeb, 0xa8, 0x82, 0x83, 0xbb, 0x5f, 0xc9, 0x86, 0xd8, 0x76, + 0xb9, 0xf3, 0xe7, 0x32, 0xdd, 0xe0, 0x44, 0x6a, 0xab, 0x78, 0xa0, 0x8c, + 0xa4, 0x99, 0x6f, 0x71, 0x42, 0x8b, 0x31, 0x32, 0xbb, 0x80, 0x36, 0x61, + 0x1c, 0xe5, 0x6d, 0x87, 0xf2, 0x68, 0xca, 0xcd, 0xe0, 0x5f, 0xa2, 0x68, + 0x5b, 0xfc, 0x73, 0xc9, 0x26, 0x2b, 0x13, 0x05, 0x1c, 0xde, 0x19, 0xdf, + 0x34, 0xba, 0xf5, 0xec, 0xaf, 0x26, 0xfb, 0x64, 0xc4, 0x38, 0x7e, 0xdb, + 0x51, 0x28, 0x49, 0xa7, 0x12, 0x88, 0xa5, 0x6d, 0xa2, 0xfa}; + +TestCertificates::TestCertificates() + : test_root_certificate_( + kTestRootCertificate, + kTestRootCertificate + sizeof(kTestRootCertificate)), + test_intermediate_certificate_( + kTestIntermediateCertificate, + kTestIntermediateCertificate + sizeof(kTestIntermediateCertificate)), + test_user_device_certificate_( + kTestUserDrmCertificate, + kTestUserDrmCertificate + sizeof(kTestUserDrmCertificate)), + test_service_certificate_( + kTestDrmServiceCertificate, + kTestDrmServiceCertificate + sizeof(kTestDrmServiceCertificate)) {} + +} // namespace widevine diff --git a/common/test_certificates.h b/common/test_certificates.h new file mode 100644 index 0000000..598b492 --- /dev/null +++ b/common/test_certificates.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// +// Class contains certificates that can be used for testing. Provides methods +// to retrieve a test root certificate, a test intermediate certificate and a +// test user device certificate. +#ifndef COMMON_TEST_CERTIFICATES_H_ +#define COMMON_TEST_CERTIFICATES_H_ + +#include +#include "base/macros.h" + +namespace widevine { + +class TestCertificates { + public: + TestCertificates(); + virtual ~TestCertificates() {} + + // returns a test root certificate + const std::string& test_root_certificate() const { return test_root_certificate_; } + + // returns a test intermediate certificate + const std::string& test_intermediate_certificate() const { + return test_intermediate_certificate_; + } + + // returns an user device certificate + const std::string& test_user_device_certificate() const { + return test_user_device_certificate_; + } + + // returns a service certificate + const std::string& test_service_certificate() const { + return test_service_certificate_; + } + + private: + const std::string test_root_certificate_; + const std::string test_intermediate_certificate_; + const std::string test_user_device_certificate_; + const std::string test_service_certificate_; + + DISALLOW_COPY_AND_ASSIGN(TestCertificates); +}; + +} // namespace widevine +#endif // COMMON_TEST_CERTIFICATES_H_ diff --git a/common/test_utils.cc b/common/test_utils.cc new file mode 100644 index 0000000..8628d64 --- /dev/null +++ b/common/test_utils.cc @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/test_utils.h" + +#include +#include + +#include "glog/logging.h" +#include "openssl/pem.h" +#include "openssl/rsa.h" +#include "openssl/sha.h" + +namespace widevine { + +util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key, + const std::string& message, + std::string* signature) { + CHECK(signature); + if (pem_private_key.empty()) { + return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM private key"); + } + if (message.empty()) { + return util::Status(util::error::INVALID_ARGUMENT, "Empty message"); + } + BIO* bio(NULL); + bio = BIO_new_mem_buf(const_cast(pem_private_key.data()), + pem_private_key.size()); + if (bio == NULL) { + return util::Status(util::error::INTERNAL, "BIO allocation failed"); + } + util::Status status; + RSA* key(NULL); + std::unique_ptr sig_buffer; + unsigned int sig_size; + unsigned char digest[SHA256_DIGEST_LENGTH]; + key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL); + if (key == NULL) { + status = util::Status(util::Status::canonical_space(), + util::error::INVALID_ARGUMENT, + "PEM RSA private key load failed"); + goto cleanup; + } + SHA256(reinterpret_cast(message.data()), message.size(), + digest); + sig_size = RSA_size(key); + sig_buffer.reset(new char[sig_size]); + if (RSA_sign(NID_sha256, digest, sizeof(digest), + reinterpret_cast(sig_buffer.get()), &sig_size, + key) != 1) { + status = + util::Status(util::Status::canonical_space(), util::error::INTERNAL, + "RSA private encrypt failed"); + goto cleanup; + } + signature->assign(sig_buffer.get(), sig_size); + +cleanup: + if (key != NULL) { + RSA_free(key); + } + if (bio != NULL) { + BIO_free(bio); + } + return status; +} + +} // namespace widevine diff --git a/common/test_utils.h b/common/test_utils.h new file mode 100644 index 0000000..e78ef12 --- /dev/null +++ b/common/test_utils.h @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Auxiliary functions for license server SDK testing. + +#ifndef COMMON_TEST_UTILS_H_ +#define COMMON_TEST_UTILS_H_ + +#include + +#include "util/status.h" + +namespace widevine { + +// Generate RSA signature using the specified RSA private key, SHA256 digest, +// and PKCS#1 1.5 padding. |pem_private_key| is a PEM-encoded private RSA key, +// |message| is the message to be signed, and |signature| is a pointer to a +// std::string where the signature will be stored. The caller returns ownership of +// all paramters. +util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key, + const std::string& message, + std::string* signature); + +} // namespace widevine + +#endif // COMMON_TEST_UTILS_H_ diff --git a/common/verified_media_pipeline.cc b/common/verified_media_pipeline.cc new file mode 100644 index 0000000..641ddaf --- /dev/null +++ b/common/verified_media_pipeline.cc @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Helper methods for verifying VMP (Verified Media Pipeline) data. + +#include "common/verified_media_pipeline.h" + +#include "common/vmp_checker.h" + +namespace widevine { +util::Status VerifyVmpData( + const std::string& vmp_data, + PlatformVerificationStatus* platform_verification_status) { + *platform_verification_status = PLATFORM_UNVERIFIED; + VmpChecker::Result vmp_result; + util::Status status = + VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result); + if (status.ok()) { + switch (vmp_result) { + case VmpChecker::kUnverified: + *platform_verification_status = PLATFORM_UNVERIFIED; + break; + case VmpChecker::kVerified: + *platform_verification_status = PLATFORM_SOFTWARE_VERIFIED; + break; + case VmpChecker::kSecureStorageVerified: + *platform_verification_status = + PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED; + break; + case VmpChecker::kTampered: + *platform_verification_status = PLATFORM_TAMPERED; + break; + } + } else { + *platform_verification_status = PLATFORM_TAMPERED; + } + return status; +} +} // namespace widevine diff --git a/common/verified_media_pipeline.h b/common/verified_media_pipeline.h new file mode 100644 index 0000000..64a9582 --- /dev/null +++ b/common/verified_media_pipeline.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Helper methods for verifying VMP (Verified Media Pipeline) data. + +#ifndef COMMON_VERIFIED_MEDIA_PIPELINE_H_ +#define COMMON_VERIFIED_MEDIA_PIPELINE_H_ + +#include +#include "util/status.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +// Retrieve the PlatformVerificationStatus for |vmp_data|. The +// PlatformVerificationStatus is defined at +util::Status VerifyVmpData( + const std::string& vmp_data, + PlatformVerificationStatus* platform_verification_status); + +} // namespace widevine +#endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_ diff --git a/common/vmp_checker.cc b/common/vmp_checker.cc new file mode 100644 index 0000000..cbba839 --- /dev/null +++ b/common/vmp_checker.cc @@ -0,0 +1,359 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Singleton object which validates VMP (Verified Media Pipeline) data for +// purposes of platform software verification. Thread safe. + +#include "common/vmp_checker.h" + +#include +#include + +#include +#include "glog/logging.h" +#include "common/certificate_type.h" +#include "common/error_space.h" +#include "common/rsa_key.h" +#include "common/x509_cert.h" +#include "protos/public/errors.pb.h" +#include "protos/public/verified_media_pipeline.pb.h" + +namespace { + +const uint32_t kBlessedBinaryFlag = 0x00000001; + +const uint8_t kDevVmpCodeSigningDrmRootCertificate[] = { + 0x30, 0x82, 0x04, 0xb8, 0x30, 0x82, 0x03, 0x20, 0x02, 0x09, 0x00, 0xc5, + 0xf8, 0x2f, 0x03, 0x8f, 0xac, 0xf1, 0x58, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, + 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, + 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, + 0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, + 0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73, 0x6b, + 0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x30, 0x32, 0x38, 0x30, + 0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x31, 0x36, + 0x31, 0x30, 0x30, 0x34, 0x30, 0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x30, + 0x81, 0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, + 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, + 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, + 0x6e, 0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, + 0x69, 0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73, + 0x6b, 0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, + 0x6f, 0x6d, 0x30, 0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x8f, 0x00, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xd2, + 0x6f, 0x60, 0x7f, 0xff, 0x7c, 0xbd, 0xa4, 0xe5, 0x8c, 0xa6, 0xcf, 0xde, + 0x22, 0x6d, 0x3a, 0x5e, 0x83, 0xa9, 0x0e, 0x9b, 0xd4, 0x93, 0xb8, 0xb0, + 0xe0, 0x5d, 0x03, 0x3d, 0xc1, 0x00, 0xb8, 0x1a, 0xcc, 0x84, 0x31, 0xfb, + 0x9e, 0x97, 0x79, 0x17, 0x04, 0x48, 0xe7, 0x13, 0x98, 0x0a, 0x47, 0x41, + 0xac, 0x6c, 0x52, 0xb0, 0xca, 0x9e, 0xfb, 0xfd, 0x78, 0x65, 0xd0, 0xd6, + 0x12, 0x07, 0x7e, 0x24, 0x65, 0x46, 0x6c, 0xb9, 0x23, 0xbb, 0xdc, 0x02, + 0x03, 0xc7, 0xb0, 0x02, 0xc1, 0xd3, 0x10, 0x59, 0xe7, 0x0b, 0x45, 0x13, + 0x73, 0x5f, 0xae, 0x58, 0xcd, 0xbf, 0x42, 0x8a, 0xac, 0xf5, 0x6a, 0x1e, + 0x75, 0x26, 0xb1, 0x69, 0x07, 0xad, 0xf5, 0xfd, 0x4c, 0xaa, 0x66, 0x55, + 0x74, 0x56, 0x9a, 0x9e, 0x40, 0x78, 0x77, 0x9a, 0x39, 0x7a, 0x37, 0xe4, + 0x25, 0x6b, 0x07, 0x09, 0xbe, 0xe2, 0x0d, 0x23, 0x83, 0xfc, 0x94, 0x9f, + 0x26, 0x98, 0x0e, 0x49, 0x81, 0x7b, 0xf4, 0xe6, 0xd4, 0xda, 0x7a, 0xc9, + 0xa4, 0x14, 0x5a, 0xa9, 0xaf, 0x0c, 0xd9, 0xf1, 0xbc, 0xd8, 0x6c, 0xd2, + 0xd4, 0x1b, 0x82, 0x10, 0x3d, 0x87, 0xf1, 0x81, 0xe6, 0x1a, 0xb7, 0xfa, + 0xfa, 0x1f, 0x9c, 0xde, 0xa1, 0x3f, 0x11, 0xb1, 0xd8, 0x26, 0xd1, 0x86, + 0x21, 0xdc, 0x03, 0xcb, 0xd6, 0x40, 0xfb, 0x5f, 0xb0, 0x84, 0x7f, 0x69, + 0x9e, 0xa3, 0xbc, 0xfb, 0x03, 0xb6, 0xc1, 0xb9, 0x23, 0xb1, 0x20, 0x6f, + 0x71, 0xf5, 0x7a, 0x3b, 0x84, 0x30, 0xa8, 0x59, 0xc0, 0x8f, 0x75, 0xfd, + 0xcb, 0xe1, 0xc3, 0x5f, 0xe7, 0x8a, 0xb0, 0xe9, 0xf8, 0xef, 0x04, 0x4b, + 0x4a, 0xf6, 0xc3, 0x7d, 0x08, 0xfe, 0x08, 0x52, 0x2e, 0xbc, 0x1f, 0x65, + 0xf6, 0x51, 0xb7, 0xd8, 0x24, 0x21, 0x49, 0x1d, 0x7f, 0x16, 0x28, 0x14, + 0xd9, 0xc2, 0x19, 0xdb, 0xa2, 0xc6, 0xf0, 0x3a, 0x2d, 0x98, 0x70, 0x72, + 0x45, 0xf7, 0x80, 0x37, 0x56, 0x0b, 0x0c, 0x6f, 0x80, 0xf1, 0x8c, 0xe2, + 0xf3, 0x4d, 0x16, 0xc5, 0x74, 0x90, 0x34, 0x1e, 0x57, 0x3c, 0xde, 0xf1, + 0xc4, 0x8c, 0x17, 0x09, 0xd3, 0xc5, 0x92, 0x9d, 0xcf, 0xdc, 0x7b, 0x4f, + 0xae, 0x20, 0x10, 0xd7, 0x04, 0x56, 0x9d, 0x9a, 0xa9, 0xd8, 0x06, 0x4a, + 0xd6, 0x68, 0xd4, 0x83, 0xcb, 0x7d, 0xe2, 0x62, 0xd1, 0x99, 0xb8, 0x9d, + 0x81, 0xc7, 0xfc, 0x94, 0x69, 0x0d, 0x2a, 0x1c, 0x99, 0xcf, 0x40, 0xc3, + 0xfd, 0xe9, 0x64, 0x5b, 0xc3, 0x1d, 0xda, 0x1c, 0x89, 0xab, 0x34, 0x1b, + 0x53, 0x6e, 0xad, 0xf0, 0x6e, 0x97, 0x87, 0xe8, 0xfb, 0x0c, 0x96, 0x93, + 0x1b, 0x52, 0x82, 0x6a, 0xba, 0x0f, 0xe3, 0x5d, 0xc4, 0x17, 0xdc, 0xe4, + 0x31, 0x78, 0x12, 0x26, 0x10, 0x74, 0x14, 0x7c, 0x45, 0xb2, 0xb9, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00, + 0x8f, 0x5b, 0x2a, 0x40, 0xce, 0xae, 0xa2, 0x79, 0xfc, 0xc5, 0x5e, 0x94, + 0xc8, 0x10, 0x4b, 0xf1, 0x21, 0x7f, 0x4b, 0xeb, 0x81, 0x3d, 0xb8, 0x26, + 0x83, 0x84, 0x76, 0x79, 0xda, 0x35, 0xfc, 0xdf, 0xfe, 0x10, 0x7a, 0xd5, + 0x17, 0xc0, 0xad, 0x0d, 0xf9, 0x3f, 0xa6, 0xa1, 0xcd, 0x6c, 0x9c, 0x3b, + 0x52, 0xbd, 0x04, 0xf9, 0xe2, 0x9e, 0x86, 0x83, 0x98, 0x60, 0x01, 0x99, + 0xb7, 0xbd, 0x02, 0x87, 0xd8, 0xea, 0x65, 0xaa, 0x60, 0x6f, 0x33, 0x50, + 0x25, 0x84, 0xb7, 0x42, 0x63, 0x39, 0xfd, 0x17, 0x67, 0x74, 0x88, 0x66, + 0xe2, 0x38, 0x59, 0xf6, 0x9b, 0x98, 0x95, 0xdd, 0x54, 0x2c, 0x69, 0x6a, + 0x0a, 0x51, 0x66, 0x4d, 0x65, 0xc0, 0x58, 0x3e, 0xcf, 0x15, 0x63, 0x7a, + 0x32, 0xc8, 0xfb, 0xe7, 0x11, 0x2e, 0x25, 0x17, 0x52, 0x4d, 0x8e, 0x6b, + 0x6d, 0x58, 0x4e, 0xf6, 0xd6, 0xb0, 0xfa, 0x0d, 0x7a, 0xb1, 0x44, 0x52, + 0x9a, 0x6c, 0x90, 0x38, 0x68, 0xa5, 0xa9, 0x9b, 0xc5, 0x45, 0x09, 0xfa, + 0xaa, 0x8c, 0xfe, 0x91, 0x55, 0x93, 0x35, 0x52, 0x45, 0xbb, 0xaa, 0x5b, + 0xf0, 0x63, 0x53, 0x13, 0xcf, 0x48, 0x7b, 0xaa, 0x20, 0xa0, 0x07, 0x43, + 0x1d, 0xd7, 0xb3, 0x4a, 0x1b, 0x8c, 0x51, 0xb5, 0xf6, 0xb9, 0x5b, 0x13, + 0x02, 0x74, 0x3e, 0x48, 0xde, 0xec, 0xeb, 0x65, 0xfe, 0xf2, 0x61, 0xe5, + 0x68, 0xd5, 0xea, 0xd9, 0x79, 0xa6, 0x71, 0xb1, 0x57, 0x0f, 0xbc, 0xd0, + 0x31, 0x4f, 0xff, 0xc5, 0x95, 0xe8, 0xee, 0x70, 0x18, 0xb9, 0xbc, 0x19, + 0xcd, 0x3a, 0x06, 0x75, 0xe4, 0x57, 0xc1, 0x2e, 0x32, 0x19, 0xdd, 0x2e, + 0x45, 0xc0, 0x19, 0xe6, 0x72, 0x81, 0x2c, 0xb6, 0xed, 0x1c, 0xd4, 0xef, + 0x42, 0x18, 0x44, 0x44, 0x75, 0xd6, 0x29, 0x81, 0xe1, 0xf7, 0x5b, 0x48, + 0xa3, 0xf8, 0x92, 0x54, 0xd0, 0x79, 0xa1, 0xe1, 0x8e, 0xa8, 0x98, 0x2d, + 0x57, 0x5d, 0xb5, 0x5a, 0x01, 0x1b, 0xb3, 0xcf, 0x5f, 0x64, 0x2e, 0x70, + 0xba, 0xa0, 0x41, 0xbb, 0xd4, 0x82, 0x28, 0x3c, 0xb1, 0x81, 0x76, 0xd6, + 0x85, 0x2e, 0xc6, 0x01, 0x7f, 0xae, 0xc3, 0x17, 0x2f, 0xed, 0xbe, 0xad, + 0xa2, 0x7c, 0x53, 0xc6, 0x77, 0x73, 0x1d, 0x19, 0x90, 0x2a, 0xf8, 0xd5, + 0x50, 0x65, 0xc1, 0x22, 0x3c, 0x24, 0x96, 0xeb, 0x7b, 0x53, 0x8f, 0xbf, + 0xd9, 0xf7, 0x80, 0xa8, 0x76, 0x72, 0xea, 0xb7, 0x7f, 0xf7, 0xa2, 0x52, + 0xc7, 0xa7, 0xd6, 0x88, 0xa1, 0x38, 0x40, 0x5d, 0xcd, 0xdb, 0xe3, 0x8e, + 0xc7, 0xf9, 0x39, 0xbe, 0xfa, 0x27, 0x41, 0x73, 0x3a, 0x1c, 0xb3, 0x03, + 0xf1, 0x36, 0xea, 0xe8, 0xb3, 0xe1, 0x6e, 0x59, 0xcc, 0xe2, 0x75, 0x2b, + 0xf9, 0x55, 0xb9, 0xc2, 0xdf, 0x0a, 0x8d, 0x8d, 0xd1, 0x62, 0x3b, 0x86}; + +const uint8_t kProdVmpCodeSigningDrmRootCertificate[] = { + 0x30, 0x82, 0x04, 0xd7, 0x30, 0x82, 0x03, 0x3f, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x11, 0x00, 0xca, 0xa4, 0xbd, 0x6b, 0x93, 0x56, 0x4a, 0xf1, + 0x84, 0xef, 0x5e, 0xed, 0xe8, 0xf7, 0xe2, 0x0b, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, + 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, + 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, + 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, + 0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x72, + 0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x37, + 0x30, 0x31, 0x32, 0x34, 0x32, 0x33, 0x33, 0x38, 0x31, 0x34, 0x5a, 0x18, + 0x0f, 0x33, 0x30, 0x31, 0x36, 0x30, 0x35, 0x32, 0x37, 0x32, 0x33, 0x33, + 0x38, 0x31, 0x34, 0x5a, 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, + 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, + 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, + 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69, + 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, + 0x69, 0x67, 0x6e, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30, + 0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30, + 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xe0, 0xb5, 0xc5, 0x6d, + 0x04, 0xc1, 0x97, 0xaf, 0xe7, 0x62, 0xfb, 0x84, 0xdc, 0xd1, 0xf4, 0xb1, + 0xb5, 0xa2, 0x7c, 0xca, 0x31, 0xf8, 0xce, 0xa7, 0x7a, 0x92, 0xc2, 0xbe, + 0x14, 0xdc, 0x85, 0x9f, 0x18, 0x9e, 0x78, 0xba, 0x65, 0x05, 0x56, 0x88, + 0x88, 0xdc, 0x1f, 0x4f, 0x24, 0x7f, 0xf5, 0x26, 0x6f, 0x6e, 0xcc, 0x04, + 0x2f, 0x38, 0xb8, 0xcd, 0x27, 0xd7, 0x9e, 0x07, 0xd3, 0xa9, 0xd4, 0x6b, + 0x84, 0xfe, 0xf8, 0xac, 0x9c, 0x53, 0xdf, 0x7a, 0x45, 0x5e, 0x77, 0xf8, + 0x4e, 0x88, 0x00, 0x5c, 0x6f, 0xb6, 0xa7, 0x0b, 0x4b, 0x63, 0x57, 0x92, + 0x7a, 0x7b, 0x3d, 0x20, 0x88, 0x3e, 0x7b, 0xb4, 0x28, 0x2b, 0x63, 0x81, + 0xd4, 0x0c, 0x4c, 0xb7, 0x54, 0x68, 0x68, 0x2c, 0x0d, 0xf5, 0xa6, 0x9e, + 0x16, 0x93, 0x76, 0xdd, 0xc0, 0xd5, 0x93, 0x65, 0x99, 0x90, 0x17, 0x2d, + 0x2b, 0xdc, 0x6f, 0xaf, 0x58, 0xfd, 0x78, 0xe9, 0xf5, 0xde, 0x2e, 0x36, + 0x95, 0xf0, 0xcf, 0x25, 0x41, 0x3d, 0x4f, 0x37, 0xd1, 0x70, 0x5b, 0xb5, + 0xc0, 0xc8, 0xf3, 0x63, 0xa3, 0xda, 0x9f, 0x94, 0xdb, 0xf8, 0x51, 0xf2, + 0xa9, 0xe5, 0x67, 0x0e, 0x29, 0xb3, 0x45, 0x12, 0xc5, 0x42, 0xf9, 0x3b, + 0x38, 0xf9, 0xa5, 0x7b, 0x41, 0x88, 0x6f, 0x32, 0x62, 0x03, 0x5f, 0xfd, + 0x35, 0x97, 0xc2, 0x83, 0x15, 0xb6, 0x56, 0x4f, 0xbb, 0x81, 0x39, 0x37, + 0xf2, 0x9c, 0x2a, 0x61, 0xa8, 0x63, 0x5f, 0xa0, 0x27, 0x30, 0x06, 0xd4, + 0xcb, 0x9d, 0xb7, 0xe9, 0xf2, 0xae, 0xd6, 0xc9, 0xcd, 0x72, 0xa3, 0xe6, + 0xf8, 0x54, 0x03, 0x6e, 0xe1, 0x95, 0x03, 0xdd, 0x7a, 0x85, 0xb3, 0x5c, + 0xa7, 0xca, 0x99, 0xec, 0xa6, 0xe8, 0x1a, 0xb2, 0x72, 0xe1, 0x91, 0x2d, + 0x97, 0xe3, 0x2a, 0x9c, 0x42, 0xaa, 0x45, 0xf2, 0x8e, 0x51, 0xc0, 0xd8, + 0x21, 0x83, 0x66, 0x07, 0xb5, 0x20, 0xb8, 0x28, 0xa5, 0xde, 0xfb, 0x4e, + 0x2e, 0xc7, 0x70, 0x9b, 0x3d, 0x52, 0x66, 0x24, 0xc5, 0xa2, 0x2e, 0x49, + 0x54, 0x5c, 0xfd, 0xc0, 0xde, 0xf6, 0x9d, 0xb4, 0x70, 0x31, 0x2b, 0xac, + 0x14, 0xfb, 0x19, 0x9e, 0x89, 0xd5, 0x07, 0x87, 0xa0, 0xd8, 0x15, 0x96, + 0xe9, 0xf7, 0x91, 0x36, 0x52, 0x83, 0x3c, 0x2c, 0xfa, 0xb5, 0xc4, 0xc6, + 0x1a, 0x34, 0xf0, 0x53, 0x94, 0x15, 0x82, 0xa2, 0x2f, 0x98, 0xbb, 0x49, + 0xca, 0xf7, 0xe0, 0xcb, 0x9e, 0x3c, 0xa6, 0x64, 0x59, 0x77, 0x63, 0xd1, + 0x05, 0x03, 0x99, 0x6c, 0x50, 0x08, 0xec, 0x64, 0x86, 0xf7, 0x97, 0xaf, + 0xf8, 0xcc, 0xdf, 0x91, 0xc7, 0x2c, 0x15, 0x0f, 0xa7, 0x0e, 0x02, 0x33, + 0x63, 0x84, 0xb7, 0x7e, 0xd3, 0x10, 0x89, 0x05, 0x2d, 0xb4, 0x68, 0x38, + 0xe0, 0x00, 0x49, 0xda, 0xaa, 0xb9, 0xf9, 0xbf, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f, + 0xd0, 0x9a, 0xd9, 0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f, 0xd0, 0x9a, 0xd9, + 0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86, 0x30, 0x0c, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00, 0xc1, 0x81, 0x17, 0x08, 0x0a, + 0xcb, 0xae, 0x54, 0x92, 0xed, 0x0f, 0xc0, 0xf0, 0x83, 0xb8, 0xe9, 0x2a, + 0xd5, 0x65, 0x18, 0xb5, 0x92, 0xfc, 0x67, 0x9c, 0x39, 0x9e, 0x3a, 0x93, + 0x1f, 0x91, 0x7b, 0x35, 0x6d, 0x09, 0x6c, 0x3c, 0x02, 0x85, 0xe8, 0xc6, + 0x0c, 0x6b, 0x35, 0xeb, 0x8c, 0xe4, 0x44, 0x80, 0xc5, 0x4e, 0x65, 0xa6, + 0xbc, 0x25, 0x93, 0x5a, 0xed, 0x5a, 0xd1, 0x5a, 0xcf, 0xf1, 0xbe, 0xd5, + 0x46, 0x7c, 0x78, 0xfe, 0xb6, 0xa6, 0x9c, 0x85, 0xa9, 0xe2, 0x13, 0x70, + 0x99, 0x03, 0x5a, 0x3a, 0xa8, 0x7b, 0xdd, 0x50, 0x76, 0x83, 0xfe, 0x49, + 0xc0, 0x5e, 0xc7, 0xf1, 0x18, 0xc0, 0xe6, 0xb7, 0xc7, 0xe4, 0x9d, 0x54, + 0xaf, 0x25, 0xdf, 0xe4, 0x81, 0xc1, 0xe9, 0xaa, 0x7c, 0x05, 0x20, 0xfa, + 0x91, 0x47, 0xd1, 0x4a, 0xe2, 0x24, 0x6f, 0x72, 0x22, 0x69, 0xd0, 0x89, + 0x78, 0x2c, 0x9a, 0x16, 0x7d, 0x92, 0xdd, 0x64, 0x6c, 0xc8, 0xbf, 0x33, + 0xe3, 0x91, 0xb9, 0xb5, 0x16, 0xfe, 0xfd, 0xa2, 0xdb, 0x82, 0xf0, 0xec, + 0xac, 0xe7, 0xf9, 0x2e, 0xac, 0x50, 0xf0, 0xcf, 0xba, 0xe1, 0x55, 0xa9, + 0xb0, 0xd9, 0x66, 0x3f, 0xb6, 0xee, 0x57, 0xff, 0x8d, 0x43, 0xb7, 0xc3, + 0xb4, 0x44, 0xa9, 0xcc, 0x99, 0xa2, 0xbf, 0xac, 0x4f, 0xec, 0xed, 0xb5, + 0xd0, 0x3f, 0xa9, 0x50, 0x3b, 0xc1, 0x24, 0xf2, 0xd0, 0xef, 0x5c, 0xbf, + 0x5c, 0xc2, 0x41, 0x29, 0xbe, 0xb6, 0x76, 0x5a, 0x19, 0xde, 0x67, 0x1c, + 0x2a, 0x67, 0xae, 0x07, 0xe8, 0xfa, 0x49, 0xf1, 0x81, 0xbc, 0x22, 0xa1, + 0xe6, 0x5d, 0x27, 0x76, 0x0f, 0x4d, 0x41, 0x57, 0xcf, 0x0f, 0x12, 0x2f, + 0xdd, 0x20, 0x88, 0xcf, 0xc8, 0xa7, 0xc0, 0xe5, 0xec, 0xcd, 0xb9, 0xa7, + 0x1c, 0x29, 0x55, 0xed, 0x67, 0xf9, 0x38, 0x33, 0xea, 0x85, 0xe9, 0x69, + 0x5a, 0x7c, 0xfe, 0x37, 0x3b, 0xdd, 0x61, 0x5f, 0xaa, 0xc3, 0x18, 0xbc, + 0x58, 0x95, 0x39, 0x61, 0x79, 0xa1, 0x46, 0xcc, 0xc0, 0xe7, 0xd6, 0x52, + 0x3c, 0xc7, 0xfa, 0xed, 0x89, 0x06, 0xeb, 0xd4, 0x5e, 0x9c, 0xa5, 0x55, + 0x8c, 0xe3, 0x5f, 0xe6, 0xb4, 0x0a, 0xf4, 0xf6, 0x7d, 0xeb, 0x64, 0x74, + 0xa9, 0x1a, 0x8d, 0x6e, 0xf1, 0x41, 0xc7, 0x7e, 0xc6, 0x26, 0x3a, 0x47, + 0x70, 0x49, 0x07, 0x27, 0xa2, 0xb9, 0xc6, 0x79, 0x9d, 0x94, 0xf5, 0x51, + 0x69, 0xdf, 0xbd, 0x84, 0xee, 0xaa, 0x46, 0xea, 0x4b, 0x27, 0xb6, 0x5c, + 0xac, 0xcf, 0x4a, 0x48, 0x12, 0x40, 0x86, 0x80, 0xd4, 0xb8, 0x0a, 0xb1, + 0x9f, 0xdc, 0x68, 0x60, 0x14, 0x33, 0x1d, 0x88, 0xfb, 0xa2, 0xfc, 0x49, + 0x0c, 0xa9, 0x76, 0x2d, 0xd7, 0x32, 0x3f, 0x77, 0xdb, 0x62, 0x8c, 0x35, + 0x88, 0x5d, 0x66, 0xc9, 0x8d, 0x07, 0xe5}; + +const char kCodeSigningDevelopmentFlagOid[] = "1.3.6.1.4.1.11129.4.1.2"; +const char kSecureStorageFlagOid[] = "1.3.6.1.4.1.11129.4.1.3"; + +} // namespace + +namespace widevine { + +VmpChecker::VmpChecker() : allow_development_vmp_(false) {} + +VmpChecker::~VmpChecker() {} + +util::Status VmpChecker::SelectDrmCertificateType(CertificateType cert_type) { + std::unique_ptr ca_cert(new X509Cert); + util::Status status = ca_cert->LoadDer( + cert_type == kCertificateTypeProduction + ? std::string(reinterpret_cast( + kProdVmpCodeSigningDrmRootCertificate), + sizeof(kProdVmpCodeSigningDrmRootCertificate)) + : std::string(reinterpret_cast( + kDevVmpCodeSigningDrmRootCertificate), + sizeof(kDevVmpCodeSigningDrmRootCertificate))); + if (!status.ok()) return status; + + ca_.reset(new X509CA(ca_cert.release())); + + return util::OkStatus(); +} + +VmpChecker* VmpChecker::Instance() { + static VmpChecker instance; + return &instance; +} + +// Verify VMP data and return appropriate result. +util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) { + DCHECK(!vmp_data.empty()); + DCHECK(result); + + if (!ca_) return util::Status(error_space, CERT_CHAIN_NOT_SELECTED, ""); + + vmp::VmpData vmp_data_obj; + if (!vmp_data_obj.ParseFromString(vmp_data)) { + LOG(INFO) << "Error deserializing VmpData."; + return util::Status(error_space, INVALID_MESSAGE, + "vmp-data-deserialize-failed"); + } + + std::vector> code_signing_certs; + const std::string kDevelopmentFlagOid(kCodeSigningDevelopmentFlagOid); + bool secure_storage_verified(true); + for (int cert_idx = 0; cert_idx < vmp_data_obj.certificates_size(); + ++cert_idx) { + code_signing_certs.emplace_back(new X509Cert); + util::Status status(code_signing_certs.back()->LoadDer( + vmp_data_obj.certificates(cert_idx))); + if (!status.ok()) return status; + + if (!allow_development_vmp_) { + bool dev_flag; + if (code_signing_certs.back()->GetV3BooleanExtension(kDevelopmentFlagOid, + &dev_flag) && + dev_flag) { + return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, + "development-vmp-certificate-not-allowed"); + } + } + status = ca_->VerifyCert(*code_signing_certs.back()); + if (!status.ok()) return status; + + if (secure_storage_verified) { + bool secure_storage_flag; + if (!code_signing_certs.back()->GetV3BooleanExtension( + kSecureStorageFlagOid, &secure_storage_flag) || + !secure_storage_flag) { + secure_storage_verified = false; + } + } + } + + size_t num_blessed_binaries(0); + for (int binary_idx = 0; binary_idx < vmp_data_obj.signed_binary_info_size(); + ++binary_idx) { + const vmp::VmpData::SignedBinaryInfo& binary_info( + vmp_data_obj.signed_binary_info(binary_idx)); + if (binary_info.signature().empty()) { + LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\"."; + *result = kTampered; + return util::OkStatus(); + } + if (binary_info.certificate_index() >= code_signing_certs.size()) { + LOG(INFO) << "Invalid code signing certificate index."; + *result = kTampered; + return util::OkStatus(); + } + X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get(); + std::unique_ptr key(cert->GetRsaPublicKey()); + std::string message(binary_info.binary_hash()); + message += binary_info.flags() & 0xff; + if (!key->VerifySignature(message, binary_info.signature())) { + LOG(INFO) << "Code signature verification failed for file \"" + << binary_info.file_name() << "\"."; + *result = kTampered; + return util::OkStatus(); + } + if (binary_info.flags() & kBlessedBinaryFlag) ++num_blessed_binaries; + } + if (num_blessed_binaries != 1) { + LOG(INFO) << "Invalid number of blessed binaries (" << num_blessed_binaries + << ")."; + *result = kTampered; + return util::OkStatus(); + } + + VLOG(2) << "VMP verification success. Secure storage: " + << secure_storage_verified; + *result = secure_storage_verified ? kSecureStorageVerified : kVerified; + return util::OkStatus(); +} + +} // namespace widevine diff --git a/common/vmp_checker.h b/common/vmp_checker.h new file mode 100644 index 0000000..e5269ad --- /dev/null +++ b/common/vmp_checker.h @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Singleton object which validates VMP (Verified Media Pipeline) data for +// purposes of platform software verification. + +#ifndef COMMON_VMP_CHECKER_H_ +#define COMMON_VMP_CHECKER_H_ + +#include +#include + +#include "util/status.h" +#include "common/certificate_type.h" + +namespace widevine { +class X509CA; + +class VmpChecker { + public: + enum Result { + kUnverified = 0, + kVerified = 1, + kSecureStorageVerified = 2, + kTampered = 3 + }; + + // Singleton accessor. + static VmpChecker* Instance(); + + // Select the type of root to use. Not thread-safe. + virtual util::Status SelectDrmCertificateType(CertificateType root_type); + + // Verify VMP data and return appropriate result. + virtual util::Status VerifyVmpData(const std::string& vmp_data, Result* result); + + // Enable/disable development code signing certificates. + void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; } + bool allow_development_vmp() const { return allow_development_vmp_; } + + private: + VmpChecker(); + ~VmpChecker(); + + std::unique_ptr ca_; + bool allow_development_vmp_ = false; +}; + +} // namespace widevine + +#endif // COMMON_VMP_CHECKER_H_ diff --git a/common/vmp_checker_test.cc b/common/vmp_checker_test.cc new file mode 100644 index 0000000..521b5a6 --- /dev/null +++ b/common/vmp_checker_test.cc @@ -0,0 +1,321 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include + +#include "glog/logging.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "common/rsa_key.h" +#include "common/vmp_checker.h" +#include "protos/public/errors.pb.h" +#include "protos/public/verified_media_pipeline.pb.h" + +namespace widevine { + +namespace { + +const uint32_t kBlessedBinaryFlag = 0x00000001; + +const char kDevVmpCodeSigningKey[] = + "308204a50201000282010100b3a7da87309390688388d614a5a37e7ee73a" + "c3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd" + "8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6" + "d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b" + "b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51" + "957dc4608fd34c999a7b35245bce2795715d8a5957a0872398473eed4860" + "29286f51d4118927e88634ad040034e061fd3f58632961761b1fbb8faf45" + "391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706" + "424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203" + "01000102820101008befeb1ff28e7ea56a0f63f1a133c08c48c0553efe86" + "07cfd9d216d981aef81a81db226b47277a6d32d09207df88e03316247ae7" + "39325456a00644bbfacd6dc2990851f047ccdc1226bec21afac9eacfb957" + "1e51889cfb6dac853fcdd1bb1593bd5528cdd3cbbb32c69183ef018fd3f5" + "3153f097fd3de9aca7998a6f4522e60c5e76ecc717033a2be16c233fe3e2" + "94c0ae68179a1ae5d33d51f2c0bcda35cffa21e1814374af1c014c280cfb" + "09e294a099f9a6f003d3da026af0f53029cad3d401178b0db53ca97fd166" + "a5eddf1384ddd92c55ab31758e0651e41dd7b24f2c438d58de00f55343cc" + "a461b94ff6358fc37f404ecbda2365d3e75391566248d3e102818100e4c8" + "783343b43fc127f5077fb35b37145ab6ee5f9a802e8100161ce441cbdbdc" + "1eb15f50bbef1f5088ba83ff914977862dffc63bd364172c9044e1131dbb" + "ff98755daa49670a014163b3c121395d880ad874b9ebdf66ff72a245c2b5" + "4f8fe1e233e72fd9cc253cbcd144f52537e9b7ed16b8a6d328de61c41f86" + "ea1d9ef326fb02818100c90738b2cf61af785e53393d9e96bc2f4d79358b" + "7bd487a242d170b8372af0a26af4980534a099647a4f38efb1a47d9dcdcf" + "a4ea6036396ef333e4f1de62ec529c3122f0d3b6abc818918494a52b028b" + "2cdf06bf2b450d5b99da7f203ff011318c030a01659c4c56244c2c312528" + "e30dd3b7709022d8fd14a2dba23a855da02f0281804f8ebeede4cf5b9449" + "d6d582bcd62d73309088984a5be4d00b3da55262e7074fa684bbc69173f8" + "09c36248e0a89f49a7297bd66d9b7724efe4436f997c2f92146c4be4199e" + "71463a7cf75763bc552027d559d2058a2c810c560db845e0a30243ed14a9" + "f92d1a8de2834b5d8c51c33ea87dcc3c8715a12f9249fc5a916e62d3dd02" + "8181008f74c7d1528cb35b82748174a7a789c377d5f790025e382c62e273" + "3e02a071f875baf681407d1af9c90e9fe2ed322532679cb6634b2566f6f6" + "37223a3828ffdc33fa1ca51f704c460ec2498a8a13974d1a484dd83e5898" + "9fb5bb66dcecc3b4815719141acb182ea18a659163c0d0dcb7114ee6d4f5" + "09441165e6b66e6c9dd3a102818100c449003eee6d9a0b724b66027a791e" + "3b86a72b2098ab085c2e69c276627fd6beeee98918de14ce15d17e059906" + "a178015d04e205918203ffa6c1324868d5c0a019f6fe82335403743322ee" + "14b1159692194ac22d9056c0c39309d249b33abaddb0d08ee87cc053a12a" + "83f12d261525e1b37df643c3e55770ef247d5d094816a0"; + +const char kDevVmpCodeSigningCert[] = + "3082056a308203d2a003020102020f112233445566778899aabbccddeeff300d06092a86" + "4886f70d01010b050030819c310b30090603550406130255533113301106035504080c0a" + "57617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d060355" + "040a0c06476f6f676c653111300f060355040b0c085769646576696e65311e301c060355" + "04030c157769646576696e652d6465762d636f64657369676e3121301f06092a864886f7" + "0d010901161274696e736b697040676f6f676c652e636f6d301e170d3137313030393231" + "323835385a170d3237313030373231323835385a3081a0310b3009060355040613025553" + "3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b" + "6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465" + "76696e653122302006035504030c197769646576696e652d6465762d766d702d636f6465" + "7369676e3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e" + "636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100" + "b3a7da87309390688388d614a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5d" + "e2f7b409b1eac2dc578b2bfd8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c" + "62c71d894fe6d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b" + "b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd3" + "4c999a7b35245bce2795715d8a5957a0872398473eed486029286f51d4118927e88634ad" + "040034e061fd3f58632961761b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be" + "2fd258331e7a86baa7394706424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3b" + "b73008150203010001a38201213082011d301d0603551d0e041604147266b4ce84aafd02" + "b1159cd2fa04c2553c6c02463081bb0603551d230481b33081b0a181a2a4819f30819c31" + "0b30090603550406130255533113301106035504080c0a57617368696e67746f6e311130" + "0f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c65311130" + "0f060355040b0c085769646576696e65311e301c06035504030c157769646576696e652d" + "6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b6970" + "40676f6f676c652e636f6d820900c5f82f038facf15830090603551d1304023000300b06" + "03551d0f04040302078030130603551d25040c300a06082b060105050703033011060a2b" + "06010401d67904010204030101ff300d06092a864886f70d01010b05000382018100aa23" + "6a5c0e23d5bf67c9f80f893d8347ba489541cf7f4ab7dfffda0ca21a3372e8ee8cfea863" + "628b9e0795904bc0e7495517246143c7b8555884e82fe1c305f0f4c3575447d4e7ce3243" + "4e1e0cf11712d537cd434c11d1328b814c94dbd0bab802e8fed5390da5f0cd719ce0e366" + "47620bcf40ed3945c80ab19beb7728080a74d4ff5d62564f47b32c4915c1f14890d379c8" + "8060f0bac73301defda06275a2e1a2024f92f0b4700d4e50d2d6f8d033715a362d7ca5ab" + "6d2dae20d8cefa2d3fc4f61e1734802984e5078dd6d957719fa75ea10dd02d983f7e383b" + "10fa92be7add70238388e63ec2c7ec49a37aa0c8a2566c46e9755cd9ce654c6d42053d1e" + "1cd9555f8a3bc5e426857072a8e8b44a756543893b1d29dabf31a2301597df2666612f23" + "a442613526e19f2aa9b2ea49f16f14b16794e053967a21b821c7b2495b2e02b01344a339" + "9d7e31dd71982cf21a546b1947bbce236381d717070c27096b6a91413abc69c3c0759574" + "4b7e91daf24b2a0acfd85924669f00292a4bd0c57b4c"; + +const char kDevSecureStorageVmpCodeSigningCert[] = + "30820571308203d9a0030201020203112233300d06092a864886f70d01010b050030819c" + "310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3111" + "300f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c653111" + "300f060355040b0c085769646576696e65311e301c06035504030c157769646576696e65" + "2d6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b69" + "7040676f6f676c652e636f6d301e170d3138303232323231323834395a170d3238303232" + "303231323834395a3081a0310b30090603550406130255533113301106035504080c0a57" + "617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d06035504" + "0a0c06476f6f676c653111300f060355040b0c085769646576696e653122302006035504" + "030c197769646576696e652d6465762d766d702d636f64657369676e3121301f06092a86" + "4886f70d010901161274696e736b697040676f6f676c652e636f6d30820122300d06092a" + "864886f70d01010105000382010f003082010a0282010100b3a7da87309390688388d614" + "a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd" + "8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6d5524e1b28af" + "1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646bb50d3a13d8687ad304005b0b" + "0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd34c999a7b35245bce2795715d" + "8a5957a0872398473eed486029286f51d4118927e88634ad040034e061fd3f5863296176" + "1b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706" + "424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203010001a38201" + "3430820130301d0603551d0e041604147266b4ce84aafd02b1159cd2fa04c2553c6c0246" + "3081bb0603551d230481b33081b0a181a2a4819f30819c310b3009060355040613025553" + "3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b" + "6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465" + "76696e65311e301c06035504030c157769646576696e652d6465762d636f64657369676e" + "3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e636f6d82" + "0900c5f82f038facf15830090603551d1304023000300b0603551d0f0404030207803013" + "0603551d25040c300a06082b060105050703033011060a2b06010401d679040102040301" + "01ff3011060a2b06010401d67904010304030101ff300d06092a864886f70d01010b0500" + "038201810069482a9bb125b17c76c77f86004e36ca4fc36bbde9c86901481c70d165e3cf" + "1c8541264190b3e7106c33ff4920a99bc9f939298091dc72ff898d22f5017b04d213eb60" + "626382656fdd66b9cd1830d98ef89d8e405b2d7ce9445db8de2ca365438b848f85d5266b" + "c211c8934154bb88ddb9e6f9aaff761814c1c0da90dd499f174507b5f4e89339e8abd157" + "8b3238d8f5c9dcb1f76e4d810679c2eca3583144f1ac0ce955b7c6a2cc9fc9f3d6c87069" + "28e301ebc3844e53f4905ff60803110dfc3b4f74b50ae1baffd091daad3ea29925f8009e" + "adc471a9ae673d9a9a003901d962f58fede85b2f65d9a470725459b19a69b06ae49179a3" + "395286b7d7039b32985a6db5b30bcd5cbed975d9a68de44fbcd1dbc8b6fbf67746b87d30" + "122c1cd8ed303a8bec7e23d284c0de35cfdccf261c317efe192efa84d2600bba3f9af846" + "8f89953a98f92c97b13d320f484627790d5b7b407f29f343c41154efbcf06e98632a3f3d" + "7138184a6f40af2435b3d98054ed9f4c3aa9ecf95c5c3014d3aa4d12f2"; + +const char kSameAsPrevious[] = ""; + +} // namespace + +class VmpCheckerTest : public ::testing::Test { + public: + void SetUp() override { + ASSERT_OK(VmpChecker::Instance()->SelectDrmCertificateType( + kCertificateTypeTesting)); + vmp_data_.Clear(); + VmpChecker::Instance()->set_allow_development_vmp(true); + signing_key_.reset( + RsaPrivateKey::Create(absl::HexStringToBytes(kDevVmpCodeSigningKey))); + ASSERT_TRUE(signing_key_); + } + + // Adds a binary to the VMP data to be verified. If |signing_cert| is + // |kSameAsPrevious| (empty), then the binary is signed using the same + // certificate as the previously added binary. This means that the first + // call to this function should not use |kSameAsPrevious|. + void AddVmpBinary(const std::string& signing_cert, const std::string& file_name, + const std::string& binary_hash, uint32_t flags) { + DCHECK(!signing_cert.empty() || !vmp_data_.certificates().empty()); + + if (!signing_cert.empty()) { + *vmp_data_.add_certificates() = absl::HexStringToBytes(signing_cert); + } + vmp::VmpData::SignedBinaryInfo* new_binary = + vmp_data_.add_signed_binary_info(); + new_binary->set_file_name(file_name); + new_binary->set_certificate_index(vmp_data_.certificates_size() - 1); + new_binary->set_binary_hash(binary_hash); + new_binary->set_flags(flags); + std::string message(binary_hash); + message += flags & 0xff; + std::string signature; + ASSERT_TRUE(signing_key_->GenerateSignature(message, &signature)); + new_binary->set_signature(signature); + } + + const std::string GetVmpData() { + std::string result; + vmp_data_.SerializeToString(&result); + return result; + } + + protected: + vmp::VmpData vmp_data_; + std::unique_ptr signing_key_; +}; + +TEST_F(VmpCheckerTest, Success) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kVerified, result); +} + +TEST_F(VmpCheckerTest, SecureStorageSuccess) { + AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kSecureStorageVerified, result); +} + +TEST_F(VmpCheckerTest, FailDevelopmentCert) { + VmpChecker::Instance()->set_allow_development_vmp(false); + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + VmpChecker::Result result; + EXPECT_EQ(DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, + VmpChecker::Instance() + ->VerifyVmpData(GetVmpData(), &result) + .error_code()); +} + +TEST_F(VmpCheckerTest, FailTwoBlessedBinaries) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + vmp_data_.mutable_signed_binary_info(0)->set_flags(kBlessedBinaryFlag); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +TEST_F(VmpCheckerTest, FailNoBlessedBinaries) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + vmp_data_.mutable_signed_binary_info(3)->set_flags(0); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +TEST_F(VmpCheckerTest, FailBadSignature) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + ++((*vmp_data_.mutable_signed_binary_info(2)->mutable_signature())[10]); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +TEST_F(VmpCheckerTest, FailSignatureVerification) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + ++((*vmp_data_.mutable_signed_binary_info(3)->mutable_binary_hash())[16]); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +TEST_F(VmpCheckerTest, FailUnsignedBinary) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + vmp_data_.mutable_signed_binary_info(2)->clear_binary_hash(); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +} // namespace widevine diff --git a/common/widevine_system_id.cc b/common/widevine_system_id.cc new file mode 100644 index 0000000..3caf4ac --- /dev/null +++ b/common/widevine_system_id.cc @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Common Encryption (CENC) system ID for Widevine DRM. + +#include "common/widevine_system_id.h" + +namespace widevine { + +const uint8_t kWidevineSystemId[16] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, + 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, + 0xd5, 0x1d, 0x21, 0xed}; + +} // namespace widevine diff --git a/common/widevine_system_id.h b/common/widevine_system_id.h new file mode 100644 index 0000000..4b02791 --- /dev/null +++ b/common/widevine_system_id.h @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Common Encryption (CENC) system ID for Widevine DRM. + +#ifndef COMMON_WIDEVINE_SYSTEM_ID_H_ +#define COMMON_WIDEVINE_SYSTEM_ID_H_ + +#include + +namespace widevine { + +extern const uint8_t kWidevineSystemId[16]; + +} // namespace widevine + +#endif // COMMON_WIDEVINE_SYSTEM_ID_H_ diff --git a/common/wvm_test_keys.cc b/common/wvm_test_keys.cc new file mode 100644 index 0000000..7c39ad9 --- /dev/null +++ b/common/wvm_test_keys.cc @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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 + +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "common/wvm_test_keys.h" +#include "common/wvm_token_handler.h" + +namespace widevine { +namespace wvm_test_keys { + +// Preprov key and keyboxes in this header use system ID 0x112. +const char kTestPreprovKeyHex[] = "f7538b38acc78ec68c732ac665c55c65"; +const char kBadPreprovKey1Hex[] = "1badbadbadbadbadbadbadbadbadbad1"; +const char kBadPreprovKey2Hex[] = "2badbadbadbadbadbadbadbadbadbad2"; + +const char kTestToken1Hex[] = + "00000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"; + +// The device key corresponding to kTestToken1. +const char kTestDeviceKey1Hex[] = "4071197f1f8910d9bf10c6bc4c987638"; + +// Base64Encode(kTestToken1) +const char kTestToken1Base64[] = + "AAAAAgAAARKOHr/gN4KAlsplOLT29Ly1HCtxkc8Dfpi+qiSSSQfhKPn/SbVKFlzZ" + "wz5lR1N+tNKft+jfPCwc2SUXoS9JIpU+"; + +const char kTestToken2Hex[] = + "0000000200000112d906feebe1750c5886ff77c2dfa31bb40e002f3adbc0fa5b" + "eb2486cf5f419549cdaa23230e5165ac2ffab56d53b692b7ba0c1857400c6add" + "3af3ff3d5cb24985"; + +// The device key corresponding to kTestToken2. +const char kTestDeviceKey2Hex[] = "42cfb1765201042302a404d1e0fac8ed"; + +// Base64Encode(kTestToken2) +const char kTestToken2Base64[] = + "AAAAAgAAARLZBv7r4XUMWIb/d8Lfoxu0DgAvOtvA+lvrJIbPX0GVSc2qIyMOUWWs" + "L/q1bVO2kre6DBhXQAxq3Trz/z1cskmF"; + +const char kTestToken3DesHex[] = + "00000002100000138e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"; + +const char kTestDeviceKey3DesHex[] = "4071197f1f8910d9bf10c6bc4c987638"; + +std::vector GetPreprovKeyVector() { + return { + // Return multiple preprov keys for the same device. + WvmTokenHandler::PreprovKey(kTestSystemId, + absl::HexStringToBytes(kBadPreprovKey1Hex)), + WvmTokenHandler::PreprovKey(kTestSystemId, + absl::HexStringToBytes(kTestPreprovKeyHex)), + WvmTokenHandler::PreprovKey(kTestSystemId, + absl::HexStringToBytes(kBadPreprovKey2Hex)), + + // Add another device that uses 3DES encryption for asset keys. Tokens + // the same except for the system ID portion. + WvmTokenHandler::PreprovKey(kTestSystemId3Des, + absl::HexStringToBytes(kTestPreprovKeyHex), + WvmTokenHandler::DES3), + }; +} + +std::map GetPreprovKeyTable() { + return {{kTestSystemId, std::string(kTestPreprovKeyHex)}}; +} + +std::multimap GetPreprovKeyMultimap() { + return {{kTestSystemId, kBadPreprovKey1Hex}, + {kTestSystemId, kTestPreprovKeyHex}, + {kTestSystemId, kBadPreprovKey2Hex}}; +} + +} // namespace wvm_test_keys +} // namespace widevine diff --git a/common/wvm_test_keys.h b/common/wvm_test_keys.h new file mode 100644 index 0000000..b39d194 --- /dev/null +++ b/common/wvm_test_keys.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// Sample keys for use in tests of Widevine WVM crypto code. + +#ifndef COMMON_WVM_TEST_KEYS_H_ +#define COMMON_WVM_TEST_KEYS_H_ + +#include +#include +#include + +#include +#include "common/wvm_token_handler.h" + +namespace widevine { +namespace wvm_test_keys { + +// Most preprov key and keyboxes in this header use system ID 0x112. +const uint32_t kTestSystemId = 0x112; + +// One oddball system that uses 3DES. (We'll re-use the system and device keys). +const uint32_t kTestSystemId3Des = 0x10000013; + +extern const char kTestPreprovKeyHex[]; + +extern const char kTestToken1Hex[]; +extern const char kTestDeviceKey1Hex[]; + +extern const char kTestToken2Hex[]; +extern const char kTestDeviceKey2Hex[]; + +extern const char kTestToken3DesHex[]; +extern const char kTestDeviceKey3DesHex[]; + +// Return a list of preprovisioning keys, suitable for initializing +// the preprov key table for tests. +std::vector GetPreprovKeyVector(); + +// Old version of GetPreprovKeyTable() which uses a simple int->std::string +// map. Doesn't support multiple devices per system ID. +// TODO(user): get rid of this once other code as been migrated off of it. +std::map GetPreprovKeyTable(); + +// Version of GetPreprovKeyTable() which uses a simple int->hex_string +// multimap, useful for modular DRM code which doesn't care about the asset +// key cipher. +std::multimap GetPreprovKeyMultimap(); + +} // namespace wvm_test_keys +} // namespace widevine + +#endif // COMMON_WVM_TEST_KEYS_H_ diff --git a/common/wvm_token_handler.cc b/common/wvm_token_handler.cc new file mode 100644 index 0000000..99986cf --- /dev/null +++ b/common/wvm_token_handler.cc @@ -0,0 +1,317 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/wvm_token_handler.h" + +#include + +#include "glog/logging.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "util/endian/endian.h" +#include "util/gtl/map_util.h" +#include "util/status.h" +#include "common/aes_cbc_util.h" +#include "common/ecb_util.h" +#include "common/sha_util.h" + +namespace widevine { + +namespace { +const int kKeyboxFlagInsecure = 1; +const int kSystemIdSamsungTVFactory = 8; +const int kPreProvisioningKeySizeBytes = 16; +const int kKeyboxSizeBytes = 72; +const std::string kZeroIV(16, '\0'); + +class PreprovKeysMap { + public: + void Update(const std::vector& key_vector); + std::vector GetPreprovKeys(uint32_t system_id); + bool IsSystemIdKnown(uint32_t system_id); + bool IsEmpty(); + + static PreprovKeysMap* GetSingleton(); + + private: + absl::Mutex mutex_; + std::multimap preprov_keys_; +}; + +void PreprovKeysMap::Update( + const std::vector& key_vector) { + absl::WriterMutexLock lock(&mutex_); + preprov_keys_.clear(); + for (const WvmTokenHandler::PreprovKey& ppk : key_vector) { + if (ppk.key_bytes.size() != kPreProvisioningKeySizeBytes) { + LOG(WARNING) << "Invalid preprov key for system id: " << ppk.system_id; + continue; + } + preprov_keys_.insert(std::make_pair(ppk.system_id, ppk)); + } +} + +std::vector PreprovKeysMap::GetPreprovKeys( + uint32_t system_id) { + absl::ReaderMutexLock lock(&mutex_); + std::vector key_vector; + auto range = preprov_keys_.equal_range(system_id); + for (auto it = range.first; it != range.second; ++it) + key_vector.push_back(it->second); + return key_vector; +} + +bool PreprovKeysMap::IsSystemIdKnown(uint32_t system_id) { + absl::ReaderMutexLock lock(&mutex_); + return gtl::ContainsKey(preprov_keys_, system_id); +} + +bool PreprovKeysMap::IsEmpty() { + absl::ReaderMutexLock lock(&mutex_); + return preprov_keys_.empty(); +} + +PreprovKeysMap* PreprovKeysMap::GetSingleton() { + static auto* const kInstance = new PreprovKeysMap(); + return kInstance; +} + +} // namespace + +WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id, + const std::string& key_bytes, Cipher cipher, + const std::string& model_filter) + : system_id(system_id), + key_bytes(key_bytes), + cipher(cipher), + model_filter(model_filter) {} + +WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id, + const std::string& key_bytes, Cipher cipher) + : system_id(system_id), key_bytes(key_bytes), cipher(cipher) {} + +WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id, + const std::string& key_bytes) + : system_id(system_id), key_bytes(key_bytes), cipher(AES) {} + +void WvmTokenHandler::SetPreprovKeys(const std::vector& keyvec) { + PreprovKeysMap::GetSingleton()->Update(keyvec); +} + +bool WvmTokenHandler::IsSystemIdKnown(uint32_t system_id) { + return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id); +} + +util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token, + std::string* device_key_out, + Cipher* cipher_out, + bool* insecure_out) { + const std::string default_make_model; + return DecryptDeviceKey(token, default_make_model, device_key_out, cipher_out, + insecure_out); +} + +util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token, + const std::string& make_model, + std::string* device_key_out, + Cipher* cipher_out, + bool* insecure_out) { + DCHECK(device_key_out); + // DCHECK below is commented out because preprov_keys_ being nullptr + // is a valid test in wvm_token_handler_test.cc. If we have + // DCHECK(preprov_keys_) here it would thrown an failure in Kokoro + // presubmit because evidently Kokoro does debug build. + // DCHECK(preprov_keys_); + if (token.size() < kKeyboxSizeBytes) { + return util::Status(util::error::INVALID_ARGUMENT, + "Keybox token is too short."); + } + if (PreprovKeysMap::GetSingleton()->IsEmpty()) { + return util::Status(util::error::INVALID_ARGUMENT, + "Pre-provisioning key map is nullptr."); + } + + uint32_t system_id = GetSystemId(token); + + // There may be multiple preprov keys for a system ID; try them all. + std::vector key_vector = + PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id); + + util::Status status; + // First pass through the matching system Ids is an attempt to find an + // alternate preprov key specific to this make/model. + const PreprovKey* preferred_ppk = NULL; + for (const PreprovKey& ppk : key_vector) { + if (!ppk.model_filter.empty() && ppk.model_filter == make_model) { + preferred_ppk = &ppk; + break; + } + } + for (const PreprovKey& ppk : key_vector) { + if (preferred_ppk && preferred_ppk != &ppk) { + continue; + } + uint32_t version; + status = DecryptDeviceKeyWithPreprovKey( + ppk.key_bytes, token, device_key_out, insecure_out, &version); + if (version != 2) { + // Only version 2 keyboxes supported. + return util::Status(util::error::PERMISSION_DENIED, + absl::StrCat("invalid-keybox-version ", version)); + } + if (status.ok()) { + if (cipher_out) { + *cipher_out = ppk.cipher; + } + return status; + } + } + if (!status.ok()) { + // Return error from last attempt. + return status; + } + return util::Status( + util::error::NOT_FOUND, + absl::StrCat("Unknown system id: ", system_id).c_str()); // NOLINT +} + +// 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 +// asset key in |result|. +// On failure, returns an error from the Widevine Server SDK error space. +util::Status WvmTokenHandler::GetEncryptedAssetKey( + absl::string_view token, absl::string_view raw_asset_key, + const std::string& make_model, std::string* result) { + std::string device_key; + Cipher cipher = AES; + util::Status status = + DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr); + if (!status.ok()) { + return status; + } + return EncryptAssetKey(device_key, raw_asset_key, cipher, result); +} + +uint32_t WvmTokenHandler::GetSystemId(absl::string_view token) { + uint32_t system_id = 0; + if (token.size() >= 8) { + // Bytes 4-8 contain the little-endian system ID. + system_id = BigEndian::Load32(token.data() + 4); + } + return system_id; +} + +std::string WvmTokenHandler::GetEncryptedUniqueId(absl::string_view token) { + std::string encrypted_unique_id(""); + if (token.size() >= 24) { + encrypted_unique_id = std::string(token.substr(8, 16)); + } + return encrypted_unique_id; +} + +util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + absl::string_view preprov_key, absl::string_view token, + std::string* device_key_out) { + return DecryptDeviceKeyWithPreprovKey(preprov_key, token, device_key_out, + nullptr, nullptr); +} + +util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + absl::string_view preprov_key, absl::string_view token, + std::string* device_key_out, bool* insecure_out, uint32_t* version) { + CHECK(device_key_out); + if (token.size() < kKeyboxSizeBytes) { + return util::Status(util::error::INVALID_ARGUMENT, + "Keybox token is too short."); + } + if (version) { + *version = BigEndian::Load32(token.data()); + } + // This was checked at initialization, so if it fails now something is wrong. + CHECK_EQ(preprov_key.size(), kPreProvisioningKeySizeBytes); + + absl::string_view encrypted_unique_id = token.substr(8, 16); + std::string unique_id = crypto_util::DecryptAesCbcNoPad( + std::string(preprov_key), kZeroIV, std::string(encrypted_unique_id)); + + if (unique_id.size() != 16) { + // Decrypting 16 bytes should result in 16 bytes. + LOG(WARNING) << "Internal error decrypting unique id from token."; + return util::Status(util::error::INTERNAL, "Wrong size after decrypt/16."); + } + + absl::string_view encrypted_bits = token.substr(24, 48); + std::string decrypted_bits = crypto_util::DecryptAesCbcNoPad( + unique_id, kZeroIV, std::string(encrypted_bits)); + if (decrypted_bits.size() != 48) { + // Decrypting 48 bytes should result in 48 bytes. + LOG(WARNING) << "Internal error decrypting device key from token."; + return util::Status(util::error::INTERNAL, "Wrong size after decrypt/48."); + } + uint8_t keybox_flags = decrypted_bits[36]; + absl::string_view device_key = + absl::string_view(decrypted_bits).substr(0, 16); + absl::string_view expected_hash = + absl::string_view(decrypted_bits).substr(16, 20); + std::string actual_hash = Sha1_Hash(std::string(device_key)); + + if (GetSystemId(token) == kSystemIdSamsungTVFactory) { + // Keyboxes with this system ID have corrupted bytes starting after the + // first 16 bytes of the hash, so we use only the uncorrupted part. + expected_hash = expected_hash.substr(0, 16); + actual_hash.resize(16); + keybox_flags = 0; + } + if (expected_hash != actual_hash) { + return util::Status(util::error::PERMISSION_DENIED, + "Keybox validation failed."); + } + *device_key_out = std::string(device_key); + if (insecure_out) { + *insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0; + } + return util::OkStatus(); +} + +util::Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key, + absl::string_view raw_asset_key, + Cipher cipher, std::string* result) { + CHECK(result); + if (device_key.size() != 16) { + return util::Status(util::error::INVALID_ARGUMENT, + "Invalid device key: size != 16"); + } + if (raw_asset_key.size() < 16) { + return util::Status(util::error::INVALID_ARGUMENT, + "Invalid asset key: size < 16"); + } + // Truncate extra characters in the key; wvm always uses 16. + absl::string_view asset_key = raw_asset_key.substr(0, 16); + switch (cipher) { + case DES3: + if (!crypto_util::Encrypt3DesEcb(device_key, asset_key, result)) { + return util::Status(util::error::INTERNAL, + "Error encrypting asset key with 3DES."); + } + return util::OkStatus(); + case AES: + if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) { + return util::Status(util::error::INTERNAL, + "Error encrypting asset key with AES."); + } + return util::OkStatus(); + case PASS_THRU: + result->assign(raw_asset_key.data(), raw_asset_key.size()); + return util::OkStatus(); + default: + return util::Status(util::error::INVALID_ARGUMENT, "Unknown cipher type"); + } +} + +} // namespace widevine diff --git a/common/wvm_token_handler.h b/common/wvm_token_handler.h new file mode 100644 index 0000000..0bd34d5 --- /dev/null +++ b/common/wvm_token_handler.h @@ -0,0 +1,126 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_WVM_TOKEN_HANDLER_H_ +#define COMMON_WVM_TOKEN_HANDLER_H_ + +#include +#include + +#include "base/macros.h" +#include "absl/strings/string_view.h" +#include "util/status.h" + +namespace widevine { + +// Class for decoding the encrypted token that comes from a WVM classic keybox. +// +// Internally, this class keeps a multimap from system ID to the preprov key(s) +// for that system, so construction is relatively expensive; don't create an +// instance of this class per-request. +// +// Errors in this file are returned in the canonical space, but are chosen so +// that it's possible to map different failure modes to the appropriate codes +// in the WVM or server SDK error spaces: +// OK - success. +// NOT_FOUND - system id from token wasn't in the preprov key table. +// PERMISSION_DENIED - hash of device key didn't match. +// INVALID_ARGUMENT - token or a key is wrong size or otherwise invalid. +// INTERNAL_ERROR - something went wrong that shouldn't have been able to. +class WvmTokenHandler { + public: + // Cipher type to use for encrypting asset keys. This matches the enum in + // video/widevine/lockbox/public/key.proto. + enum Cipher { + DES3 = 0, + AES = 1, + PASS_THRU = 2, + }; + + struct PreprovKey { + // Utility constructor. + PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher, + const std::string& model_filter); + PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher); + // Constructor if the cipher isn't needed (i.e. modular DRM). + PreprovKey(uint32_t system_id, const std::string& key_bytes); + uint32_t system_id; + std::string key_bytes; + Cipher cipher; + // If set, the make/model in the license request must match this value. + std::string model_filter; + }; + + // Set pre-provisioning keys from the given vector. This may be called + // concurrently with other methods. + static void SetPreprovKeys(const std::vector& preprov_keys); + + // Returns true if system_id is in the preprov key table. + static bool IsSystemIdKnown(uint32_t system_id); + + // Decrypt a token using the preprov key for its system ID, and + // return the decrypted device key in result. + // On failure, returns one of the errors listed above. + // cipher_out may be null; if not, *cipher_out will be set to the cipher type + // to use with the device key. + // insecure_out may be null; if not, *insecure_out will be set to the + // decrypted value of the 'insecure keybox' flag. + static util::Status DecryptDeviceKey(absl::string_view token, + std::string* device_key_out, + Cipher* cipher_out, bool* insecure_out); + // 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 + // the make/model as we apply alternate keys. + static util::Status DecryptDeviceKey(absl::string_view token, + const std::string& make_model, + std::string* device_key_out, + Cipher* cipher_out, bool* insecure_out); + + // 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 + // asset key in result. + static util::Status GetEncryptedAssetKey(absl::string_view token, + absl::string_view raw_asset_key, + const std::string& make_model, + std::string* result); + + // Extract the system ID component of a token (bytes 4-8). + static uint32_t GetSystemId(absl::string_view token); + + // Extract the encrypted unique ID component of a token (bytes 8-24). + static std::string GetEncryptedUniqueId(absl::string_view token); + + // Try to decrypt a token using the provided preprov key, and return the + // decrypted device key in result. + // + // 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 + // being incorrect. + static util::Status DecryptDeviceKeyWithPreprovKey( + absl::string_view preprov_key_bytes, absl::string_view token, + std::string* device_key_out); + + // Same as above, but allows extracting the 'insecure keybox' flag and keybox + // version. + static util::Status DecryptDeviceKeyWithPreprovKey( + absl::string_view preprov_key_bytes, absl::string_view token, + std::string* device_key_out, bool* insecure_out, uint32_t* version); + + // Given a decrypted device key as returned by DecryptToken(), use it to + // encrypt an asset key with the given cipher. + static util::Status EncryptAssetKey(absl::string_view device_key, + absl::string_view raw_asset_key, + Cipher cipher, std::string* result); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler); +}; + +} // namespace widevine + +#endif // COMMON_WVM_TOKEN_HANDLER_H_ diff --git a/common/wvm_token_handler_test.cc b/common/wvm_token_handler_test.cc new file mode 100644 index 0000000..364d568 --- /dev/null +++ b/common/wvm_token_handler_test.cc @@ -0,0 +1,235 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/wvm_token_handler.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "common/wvm_test_keys.h" + +using widevine::wvm_test_keys::kTestSystemId; +using widevine::wvm_test_keys::kTestSystemId3Des; +using widevine::wvm_test_keys::kTestPreprovKeyHex; +using widevine::wvm_test_keys::kTestDeviceKey1Hex; +using widevine::wvm_test_keys::kTestDeviceKey2Hex; +using widevine::wvm_test_keys::kTestDeviceKey3DesHex; +using widevine::wvm_test_keys::kTestToken1Hex; +using widevine::wvm_test_keys::kTestToken2Hex; +using widevine::wvm_test_keys::kTestToken3DesHex; +using widevine::wvm_test_keys::GetPreprovKeyVector; + +namespace widevine { + +using absl::BytesToHexString; +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) { + EXPECT_EQ(kTestSystemId, + WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex))); + EXPECT_EQ(kTestSystemId, + WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken2Hex))); +} + +TEST(WvmTokenHandlerTest, GetEncryptedUniqueId) { + EXPECT_EQ( + HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"), + WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken1Hex))); + EXPECT_EQ( + HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"), + WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken2Hex))); +} + +TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) { + util::Status status; + std::string device_key; + + status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken1Hex), + &device_key); + EXPECT_OK(status) << status; + EXPECT_EQ(kTestDeviceKey1Hex, BytesToHexString(device_key)); + + status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken2Hex), + &device_key); + EXPECT_OK(status); + EXPECT_EQ(kTestDeviceKey2Hex, BytesToHexString(device_key)); + + // Test with invalid token. Hash failure should produce PERMISSION_DENIED. + device_key.clear(); + std::string token = HexStringToBytes( + "00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"); + status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + HexStringToBytes(kTestPreprovKeyHex), token, &device_key); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code()); + EXPECT_TRUE(device_key.empty()); +} + +// See b/68798704 for background of this test. +// It is important to keep this test *before* all the DecryptDeviceKey* tests +// below, in which WvmTokenHandler::SetPreprovKeys() would be called. +TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) { + // Not calling WvmTokenHandler::SetPreprovKeys() + // So preprov_keys_ would be nullptr. + util::Status status; + std::string device_key; + status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex), + &device_key, nullptr, nullptr); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(util::error::INVALID_ARGUMENT, status.error_code()); +} + +// Same tests as DecryptDeviceKeyWithPreprovKey(), but we use the handler's +// table of preprov keys instead of providing our own. +TEST(WvmTokenHandlerTest, DecryptDeviceKey) { + util::Status status; + std::string device_key; + WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector()); + + status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex), + &device_key, nullptr, nullptr); + EXPECT_OK(status); + EXPECT_EQ(HexStringToBytes(kTestDeviceKey1Hex), device_key); + + status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken2Hex), + &device_key, nullptr, nullptr); + EXPECT_OK(status); + EXPECT_EQ(HexStringToBytes(kTestDeviceKey2Hex), device_key); + + // Test with invalid token. Hash failure should produce PERMISSION_DENIED. + device_key.clear(); + std::string token = HexStringToBytes( + "00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"); + status = + WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code()); + EXPECT_TRUE(device_key.empty()); + + // Test with nonexistent system id. Should produce NOT_FOUND. + device_key.clear(); + token = HexStringToBytes( + "00000002555555559e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"); + status = + WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(util::error::NOT_FOUND, status.error_code()); + EXPECT_TRUE(device_key.empty()); +} + +TEST(WvmTokenHandlerTest, GetEncryptedAssetKey) { + WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector()); + + std::string raw_asset_key = "asset-key-000000"; + std::string asset_key; + std::string make_model; + util::Status status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key); + EXPECT_OK(status); + EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key)); + + status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken2Hex), raw_asset_key, make_model, &asset_key); + EXPECT_OK(status); + EXPECT_EQ("091802159bf8da12aecfcdfb092075c8", BytesToHexString(asset_key)); + + // Check 3DES encryption of asset keys + status = WvmTokenHandler::EncryptAssetKey( + HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key, + WvmTokenHandler::DES3, &asset_key); + EXPECT_OK(status); + EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); + + asset_key.clear(); + status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model, + &asset_key); + EXPECT_OK(status); + EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); + + // Test with pass-thru (Cipher=PASS_THRU). + asset_key.clear(); + status = WvmTokenHandler::EncryptAssetKey( + HexStringToBytes(kTestDeviceKey1Hex), raw_asset_key, + WvmTokenHandler::PASS_THRU, &asset_key); + + EXPECT_OK(status); + EXPECT_EQ(BytesToHexString(raw_asset_key), BytesToHexString(asset_key)); +} + +TEST(WvmTokenHandlerTest, FilterOnMakeModel) { + // Use all good keys, but only the key for LG:BD572 should work. It works + // by setting only that one to DES3. All other AES ppks will encrypt the + // asset key incorrectly. + std::vector ppks; + ppks.push_back(WvmTokenHandler::PreprovKey( + kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex), + WvmTokenHandler::AES, "")); + ppks.push_back(WvmTokenHandler::PreprovKey( + kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex), + WvmTokenHandler::DES3, "LG:BD572")); + ppks.push_back(WvmTokenHandler::PreprovKey( + kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex), + WvmTokenHandler::AES, "")); + WvmTokenHandler::SetPreprovKeys(ppks); + std::string raw_asset_key = "asset-key-000000"; + std::string asset_key; + // Check 3DES encryption of asset keys + util::Status status = WvmTokenHandler::EncryptAssetKey( + HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key, + WvmTokenHandler::DES3, &asset_key); + EXPECT_OK(status); + EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); + + asset_key.clear(); + std::string make_model; + status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model, + &asset_key); + EXPECT_OK(status); + // Should fail because the asset key was encrypted with AES instead of DES3. + EXPECT_NE("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); + + // Set the make/model so we find and use the correct ppk. + make_model = "LG:BD572"; + status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model, + &asset_key); + EXPECT_OK(status); + // Should work because the asset key was encrypted with DES3. + EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); +} + +TEST(WvmTokenHandlerTest, AncientKeybox) { + util::Status status; + std::string device_key; + + std::string v1_token( + std::string(kTestPreprovKeyHex).replace(0, 16, "0000000100000001")); + status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex), + &device_key); + EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code()); + EXPECT_TRUE(device_key.empty()); +} + +} // namespace widevine diff --git a/common/x509_cert.cc b/common/x509_cert.cc new file mode 100644 index 0000000..a8e9a45 --- /dev/null +++ b/common/x509_cert.cc @@ -0,0 +1,363 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/x509_cert.h" + +#include + +#include +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" +#include "openssl/bio.h" +#include "openssl/evp.h" +#include "openssl/pem.h" +#include "openssl/pkcs7.h" +#include "openssl/x509.h" +#include "openssl/x509v3.h" +#include "common/openssl_util.h" +#include "common/rsa_key.h" + +namespace { +// Serializes the X509 |certificate| into a PEM-encoded string. Returns true +// on success, false otherwise. The caller retains ownership of +// |certificate| and |serialized_certificate|. |serialized_certificate| must +// not be NULL. +bool PemEncodeX509Certificate(const X509& certificate, + std::string* serialized_certificate) { + CHECK(serialized_certificate) << "serialized_certificate can not be null."; + + ScopedBIO bio(BIO_new(BIO_s_mem())); + if (bio == nullptr) { + return false; + } + + // The const_cast is necessary for the openssl call. + PEM_write_bio_X509(bio.get(), const_cast(&certificate)); + int serialized_size = BIO_pending(bio.get()); + serialized_certificate->resize(serialized_size); + if (BIO_read(bio.get(), &(*serialized_certificate)[0], serialized_size) != + serialized_size) { + return false; + } + + return true; +} + +} // anonymous namespace. + +namespace widevine { + +std::unique_ptr X509Cert::FromOpenSslCert(ScopedX509 certificate) { + return std::unique_ptr(new X509Cert(certificate.release())); +} + +X509Cert::X509Cert() : openssl_cert_(NULL) {} + +X509Cert::~X509Cert() { + if (openssl_cert_ != NULL) { + X509_free(openssl_cert_); + } +} + +X509Cert::X509Cert(X509* openssl_cert) : openssl_cert_(openssl_cert) {} + +util::Status X509Cert::LoadPem(const std::string& pem_cert) { + if (pem_cert.empty()) { + return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM certificate"); + } + BIO* bio(NULL); + X509* new_cert(NULL); + bio = BIO_new_mem_buf(const_cast(pem_cert.data()), pem_cert.size()); + if (bio == NULL) { + return util::Status(util::error::INTERNAL, "BIO allocation failed"); + } + util::Status status; + new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); + if (new_cert == NULL) { + status = util::Status(util::Status::canonical_space(), + util::error::INVALID_ARGUMENT, + "PEM certificate load failed"); + goto cleanup; + } + if (openssl_cert_ != NULL) { + X509_free(openssl_cert_); + } + openssl_cert_ = new_cert; + +cleanup: + if (bio != NULL) { + BIO_free(bio); + } + return status; +} + +util::Status X509Cert::LoadDer(const std::string& der_cert) { + if (der_cert.empty()) { + return util::Status(util::error::INVALID_ARGUMENT, "Empty DER certificate"); + } + const unsigned char* cert_data = + reinterpret_cast(der_cert.data()); + X509* new_cert = d2i_X509(NULL, &cert_data, der_cert.size()); + if (new_cert == NULL) { + return util::Status(util::error::INVALID_ARGUMENT, + "DER certificate load failed"); + } + if (openssl_cert_ != NULL) { + X509_free(openssl_cert_); + } + openssl_cert_ = new_cert; + return util::OkStatus(); +} + +std::string X509Cert::GetPem() const { + std::string serialized_certificate; + if (!PemEncodeX509Certificate(*openssl_cert_, &serialized_certificate)) { + return ""; + } + return serialized_certificate; +} + +std::unique_ptr X509Cert::GetRsaPublicKey() const { + ScopedPKEY pkey(X509_get_pubkey(openssl_cert_)); + return std::unique_ptr( + new RsaPublicKey(EVP_PKEY_get1_RSA(pkey.get()))); +} + +const std::string& X509Cert::GetSubjectName() { + if (subject_name_.empty() && (openssl_cert_ != NULL)) { + X509_NAME* subject = X509_get_subject_name(openssl_cert_); + if (subject != NULL) { + BIO* bio = BIO_new(BIO_s_mem()); + if (bio != NULL) { + X509_NAME_print_ex(bio, subject, 0, 0); + int size = BIO_pending(bio); + std::unique_ptr buffer(new char[size]); + int bytes_read = BIO_read(bio, buffer.get(), size); + if (bytes_read == size) { + subject_name_.assign(buffer.get(), bytes_read); + } + BIO_free(bio); + } + } + } + return subject_name_; +} + +std::string X509Cert::GetSubjectNameField(const std::string& field) { + if (field.empty()) { + return std::string(); + } + const std::string& subject = GetSubjectName(); + size_t start_pos = subject.find(field + "="); + if (start_pos == std::string::npos) { + return std::string(); + } + start_pos += field.size() + 1; + size_t end_pos = subject.find(",", start_pos); + if (end_pos == std::string::npos) { + end_pos = subject.size(); + } + return subject.substr(start_pos, end_pos - start_pos); +} + +std::string X509Cert::GetSerialNumber() const { + if (openssl_cert_ == NULL) { + return std::string(); + } + BIGNUM* bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(openssl_cert_), NULL); + if (bn == NULL) { + return std::string(); + } + std::string result; + char* openssl_sn = BN_bn2hex(bn); + if (openssl_sn != NULL) { + result = absl::HexStringToBytes(openssl_sn); + OPENSSL_free(openssl_sn); + } + BN_free(bn); + return result; +} + +bool X509Cert::IsCaCertificate() const { + return X509_check_ca(openssl_cert_) != 0; +} + +bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const { + ScopedAsn1Object extension_name(OBJ_txt2obj(oid.c_str(), 1)); + int ext_pos = X509_get_ext_by_OBJ(openssl_cert_, extension_name.get(), -1); + if (ext_pos < 0) return false; + X509_EXTENSION* extension(X509_get_ext(openssl_cert_, ext_pos)); + if (!extension) return false; + ASN1_OCTET_STRING* extension_data(X509_EXTENSION_get_data(extension)); + if (!extension_data) return false; + if ((extension_data->length != 3) || (extension_data->data[0] != 1) || + (extension_data->data[1] != 1)) + return false; + + *value = extension_data->data[2] != 0; + return true; +} + +X509CertChain::~X509CertChain() { Reset(); } + +void X509CertChain::Reset() { + for (auto certp : cert_chain_) { + delete certp; + } + cert_chain_.clear(); +} + +util::Status X509CertChain::LoadPem(const std::string& pem_cert_chain) { + static const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----"; + static const char kEndCertificate[] = "-----END CERTIFICATE-----"; + + Reset(); + size_t begin_pos = pem_cert_chain.find(kBeginCertificate); + while (begin_pos != std::string::npos) { + size_t end_pos = pem_cert_chain.find( + kEndCertificate, begin_pos + sizeof(kBeginCertificate) - 1); + if (end_pos != std::string::npos) { + end_pos += sizeof(kEndCertificate) - 1; + std::unique_ptr new_cert(new X509Cert); + util::Status status = new_cert->LoadPem( + pem_cert_chain.substr(begin_pos, end_pos - begin_pos)); + if (!status.ok()) { + return status; + } + cert_chain_.push_back(new_cert.release()); + begin_pos = pem_cert_chain.find(kBeginCertificate, end_pos); + } + } + return util::OkStatus(); +} + +util::Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) { + ScopedX509Stack cert_stack(sk_X509_new_null()); + CBS cbs; + CBS_init(&cbs, reinterpret_cast(pk7_cert_chain.data()), + pk7_cert_chain.size()); + if (!PKCS7_get_certificates(cert_stack.get(), &cbs)) { + return util::Status(util::error::INVALID_ARGUMENT, + "Unable to load PKCS#7 certificate chain"); + } + + while (sk_X509_num(cert_stack.get()) > 0) { + cert_chain_.insert(cert_chain_.begin(), + new X509Cert(sk_X509_pop(cert_stack.get()))); + } + + return util::OkStatus(); +} + +X509Cert* X509CertChain::GetCert(size_t cert_index) const { + if (cert_index >= cert_chain_.size()) { + return NULL; + } + return cert_chain_[cert_index]; +} + +X509CA::X509CA(X509Cert* ca_cert) : ca_cert_(ca_cert), openssl_store_(NULL) {} + +X509CA::~X509CA() { + if (openssl_store_ != NULL) { + X509_STORE_free(openssl_store_); + } +} + +util::Status X509CA::InitializeStore() { + absl::WriterMutexLock lock(&openssl_store_mutex_); + if (openssl_store_ == NULL) { + if (ca_cert_ == NULL) { + return util::Status(util::error::INTERNAL, "CA X.509Cert is NULL"); + } + openssl_store_ = X509_STORE_new(); + if (openssl_store_ == NULL) { + return util::Status(util::error::INTERNAL, + "Failed to allocate X.509 store"); + } + if (X509_STORE_add_cert(openssl_store_, + const_cast(ca_cert_->openssl_cert())) == 0) { + X509_STORE_free(openssl_store_); + openssl_store_ = NULL; + + return util::Status(util::error::INTERNAL, + "Failed to add X.509 CA certificate to store"); + } + } + return util::OkStatus(); +} + +util::Status X509CA::VerifyCert(const X509Cert& cert) { + return OpenSslX509Verify(cert.openssl_cert(), nullptr); +} + +util::Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) { + if (cert_chain.GetNumCerts() < 1) { + return util::Status(util::error::INVALID_ARGUMENT, + "Cannot verify empty certificate chain"); + } + + ScopedX509StackOnly intermediates(sk_X509_new_null()); + if (!intermediates) { + return util::Status( + util::Status::canonical_space(), util::error::INTERNAL, + "Failed to allocate X.509 intermediate certificate stack"); + } + const X509Cert* leaf_cert(nullptr); + for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) { + if (cert_chain.GetCert(idx)->IsCaCertificate()) { + sk_X509_push(intermediates.get(), + const_cast(cert_chain.GetCert(idx)->openssl_cert())); + } else { + leaf_cert = cert_chain.GetCert(idx); + } + } + if (!leaf_cert) { + return util::Status(util::Status::canonical_space(), + util::error::INVALID_ARGUMENT, + "X.509 certificate chain without leaf certificate."); + } + return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get()); +} + +util::Status X509CA::OpenSslX509Verify(const X509* cert, + STACK_OF(X509) * intermediates) { + DCHECK(cert); + + absl::ReaderMutexLock lock(&openssl_store_mutex_); + if (openssl_store_ == NULL) { + openssl_store_mutex_.ReaderUnlock(); + util::Status status = InitializeStore(); + if (!status.ok()) { + return status; + } + openssl_store_mutex_.ReaderLock(); + } + ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new()); + if (!store_ctx) { + return util::Status(util::Status::canonical_space(), util::error::INTERNAL, + "Failed to allocate X.509 store context"); + } + if (X509_STORE_CTX_init(store_ctx.get(), openssl_store_, + const_cast(cert), intermediates) == 0) { + return util::Status(util::Status::canonical_space(), util::error::INTERNAL, + "Failed to initialize X.509 store context"); + } + int x509_status = X509_verify_cert(store_ctx.get()); + if (x509_status != 1) { + return util::Status(util::Status::canonical_space(), util::error::INTERNAL, + std::string("X.509 certificate chain validation failed: ") + + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get()))); + } + + return util::OkStatus(); +} + +} // namespace widevine diff --git a/common/x509_cert.h b/common/x509_cert.h new file mode 100644 index 0000000..db08235 --- /dev/null +++ b/common/x509_cert.h @@ -0,0 +1,154 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// X.509 certificate classes used by the license server SDK. + +#ifndef COMMON_X509_CERT_H_ +#define COMMON_X509_CERT_H_ + +#include +#include +#include +#include +#include + +#include "base/macros.h" +#include "base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "openssl/pem.h" +#include "openssl/x509.h" +#include "openssl/x509v3.h" +#include "util/status.h" +#include "common/openssl_util.h" +#include "common/rsa_key.h" + +namespace widevine { + +// NOTE: All util::Status codes are in the canonical error space. + +// Class which holds a single X.509 certificates. +class X509Cert { + public: + // Load the certificate from an openssl X509 certificate instance. + static std::unique_ptr FromOpenSslCert(ScopedX509 openssl_cert_); + + X509Cert(); + virtual ~X509Cert(); + + // Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is + // a PEM-encoded certificate. + util::Status LoadPem(const std::string& pem_cert); + + // Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is + // a DER-encoded certificate. + util::Status LoadDer(const std::string& der_cert); + + // Return a std::string containing the PEM-encoded certificate. + std::string GetPem() const; + + // Returns certificate RSA public key. nullptr if not found, or if key type + // is not RSA. + std::unique_ptr GetRsaPublicKey() const; + + // Returns the internal OpenSSL X509 certificate. + const X509* openssl_cert() const { return openssl_cert_; } + + // Returns the certificate subject name. + const std::string& GetSubjectName(); + + // Returns a field within the certificate subject name, or an empty std::string + // if the field is not found. + std::string GetSubjectNameField(const std::string& field); + + // Returns the certificate serial number, binary encoded, or an empty std::string + // if an error occurs. + std::string GetSerialNumber() const; + + // Returns true if the certificate is a CA (root or intermediate) certificate. + bool IsCaCertificate() const; + + // Gets the value of an X.509 V3 extension encoded as a boolean. |oid| is the + // object identifier sequence for the extension, and |value| is a pointer to + // an integer which will contain the extension value upon successful return. + // Returns true if successful, or false otherwise. + bool GetV3BooleanExtension(const std::string& oid, bool* value) const; + + private: + explicit X509Cert(X509* openssl_cert); + + X509* openssl_cert_; + std::string subject_name_; + + friend class X509CertChain; + + DISALLOW_COPY_AND_ASSIGN(X509Cert); +}; + +// Class which holds a chain of X.509 certificates. +class X509CertChain { + public: + X509CertChain() {} + virtual ~X509CertChain(); + + // Loads a chain of PEM-encoded X.509 certificates. Takes a single parameter, + // |pem_cert_chain|, which is the concatenation of a number of PEM X.509 + // certificates, beginning with the leaf certificate, and ending with the + // certificate signed by the root CA. + util::Status LoadPem(const std::string& pem_cert_chain); + + // 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 + // container. + util::Status LoadPkcs7(const std::string& pk7_cert_chain); + + // Returns the number of certificates in the chain. + size_t GetNumCerts() const { return cert_chain_.size(); } + + // Returns the X509Cert at the specified chain index (0 start). Returns + // NULL if cert_index is out of range. The X509CertChain retains ownwership + // of the object returned. + X509Cert* GetCert(size_t cert_index) const; + + private: + void Reset(); + + std::vector cert_chain_; + + DISALLOW_COPY_AND_ASSIGN(X509CertChain); +}; + +// CA class which holds the root CA cert, and verifies certificate chains. +class X509CA { + public: + // New object assumes ownership of |ca_cert|. + explicit X509CA(X509Cert* ca_cert); + virtual ~X509CA(); + + // Does X.509 PKI validation of |cert| against the root CA certificate + // used when constructing X509CA. This method is thread-safe. + util::Status VerifyCert(const X509Cert& cert); + + // Does X.509 PKI validation of |cert_chain| against the root CA certificate + // used when constructing X509CA. This method is thread-safe. + util::Status VerifyCertChain(const X509CertChain& cert_chain); + + private: + util::Status InitializeStore(); + util::Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * stack); + + std::unique_ptr ca_cert_; + absl::Mutex openssl_store_mutex_; + X509_STORE* openssl_store_ GUARDED_BY(openssl_store_mutex_); + + DISALLOW_IMPLICIT_CONSTRUCTORS(X509CA); +}; + +} // namespace widevine + +#endif // COMMON_X509_CERT_H_ diff --git a/common/x509_cert_test.cc b/common/x509_cert_test.cc new file mode 100644 index 0000000..0cc8ccb --- /dev/null +++ b/common/x509_cert_test.cc @@ -0,0 +1,403 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include + +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "common/rsa_key.h" +#include "common/test_utils.h" +#include "common/x509_cert.h" + +namespace widevine { +const char kTestRootCaDerCert[] = + "30820403308202eba003020102020900a24f94af7ae6831f300d06092a86" + "4886f70d0101050500308197310b30090603550406130255533113301106" + "035504080c0a57617368696e67746f6e3111300f06035504070c084b6972" + "6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f" + "060355040b0c085769646576696e653115301306035504030c0c54657374" + "20526f6f742043413121301f06092a864886f70d010901161274696e736b" + "697040676f6f676c652e636f6d301e170d3133303831363030353731305a" + "170d3333303831353030353731305a308197310b30090603550406130255" + "533113301106035504080c0a57617368696e67746f6e3111300f06035504" + "070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049" + "6e633111300f060355040b0c085769646576696e65311530130603550403" + "0c0c5465737420526f6f742043413121301f06092a864886f70d01090116" + "1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648" + "86f70d01010105000382010f003082010a0282010100c6eee629d99f7736" + "2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c" + "e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319" + "0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3" + "f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93" + "52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7" + "e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f" + "d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20" + "79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7" + "3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30" + "ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680" + "144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405" + "30030101ff300d06092a864886f70d01010505000382010100779e9b98d3" + "ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee" + "79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124" + "ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436" + "026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140" + "ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053" + "0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258" + "e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82" + "d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4" + "c270d87191c13aefdd177b"; + +const char kTestPemCert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n" + "VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n" + "Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n" + "ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n" + "MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n" + "BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n" + "aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n" + "Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n" + "aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n" + "DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n" + "l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n" + "krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n" + "wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n" + "fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n" + "FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n" + "qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n" + "EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n" + "f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n" + "QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n" + "7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n" + "3rDeUI+Odg==\n" + "-----END CERTIFICATE-----\n"; +const char kTestPemCertSubjectField_C[] = "US"; +const char kTestPemCertSubjectField_CN[] = + "stable id/emailAddress=tinskip@google.com"; +const char kTestPemCertSerialNumber[] = "\002"; + +const char kTestPemCertChain[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n" + "VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n" + "Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n" + "ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n" + "MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n" + "BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n" + "aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n" + "Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n" + "aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n" + "DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n" + "l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n" + "krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n" + "wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n" + "fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n" + "FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n" + "qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n" + "EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n" + "f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n" + "QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n" + "7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n" + "3rDeUI+Odg==\n" + "-----END CERTIFICATE-----\n" + "-----BEGIN CERTIFICATE-----\n" + "MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n" + "EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n" + "DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n" + "b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n" + "ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n" + "BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n" + "bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n" + "ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n" + "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n" + "f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n" + "oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n" + "UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n" + "gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n" + "9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n" + "AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n" + "FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n" + "BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n" + "xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n" + "HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n" + "ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n" + "qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n" + "6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n" + "-----END CERTIFICATE-----\n"; + +const char kTestPk7CertChain[] = + "308207fb06092a864886f70d010702a08207ec308207e80201013100300b" + "06092a864886f70d010701a08207ce308203c3308202ab020102300d0609" + "2a864886f70d010105050030819f310b3009060355040613025553311330" + "1106035504080c0a57617368696e67746f6e3111300f06035504070c084b" + "69726b6c616e6431133011060355040a0c0a476f6f676c6520496e633111" + "300f060355040b0c085769646576696e65311d301b06035504030c145465" + "737420496e7465726d6564696174652043413121301f06092a864886f70d" + "010901161274696e736b697040676f6f676c652e636f6d301e170d313330" + "3831363231343430305a170d3333303831353231343430305a3081ae310b" + "30090603550406130255533113301106035504080c0a57617368696e6774" + "6f6e3111300f06035504070c084b69726b6c616e6431293027060355040a" + "0c204368726f6d652044657669636520436f6e74656e742050726f746563" + "74696f6e31153013060355040b0c0c74657374696e672e74657374311230" + "1006035504030c09737461626c652069643121301f06092a864886f70d01" + "0901161274696e736b697040676f6f676c652e636f6d30820122300d0609" + "2a864886f70d01010105000382010f003082010a0282010100a95b0eab2d" + "38ad1394bb4964e1b3cac8c3e3c657127a70b62d1c4002ba25c37d86dc29" + "1de98197301b22e3888de636b5fbe4976b8838e9cd3200222acfc3a6ef55" + "6de7570150a7bb113bff2ad6230feb835666a9dc505c5a93a3ee7612bb5e" + "32bbb892b39a36043add213ddf8c91d0c485cee49bbd381314b3fb847b9e" + "69e83cf8252f4115345a80b66a4317cd8d46e80915c3263f96e7c0fd7110" + "5e03db86f3faed8c51f9ec7009fcd01a8941e6192588abb1d2b9b30df708" + "88730cd080da13a9ee7db9bc04a3f19594bf42bcd3640afd43918cca372e" + "d174c8d5f927195ac4e2964c87c81efa89dd720210c5704e85f24a142a1f" + "6123fbac1c6d6ac2bee2770203010001300d06092a864886f70d01010505" + "0003820101004dc35f68ba5f2db5faab3d6a28c2d868d7b8388d17f2df19" + "357a8476ac8d7780bb91275a424c0dba7540ab0d4269d5332d18bc39fd7c" + "73095011704de1b3d378e0c2a1b3e34bbd587183e10ecbc641c4371900ba" + "04ec872824d8a6a81c2083c4185cc8f85ac009e75e7f8b7221ec6aeb7888" + "c75229993067a589e77e073cbf86a7a6134e0c728c4411f2f0125170aa53" + "145168e7bd5ce4e8c442384a10ea7d783ef61ae120b4ad1fee35d81f66d1" + "4f896648bc60d8bd636f0837a888e85f68237882f2cd417e153cceec6e49" + "5909f20e377f649ba577b151b099aecc080849ea957ab6985d4d69e2eb5b" + "aaeb404e698705c62191724a04a91fdeb0de508f8e7630820403308202eb" + "a003020102020101300d06092a864886f70d0101050500308197310b3009" + "0603550406130255533113301106035504080c0a57617368696e67746f6e" + "3111300f06035504070c084b69726b6c616e6431133011060355040a0c0a" + "476f6f676c6520496e633111300f060355040b0c085769646576696e6531" + "15301306035504030c0c5465737420526f6f742043413121301f06092a86" + "4886f70d010901161274696e736b697040676f6f676c652e636f6d301e17" + "0d3133303831363231343134365a170d3333303831353231343134365a30" + "819f310b30090603550406130255533113301106035504080c0a57617368" + "696e67746f6e3111300f06035504070c084b69726b6c616e643113301106" + "0355040a0c0a476f6f676c6520496e633111300f060355040b0c08576964" + "6576696e65311d301b06035504030c145465737420496e7465726d656469" + "6174652043413121301f06092a864886f70d010901161274696e736b6970" + "40676f6f676c652e636f6d30820122300d06092a864886f70d0101010500" + "0382010f003082010a0282010100da28062eb1dc8f489dcb3acad94f8c1b" + "bbc84cb97fa04b7f9351104e67426ef846decf000ed8543b711cffa98b5b" + "b2c6c2f77d4af1f74e2ce2d7573512bce7fdb39fbfa8a193d86fc7c7e9d8" + "25ee872661ec82d234a683bfbe662692e8eb233d25131366846c07ba43e6" + "6162c01b7642fb67674d50947d6a484b12a96a7d7e2ae5b580d0cdcfff6f" + "dfa8d60c4b4c34d74370a5df8ac9d6dc793d2b1d04e64c656ca8208b8266" + "8ff5af1a4113123f4767e4839c73f2b54279dfc358319e3185e1a04feca6" + "c2331341adb8013b0893c6010c21c299f481accc07297ed297e6b6b75de3" + "f315609c9f08a5a6969a13fd9759791c01e85cf1ddf1b64740619c952015" + "0203010001a350304e301d0603551d0e0416041439b7e60cccea5975dc78" + "5951b5b9b83c3b062793301f0603551d230418301680144d30ff181ac4f1" + "0da99e6a12c01e02accadf840a300c0603551d13040530030101ff300d06" + "092a864886f70d01010505000382010100b8fefd9f3281f595367715d0df" + "54b0073094651ee4c13e5685d417fd123a676b73c2ed3dcd8391cf1bc624" + "0dd701f113f385b1bb0e7686cfcf038187aecac98c955adc73cec2ced0c1" + "c6c6c6ce9b06b501eaeca0fe31f31a021da054f7472f898ab3e64eab6720" + "4894522b33996a1b5241dd4d244dc786ff5b2ac11000bbde0162a316d625" + "b2afd32a0124425413ab92c59d6b8a599022e9eb6be4ec8b4e72cd8fac77" + "fa5166c03530c7fbc43ea48c36c2e27995ffe29aa28eaab602266022e950" + "5ada3c87a9754f324ea52f5466764e1480eb11bec6e62b36f6bd87962308" + "6c5d8e52a68dd65532efea42241990853ffc2cedcb64f9f4328e3b6d9dc2" + "1d2f69a1003100"; + +const char kTestCertPrivateKey[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEowIBAAKCAQEAqVsOqy04rROUu0lk4bPKyMPjxlcSenC2LRxAArolw32G3Ckd\n" + "6YGXMBsi44iN5ja1++SXa4g46c0yACIqz8Om71Vt51cBUKe7ETv/KtYjD+uDVmap\n" + "3FBcWpOj7nYSu14yu7iSs5o2BDrdIT3fjJHQxIXO5Ju9OBMUs/uEe55p6Dz4JS9B\n" + "FTRagLZqQxfNjUboCRXDJj+W58D9cRBeA9uG8/rtjFH57HAJ/NAaiUHmGSWIq7HS\n" + "ubMN9wiIcwzQgNoTqe59ubwEo/GVlL9CvNNkCv1DkYzKNy7RdMjV+ScZWsTilkyH\n" + "yB76id1yAhDFcE6F8koUKh9hI/usHG1qwr7idwIDAQABAoIBADdwlZa30QvnkxLU\n" + "be/s+X9LkS8GpgfrCdgunU3HPkGGwDUmSKJ+R835tCwkMb+hPWXeaStMhsUS5UFh\n" + "7f3hoK5MmxPWSZnrrrNvnpKZUxUNFgucxBJZREJqfom7oVow9g6511xwKSqtUmJl\n" + "bN8JhPwwiZAQ45qNtINO3QnSy/y4IGrUPgjMpmJa26a+JhduTRq+LMPu2wz+HxS1\n" + "Vf2q0H1IOJr/kimMFMaBRYErNclFa8VIFjwjz5reH5lJyptajGhruor6EK1qqhNc\n" + "zPSRY4TZH5QcjM46zui6l3tL9e32j6oUd4mAp4HhH0fws/pwawFYECI+M+7OCjgK\n" + "y+qSJ1ECgYEA1g+L0yN4i+uScs7EpsYJfaRP1PMtGnUsof64Pg6i9IKcuf5mi5Kp\n" + "aIgZdXAZIzsACH5XbfuC5Srs4565k/9XrHehLcuBzodulrzwmOUDbJAxIDw4uTUX\n" + "95W0uK9UqyGLyM8wNYs/EzhveSFL8fnFWzOAL/+HshQpKCBzedSU+G0CgYEAyolH\n" + "xws2mim7rSrYyRz1Vj02rLZuBUR7cPaHDxjjuuSUbI2nsDRsm6ZUCNlJtReHBkpH\n" + "eW5iClBGkksVsJJYJBmyDw6a3mnj0mfxBnh9zGaHQi0RCuOwmYlu2L/XVQXiMFKT\n" + "gffazuvysg7N/bz7CJjm8PRRx/cAxxFfAozdf/MCgYEAtBagLCHLaOvnaW9LQoOZ\n" + "uHpkL2PmrjumMSN7HbpyngLEmDXPT90zaR4XTRXiECGzBXJFW+IdXW+fnGANANXx\n" + "jMeYck6kBn0qLOcIA5moJ82nhtcjYa2pXEI2qKnZMaAnWen1RRbBGgqAvgelPQ5F\n" + "W1UYo0j3gHo1peynOff+3IECgYAsP53M4KhHOgLkrE28cnUvKCR/y0NyJyoI3fNX\n" + "2wo11KaQqMoP9wQbZVVKsZ4m0EMRnrzKzNDii/M/FuRgNTjIekyqeXhgSyYY29iO\n" + "n1hshaHbVVk51dDJWns7I3559tUZ1ZCgfnPxbR8Sw6VBYD4//JfH4LjVRSOIWkU1\n" + "m2zw/QKBgGE55o0xrCywF3wDUtFa6vgpsOfZu9IblsWktSbD/lk1YOqGpU//B0O4\n" + "GqihOQT7E9kDNusspFUGpZrE0T0B+GW1T9iTR0zd+lC+qExv2ggDJoH063DnH5OU\n" + "Qz2M8LESeFxf6ZlBxkcyrk6G1RAy7lUs9fHhfmpEJLVv4DTCuWDl\n" + "-----END RSA PRIVATE KEY-----\n"; + +const char kTestMessage[] = + "c8635a17ccc672c941d0cc287715411a0a0222613a04d47693a53eb7f32c" + "1ebae1f5d916a815b880426362c42f5f18f694a380756e0452018c70b3e4" + "f72ebb5269cb7233a3b8a2a1840e33ca9d473224d17ff91bae6b8d4ff2d1" + "8e5c89b5fc8a52c4f791c2063ab1a29ffd3372db483e4975c1c9c7408bf6" + "dfe5696e256e86b75313c501ab781175971b9411a73c444592afb1ec1667" + "2bfb935715ef5302f3bef712d2296be4f64ef2dc861f0611b06c35d0a5c2" + "5ff9f4a2563f265f109d2fa8f8165d7891b8a83c84520eaa284d49a4f76e" + "ac158204a5bdf018edd9401ae6593092ba97970be9a58b10720a235c9158" + "b9f235f9dda3de05990cef8c2fd04920a2a434bd5b6aa75767762d89b964" + "90e42524855a7eab49a8f82ac593e4df01990206d3fa98329aa50e31db89" + "b46b82ee0073851826f77aabb3779738a6f311b79f54d036a98dca4881ef" + "88c3cbfc86ac358c7bd107dc234d3772fc707df01637354dcb9270c7aefa" + "852dd21818ede33ab7154c32f25268b82f89b344e6469b81b6699df68c56" + "a6e61f1dd8f140f3be4edce755ceee8ee7868f45a17f8b4b4b0988f45815" + "1b43d07dcb0cd80b1ffa37b824e0abc25897cb41c242a3db845bedd37adf" + "88a13c0b2f0b158464b02f9fd97ad6e87b92c13cbeee5e69d183cc898c4e" + "0cfa9c59abde74a437d030cb966137ffe9abe6be71ed21ef751cdea73625" + "7cff9e378718f7d7e9c4d567cbec8e0afdfab0585b8ed0d5f8de159b6524" + "22c90737b44c84603ba1131f557604fe4e6b4d91e45363903b8db179cee0" + "a50f2ae73394973c8671df7a7b2eeb8341a3417727cfe43290a67ac3ad02" + "a52c3d1698c2c28a46268518aea66cecb40f43f50bb9cea4ed1d49ceb51d" + "9967fabccccc7237a36b6cecda5916234730d7b3ca3295519d77b7516824" + "10e8a238b6345e8d28132f60423a13fdf4b6a6cf272cef9a0833abb4b86d" + "9828af45442a390e241b2b8c3290671da4a163d7e55fea7828098c0749ca" + "ff65145dd6b4a6e4c65d214801bb8302d8914864e99c4d0b390b8126d4bc" + "0353e376e69aba56cf71b9943a47dcffa07c6a24986a077f69b7bec6bd9c" + "357e211875453bdadd9bfc4526f96c458e0052d27a903611c09a9c7b5f51" + "83daad078aec0e79ef991d102d4af492773f1509a265c5644cbab3253e34" + "3015e4305fffd17ce0261bcb232cfa0e1dcc71f83dc1aac490e526f6269f" + "606d0e0e556bb30b774c2208ed3771474be23f39b7fc21dcbf304a923d9c"; + +const char kTestDevCodeSigningCert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIFDjCCA3agAwIBAgIPESIzRFVmd4iZqrvM3e7/MA0GCSqGSIb3DQEBCwUAMIGc\n" + "MQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2ly\n" + "a2xhbmQxDzANBgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNV\n" + "BAMMFXdpZGV2aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tp\n" + "cEBnb29nbGUuY29tMB4XDTE3MTAwOTIwMjUwNloXDTI3MTAwNzIwMjUwNlowRTEL\n" + "MAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy\n" + "bmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAObsg/w+dJedH3x5KEsXdA/5sunWc8G+iZl0wMcngh2DiwmOSkKf68uCK/iW\n" + "T0a2XGgk13zl1HuKrjatgc7n6E1j/sqDZBGkr0q1wQgsdzm3qvGZoDG/+Z2U23WU\n" + "kX6ZcyIYUbpO2VtQELEl6DgNwoUi/9Yp+vCb6lsItpSZ1WRD9NhbWh1MxZxj1s18\n" + "OYcEzpEYg4/vHTVhocUR/1Rp9M9yn0nH1MUdtjhgBM3BmlRH7TA/nF111A4+GzMN\n" + "qyqfb0/6yXE64Ca3+fGg1hstfUUXkpmjjNPhYJ6QTgA3Xfrz04a4uwB+pSliF3SD\n" + "gip7O3rDyK0ES55lGpZ7B3s3TakCAwEAAaOCASEwggEdMB0GA1UdDgQWBBQ2jJme\n" + "0BuaGrhgFGJR2i59HR+DizCBuwYDVR0jBIGzMIGwoYGipIGfMIGcMQswCQYDVQQG\n" + "EwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxDzAN\n" + "BgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNVBAMMFXdpZGV2\n" + "aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tpcEBnb29nbGUu\n" + "Y29tggkAxfgvA4+s8VgwCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAww\n" + "CgYIKwYBBQUHAwMwEQYKKwYBBAHWeQQBAgQDAQH/MA0GCSqGSIb3DQEBCwUAA4IB\n" + "gQAtan04ZGie7rRsKpb1F6t7xs48KE6cj6L99B5dgl37fZaZIQ3XE2vbmmmY5YTx\n" + "wofCkvOZMXHeQfJEK5GIK49TW/lAR+3kJUJzSh+N67f0X8O1pUl97IUFsbi6PTw/\n" + "mjhu197Kdy/OxPu/csOkEChuOfJLagRxXtIXeIyaeVOmn6fkFTOMOL2BusWOPuIs\n" + "9OmOQ+UHXpMuX4c2x9iO4NzZwwI/MgULLCrd/c73q199H+ttdPFoNs8+xGdodqA/\n" + "NFlHtMHMLMKVGpazAf+JW1/c3nb8L3S0nw4q7vPWi216RdZTfKfSIs/f/IW3CYJh\n" + "/IAuHOYvlD0GdSOFZHfhrnAvKhJ2iRu32psN87L9rL5EL22LT8csV/gLMc3SZ35n\n" + "/viuYcTDnMbe9S/Mge3mMJ9XHD5XBhN3hzmGDQEUdRS5MXrYdY32viPE7f+GAO9s\n" + "5MXS+h+FxQ6QUar2q1zHc/0Gr1hLzA6HYBmI0/AF8LsHs799XjrMKHkSBN6UQkC1\n" + "hRk=\n" + "-----END CERTIFICATE-----\n"; +const char kDevCertFlagOid[] = "1.3.6.1.4.1.11129.4.1.2"; +const bool kTestDevCodeSigningCertFlagValue = true; + + +TEST(X509CertTest, LoadCert) { + X509Cert test_cert; + EXPECT_EQ(util::OkStatus(), + test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); + EXPECT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); + // TODO(user): Add more specific status checks to failure tests. + EXPECT_NE(util::OkStatus(), test_cert.LoadDer("bad cert")); + EXPECT_NE(util::OkStatus(), test_cert.LoadPem("bad cert")); + EXPECT_NE(util::OkStatus(), test_cert.LoadDer("")); + EXPECT_NE(util::OkStatus(), test_cert.LoadPem("")); +} + +TEST(X509CertTest, VerifySignature) { + X509Cert test_cert; + ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); + std::string message(absl::HexStringToBytes(kTestMessage)); + std::string signature; + ASSERT_EQ(util::OkStatus(), GenerateRsaSignatureSha256Pkcs1( + kTestCertPrivateKey, message, &signature)); + std::unique_ptr pub_key(test_cert.GetRsaPublicKey()); + ASSERT_TRUE(pub_key); + EXPECT_TRUE(pub_key->VerifySignatureSha256Pkcs7(message, signature)); + + EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, "bad signature")); + EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("bad digest", signature)); + EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, "")); + EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("", signature)); +} + +TEST(X509CertTest, GetSubjectNameField) { + X509Cert test_cert; + ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); + EXPECT_EQ(kTestPemCertSubjectField_C, test_cert.GetSubjectNameField("C")); + EXPECT_EQ(kTestPemCertSubjectField_CN, test_cert.GetSubjectNameField("CN")); + EXPECT_EQ("", test_cert.GetSubjectNameField("invalid_field")); +} + +TEST(X509CertTest, GetSerialNumber) { + X509Cert test_cert; + ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); + EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber()); +} + +TEST(X509CertTest, CertChain) { + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + ASSERT_EQ(2, test_chain.GetNumCerts()); + EXPECT_FALSE(test_chain.GetCert(0) == NULL); + EXPECT_FALSE(test_chain.GetCert(1) == NULL); + EXPECT_TRUE(test_chain.GetCert(2) == NULL); +} + +TEST(X509CertTest, IsCaCertificate) { + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + ASSERT_EQ(2, test_chain.GetNumCerts()); + EXPECT_FALSE(test_chain.GetCert(0)->IsCaCertificate()); + EXPECT_TRUE(test_chain.GetCert(1)->IsCaCertificate()); +} + +TEST(X509CertTest, ChainVerificationPem) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(util::OkStatus(), + ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); + X509CA ca(ca_cert.release()); + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); +} + +TEST(X509CertTest, ChainVerificationPkcs7) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(util::OkStatus(), + ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); + X509CA ca(ca_cert.release()); + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), + test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain))); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); +} + +TEST(X509CertTest, BooleanExtension) { + std::unique_ptr cert1(new X509Cert); + ASSERT_EQ(util::OkStatus(), cert1->LoadPem(kTestPemCert)); + bool extension_value; + EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value)); + + std::unique_ptr cert2(new X509Cert); + ASSERT_EQ(util::OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert)); + ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value)); + EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value); +} + +} // namespace widevine diff --git a/glog.BUILD b/glog.BUILD new file mode 100644 index 0000000..1ddd6f7 --- /dev/null +++ b/glog.BUILD @@ -0,0 +1,183 @@ +################################################################################ +# Copyright 2016 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. +################################################################################ + +# Bazel build file for glog. + +licenses(["notice"]) + +# Common options required by all library targets. +glog_copts = [ + "-Isrc", + "-D_START_GOOGLE_NAMESPACE_='namespace google {'", + "-D_END_GOOGLE_NAMESPACE_=}", + "-DGOOGLE_NAMESPACE=google", + "-DGOOGLE_GLOG_DLL_DECL=", + "-DHAVE_LIB_GFLAGS", + "-DHAVE_PTHREAD", + "-DHAVE_RWLOCK", + "-DHAVE_PREAD", + "-DHAVE_PWRITE", + "-DHAVE_SYS_TIME_H", + "-DHAVE_SYS_UTSNAME_H", + "-DHAVE_UNISTD_H", + "-DHAVE_GLOB_H", + "-DHAVE___ATTRIBUTE__", + "-D__NR_gettid", + "-Wno-sign-compare", +] + +cc_library( + name = "glog", + srcs = [ + "src/base/commandlineflags.h", + "src/base/googleinit.h", + "src/base/mutex.h", + "src/demangle.cc", + "src/demangle.h", + "src/glog/log_severity.h", + "src/logging.cc", + "src/raw_logging.cc", + "src/signalhandler.cc", + "src/stacktrace.h", + "src/stacktrace_generic-inl.h", + "src/stacktrace_libunwind-inl.h", + "src/stacktrace_powerpc-inl.h", + "src/stacktrace_x86-inl.h", + "src/stacktrace_x86_64-inl.h", + "src/symbolize.cc", + "src/symbolize.h", + "src/utilities.cc", + "src/utilities.h", + "src/vlog_is_on.cc", + ":config_h", + ":logging_h", + ":raw_logging_h", + ":stl_logging_h", + ":vlog_is_on_h", + ], + hdrs = glob(["glog/*.h"]), + copts = glog_copts, + includes = ["src/"], + linkopts = ["-pthread"], + visibility = ["//visibility:public"], + deps = ["//external:gflags"], +) + +genrule( + name = "config_h", + srcs = ["src/config.h.cmake.in"], + outs = ["config.h"], + cmd = "awk '{ gsub(/^#cmakedefine/, \"//cmakedefine\"); print; }' $(<) > $(@)", +) + +sub_cmd = ("awk '{ " + + "gsub(/@ac_google_start_namespace@/, \"namespace google {\"); " + + "gsub(/@ac_google_end_namespace@/, \"} // namespace google\"); " + + "gsub(/@ac_google_namespace@/, \"google\"); " + + ("gsub(/@(ac_cv_have_stdint_h|ac_cv_have_uint16_t|" + + "ac_cv_have_libgflags|ac_cv_have_unistd_h|" + + "ac_cv_have___builtin_expect|" + + "ac_cv_cxx_using_operator)@/, \"1\"); ") + + "gsub(/@ac_cv___attribute___noreturn@/, \"__attribute__ ((noreturn))\"); " + + "gsub(/@ac_cv___attribute___noinline@/, \"__attribute__ ((noinline))\"); " + + "gsub(/@(ac_cv___attribute___[a-z0-9_]+)@/, \"\"); " + + "gsub(/@([a-z0-9_]+)@/, \"0\"); " + + "print; }' $(<) > $(@)") + +genrule( + name = "logging_h", + srcs = ["src/glog/logging.h.in"], + outs = ["glog/logging.h"], + cmd = sub_cmd, +) + +genrule( + name = "raw_logging_h", + srcs = ["src/glog/raw_logging.h.in"], + outs = ["glog/raw_logging.h"], + cmd = sub_cmd, +) + +genrule( + name = "stl_logging_h", + srcs = ["src/glog/stl_logging.h.in"], + outs = ["glog/stl_logging.h"], + cmd = sub_cmd, +) + +genrule( + name = "vlog_is_on_h", + srcs = ["src/glog/vlog_is_on.h.in"], + outs = ["glog/vlog_is_on.h"], + cmd = sub_cmd, +) + +cc_library( + name = "test_hdrs", + hdrs = [ + "src/config_for_unittests.h", + "src/googletest.h", + ], +) + +cc_test( + name = "demangle_unittest", + size = "small", + srcs = ["src/demangle_unittest.cc"], + copts = glog_copts, + data = ["src/demangle_unittest.txt"], + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "logging_unittest", + size = "small", + srcs = ["src/logging_unittest.cc"], + copts = glog_copts, + data = ["src/logging_unittest.err"], + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "stacktrace_unittest", + size = "small", + srcs = ["src/stacktrace_unittest.cc"], + copts = glog_copts, + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "symbolize_unittest", + size = "small", + srcs = ["src/symbolize_unittest.cc"], + copts = glog_copts, + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "utilities_unittest", + size = "small", + srcs = ["src/utilities_unittest.cc"], + copts = glog_copts, + deps = [ + ":glog", + ":test_hdrs", + ], +) diff --git a/license_server_sdk/internal/BUILD b/license_server_sdk/internal/BUILD new file mode 100644 index 0000000..bbcd9f9 --- /dev/null +++ b/license_server_sdk/internal/BUILD @@ -0,0 +1,202 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +# This package is the Widevine SDK used by DRM license servers. It supports +# Widevine modular DRM clients, e.g. Widevine CDM in Encryption Media Extension +# in Chrome. + +package(default_visibility = [":friends"]) + +# friends is a package_group which own the visibility for video widevine license_server_sdk lib +package_group( + name = "friends", + packages = [ + "//common/...", + "//license_server_sdk/...", + "//media_cas_proxy_sdk/...", + "//proxy_sdk/...", + "//sdk/...", + ], +) + + +filegroup( + name = "binary_release_files", + srcs = [ + "client_cert.h", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "session_impl", + srcs = [ + "session_impl.cc", + ], + hdrs = [ + "session_impl.h", + ], + deps = [ + ":sdk", + "//base", + "//strings", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util/endian", + "//util/random:global_id", + "//util:status", + "//common:aes_cbc_util", + "//common:certificate_type", + "//common:certificate_util", + "//common:crypto_util", + "//common:drm_root_certificate", + "//common:drm_service_certificate", + "//common:error_space", + "//common:random_util", + "//common:remote_attestation_verifier", + "//common:rsa_key", + "//common:signing_key_util", + "//common:verified_media_pipeline", + "//common:vmp_checker", + "//protos/public:client_identification_proto", + "//protos/public:errors_proto", + "//protos/public:license_protocol_proto", + "//protos/public:license_server_sdk_proto", + "//protos/public:provisioned_device_info_proto", + "//protos/public:widevine_pssh_proto", + ], +) + +cc_library( + name = "sdk", + srcs = [ + "client_cert.cc", + "device_status_list.cc", + "key_control_block.cc", + "parse_content_id.cc", + "generate_error_response.cc", + ], + hdrs = [ + "client_cert.h", + "device_status_list.h", + "generate_error_response.h", + "key_control_block.h", + "parse_content_id.h", + "session_usage_report.h", + ], + deps = [ + "//base", + "//strings", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//external:openssl", + "//util/endian", + "//util/gtl:map_util", + "//util:status", + "//common:crypto_util", + "//common:drm_service_certificate", + "//common:error_space", + "//common:random_util", + "//common:rsa_key", + "//common:signing_key_util", + "//common:wvm_token_handler", + "//sdk/external/common/wvpl:wvpl_types", + "//protos/public:client_identification_proto", + "//protos/public:device_certificate_status_proto", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:license_protocol_proto", + "//protos/public:license_server_sdk_proto", + "//protos/public:provisioned_device_info_proto", + "//protos/public:signed_drm_certificate_proto", + "//protos/public:widevine_pssh_proto", + ], +) + +cc_test( + name = "session_impl_test", + timeout = "short", + srcs = ["session_impl_test.cc"], + deps = [ + ":sdk", + ":session_impl", + "//base", + "//external:protobuf", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//common:aes_cbc_util", + "//common:crypto_util", + "//common:drm_root_certificate", + "//common:error_space", + "//common:remote_attestation_verifier", + "//common:rsa_key", + "//common:rsa_test_keys", + "//common:rsa_util", + "//common:signing_key_util", + "//common:test_certificates", + "//common:test_utils", + "//protos/public:client_identification_proto", + "//protos/public:device_certificate_status_proto", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:license_protocol_proto", + "//protos/public:license_server_sdk_proto", + "//protos/public:provisioned_device_info_proto", + "//protos/public:remote_attestation_proto", + "//protos/public:signed_drm_certificate_proto", + "//protos/public:widevine_pssh_proto", + ], +) + + +cc_test( + name = "key_control_block_test", + timeout = "short", + srcs = ["key_control_block_test.cc"], + deps = [ + ":sdk", + "//base", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//protos/public:license_protocol_proto", + "//protos/public:license_server_sdk_proto", + ], +) + +cc_test( + name = "device_status_list_test", + timeout = "short", + srcs = ["device_status_list_test.cc"], + deps = [ + ":sdk", + "//base", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//common:rsa_key", + "//common:rsa_test_keys", + "//protos/public:client_identification_proto", + "//protos/public:errors_proto", + "//protos/public:provisioned_device_info_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_test( + name = "parse_content_id_test", + timeout = "short", + srcs = ["parse_content_id_test.cc"], + deps = [ + ":sdk", + "//base", + "//testing:gunit_main", + "//util/endian", + "//common:error_space", + "//protos/public:errors_proto", + "//protos/public:license_server_sdk_proto", + ], +) diff --git a/license_server_sdk/internal/client_cert.cc b/license_server_sdk/internal/client_cert.cc new file mode 100644 index 0000000..5737c57 --- /dev/null +++ b/license_server_sdk/internal/client_cert.cc @@ -0,0 +1,408 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "license_server_sdk/internal/client_cert.h" + +#include +#include +#include + +#include "glog/logging.h" +#include "strings/serialize.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" +#include "util/gtl/map_util.h" +#include "util/status.h" +#include "common/crypto_util.h" +#include "common/error_space.h" +#include "common/random_util.h" +#include "common/signing_key_util.h" +#include "common/wvm_token_handler.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { +namespace { + +const int kPreProvisioningKeySizeBytes = 16; +const int kKeyboxSizeBytes = 72; + +struct ValidatedSignerCertificate { + std::string certificate; + std::string signature; +}; + +class ValidatedSignerCache { + public: + void SetRootPublicKey(std::unique_ptr new_root_key); + bool Exist(const std::string& serial_number, const std::string& certificate, + const std::string& signature); + util::Status ValidateSigner(const std::string& serial_number, + const std::string& certificate, + const std::string& signature); + + void ResetSignerCache(); + size_t SignerCacheSize(); + + static ValidatedSignerCache* GetSingleton(); + + private: + absl::Mutex rsa_root_public_key_mutex_; + std::unique_ptr rsa_root_public_key_ + GUARDED_BY(&rsa_root_public_key_mutex_); + absl::Mutex validated_signer_cache_mutex_; + std::map validated_signer_cache_ + GUARDED_BY(&validated_signer_cache_mutex_); +}; + +void ValidatedSignerCache::SetRootPublicKey( + std::unique_ptr new_root_key) { + absl::WriterMutexLock lock(&rsa_root_public_key_mutex_); + rsa_root_public_key_ = std::move(new_root_key); +} + +bool ValidatedSignerCache::Exist(const std::string& serial_number, + const std::string& certificate, + const std::string& signature) { + absl::ReaderMutexLock lock(&validated_signer_cache_mutex_); + ValidatedSignerCertificate* validated_signer = + gtl::FindOrNull(validated_signer_cache_, serial_number); + return (validated_signer != nullptr) && + (validated_signer->certificate == certificate) && + (validated_signer->signature == signature); +} + +util::Status ValidatedSignerCache::ValidateSigner(const std::string& serial_number, + const std::string& certificate, + const std::string& signature) { + { + absl::ReaderMutexLock key_lock(&rsa_root_public_key_mutex_); + if (rsa_root_public_key_ == nullptr) { + return util::Status(error_space, ROOT_CERTIFICATE_NOT_SET, ""); + } + if (!rsa_root_public_key_->VerifySignature(certificate, signature)) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "signer-certificate-verification-failed"); + } + } + absl::WriterMutexLock cache_lock(&validated_signer_cache_mutex_); + validated_signer_cache_[serial_number].certificate = certificate; + validated_signer_cache_[serial_number].signature = signature; + return util::OkStatus(); +} + +void ValidatedSignerCache::ResetSignerCache() { + absl::WriterMutexLock lock(&validated_signer_cache_mutex_); + validated_signer_cache_.clear(); +} + +size_t ValidatedSignerCache::SignerCacheSize() { + absl::ReaderMutexLock lock(&validated_signer_cache_mutex_); + return validated_signer_cache_.size(); +} + +ValidatedSignerCache* ValidatedSignerCache::GetSingleton() { + static auto* const kInstance = new ValidatedSignerCache(); + return kInstance; +} + +} // namespace + +// TODO(user): change to util::StatusOr> +// instead of ClientCert** to explicitly assigning ownership of the created +// object to the caller. + +util::Status ClientCert::Create(ClientIdentification::TokenType token_type, + const std::string& token, ClientCert** client_cert) { + DCHECK(client_cert); + if (token_type == ClientIdentification::KEYBOX) { + *client_cert = nullptr; + if (token.size() < kKeyboxSizeBytes) { + return util::Status(error_space, INVALID_KEYBOX_TOKEN, + "keybox-token-is-too-short"); + } + return ClientCert::CreateWithToken(token, client_cert); + } else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) { + return CreateCertificateClientCert(token, client_cert); + } else { + return util::Status(error_space, util::error::UNIMPLEMENTED, + "client-type-not-implemented"); + } +} + +util::Status ClientCert::CreateWithToken(const std::string& keybox_token, + ClientCert** client_cert) { + *client_cert = nullptr; + std::unique_ptr ret(new KeyboxClientCert(keybox_token)); + if (ret->status() != util::OkStatus()) { + return ret->status(); + } + *client_cert = ret.release(); + return util::OkStatus(); +} + +util::Status ClientCert::CreateCertificateClientCert( + const std::string& drm_certificate, ClientCert** client_cert) { + std::unique_ptr ret(new CertificateClientCert(drm_certificate)); + if (ret->status() != util::OkStatus()) { + return ret->status(); + } + *client_cert = ret.release(); + return util::OkStatus(); +} + +void ClientCert::CreateSignature(const std::string& message, std::string* signature) { + DCHECK(signature); + DCHECK(!signing_key().empty()); + if (signature == nullptr) { + return; + } + using crypto_util::CreateSignatureHmacSha256; + *signature = + CreateSignatureHmacSha256(GetServerSigningKey(signing_key()), message); +} + +void ClientCert::GenerateSigningKey(const std::string& message, + ProtocolVersion protocol_version) { + if (!signing_key_.empty()) return; + DCHECK(!key().empty()); + using crypto_util::DeriveKey; + using crypto_util::kSigningKeyLabel; + set_signing_key(DeriveKey(key(), kSigningKeyLabel, message, + SigningKeyMaterialSize(protocol_version))); +} + +KeyboxClientCert::~KeyboxClientCert() {} + +void KeyboxClientCert::SetPreProvisioningKeys( + const std::multimap& keymap) { + std::vector keyvector; + keyvector.reserve(keymap.size()); + for (std::multimap::const_iterator it = keymap.begin(); + it != keymap.end(); ++it) { + std::string key = absl::HexStringToBytes(it->second); + DCHECK_EQ(key.size(), 16); + keyvector.push_back(WvmTokenHandler::PreprovKey(it->first, key)); + } + WvmTokenHandler::SetPreprovKeys(keyvector); +} + +bool KeyboxClientCert::IsSystemIdKnown(const uint32_t system_id) { + return WvmTokenHandler::IsSystemIdKnown(system_id); +} + +uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) { + return WvmTokenHandler::GetSystemId(keybox_bytes); +} + +KeyboxClientCert::KeyboxClientCert(const std::string& keybox_bytes) { + if (keybox_bytes.size() < kKeyboxSizeBytes) { + set_status(util::Status(error_space, INVALID_KEYBOX_TOKEN, + "keybox-token-is-too-short")); + return; + } + + set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes)); + set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes)); + bool insecure_keybox = false; + util::Status status = WvmTokenHandler::DecryptDeviceKey( + keybox_bytes, &device_key_, nullptr, &insecure_keybox); + if (!status.ok()) { + Errors new_code = status.error_code() == util::error::NOT_FOUND + ? MISSING_PRE_PROV_KEY + : KEYBOX_DECRYPT_ERROR; + set_status(util::Status(error_space, new_code, status.error_message())); + } +} + +bool KeyboxClientCert::VerifySignature(const std::string& message, + const std::string& signature, + ProtocolVersion protocol_version) { + DCHECK(!signing_key().empty()); + using crypto_util::VerifySignatureHmacSha256; + if (!VerifySignatureHmacSha256( + GetClientSigningKey(signing_key(), protocol_version), signature, + message)) { + set_status(util::Status(error_space, INVALID_SIGNATURE, "")); + return false; + } + return true; +} + +util::Status CertificateClientCert::SetDrmRootCertificatePublicKey( + const std::string& root_public_key) { + std::unique_ptr new_root_key( + RsaPublicKey::Create(root_public_key)); + if (new_root_key == nullptr) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "root-certificate-rsa-public-key-failed"); + } + ValidatedSignerCache::GetSingleton()->SetRootPublicKey( + std::move(new_root_key)); + return util::OkStatus(); +} + +// Checks the device certificate using the following steps. +// 1. Load the certificate bytes into a signed device certificate. +// 2. Get the signer for the certificate. +// 3. Verify the signature of the certificate using the signer. +// 4. Load the root certificate. +// 5. Verify the signature of the signer certificate. +util::Status CertificateClientCert::ValidateCertificate( + const SignedDrmCertificate& signed_drm_certificate) { + // TODO(user): Cache valid certificates. + // TODO(user): Find out why signed_drm_certificate.has_signer() always + // returns false. Blindly assuming signer is there for now. + const SignedDrmCertificate& signer = signed_drm_certificate.signer(); + DrmCertificate intermediate_certificate; + if (!intermediate_certificate.ParseFromString(signer.drm_certificate())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-invalid-signer"); + } + std::unique_ptr rsa_public_signer_key( + RsaPublicKey::Create(intermediate_certificate.public_key())); + if (!rsa_public_signer_key.get()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "signer-certificate-public-key-failed"); + } + if (!rsa_public_signer_key->VerifySignature( + signed_drm_certificate.drm_certificate(), + signed_drm_certificate.signature())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-verification-failed"); + } + if (!intermediate_certificate.has_serial_number()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-signer-serial-number"); + } + // Check to see if this intermediate device certificate is signed by a + // provisioner (entity using Widevine Provisioning Server SDK). + // TODO(user): refactor this code for clarity with the cert chaining. + if (signer.has_signer()) { + DrmCertificate provisioner_certificate; + if (!provisioner_certificate.ParseFromString( + signer.signer().drm_certificate())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "intermediate-certificate-invalid-signer"); + } + if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) { + set_signed_by_provisioner(true); + } else { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "expected-provisioning-provider-certificate-type"); + } + if (!CheckSignerCache(provisioner_certificate.serial_number(), + signer.signer().drm_certificate(), + signer.signature())) { + util::Status status = ValidateSigner( + provisioner_certificate.serial_number(), + signer.signer().drm_certificate(), signer.signer().signature()); + if (!status.ok()) { + return status; + } + } + if (!provisioner_certificate.has_provider_id() || + provisioner_certificate.provider_id().empty()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-provisioning-service-id"); + } + set_service_id(provisioner_certificate.provider_id()); + } else { + if (!CheckSignerCache(intermediate_certificate.serial_number(), + signer.drm_certificate(), signer.signature())) { + util::Status status = + ValidateSigner(intermediate_certificate.serial_number(), + signer.drm_certificate(), signer.signature()); + if (!status.ok()) { + return status; + } + } + } + set_signer_serial_number(intermediate_certificate.serial_number()); + set_signer_creation_time_seconds( + intermediate_certificate.creation_time_seconds()); + return util::OkStatus(); +} + +CertificateClientCert::CertificateClientCert( + const std::string& signed_drm_certificate_bytes) { + SignedDrmCertificate signed_drm_certificate; + if (!signed_drm_certificate.ParseFromString(signed_drm_certificate_bytes)) { + set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-invalid-token")); + return; + } + util::Status status = ValidateCertificate(signed_drm_certificate); + if (!status.ok()) { + set_status(status); + return; + } + DrmCertificate drm_certificate; + if (!drm_certificate.ParseFromString( + signed_drm_certificate.drm_certificate())) { + set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-invalid")); + return; + } + if (!drm_certificate.has_system_id()) { + set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-missing-system-id")); + } + set_system_id(drm_certificate.system_id()); + set_serial_number(drm_certificate.serial_number()); + set_public_key(drm_certificate.public_key()); + rsa_public_key_.reset(RsaPublicKey::Create(public_key())); + if (rsa_public_key_ == nullptr) { + set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-public-key-failed")); + return; + } + set_key(Random16Bytes()); + if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) { + set_status(util::Status(error_space, ENCRYPT_ERROR, + "device-certificate-failed-encrypt-session-key")); + return; + } +} + +CertificateClientCert::~CertificateClientCert() {} + +bool CertificateClientCert::VerifySignature(const std::string& message, + const std::string& signature, + ProtocolVersion protocol_version) { + if (!rsa_public_key_->VerifySignature(message, signature)) { + set_status(util::Status(error_space, INVALID_SIGNATURE, "")); + return false; + } + return true; +} + +bool CertificateClientCert::CheckSignerCache(const std::string& serial_number, + const std::string& certificate, + const std::string& signature) const { + return ValidatedSignerCache::GetSingleton()->Exist(serial_number, certificate, + signature); +} + +util::Status CertificateClientCert::ValidateSigner(const std::string& serial_number, + const std::string& certificate, + const std::string& signature) { + return ValidatedSignerCache::GetSingleton()->ValidateSigner( + serial_number, certificate, signature); +} + +void CertificateClientCert::ResetSignerCache() { + ValidatedSignerCache::GetSingleton()->ResetSignerCache(); +} + +size_t CertificateClientCert::SignerCacheSize() { + return ValidatedSignerCache::GetSingleton()->SignerCacheSize(); +} + +} // namespace widevine diff --git a/license_server_sdk/internal/client_cert.h b/license_server_sdk/internal/client_cert.h new file mode 100644 index 0000000..6e27b0d --- /dev/null +++ b/license_server_sdk/internal/client_cert.h @@ -0,0 +1,216 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__ +#define LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__ + +#include +#include +#include + +#include "util/status.h" +#include "common/rsa_key.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +class SignedDrmCertificate; + +// Handler class for LicenseRequests; validates requests and encrypts licenses. +// TODO(user): Remove extra accessors after Keybox parsing is moved +// to a separate class in KeyboxClientCert class. +class ClientCert { + public: + virtual ~ClientCert() {} + static util::Status Create( + widevine::ClientIdentification::TokenType token_type, + const std::string& token, ClientCert** client_cert); + // Creates a Keybox based ClientCert. + static util::Status CreateWithToken(const std::string& keybox_token, + ClientCert** client_cert); + // Creates a Device Certificate based ClientCert. + static util::Status CreateCertificateClientCert(const std::string& drm_certificate, + ClientCert** client_cert); + // Creates a HMAC SHA256 signature based on the message and the key(). + // signature is owned by the caller and can not be NULL. + virtual void CreateSignature(const std::string& message, std::string* signature); + // Checks the passed in signature against a signature created used the + // classes information and the passed in message. Returns true if signature + // is valid. + virtual bool VerifySignature(const std::string& message, const std::string& signature, + ProtocolVersion protocol_version) = 0; + // Creates a signing_key that is accessible using signing_key(). Signing_key + // is constructed by doing a key derivation using the key() and message. + virtual void GenerateSigningKey(const std::string& message, + ProtocolVersion protocol_version); + // Used to create signing keys. For Keybox token types this is the device key. + // For Device Certificate token types this the session key. + virtual const std::string& key() const = 0; + virtual void set_key(const std::string& key) = 0; + virtual const std::string& encrypted_key() const = 0; + virtual uint32_t system_id() const { return system_id_; } + virtual const std::string& signing_key() const { return signing_key_; } + virtual const std::string& public_key() const { return public_key_; } + virtual const std::string& serial_number() const { return serial_number_; } + virtual void set_serial_number(const std::string& serial_number) { + serial_number_ = serial_number; + } + virtual const std::string& signer_serial_number() const { + return signer_serial_number_; + } + virtual uint32_t signer_creation_time_seconds() const { + return signer_creation_time_seconds_; + } + virtual const util::Status& status() const { return status_; } + virtual widevine::ClientIdentification::TokenType type() const = 0; + virtual std::string service_id() const { return service_id_; } + virtual bool signed_by_provisioner() const { return signed_by_provisioner_; } + + protected: + ClientCert() {} + + virtual void set_system_id(uint32_t system_id) { system_id_ = system_id; } + virtual void set_signing_key(const std::string& signing_key) { + signing_key_ = signing_key; + } + virtual void set_status(const util::Status& status) { status_ = status; } + virtual void set_service_id(const std::string& service_id) { + service_id_ = service_id; + } + virtual void set_signed_by_provisioner(bool provisioner_signed_flag) { + signed_by_provisioner_ = provisioner_signed_flag; + } + + std::string public_key_; + std::string serial_number_; + std::string signer_serial_number_; + uint32_t signer_creation_time_seconds_ = 0; + bool signed_by_provisioner_ = false; + + private: + uint32_t system_id_ = 0; + std::string signing_key_; + util::Status status_; + std::string service_id_; + + DISALLOW_COPY_AND_ASSIGN(ClientCert); +}; + +// This class implements the crypto operations based on the Widevine keybox. +// It will unpack token and perform all the crypto operations for securing +// the key material in the license response. +class KeyboxClientCert : public ClientCert { + public: + ~KeyboxClientCert() override; + + // Set the system-wide pre-provisioning keys; argument must be human-readable + // hex digits. + // Must be called before any other method of this class is called, unless + // created by ClientCert::CreateWithPreProvisioningKey(...). + static void SetPreProvisioningKeys(const std::multimap& keys); + static bool IsSystemIdKnown(const uint32_t system_id); + static uint32_t GetSystemId(const std::string& keybox_bytes); + + bool VerifySignature(const std::string& message, const std::string& signature, + ProtocolVersion protocol_version) override; + const std::string& key() const override { return device_key_; } + void set_key(const std::string& key) override { device_key_ = key; } + const std::string& encrypted_key() const override { return encrypted_device_key_; } + widevine::ClientIdentification::TokenType type() const override { + return widevine::ClientIdentification::KEYBOX; + } + + private: + friend class ClientCert; + friend class MockKeyboxClientCert; + explicit KeyboxClientCert(const std::string& keybox_bytes); + + std::string device_key_; + std::string encrypted_device_key_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(KeyboxClientCert); +}; +// This class implements the device certificate operations based on RSA keys. +// It will unpack token and perform all the crypto operations for securing +// the key material in the license response. +using widevine::RsaPublicKey; +class CertificateClientCert : public ClientCert { + public: + ~CertificateClientCert() override; + // Sets the root certificate for certificate validation. + static util::Status SetDrmRootCertificatePublicKey( + const std::string& root_public_key); + bool VerifySignature(const std::string& message, const std::string& signature, + ProtocolVersion protocol_version) override; + const std::string& key() const override { return session_key_; } + void set_key(const std::string& key) override { session_key_ = key; } + const std::string& encrypted_key() const override { + return encrypted_session_key_; + } + widevine::ClientIdentification::TokenType type() const override { + return widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE; + } + + protected: + friend class ClientCert; + friend class MockCertificateClientCert; + explicit CertificateClientCert(const std::string& signed_drm_certificate_bytes); + util::Status ValidateCertificate( + const SignedDrmCertificate& signed_drm_certificate); + virtual void set_public_key(const std::string& public_key) { + public_key_ = public_key; + } + virtual void set_signer_serial_number(const std::string& signer_serial_number) { + signer_serial_number_ = signer_serial_number; + } + virtual void set_signer_creation_time_seconds(uint32_t creation_time_seconds) { + signer_creation_time_seconds_ = creation_time_seconds; + } + // This method checks to see if a cached signature for the signer certificate + // exists. The cache is populated by the ValidateSignature method, below. + // - serial_number is the signer (intermediate) certificate serial number. + // This method does a cached signature lookup using this value as the key. + // - certificate is the serialized signer certificate. This method compares + // this value to the value cached to determine whether there is a match. + // - signature is the signature for the serialized signer certificate. This + // method compares this value to the value cached to determine whether + // there is a match. + // Returns true if there exists a matching cached signature for the signer + // certificate with the specified serial number, serialized + // DrmCertificate, and serialized DrmCertificate signature. + // Returns false otherwise. + virtual bool CheckSignerCache(const std::string& serial_number, + const std::string& certificate, + const std::string& signature) const; + // This method verifies the signature of a signer (intermediate) certificate, + // caching it in the signer cache if verification succeeds. + // - serial_number is the signer (intermediate) certificate serial number. + // - certificate is the serialized signer certificate. + // - signature is the signature for the serialized signer certificate, signed + // with the root certificate private key. + // Returns util::Status::OK and caches the validated signer information in + // the signer cache if signature validation succeeds. Otherwise, returns + virtual util::Status ValidateSigner(const std::string& serial_number, + const std::string& certificate, + const std::string& signature); + + // The below two functions are only used for testing. + static void ResetSignerCache(); + static size_t SignerCacheSize(); + + std::string session_key_; + std::string encrypted_session_key_; + std::unique_ptr rsa_public_key_; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(CertificateClientCert); +}; + +} // namespace widevine +#endif // LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__ diff --git a/license_server_sdk/internal/client_cert_test.cc b/license_server_sdk/internal/client_cert_test.cc new file mode 100644 index 0000000..0ced7e3 --- /dev/null +++ b/license_server_sdk/internal/client_cert_test.cc @@ -0,0 +1,607 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "license_server_sdk/internal/client_cert.h" + +#include +#include +#include +#include + +#include "glog/logging.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "common/drm_root_certificate.h" +#include "common/error_space.h" +#include "common/rsa_test_keys.h" +#include "common/wvm_test_keys.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +// TODO(user): Change these tests to use on-the-fly generated intermediate +// and device certificates based on RsaTestKeys. +// TODO(user): Add testcase(s) VerifySignature, CreateSignature, +// and GenerateSigningKey. + +namespace widevine { + +using ::testing::_; +using ::testing::Return; + +class ClientCertTest : public ::testing::Test { + public: + void SetUp() override { + if (!setup_preprov_keys_) { + KeyboxClientCert::SetPreProvisioningKeys( + wvm_test_keys::GetPreprovKeyMultimap()); + setup_preprov_keys_ = true; + } + CHECK_OK(CertificateClientCert::SetDrmRootCertificatePublicKey( + test_keys_.public_test_key_1_3072_bits())); + } + + protected: + // Simple container struct for test value and expected keys. + class TestTokenAndKeys { + public: + const std::string token_; + uint32_t expected_system_id_; + const std::string expected_serial_number_; + const std::string expected_device_key_; + TestTokenAndKeys(const std::string& token, uint32_t expected_system_id, + const std::string& expected_serial_number, + const std::string& expected_device_key) + : token_(token), + expected_system_id_(expected_system_id), + expected_serial_number_(expected_serial_number), + expected_device_key_(expected_device_key) {} + }; + + class TestCertificateAndData { + public: + const std::string certificate_; + const std::string expected_serial_number_; + uint32_t expected_system_id_; + util::Status expected_status_; + TestCertificateAndData(const std::string& certificate, + const std::string& expected_serial_number, + uint32_t expected_system_id, + util::Status expected_status) + : certificate_(certificate), + expected_serial_number_(expected_serial_number), + expected_system_id_(expected_system_id), + expected_status_(std::move(expected_status)) {} + }; + + void TestBasicValidation(const TestTokenAndKeys& expectation, + const bool expect_success, + const bool compare_device_key); + void TestBasicValidationDrmCertificate( + const TestCertificateAndData& expectation, const bool compare_data); + + void GenerateSignature(const std::string& message, const std::string& private_key, + std::string* signature); + SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate, + SignedDrmCertificate* signer, + const std::string& private_key); + DrmCertificate* GenerateProvisionerCertificate(uint32_t system_id, + const std::string& serial_number, + const std::string& provider_id); + SignedDrmCertificate* GenerateSignedProvisionerCertificate( + uint32_t system_id, const std::string& serial_number, const std::string& service_id); + DrmCertificate* GenerateIntermediateCertificate(uint32_t system_id, + const std::string& serial_number); + SignedDrmCertificate* GenerateSignedIntermediateCertificate( + SignedDrmCertificate* signer, uint32_t system_id, + const std::string& serial_number); + DrmCertificate* GenerateDrmCertificate(uint32_t system_id, + const std::string& serial_number); + SignedDrmCertificate* GenerateSignedDrmCertificate( + SignedDrmCertificate* signer, uint32_t system_id, + const std::string& serial_number); + + RsaTestKeys test_keys_; + static bool setup_preprov_keys_; +}; +bool ClientCertTest::setup_preprov_keys_(false); + +void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation, + const bool expect_success, + const bool compare_device_key) { + // Test validation of a valid request. + util::Status status; + ClientCert* client_cert_ptr = nullptr; + + // Two ways to create a client cert object, test both. + for (int i = 0; i < 2; i++) { + if (i == 0) { + status = ClientCert::Create(ClientIdentification::KEYBOX, + expectation.token_, &client_cert_ptr); + } else { + status = + ClientCert::CreateWithToken(expectation.token_, &client_cert_ptr); + } + std::unique_ptr keybox_cert(client_cert_ptr); + if (expect_success) { + ASSERT_EQ(util::OkStatus(), status); + ASSERT_TRUE(keybox_cert.get()); + EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id()); + EXPECT_EQ(expectation.expected_serial_number_, + keybox_cert->serial_number()); + if (compare_device_key) { + EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key()); + } + } else { + EXPECT_NE(util::OkStatus(), status); + EXPECT_FALSE(keybox_cert); + } + } +} + +void ClientCertTest::TestBasicValidationDrmCertificate( + const TestCertificateAndData& expectation, const bool compare_data) { + // Test validation of a valid request. + util::Status status; + ClientCert* client_cert_ptr = nullptr; + status = ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE, + expectation.certificate_, &client_cert_ptr); + std::unique_ptr drm_certificate_cert(client_cert_ptr); + ASSERT_EQ(expectation.expected_status_, status); + if (expectation.expected_status_.ok()) { + ASSERT_TRUE(drm_certificate_cert.get()); + if (compare_data) { + ASSERT_EQ(expectation.expected_serial_number_, + drm_certificate_cert->signer_serial_number()); + ASSERT_EQ(expectation.expected_system_id_, + drm_certificate_cert->system_id()); + } + } else { + ASSERT_FALSE(drm_certificate_cert.get()); + } +} + +void ClientCertTest::GenerateSignature(const std::string& message, + const std::string& private_key, + std::string* signature) { + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(private_key)); + ASSERT_TRUE(rsa_private_key != nullptr); + rsa_private_key->GenerateSignature(message, signature); +} + +// The caller relinquishes ownership of |signer|, which may also be nullptr. +SignedDrmCertificate* ClientCertTest::SignCertificate( + const DrmCertificate& certificate, SignedDrmCertificate* signer, + const std::string& private_key) { + std::unique_ptr signed_certificate( + new SignedDrmCertificate); + signed_certificate->set_drm_certificate(certificate.SerializeAsString()); + GenerateSignature(signed_certificate->drm_certificate(), private_key, + signed_certificate->mutable_signature()); + if (signer != nullptr) { + signed_certificate->set_allocated_signer(signer); + } + return signed_certificate.release(); +} + +DrmCertificate* ClientCertTest::GenerateIntermediateCertificate( + uint32_t system_id, const std::string& serial_number) { + std::unique_ptr intermediate_certificate(new DrmCertificate); + intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL); + intermediate_certificate->set_serial_number(serial_number); + intermediate_certificate->set_public_key( + test_keys_.public_test_key_2_2048_bits()); + intermediate_certificate->set_system_id(system_id); + intermediate_certificate->set_creation_time_seconds(1234); + return intermediate_certificate.release(); +} + +SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate( + SignedDrmCertificate* signer, uint32_t system_id, + const std::string& serial_number) { + std::unique_ptr intermediate_certificate( + GenerateIntermediateCertificate(system_id, serial_number)); + return SignCertificate(*intermediate_certificate, signer, + test_keys_.private_test_key_1_3072_bits()); +} + +DrmCertificate* ClientCertTest::GenerateDrmCertificate( + uint32_t system_id, const std::string& serial_number) { + std::unique_ptr drm_certificate(new DrmCertificate); + drm_certificate->set_type(DrmCertificate::DEVICE); + drm_certificate->set_serial_number(serial_number); + drm_certificate->set_system_id(system_id); + drm_certificate->set_public_key(test_keys_.public_test_key_3_2048_bits()); + drm_certificate->set_creation_time_seconds(4321); + return drm_certificate.release(); +} + +SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate( + SignedDrmCertificate* signer, uint32_t system_id, + const std::string& serial_number) { + std::unique_ptr drm_certificate( + GenerateDrmCertificate(system_id, serial_number)); + std::unique_ptr signed_drm_certificate(SignCertificate( + *drm_certificate, signer, test_keys_.private_test_key_2_2048_bits())); + return signed_drm_certificate.release(); +} + +DrmCertificate* ClientCertTest::GenerateProvisionerCertificate( + uint32_t system_id, const std::string& serial_number, const std::string& provider_id) { + std::unique_ptr provisioner_certificate(new DrmCertificate); + provisioner_certificate->set_type(DrmCertificate::PROVISIONER); + provisioner_certificate->set_serial_number(serial_number); + // TODO(user): Need to generate 3072 bit test for provisioner certificates. + provisioner_certificate->set_public_key( + test_keys_.public_test_key_2_2048_bits()); + provisioner_certificate->set_system_id(system_id); + provisioner_certificate->set_provider_id(provider_id); + provisioner_certificate->set_creation_time_seconds(1234); + return provisioner_certificate.release(); +} + +SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate( + uint32_t system_id, const std::string& serial_number, const std::string& service_id) { + std::unique_ptr provisioner_certificate( + GenerateProvisionerCertificate(system_id, serial_number, service_id)); + return SignCertificate(*provisioner_certificate, nullptr, + test_keys_.private_test_key_1_3072_bits()); +} + +TEST_F(ClientCertTest, BasicValidation) { + const TestTokenAndKeys kValidTokenAndExpectedKeys[] = { + TestTokenAndKeys( + absl::HexStringToBytes( + "00000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"), + 274, absl::HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"), + absl::HexStringToBytes("4071197f1f8910d9bf10c6bc4c987638")), + TestTokenAndKeys( + absl::HexStringToBytes( + "0000000200000112d906feebe1750c5886ff77c2dfa31bb40e002f3adbc0fa5b" + "eb2486cf5f419549cdaa23230e5165ac2ffab56d53b692b7ba0c1857400c6add" + "3af3ff3d5cb24985"), + 274, absl::HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"), + absl::HexStringToBytes("42cfb1765201042302a404d1e0fac8ed"))}; + + for (size_t i = 0; i < ABSL_ARRAYSIZE(kValidTokenAndExpectedKeys); ++i) { + SCOPED_TRACE("Test data: " + absl::StrCat(i)); + TestBasicValidation(kValidTokenAndExpectedKeys[i], true, true); + } + + EXPECT_EQ( + wvm_test_keys::kTestSystemId, + KeyboxClientCert::GetSystemId(kValidTokenAndExpectedKeys[0].token_)); +} + +TEST_F(ClientCertTest, BasicCertValidation) { + const uint32_t system_id = 1234; + const std::string serial_number("serial_number"); + std::unique_ptr signed_cert( + GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate( + nullptr, system_id, serial_number), + system_id, serial_number + "-device")); + const TestCertificateAndData kValidCertificateAndExpectedData( + signed_cert->SerializeAsString(), serial_number, system_id, + util::OkStatus()); + const bool compare_data = true; + TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData, + compare_data); +} + +TEST_F(ClientCertTest, InvalidKeybox) { + const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = { + // This tests a malformed, but appropriately sized keybox. + TestTokenAndKeys( + absl::HexStringToBytes( + "00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"), + 0, absl::HexStringToBytes(""), absl::HexStringToBytes("")), + // This has a length and system_id, but nothing else. + TestTokenAndKeys(absl::HexStringToBytes("0000000200000112"), 0, + absl::HexStringToBytes(""), absl::HexStringToBytes("")), + // This has only a byte. + TestTokenAndKeys(absl::HexStringToBytes(""), 0, + absl::HexStringToBytes(""), absl::HexStringToBytes("")), + // This has an emptry std::string for the keybox. + TestTokenAndKeys(absl::HexStringToBytes(""), 0, + absl::HexStringToBytes(""), absl::HexStringToBytes(""))}; + + for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidTokenAndExpectedKeys); ++i) { + SCOPED_TRACE("Test data: " + absl::StrCat(i)); + TestBasicValidation(kInvalidTokenAndExpectedKeys[i], false, false); + } +} + +TEST_F(ClientCertTest, InvalidCertificate) { + const uint32_t system_id(1234); + const std::string device_sn("device-serial-number"); + const std::string signer_sn("signer-serial-number"); + std::unique_ptr dev_cert; + std::unique_ptr signer_cert; + std::unique_ptr signed_signer; + + // Invalid serialized device certificate. + std::unique_ptr invalid_drm_cert( + new SignedDrmCertificate); + invalid_drm_cert->set_drm_certificate("bad-serialized-cert"); + GenerateSignature(invalid_drm_cert->drm_certificate(), + test_keys_.private_test_key_2_2048_bits(), + invalid_drm_cert->mutable_signature()); + invalid_drm_cert->set_allocated_signer( + GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn)); + // Missing system ID. + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + dev_cert->clear_system_id(); + std::unique_ptr no_system_id(SignCertificate( + *dev_cert.get(), + GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn), + test_keys_.private_test_key_2_2048_bits())); + // Invalid device public key. + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + dev_cert->set_public_key("bad-device-public-key"); + std::unique_ptr bad_device_public_key(SignCertificate( + *dev_cert, + GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn), + test_keys_.private_test_key_2_2048_bits())); + // Invalid serialized intermediate certificate. + signed_signer.reset( + GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn)); + signed_signer->set_drm_certificate("bad-serialized-cert"); + GenerateSignature(signed_signer->drm_certificate(), + test_keys_.private_test_key_1_3072_bits(), + signed_signer->mutable_signature()); + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + std::unique_ptr invalid_signer( + SignCertificate(*dev_cert, signed_signer.release(), + test_keys_.private_test_key_2_2048_bits())); + // Invalid signer public key. + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); + signer_cert->set_public_key("bad-signer-public-key"); + std::unique_ptr bad_signer_public_key(SignCertificate( + *dev_cert, + SignCertificate(*signer_cert, nullptr, + test_keys_.private_test_key_1_3072_bits()), + test_keys_.private_test_key_2_2048_bits())); + // Invalid device certificate signature. + std::unique_ptr bad_device_signature( + GenerateSignedDrmCertificate( + GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn), + system_id, device_sn)); + bad_device_signature->set_signature("bad-signature"); + // Missing signer serial number. + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); + signer_cert->clear_serial_number(); + std::unique_ptr missing_signer_sn(SignCertificate( + *dev_cert, + SignCertificate(*signer_cert, nullptr, + test_keys_.private_test_key_1_3072_bits()), + test_keys_.private_test_key_2_2048_bits())); + // Invalid serialized intermediate certificate. + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + signed_signer.reset( + GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn)); + signed_signer->set_signature("bad-signature"); + std::unique_ptr bad_signer_signature( + SignCertificate(*dev_cert, signed_signer.release(), + test_keys_.private_test_key_2_2048_bits())); + + const TestCertificateAndData kInvalidCertificate[] = { + TestCertificateAndData("f", "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-invalid-token")), + TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-invalid")), + TestCertificateAndData( + no_system_id->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-missing-system-id")), + TestCertificateAndData( + bad_device_public_key->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-public-key-failed")), + TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-invalid-signer")), + TestCertificateAndData( + bad_signer_public_key->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "signer-certificate-public-key-failed")), + TestCertificateAndData( + bad_device_signature->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-verification-failed")), + TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-signer-serial-number")), + TestCertificateAndData( + bad_signer_signature->SerializeAsString(), "", 0, + util::Status(error_space, INVALID_DRM_CERTIFICATE, + "signer-certificate-verification-failed")), + }; + + for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) { + TestBasicValidationDrmCertificate(kInvalidCertificate[i], false); + } +} + +class MockCertificateClientCert : public CertificateClientCert { + public: + using CertificateClientCert::ResetSignerCache; + using CertificateClientCert::SignerCacheSize; + + explicit MockCertificateClientCert(const std::string& cert_bytes) + : CertificateClientCert(cert_bytes) {} + MOCK_METHOD3(ValidateSigner, util::Status(const std::string& serial_number, + const std::string& certificate, + const std::string& signature)); + util::Status CallValidateCertificate( + const SignedDrmCertificate& signed_drm_certificate) { + return ValidateCertificate(signed_drm_certificate); + } +}; + +TEST_F(ClientCertTest, SignerCache) { + const uint32_t system_id = 1234; + const std::string serial_number("serial-number"); + std::unique_ptr signed_cert( + GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate( + nullptr, system_id, serial_number), + system_id, serial_number + "-device")); + // TODO(user): Remove work from the ClientCert constructors to make it + // more testable, and because it's just bad practice. + MockCertificateClientCert::ResetSignerCache(); + MockCertificateClientCert client_cert(signed_cert->SerializeAsString()); + EXPECT_EQ(1, MockCertificateClientCert::SignerCacheSize()); + EXPECT_CALL(client_cert, ValidateSigner(_, _, _)) + .Times(0) + .WillRepeatedly(Return(util::OkStatus())); + EXPECT_EQ(util::OkStatus(), + client_cert.CallValidateCertificate(*signed_cert)); +} + +TEST_F(ClientCertTest, MissingPreProvKey) { + // system ID in token is 0x01234567 + const std::string token(absl::HexStringToBytes( + "00000002012345678e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e")); + ClientCert* client_cert_ptr = nullptr; + util::Status status = ClientCert::CreateWithToken(token, &client_cert_ptr); + ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code()); +} + +TEST_F(ClientCertTest, ValidProvisionerDeviceCert) { + const uint32_t system_id = 4890; + const std::string service_id("widevine_test.com"); + const std::string device_serial_number("device-serial-number"); + const std::string intermediate_serial_number("intermediate-serial-number"); + const std::string provisioner_serial_number("provisioner-serial-number"); + + std::unique_ptr signed_provisioner_cert( + GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number, + service_id)); + + std::unique_ptr signed_intermediate_cert( + GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(), + system_id, + intermediate_serial_number)); + + std::unique_ptr signed_device_cert( + GenerateSignedDrmCertificate(signed_intermediate_cert.release(), + system_id, device_serial_number)); + + std::string serialized_cert; + signed_device_cert->SerializeToString(&serialized_cert); + ClientCert* client_cert_ptr = nullptr; + + EXPECT_OK(ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE, + serialized_cert, &client_cert_ptr)); + ASSERT_TRUE(client_cert_ptr != nullptr); + std::unique_ptr drm_cert(client_cert_ptr); + + EXPECT_EQ(service_id, drm_cert->service_id()); + EXPECT_EQ(device_serial_number, drm_cert->serial_number()); + EXPECT_EQ(intermediate_serial_number, drm_cert->signer_serial_number()); + EXPECT_EQ(system_id, drm_cert->system_id()); +} + +TEST_F(ClientCertTest, InValidProvisionerDeviceCertEmptyServiceId) { + const uint32_t system_id = 4890; + const std::string service_id(""); + const std::string device_serial_number("device-serial-number"); + const std::string intermediate_serial_number("intermediate-serial-number"); + const std::string provisioner_serial_number("provisioner-serial-number"); + + std::unique_ptr signed_provisioner_cert( + GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number, + service_id)); + + std::unique_ptr signed_intermediate_cert( + GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(), + system_id, + intermediate_serial_number)); + + std::unique_ptr signed_device_cert( + GenerateSignedDrmCertificate(signed_intermediate_cert.release(), + system_id, device_serial_number)); + + std::string serialized_cert; + signed_device_cert->SerializeToString(&serialized_cert); + ClientCert* client_cert_ptr = nullptr; + + EXPECT_EQ("missing-provisioning-service-id", + ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE, + serialized_cert, &client_cert_ptr) + .error_message()); + EXPECT_FALSE(client_cert_ptr); +} + +TEST_F(ClientCertTest, InValidProvisionerDeviceCertChain) { + const uint32_t system_id = 4890; + const uint32_t system_id2 = 4892; + const std::string service_id("widevine_test.com"); + const std::string device_serial_number("device-serial-number"); + const std::string intermediate_serial_number("intermediate-serial-number"); + const std::string intermediate_serial_number2("intermediate-serial-number-2"); + + std::unique_ptr signed_intermediate_cert2( + GenerateSignedIntermediateCertificate(nullptr, system_id2, + intermediate_serial_number2)); + + // Instead of using a provisioner certificate to sign this intermediate + // certificate, use another intermediate certificate. This is an invalid + // chain and should generate an error when trying to create a client + // certificate. + std::unique_ptr signed_intermediate_cert( + GenerateSignedIntermediateCertificate(signed_intermediate_cert2.release(), + system_id, + intermediate_serial_number)); + std::unique_ptr signed_device_cert( + GenerateSignedDrmCertificate(signed_intermediate_cert.release(), + system_id, device_serial_number)); + std::string serialized_cert; + signed_device_cert->SerializeToString(&serialized_cert); + ClientCert* client_cert_ptr = nullptr; + + ASSERT_EQ("expected-provisioning-provider-certificate-type", + ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE, + serialized_cert, &client_cert_ptr) + .error_message()); + EXPECT_FALSE(client_cert_ptr); + + // Make a normal intermediate certificate. + signed_intermediate_cert.reset(GenerateSignedIntermediateCertificate( + nullptr, system_id, intermediate_serial_number)); + signed_device_cert.reset(GenerateSignedDrmCertificate( + signed_intermediate_cert.release(), system_id, device_serial_number)); + + signed_device_cert->SerializeToString(&serialized_cert); + EXPECT_OK(ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE, + serialized_cert, &client_cert_ptr)); + + // The service Id should only get set if a provisioning cert exist. + std::unique_ptr drm_cert(client_cert_ptr); + EXPECT_TRUE(drm_cert->service_id().empty()); + EXPECT_EQ(device_serial_number, drm_cert->serial_number()); + EXPECT_EQ(intermediate_serial_number, drm_cert->signer_serial_number()); + EXPECT_EQ(system_id, drm_cert->system_id()); +} + +} // namespace widevine diff --git a/license_server_sdk/internal/device_status_list.cc b/license_server_sdk/internal/device_status_list.cc new file mode 100644 index 0000000..55b2325 --- /dev/null +++ b/license_server_sdk/internal/device_status_list.cc @@ -0,0 +1,241 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Implements the DeviceStatusList class. + +#include "license_server_sdk/internal/device_status_list.h" + +#include +#include + +#include "glog/logging.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "util/gtl/map_util.h" +#include "common/error_space.h" +#include "common/rsa_key.h" +#include "license_server_sdk/internal/client_cert.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/errors.pb.h" + +namespace widevine { + +DeviceStatusList* DeviceStatusList::Instance() { + // TODO(user): This is "ok" according to Google's Coding for Dummies, but + // we should inject the status list into the sessions. This will require + // exposing additional objects in the public interface. + static DeviceStatusList* device_status_list(nullptr); + if (!device_status_list) device_status_list = new DeviceStatusList; + return device_status_list; +} + +DeviceStatusList::DeviceStatusList() + : creation_time_seconds_(0), + expiration_period_seconds_(0), + allow_unknown_devices_(true), + allow_test_only_devices_(false) {} + +DeviceStatusList::~DeviceStatusList() {} + +util::Status DeviceStatusList::UpdateStatusList( + const std::string& root_certificate_public_key, + const std::string& serialized_certificate_status_list, + uint32_t expiration_period_seconds) { + SignedDeviceCertificateStatusList signed_certificate_status_list; + if (!signed_certificate_status_list.ParseFromString( + serialized_certificate_status_list)) { + return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "signed-certificate-status-list-parse-error"); + } + if (!signed_certificate_status_list.has_certificate_status_list()) { + return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "missing-status-list"); + } + if (!signed_certificate_status_list.has_signature()) { + return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "missing-status-list-signature"); + } + std::unique_ptr root_key( + RsaPublicKey::Create(root_certificate_public_key)); + if (root_key == nullptr) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-root-public-key"); + } + if (!root_key->VerifySignature( + signed_certificate_status_list.certificate_status_list(), + signed_certificate_status_list.signature())) { + return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "invalid-status-list-signature"); + } + DeviceCertificateStatusList certificate_status_list; + if (!certificate_status_list.ParseFromString( + signed_certificate_status_list.certificate_status_list())) { + return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "certificate-status-list-parse-error"); + } + if (expiration_period_seconds && + (GetCurrentTime() > (certificate_status_list.creation_time_seconds() + + expiration_period_seconds))) { + return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST, + "certificate-status-list-expired"); + } + + absl::WriterMutexLock lock(&status_map_lock_); + device_status_map_.clear(); + for (int i = 0, n = certificate_status_list.certificate_status_size(); i < n; + i++) { + const DeviceCertificateStatus& cert_status = + certificate_status_list.certificate_status(i); + if (cert_status.has_device_info()) { + const ProvisionedDeviceInfo& device_info = cert_status.device_info(); + if (device_info.has_system_id()) { + device_status_map_[device_info.system_id()] = cert_status; + } else { + return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "device-info-missing-system-id"); + } + } + } + creation_time_seconds_ = certificate_status_list.creation_time_seconds(); + expiration_period_seconds_ = expiration_period_seconds; + return util::OkStatus(); +} + +util::Status DeviceStatusList::GetCertStatus( + const ClientCert& client_cert, ProvisionedDeviceInfo* device_info) { + CHECK(device_info); + + // Keybox checks. + if (client_cert.type() == ClientIdentification::KEYBOX) { + if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) { + return util::Status(error_space, UNSUPPORTED_SYSTEM_ID, + "keybox-unsupported-system-id"); + } + // Get device information from certificate status list if available. + if (!GetDeviceInfo(client_cert, device_info)) { + device_info->Clear(); + } + return util::OkStatus(); + } + + // DRM certificate checks. + if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-unsupported-token-type"); + } + absl::ReaderMutexLock lock(&status_map_lock_); + if (expiration_period_seconds_ && + (GetCurrentTime() > + (creation_time_seconds_ + expiration_period_seconds_))) { + return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST, + "certificate-status-list-expired"); + } + DeviceCertificateStatus* device_cert_status = + gtl::FindOrNull(device_status_map_, client_cert.system_id()); + if (device_cert_status) { + *device_info = device_cert_status->device_info(); + if (device_cert_status->status() == + DeviceCertificateStatus::STATUS_REVOKED) { + if (IsRevokedSystemIdAllowed(client_cert.system_id())) { + LOG(WARNING) << "Allowing REVOKED device: " + << device_info->ShortDebugString(); + } else { + return util::Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED, + "device-certificate-revoked"); + } + } + if ((device_cert_status->status() == + DeviceCertificateStatus::STATUS_TEST_ONLY) && + !allow_test_only_devices_) { + return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, + "test-only-drm-certificate-not-allowed"); + } + if (!client_cert.signed_by_provisioner() && + (client_cert.signer_serial_number() != + device_cert_status->drm_serial_number())) { + // Widevine-provisioned device, and the intermediate certificate serial + // number does not match that in the status list. If the status list is + // newer than the certificate, indicate an invalid certificate, so that + // the device re-provisions. If, on the other hand, the certificate status + // list is older than the certificate, the certificate is for all purposes + // unknown. + if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "intermediate-certificate-serial-number-mismatch"); + } + return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, + "device-certificate-status-unknown"); + } + } else { + if (!allow_unknown_devices_) { + return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, + "device-certificate-status-unknown"); + } + device_info->Clear(); + } + + return util::OkStatus(); +} + +bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert, + ProvisionedDeviceInfo* device_info) { + CHECK(device_info); + absl::ReaderMutexLock lock(&status_map_lock_); + DeviceCertificateStatus* device_cert_status = + gtl::FindOrNull(device_status_map_, client_cert.system_id()); + if (device_cert_status) { + *device_info = device_cert_status->device_info(); + return true; + } + return false; +} + +bool DeviceStatusList::IsSystemIdActive(uint32_t system_id) { + absl::ReaderMutexLock lock(&status_map_lock_); + DeviceCertificateStatus* device_cert_status = + gtl::FindOrNull(device_status_map_, system_id); + if (!device_cert_status) { + return allow_unknown_devices_ || + KeyboxClientCert::IsSystemIdKnown(system_id); + } + if (device_cert_status->status() == + DeviceCertificateStatus::STATUS_TEST_ONLY) { + return allow_test_only_devices_; + } + if (device_cert_status) { + ProvisionedDeviceInfo device_info = device_cert_status->device_info(); + if (device_cert_status->status() == + DeviceCertificateStatus::STATUS_REVOKED) { + if (IsRevokedSystemIdAllowed(system_id)) { + LOG(WARNING) << "REVOKED system_id: " << system_id + << " is allowed to be active"; + return true; + } + } + } + return device_cert_status->status() != + DeviceCertificateStatus::STATUS_REVOKED; +} + +uint32_t DeviceStatusList::GetCurrentTime() const { return time(nullptr); } + +void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) { + for (absl::string_view sp : absl::StrSplit(system_id_list, ',')) { + allowed_revoked_devices_.push_back(std::stoi(std::string(sp))); + } + std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end()); +} + +bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) { + auto it = std::binary_search(allowed_revoked_devices_.begin(), + allowed_revoked_devices_.end(), system_id); + return it; +} + +} // namespace widevine diff --git a/license_server_sdk/internal/device_status_list.h b/license_server_sdk/internal/device_status_list.h new file mode 100644 index 0000000..d1047fb --- /dev/null +++ b/license_server_sdk/internal/device_status_list.h @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// DeviceStatusList class header. + +#ifndef LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__ +#define LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__ + +#include +#include + +#include "base/macros.h" +#include "absl/synchronization/mutex.h" +#include "util/status.h" +#include "protos/public/device_certificate_status.pb.h" +#include "protos/public/provisioned_device_info.pb.h" + +namespace widevine { + +class ClientCert; +// Manages the certificate status of devices. The list of +// DeviceCertificateStatus is provided by the DRM server. Each license +// request is checked to ensure the certificate in the request is valid and +// not revoked. Also checks to see if the intermediate certificates were +// updated where the system Id is the same, but the serial number changes. +// This case should cause the clients to re-provision. +class DeviceStatusList { + public: + // Returns a pointer to a singleton DeviceStatusList. + static DeviceStatusList* Instance(); + + DeviceStatusList(); + virtual ~DeviceStatusList(); + // Takes |signed_certificate_status_list| and copies to an internal map of + // device certifcate status list. The internal map is used to verify + // a device was not revoked. Returns true is the list was successfully parsed. + util::Status UpdateStatusList(const std::string& root_certificate_public_key, + const std::string& signed_certificate_status_list, + uint32_t expiration_period_seconds); + void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; } + bool allow_unknown_devices() const { return allow_unknown_devices_; } + void set_allow_test_only_devices(bool allow) { + allow_test_only_devices_ = allow; + } + bool allow_test_only_devices() const { return allow_test_only_devices_; } + + // Checks the device status list and returns either: + // OK + // UNSUPPORTED_SYSTEM_ID + // INVALID_DRM_CERTIFICATE + // DRM_DEVICE_CERTIFICATE_REVOKED + // DRM_DEVICE_CERTIFICATE_UNKNOWN + // If status is OK, a copy of the provisioned device info is copied + // into |device_info|. Caller owns |device_info| and it must not be null. + util::Status GetCertStatus( + const ClientCert& client_cert, + widevine::ProvisionedDeviceInfo* device_info); + // Returns true if the pre-provisioning key or certificate for the specified + // system ID are active (not disallowed or revoked). + bool IsSystemIdActive(uint32_t system_id); + + // Returns true if the system ID + // Returns true is a ProvisionedDeviceInfo exist based on . + // Caller owns and it must not be null. + bool GetDeviceInfo(const ClientCert& client_cert, + widevine::ProvisionedDeviceInfo* device_info); + // Returns the current POSIX time. + virtual uint32_t GetCurrentTime() const; + + // Enable delivery of licenses to revoked client devices. |system_id_list| is + // a comma separated list of systems Ids to allow even if revoked. + virtual void AllowRevokedDevices(const std::string& system_id_list); + + private: + // Returns true if the system ID is allowed to be revoked. + // Caller owns |system_id|. They must not be null. + bool IsRevokedSystemIdAllowed(uint32_t system_id); + + absl::Mutex status_map_lock_; + // Key is the system id for the device. + std::map device_status_map_; + uint32_t creation_time_seconds_; + uint32_t expiration_period_seconds_; + bool allow_unknown_devices_; + bool allow_test_only_devices_; + // Contains the list of system_id values that are allowed to succeed even if + // revoked. + std::vector allowed_revoked_devices_; + + DISALLOW_COPY_AND_ASSIGN(DeviceStatusList); +}; + +} // namespace widevine +#endif // LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__ diff --git a/license_server_sdk/internal/device_status_list_test.cc b/license_server_sdk/internal/device_status_list_test.cc new file mode 100644 index 0000000..5037cc1 --- /dev/null +++ b/license_server_sdk/internal/device_status_list_test.cc @@ -0,0 +1,377 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "license_server_sdk/internal/device_status_list.h" + +#include +#include +#include +#include + +#include "glog/logging.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/str_cat.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "license_server_sdk/internal/client_cert.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/provisioned_device_info.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +using ::testing::_; +using ::testing::Return; +using ::testing::ReturnRef; +using ::testing::ReturnRefOfCopy; + +const uint32_t kValidCertSystemId = 100; +const uint32_t kRevokedCertSystemId = 101; +const uint32_t kValidPpkSystemId = 102; +const uint32_t kTestOnlyCertSystemId = 103; +const uint32_t kRevokedAllowedDeviceCertSystemId = 104; +const uint32_t kUnknownSystemId = 666; +const char kValidSerialNumber[] = "valid-serial-number"; +const char kRevokedSerialNumber[] = "revoked-serial-number"; +const char kRevokedAllowDeviceSerialNumber[] = + "revoked-allow-device-serial-number"; +const char kTestOnlySerialNumber[] = "test_only-serial-number"; +const char kMismatchSerialNumber[] = "mismatch-serial-number"; +const char kDeviceModel[] = "device-model-x"; +const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff"; +const uint32_t kStatusListCreationTime = 17798001; +const uint32_t kDefaultExpirePeriod = 0; + +class MockCertificateClientCert : public CertificateClientCert { + public: + explicit MockCertificateClientCert() : CertificateClientCert("token-bytes") {} + MOCK_CONST_METHOD0(system_id, uint32_t()); + MOCK_CONST_METHOD0(signer_serial_number, std::string &()); + MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t()); + MOCK_CONST_METHOD0(type, ClientIdentification::TokenType()); + MOCK_CONST_METHOD0(signed_by_provisioner, bool()); +}; + +class MockKeyboxClientCert : public KeyboxClientCert { + public: + explicit MockKeyboxClientCert() : KeyboxClientCert("token-bytes") {} + MOCK_CONST_METHOD0(system_id, uint32_t()); + MOCK_CONST_METHOD0(type, ClientIdentification::TokenType()); +}; + +class DeviceStatusListTest : public ::testing::Test { + public: + ~DeviceStatusListTest() override {} + + void SetUp() override { + DeviceCertificateStatus *cert_status; + + // Device cert with status RELEASED. + cert_status = cert_status_list_.add_certificate_status(); + cert_status->mutable_device_info()->set_system_id(kValidCertSystemId); + cert_status->set_drm_serial_number(kValidSerialNumber); + cert_status->mutable_device_info()->set_model(kDeviceModel); + cert_status->set_status(DeviceCertificateStatus::STATUS_RELEASED); + + // Device cert with status REVOKED. + cert_status = cert_status_list_.add_certificate_status(); + cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId); + cert_status->set_drm_serial_number(kRevokedSerialNumber); + cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED); + + // Device cert with status REVOKED ALLOWED DEVICE. + cert_status = cert_status_list_.add_certificate_status(); + cert_status->mutable_device_info()->set_system_id( + kRevokedAllowedDeviceCertSystemId); + cert_status->set_drm_serial_number(kRevokedAllowDeviceSerialNumber); + cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED); + device_status_list_.AllowRevokedDevices( + absl::StrCat(kRevokedAllowedDeviceCertSystemId)); + + // Device cert with status TEST_ONLY. + cert_status = cert_status_list_.add_certificate_status(); + cert_status->mutable_device_info()->set_system_id(kTestOnlyCertSystemId); + cert_status->set_drm_serial_number(kTestOnlySerialNumber); + cert_status->set_status(DeviceCertificateStatus::STATUS_TEST_ONLY); + + cert_status_list_.set_creation_time_seconds(kStatusListCreationTime); + cert_status_list_.SerializeToString( + signed_cert_status_list_.mutable_certificate_status_list()); + std::unique_ptr root_key( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); + ASSERT_TRUE(root_key); + + ASSERT_TRUE(root_key->GenerateSignature( + signed_cert_status_list_.certificate_status_list(), + signed_cert_status_list_.mutable_signature())); + ASSERT_TRUE( + signed_cert_status_list_.SerializeToString(&serialized_status_list_)); + + ASSERT_EQ(util::OkStatus(), + device_status_list_.UpdateStatusList( + test_keys_.public_test_key_1_3072_bits(), + serialized_status_list_, kDefaultExpirePeriod)); + } + + DeviceStatusList device_status_list_; + RsaTestKeys test_keys_; + DeviceCertificateStatusList cert_status_list_; + SignedDeviceCertificateStatusList signed_cert_status_list_; + std::string serialized_status_list_; +}; + +// Returns the number of DevcieCertificateStatus messages in the list. + +TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) { + // Test case where the Certificate status is set to Valid. + ProvisionedDeviceInfo device_info; + MockCertificateClientCert valid_client_cert; + std::string valid_drm_serial_number(kValidSerialNumber); + EXPECT_CALL(valid_client_cert, type()) + .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); + EXPECT_CALL(valid_client_cert, system_id()) + .WillRepeatedly(Return(kValidCertSystemId)); + EXPECT_CALL(valid_client_cert, signer_serial_number()) + .WillRepeatedly(ReturnRef(valid_drm_serial_number)); + EXPECT_EQ(util::OkStatus(), + device_status_list_.GetCertStatus(valid_client_cert, &device_info)); + EXPECT_TRUE(device_info.has_model()); + EXPECT_EQ(kDeviceModel, device_info.model()); + + // Test case where the Certificate status is Revoked. + MockCertificateClientCert revoked_client_cert; + std::string revoked_drm_serial_number(kRevokedSerialNumber); + EXPECT_CALL(revoked_client_cert, type()) + .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); + EXPECT_CALL(revoked_client_cert, system_id()) + .WillRepeatedly(Return(kRevokedCertSystemId)); + EXPECT_CALL(revoked_client_cert, signer_serial_number()) + .WillRepeatedly(ReturnRef(revoked_drm_serial_number)); + EXPECT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED, + device_status_list_.GetCertStatus(revoked_client_cert, &device_info) + .error_code()); + + // Test case where the revoked cert is allowed. + device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId)); + EXPECT_OK( + device_status_list_.GetCertStatus(revoked_client_cert, &device_info)); +} + +TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) { + ProvisionedDeviceInfo device_info; + MockCertificateClientCert test_only_client_cert; + std::string test_only_drm_serial_number(kTestOnlySerialNumber); + EXPECT_CALL(test_only_client_cert, type()) + .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); + EXPECT_CALL(test_only_client_cert, system_id()) + .WillRepeatedly(Return(kTestOnlyCertSystemId)); + EXPECT_CALL(test_only_client_cert, signer_serial_number()) + .WillRepeatedly(ReturnRef(test_only_drm_serial_number)); + EXPECT_EQ( + DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, + device_status_list_.GetCertStatus(test_only_client_cert, &device_info) + .error_code()); +} + +TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) { + ProvisionedDeviceInfo device_info; + MockCertificateClientCert test_only_client_cert; + std::string test_only_drm_serial_number(kTestOnlySerialNumber); + device_status_list_.set_allow_test_only_devices(true); + EXPECT_CALL(test_only_client_cert, type()) + .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); + EXPECT_CALL(test_only_client_cert, system_id()) + .WillRepeatedly(Return(kTestOnlyCertSystemId)); + EXPECT_CALL(test_only_client_cert, signer_serial_number()) + .WillRepeatedly(ReturnRef(test_only_drm_serial_number)); + EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus( + test_only_client_cert, &device_info)); +} + +TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) { + std::multimap preprov_keys; + preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey)); + KeyboxClientCert::SetPreProvisioningKeys(preprov_keys); + + // Test case where the Certificate status is set to Valid. + ProvisionedDeviceInfo device_info; + MockKeyboxClientCert valid_client_keybox; + std::string valid_drm_serial_number(kValidSerialNumber); + EXPECT_CALL(valid_client_keybox, type()) + .WillRepeatedly(Return(ClientIdentification::KEYBOX)); + EXPECT_CALL(valid_client_keybox, system_id()) + .WillRepeatedly(Return(kValidCertSystemId)); + EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus( + valid_client_keybox, &device_info)); + EXPECT_TRUE(device_info.has_model()); + EXPECT_EQ(kDeviceModel, device_info.model()); + + MockKeyboxClientCert unknown_client_keybox; + EXPECT_CALL(unknown_client_keybox, type()) + .WillRepeatedly(Return(ClientIdentification::KEYBOX)); + EXPECT_CALL(unknown_client_keybox, system_id()) + .WillRepeatedly(Return(kUnknownSystemId)); + EXPECT_EQ( + UNSUPPORTED_SYSTEM_ID, + device_status_list_.GetCertStatus(unknown_client_keybox, &device_info) + .error_code()); + EXPECT_TRUE(device_info.has_model()); + EXPECT_EQ(kDeviceModel, device_info.model()); +} + +TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) { + device_status_list_.set_allow_unknown_devices(true); + + // Test case where the signer certificate is older than the current status + // list. + MockCertificateClientCert older_client_cert; + ProvisionedDeviceInfo device_info; + std::string mismatch_drm_serial_number(kMismatchSerialNumber); + EXPECT_CALL(older_client_cert, type()) + .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); + EXPECT_CALL(older_client_cert, system_id()) + .WillRepeatedly(Return(kValidCertSystemId)); + EXPECT_CALL(older_client_cert, signer_serial_number()) + .WillRepeatedly(ReturnRef(mismatch_drm_serial_number)); + EXPECT_CALL(older_client_cert, signer_creation_time_seconds()) + .WillRepeatedly(Return(kStatusListCreationTime - 1)); + EXPECT_EQ(INVALID_DRM_CERTIFICATE, + device_status_list_.GetCertStatus(older_client_cert, &device_info) + .error_code()); + + // We allow this case only for certs signed by a provisioner cert. + EXPECT_CALL(older_client_cert, signed_by_provisioner()) + .WillOnce(Return(true)); + EXPECT_EQ(util::OkStatus(), + device_status_list_.GetCertStatus(older_client_cert, &device_info)); + EXPECT_TRUE(device_info.has_system_id()); + EXPECT_EQ(kValidCertSystemId, device_info.system_id()); + + // Test case where the signer certificate is newer than the current status + // list, and unknown devices are allowed. + MockCertificateClientCert newer_client_cert1; + EXPECT_CALL(newer_client_cert1, type()) + .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); + EXPECT_CALL(newer_client_cert1, system_id()) + .WillRepeatedly(Return(kValidCertSystemId)); + EXPECT_CALL(newer_client_cert1, signer_serial_number()) + .WillRepeatedly(ReturnRef(mismatch_drm_serial_number)); + EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds()) + .WillRepeatedly(Return(kStatusListCreationTime)); + EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN, + device_status_list_.GetCertStatus(newer_client_cert1, &device_info) + .error_code()); + + // Test case where the signer certificate is newer than the current status + // list, and unknown devices are not allowed. + device_status_list_.set_allow_unknown_devices(false); + MockCertificateClientCert newer_client_cert2; + EXPECT_CALL(newer_client_cert2, type()) + .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); + EXPECT_CALL(newer_client_cert2, system_id()) + .WillRepeatedly(Return(kValidCertSystemId)); + EXPECT_CALL(newer_client_cert2, signer_serial_number()) + .WillRepeatedly(ReturnRef(mismatch_drm_serial_number)); + EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds()) + .WillRepeatedly(Return(kStatusListCreationTime + 1)); + EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN, + device_status_list_.GetCertStatus(newer_client_cert2, &device_info) + .error_code()); +} + +TEST_F(DeviceStatusListTest, InvalidStatusList) { + EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, + device_status_list_ + .UpdateStatusList(test_keys_.public_test_key_2_2048_bits(), + serialized_status_list_, 0) + .error_code()); + + ++(*signed_cert_status_list_.mutable_certificate_status_list())[4]; + ASSERT_TRUE( + signed_cert_status_list_.SerializeToString(&serialized_status_list_)); + EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, + device_status_list_ + .UpdateStatusList(test_keys_.public_test_key_1_3072_bits(), + serialized_status_list_, 0) + .error_code()); +} + +class MockDeviceStatusList : public DeviceStatusList { + public: + MOCK_CONST_METHOD0(GetCurrentTime, uint32_t()); +}; + +TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) { + MockDeviceStatusList mock_device_status_list; + EXPECT_CALL(mock_device_status_list, GetCurrentTime()) + .Times(2) + .WillOnce(Return(kStatusListCreationTime + 100)) + .WillOnce(Return(kStatusListCreationTime + 101)); + EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList( + test_keys_.public_test_key_1_3072_bits(), + serialized_status_list_, 100)); + EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST, + mock_device_status_list + .UpdateStatusList(test_keys_.public_test_key_1_3072_bits(), + serialized_status_list_, 100) + .error_code()); +} + +TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) { + MockDeviceStatusList mock_device_status_list; + EXPECT_CALL(mock_device_status_list, GetCurrentTime()) + .Times(3) + .WillOnce(Return(kStatusListCreationTime + 100)) + .WillOnce(Return(kStatusListCreationTime + 100)) + .WillOnce(Return(kStatusListCreationTime + 101)); + EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList( + test_keys_.public_test_key_1_3072_bits(), + serialized_status_list_, 100)); + + ProvisionedDeviceInfo device_info; + MockCertificateClientCert valid_client_cert; + std::string valid_drm_serial_number(kValidSerialNumber); + EXPECT_CALL(valid_client_cert, type()) + .WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE)); + EXPECT_CALL(valid_client_cert, system_id()) + .WillRepeatedly(Return(kValidCertSystemId)); + EXPECT_CALL(valid_client_cert, signer_serial_number()) + .WillRepeatedly(ReturnRef(valid_drm_serial_number)); + EXPECT_CALL(valid_client_cert, signer_creation_time_seconds()) + .WillRepeatedly(Return(kStatusListCreationTime - 1)); + EXPECT_EQ(util::OkStatus(), mock_device_status_list.GetCertStatus( + valid_client_cert, &device_info)); + + EXPECT_EQ( + EXPIRED_CERTIFICATE_STATUS_LIST, + mock_device_status_list.GetCertStatus(valid_client_cert, &device_info) + .error_code()); +} + +TEST_F(DeviceStatusListTest, IsSystemIdActive) { + std::multimap preprov_keys; + preprov_keys.insert( + std::make_pair(kValidPpkSystemId, "00112233445566778899aabbccddeeff")); + KeyboxClientCert::SetPreProvisioningKeys(preprov_keys); + device_status_list_.set_allow_unknown_devices(false); + EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId)); + EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId)); + EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId)); + EXPECT_FALSE(device_status_list_.IsSystemIdActive(kUnknownSystemId)); + device_status_list_.set_allow_unknown_devices(true); + EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId)); + EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId)); + EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId)); + EXPECT_TRUE(device_status_list_.IsSystemIdActive(kUnknownSystemId)); + EXPECT_TRUE( + device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId)); +} + +} // namespace widevine diff --git a/license_server_sdk/internal/generate_error_response.cc b/license_server_sdk/internal/generate_error_response.cc new file mode 100644 index 0000000..e5c7a14 --- /dev/null +++ b/license_server_sdk/internal/generate_error_response.cc @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "license_server_sdk/internal/generate_error_response.h" + +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "common/drm_service_certificate.h" +#include "common/error_space.h" +#include "sdk/external/common/wvpl/wvpl_types.h" +#include "protos/public/errors.pb.h" +#include "protos/public/license_protocol.pb.h" + +namespace util = widevine::util; +using widevine::DRM_DEVICE_CERTIFICATE_REVOKED; +using widevine::DrmServiceCertificate; +using widevine::EXPIRED_CERTIFICATE_STATUS_LIST; +using widevine::INVALID_DRM_CERTIFICATE; +using widevine::LicenseError; +using widevine::SERVICE_CERTIFICATE_REQUEST_MESSAGE; +using widevine::SignedMessage; + +namespace widevine { +bool GenerateErrorResponse(const util::Status& create_session_status, + std::string* license_response) { + DCHECK(license_response); + + LicenseError error_proto; + if (create_session_status.error_space() == error_space) { + switch (create_session_status.error_code()) { + case INVALID_DRM_CERTIFICATE: + error_proto.set_error_code( + LicenseError::INVALID_DRM_DEVICE_CERTIFICATE); + break; + case DRM_DEVICE_CERTIFICATE_REVOKED: + // TODO(user): Do we want to rename this error (INACTIVE) or + // generate new ones? + error_proto.set_error_code( + LicenseError::REVOKED_DRM_DEVICE_CERTIFICATE); + break; + case EXPIRED_CERTIFICATE_STATUS_LIST: + error_proto.set_error_code(LicenseError::SERVICE_UNAVAILABLE); + break; + case SERVICE_CERTIFICATE_REQUEST_MESSAGE: { + SignedMessage signed_message; + signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE); + signed_message.set_msg( + DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() + ->certificate()); + if (!signed_message.SerializeToString(license_response)) { + return false; + } + return true; + } + default: + break; + } + } + if ((create_session_status.error_space() == + util::Status::canonical_space()) && + (create_session_status.error_code() == util::error::UNAVAILABLE)) { + error_proto.set_error_code(LicenseError::SERVICE_UNAVAILABLE); + } + if (!error_proto.has_error_code()) { + return false; + } + SignedMessage signed_message; + signed_message.set_type(SignedMessage::ERROR_RESPONSE); + if (!error_proto.SerializeToString(signed_message.mutable_msg())) { + return false; + } + if (!signed_message.SerializeToString(license_response)) { + return false; + } + switch (error_proto.error_code()) { + case LicenseError::INVALID_DRM_DEVICE_CERTIFICATE: + case LicenseError::REVOKED_DRM_DEVICE_CERTIFICATE: + case LicenseError::SERVICE_UNAVAILABLE: + return false; + default: + break; + } + return true; +} +} // namespace widevine diff --git a/license_server_sdk/internal/generate_error_response.h b/license_server_sdk/internal/generate_error_response.h new file mode 100644 index 0000000..9b02b3e --- /dev/null +++ b/license_server_sdk/internal/generate_error_response.h @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 LICENSE_SERVER_SDK_INTERNAL_GENERATE_ERROR_RESPONSE_H_ +#define LICENSE_SERVER_SDK_INTERNAL_GENERATE_ERROR_RESPONSE_H_ + +#include + +#include "util/status.h" + +namespace widevine { +// Generates a SignedMessage containing a message generated in response to +// an error condition. |status| is a previous error status returned by the +// Session or util::Status(util::error::UNAVAILABLE, ...) to indicate that the +// backend is unavailable, |signed_message| points to a std::string to contain the +// serialized SignedMessage, and may not be NULL. This method returns true if +// there is an error license to be sent to the client, or false otherwise. +// Example usage in the Session::Create comments above. +bool GenerateErrorResponse(const util::Status& status, + std::string* license_response); +} // namespace widevine +#endif // LICENSE_SERVER_SDK_INTERNAL_GENERATE_ERROR_RESPONSE_H_ diff --git a/license_server_sdk/internal/key_control_block.cc b/license_server_sdk/internal/key_control_block.cc new file mode 100644 index 0000000..92da20f --- /dev/null +++ b/license_server_sdk/internal/key_control_block.cc @@ -0,0 +1,224 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Handles population of the Key Control Block (KCB) as defined here: +// https://docs.google.com/document/d/1pHSJ2IKL0axmQz2gmDZ7olxPWb_ZcULaJrYwDZAeS7k/edit#heading=h.pmnr7d8jqxea + +#include "license_server_sdk/internal/key_control_block.h" + +#include +#include +#include +#include + +#include "glog/logging.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/license_server_sdk.pb.h" + +namespace widevine { + +namespace key_control_block { + +void DevicesCanHandleOEMCryptoVersionInKCB(const std::string& system_ids) { + for (absl::string_view sp : absl::StrSplit(system_ids, ',')) { + devices_can_handle_oemcrypto_version_.push_back(std::stoi(std::string(sp))); + } + std::sort(devices_can_handle_oemcrypto_version_.begin(), + devices_can_handle_oemcrypto_version_.end()); +} + +bool Generate( + const License::KeyContainer& key_container, uint32_t duration, uint32_t nonce, + const ClientIdentification::ClientCapabilities& client_capabilities, + bool has_provider_session_token, + const SessionInit* session_init, + LicenseType license_type, + uint32_t system_id, std::string* key_control_block) { + DCHECK(key_control_block); + + bool disable_oem_crypto_api_version_reflection = false; + std::string override_oem_crypto_api_version; + if (session_init != nullptr) { + disable_oem_crypto_api_version_reflection = + session_init->disable_oem_crypto_api_version_reflection(); + override_oem_crypto_api_version = + session_init->override_oem_crypto_api_version(); + } + uint32_t control_bits = 0; + // Indicates the device must be at OEMCrypto v9 or greater. + bool min_v9_block = false; + // Some devices (such as LSI implementation) will fail if we return kc10 in + // the KCB. Until OEMCrypto version 13 is rolled-out, we will whitelist + // devices that can support kc10, kc11, kc12, ... + bool device_can_support_kcxx = false; + auto it = std::binary_search(devices_can_handle_oemcrypto_version_.begin(), + devices_can_handle_oemcrypto_version_.end(), + system_id); + // Set |device_can_support_kcxx| if either + // (a) Device System Id is whitelisted. + // (b) The device client capabilities has an OEM Crypto API version >=13 + // AND + // |disable_oem_crypto_api_version_reflection| has been enabled. + if ((it || client_capabilities.oem_crypto_api_version() >= 13) && + !disable_oem_crypto_api_version_reflection) { + device_can_support_kcxx = true; + } + switch (key_container.type()) { + case License::KeyContainer::CONTENT: + case License::KeyContainer::ENTITLEMENT: { + if (key_container.has_required_protection() && + key_container.required_protection().has_cgms_flags()) { + control_bits |= kKeyControlFlagsObserveCgms; // Observe CGMS + if (key_container.required_protection().cgms_flags() == + License::KeyContainer::OutputProtection::COPY_ONCE) { + control_bits |= kKeyControlFlagsCgmsCopyOnce; + } else if (key_container.required_protection().cgms_flags() == + License::KeyContainer::OutputProtection::COPY_NEVER) { + control_bits |= kKeyControlFlagsCgmsCopyNever; + } + } + if (key_container.has_required_protection() && + key_container.required_protection().has_hdcp() && + key_container.required_protection().hdcp() != + License::KeyContainer::OutputProtection::HDCP_NONE) { + control_bits |= kKeyControlFlagsObserveHdcp; // Observe HDCP + control_bits |= kKeyControlFlagsHdcpRequired; + switch (key_container.required_protection().hdcp()) { + case License::KeyContainer::OutputProtection::HDCP_V1: + control_bits |= kKeyControlFlagsHdcp_V1_0; + break; + case License::KeyContainer::OutputProtection::HDCP_V2: + control_bits |= kKeyControlFlagsHdcp_V2_0; + break; + case License::KeyContainer::OutputProtection::HDCP_V2_1: + control_bits |= kKeyControlFlagsHdcp_V2_1; + break; + case License::KeyContainer::OutputProtection::HDCP_V2_2: + control_bits |= kKeyControlFlagsHdcp_V2_2; + break; + case License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT: + control_bits |= kKeyControlFlagsLocalDisplayOnly; + break; + default: + return false; + } + } + if (key_container.has_required_protection() && + key_container.required_protection().has_hdcp_srm_rule() && + key_container.required_protection().hdcp_srm_rule() == + License::KeyContainer::OutputProtection::CURRENT_SRM) { + // Observe SRM_Version_Required + control_bits |= kKeyControlFlagsSrmVersionRequired; + } + if (key_container.anti_rollback_usage_table()) { + if (client_capabilities.has_oem_crypto_api_version() && + client_capabilities.oem_crypto_api_version() < 10) { + // Attempt to send a key requiring anti rollback to a client that is + // earlier than version 10 is not allowed. + return false; + } + control_bits |= kKeyControlFlagsAntiRollbackUsageTableRequired; + } + if (key_container.has_level()) { + control_bits |= kKeyControlFlagsObserveDataPath; // Observe DataPath + if (key_container.level() == License::KeyContainer::HW_SECURE_ALL) { + control_bits |= kKeyControlFlagsDataPathSecure; + } + } + if (key_container.has_required_protection() && + key_container.required_protection().disable_analog_output()) { + control_bits |= kKeyControlFlagsDisableAnalogOutput; + } + break; + } + case License::KeyContainer::OPERATOR_SESSION: { + if (key_container.has_operator_session_key_permissions()) { + if (key_container.operator_session_key_permissions().allow_encrypt()) { + control_bits |= kKeyControlFlagsAllowEncrypt; + } + if (key_container.operator_session_key_permissions().allow_decrypt()) { + control_bits |= kKeyControlFlagsAllowDecrypt; + } + if (key_container.operator_session_key_permissions().allow_sign()) { + control_bits |= kKeyControlFlagsAllowSign; + } + if (key_container.operator_session_key_permissions() + .allow_signature_verify()) { + control_bits |= kKeyControlFlagsAllowVerify; + } + } + break; + } + case License::KeyContainer::KEY_CONTROL: + // No control bits for this type of key container used for license + // renewals. + break; + default: + // Should not generate key control blocks for any key types other than + // the above. + return false; + } + + if (license_type == STREAMING) { + control_bits |= kKeyControlFlagsNonceEnable; + } + if (client_capabilities.session_token()) { + if (has_provider_session_token) { + if (license_type == STREAMING) { + control_bits |= kKeyControlFlagsReplayControl1; + } else { + control_bits |= kKeyControlFlagsReplayControl2; + } + } + min_v9_block = true; + } + if (client_capabilities.has_oem_crypto_api_version() && + client_capabilities.oem_crypto_api_version() >= 9) { + min_v9_block = true; + } + + const size_t kcb_buffer_len = 4; + uint32_t kcb_buffer[kcb_buffer_len]; + char* kcb_char_ptr = reinterpret_cast(kcb_buffer); + kcb_char_ptr[0] = 'k'; + kcb_char_ptr[1] = 'c'; + kcb_char_ptr[2] = 't'; + kcb_char_ptr[3] = 'l'; + if (!override_oem_crypto_api_version.empty() && device_can_support_kcxx) { + memcpy(kcb_char_ptr, override_oem_crypto_api_version.data(), + (override_oem_crypto_api_version.size() > kcb_buffer_len + ? kcb_buffer_len : override_oem_crypto_api_version.size())); + } else if (min_v9_block) { + // Limited by only 2 characters in KCB, hence cannot support 3 digits + // like 100. + const int maxApiVersionSupported = 99; + if (client_capabilities.oem_crypto_api_version() <= 9 || + !device_can_support_kcxx) { + kcb_char_ptr[2] = '0'; + kcb_char_ptr[3] = '9'; + } else if (client_capabilities.oem_crypto_api_version() <= + maxApiVersionSupported) { + kcb_char_ptr[2] = '0' + client_capabilities.oem_crypto_api_version() / 10; + kcb_char_ptr[3] = '0' + client_capabilities.oem_crypto_api_version() % 10; + } else { + // TODO(user): Update KCB to handle 3 digit version. + return false; + } + } + kcb_buffer[1] = htonl(duration); + kcb_buffer[2] = htonl(nonce); + kcb_buffer[3] = htonl(control_bits); + key_control_block->assign(reinterpret_cast(&kcb_buffer[0]), + sizeof(kcb_buffer)); + return true; +} + +} // namespace key_control_block +} // namespace widevine diff --git a/license_server_sdk/internal/key_control_block.h b/license_server_sdk/internal/key_control_block.h new file mode 100644 index 0000000..2eaf98e --- /dev/null +++ b/license_server_sdk/internal/key_control_block.h @@ -0,0 +1,71 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef LICENSE_SERVER_SDK_INTERNAL_KEY_CONTROL_BLOCK_H__ +#define LICENSE_SERVER_SDK_INTERNAL_KEY_CONTROL_BLOCK_H__ + +#include +#include + +#include "protos/public/client_identification.pb.h" +#include "protos/public/license_protocol.pb.h" +#include "protos/public/license_server_sdk.pb.h" + +namespace widevine { +namespace key_control_block { + +// This class implements the Key Control Block (KCB) embedded in the license. +// Documemtation on the KCB is located here: +// 1pHSJ2IKL0axmQz2gmDZ7olxPWb_ZcULaJrYwDZAeS7k/edit# +const uint32_t kKeyControlFlagsCgmsCopyOnce = 0x00000002; +const uint32_t kKeyControlFlagsCgmsCopyNever = 0x00000003; +const uint32_t kKeyControlFlagsHdcpRequired = 0x00000004; +const uint32_t kKeyControlFlagsNonceEnable = 0x00000008; +const uint32_t kKeyControlFlagsDataPathSecure = 0x00000010; +const uint32_t kKeyControlFlagsAllowVerify = 0x00000020; +const uint32_t kKeyControlFlagsAllowSign = 0x00000040; +const uint32_t kKeyControlFlagsAllowDecrypt = 0x00000080; +const uint32_t kKeyControlFlagsAllowEncrypt = 0x00000100; +const uint32_t kKeyControlFlagsAntiRollbackUsageTableRequired = 0x10000000; +const uint32_t kKeyControlFlagsObserveCgms = 0x20000000; +const uint32_t kKeyControlFlagsObserveHdcp = 0x40000000; +const uint32_t kKeyControlFlagsObserveDataPath = 0x80000000; +const uint32_t kKeyControlFlagsReplayControlMask = 0x00006000; +const uint32_t kKeyControlFlagsReplayControl1 = 0x00002000; +const uint32_t kKeyControlFlagsReplayControl2 = 0x00004000; +const uint32_t kKeyControlFlagsHdcpMask = 0x00001e00; +const uint32_t kKeyControlFlagsHdcp_V1_0 = 0x00000200; +const uint32_t kKeyControlFlagsHdcp_V2_0 = 0x00000400; +const uint32_t kKeyControlFlagsHdcp_V2_1 = 0x00000600; +const uint32_t kKeyControlFlagsHdcp_V2_2 = 0x00000800; +const uint32_t kKeyControlFlagsLocalDisplayOnly = 0x00001E00; +const uint32_t kKeyControlFlagsDisableAnalogOutput = 0x00200000; +const uint32_t kKeyControlFlagsSrmVersionRequired = 0x00400000; + +// Generate the key control block structuure. |key_control_block| must not be +// null and is used to hold the key control data. Returns true if successful, +// false otherwise. +bool Generate( + const License::KeyContainer& key_container, uint32_t duration, uint32_t nonce, + const ClientIdentification::ClientCapabilities& client_capabilities, + bool has_provider_session_token, + const SessionInit* session_init, LicenseType license_type, + uint32_t system_id, std::string* key_control_block); + +// Specify a comma separated list of system Ids that can support having +// OEMCrypto version, as specified in the license request, refected back in +// the Key Control Block. Otherwise, only 'kctl' or 'kc09' is returned in KCB. +void DevicesCanHandleOEMCryptoVersionInKCB(const std::string& system_ids); + +// Contains the list of system_id values that can support reflecting OEMCrypto +// version in KCB. +static std::vector devices_can_handle_oemcrypto_version_; + +} // namespace key_control_block +} // namespace widevine +#endif // LICENSE_SERVER_SDK_INTERNAL_KEY_CONTROL_BLOCK_H__ diff --git a/license_server_sdk/internal/key_control_block_test.cc b/license_server_sdk/internal/key_control_block_test.cc new file mode 100644 index 0000000..cf11ba4 --- /dev/null +++ b/license_server_sdk/internal/key_control_block_test.cc @@ -0,0 +1,802 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "license_server_sdk/internal/key_control_block.h" + +#include "base/googleinit.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "protos/public/license_protocol.pb.h" +#include "protos/public/license_server_sdk.pb.h" + +namespace widevine { +namespace key_control_block { + +const char* kKCBAntiRollbackFalse = "6b633039000000000000000000002008"; +const char* kKCBAntiRollbackTrue = "6b633039000000000000000010002008"; + +TEST(KeyControlBlockTest, test_cmgs_bits) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + uint32_t system_id = 0; + ClientIdentification::ClientCapabilities client_capabilities; + SessionInit session_init; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + key_container.mutable_required_protection()->set_cgms_flags( + License::KeyContainer::OutputProtection::COPY_FREE); + + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000020002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_cgms_flags( + License::KeyContainer::OutputProtection::COPY_ONCE); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63303900000000000000002000200a", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_cgms_flags( + License::KeyContainer::OutputProtection::COPY_NEVER); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63303900000000000000002000200b", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_hdcp_bits_v0) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + uint32_t system_id = 0; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(false); + SessionInit session_init; + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + key_container.mutable_required_protection()->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_NONE); + + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63746c000000000000000000000008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V1); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63746c00000000000000004000020c", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_hdcp_bits_v9) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + + key_container.mutable_required_protection()->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_NONE); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V1); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63303900000000000000004000220c", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000040003e0c", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_hdcp_versioning) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + client_capabilities.set_oem_crypto_api_version(0); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + + key_container.mutable_required_protection()->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V2); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63303900000000000000004000240c", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V2_1); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63303900000000000000004000260c", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V2_2); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63303900000000000000004000280c", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_srm_version_required_bits) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + + key_container.mutable_requested_protection()->set_hdcp_srm_rule( + License::KeyContainer::OutputProtection::CURRENT_SRM); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_hdcp_srm_rule( + License::KeyContainer::OutputProtection::HDCP_SRM_RULE_NONE); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_hdcp_srm_rule( + License::KeyContainer::OutputProtection::CURRENT_SRM); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000402008", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_analog_output) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + + // Requested is ignored. + key_container.mutable_requested_protection()->set_disable_analog_output( + false); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_requested_protection()->set_disable_analog_output(true); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_disable_analog_output(false); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_required_protection()->set_disable_analog_output(true); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000202008", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_data_path_bits) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + + key_container.set_level(License::KeyContainer::SW_SECURE_CRYPTO); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000080002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.set_level(License::KeyContainer::SW_SECURE_DECODE); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000080002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.set_level(License::KeyContainer::HW_SECURE_CRYPTO); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000080002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.set_level(License::KeyContainer::HW_SECURE_DECODE); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000080002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.set_level(License::KeyContainer::HW_SECURE_ALL); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000080002018", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +// Test updating the duration for all keys associated with this license. +// This is the case for license renewals. +TEST(KeyControlBlockTest, test_renewal) { + uint32_t duration = 0; + uint32_t nonce = 0x12345678; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::KEY_CONTROL); + + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000001234567800002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + duration = 0x6789; + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000067891234567800002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +// Test for operator session key control blocks. +TEST(KeyControlBlockTest, test_operator_session_key) { + uint32_t duration = 0; + uint32_t nonce = 0x12345678; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::OPERATOR_SESSION); + + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000001234567800002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_operator_session_key_permissions()->set_allow_encrypt( + true); + duration = 0x87654321; + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039876543211234567800002108", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_operator_session_key_permissions()->set_allow_decrypt( + true); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039876543211234567800002188", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_operator_session_key_permissions()->set_allow_sign( + true); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b6330398765432112345678000021c8", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + key_container.mutable_operator_session_key_permissions() + ->set_allow_signature_verify(true); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b6330398765432112345678000021e8", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_signing_key) { + uint32_t duration = 0; + uint32_t nonce = 0x12345678; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::SIGNING); + + EXPECT_FALSE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); +} + +TEST(KeyControlBlockTest, test_has_provider_session_token) { + uint32_t duration = 0; + uint32_t nonce = 0x12345678; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::KEY_CONTROL); + + EXPECT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000001234567800002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + EXPECT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, OFFLINE, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000001234567800004000", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + has_provider_session_token = false; + EXPECT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000001234567800000008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + EXPECT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, OFFLINE, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000001234567800000000", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_client_supports_session_token) { + uint32_t duration = 0; + uint32_t nonce = 0x12345678; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::KEY_CONTROL); + + EXPECT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, OFFLINE, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000001234567800004000", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + client_capabilities.set_session_token(false); + EXPECT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, OFFLINE, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63746c000000001234567800000000", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + EXPECT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63746c000000001234567800000008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + client_capabilities.set_session_token(false); + EXPECT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b63746c000000001234567800000008", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_replay_control) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + client_capabilities.set_oem_crypto_api_version(9); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, OFFLINE, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + "6b633039000000000000000000004000", + absl::BytesToHexString(key_container.key_control().key_control_block())); +} + +TEST(KeyControlBlockTest, test_anti_rollback_bit) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + uint32_t system_id = 0; + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + client_capabilities.set_oem_crypto_api_version(9); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + + // anti_rollback bit in KCB is false because client does not support anti + // rollback. + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + kKCBAntiRollbackFalse, + absl::BytesToHexString(key_container.key_control().key_control_block())); + + // anti_rollback bit in KCB is false because oem api version < 10. + client_capabilities.set_anti_rollback_usage_table(true); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + kKCBAntiRollbackFalse, + absl::BytesToHexString(key_container.key_control().key_control_block())); + + // anti_rollback bit in KCB is false because provider did not specify it as + // required in the key_container. + client_capabilities.set_oem_crypto_api_version(10); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + kKCBAntiRollbackFalse, + absl::BytesToHexString(key_container.key_control().key_control_block())); + + // anti_rollback bit in KCB is true. + key_container.set_anti_rollback_usage_table(true); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + kKCBAntiRollbackTrue, + absl::BytesToHexString(key_container.key_control().key_control_block())); + + // KCB call should fail due to invalid usage. The provider is attempting to + // require anti rollback on clients earlier than version 10. + client_capabilities.set_oem_crypto_api_version(9); + ASSERT_FALSE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, system_id, + key_container.mutable_key_control()->mutable_key_control_block())); +} + +TEST(KeyControlBlockTest, test_OEMCrypto_Version) { + uint32_t duration = 0; + uint32_t nonce = 0; + bool has_provider_session_token = true; + // This device (system_id) can only support kctl, kc09 in the KCB. + const uint32_t default_system_id = 1000; + // This device (system_id) can support kcXX in the KCB, where XX is the + // OEMVersion. + const uint32_t oem_version_in_kcb_system_id = 1001; + const std::string system_id_can_handle_oem_version("2000,1001,3000"); + SessionInit session_init; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_session_token(true); + License::KeyContainer key_container; + key_container.set_type(License::KeyContainer::CONTENT); + + key_control_block::DevicesCanHandleOEMCryptoVersionInKCB( + system_id_can_handle_oem_version); + + client_capabilities.set_oem_crypto_api_version(8); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, default_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + // Should be 'kc09' because the client_capabilities has session token. + // k c 0 9 + // "6b 63 30 39 000000000000000000002008", + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + client_capabilities.set_oem_crypto_api_version(9); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, default_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + // Should be 'kc09' because the client_capabilities has session token. + // k c 0 9 + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + client_capabilities.set_oem_crypto_api_version(10); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, default_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + // Should be 'kc09' because the client_capabilities has session token. + // k c 0 9 + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + client_capabilities.set_oem_crypto_api_version(10); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, + oem_version_in_kcb_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + // Should return 'kc10' because this client can handle the version in KCB. + // k c 1 0 + EXPECT_EQ( + "6b633130000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + client_capabilities.set_oem_crypto_api_version(11); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, default_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + // k c 0 9 + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + client_capabilities.set_oem_crypto_api_version(11); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, + oem_version_in_kcb_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + // k c 1 1 + EXPECT_EQ( + "6b633131000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + // Cannot support version > 99, Generate() should fail. + client_capabilities.set_oem_crypto_api_version(999); + ASSERT_FALSE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, + oem_version_in_kcb_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + + // Return the latest OEM version starting from V13. Make sure V12 still + // returns kc09. + client_capabilities.set_oem_crypto_api_version(12); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, default_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + // k c 0 9 + EXPECT_EQ( + "6b633039000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + // Return the latest OEM version starting from V13. Make sure V13 returns + // kc13. + client_capabilities.set_oem_crypto_api_version(13); + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, default_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + // k c 1 3 + EXPECT_EQ( + "6b633133000000000000000000002008", + absl::BytesToHexString(key_container.key_control().key_control_block())); + + for (int i = 0 ; i < 2; i++) { + // For v13 OEM version or later, if + // 'disable_oem_crypto_api_version_reflection' is set to true, return kc09 + // (b/38270837). + session_init.set_disable_oem_crypto_api_version_reflection(true); + client_capabilities.set_oem_crypto_api_version(13); + if (i == 1) { + // overriding the api version should not change expectation since + // disable_oem_crypto_api_version_reflection is true. + session_init.set_override_oem_crypto_api_version("kc13"); + } + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, default_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + // k c 0 9 + EXPECT_EQ("6b633039000000000000000000002008", + absl::BytesToHexString( + key_container.key_control().key_control_block())); + } + + client_capabilities.set_oem_crypto_api_version(13); + session_init.clear_override_oem_crypto_api_version(); + std::string expected_kcb; + for (int i = 0; i < 6; i++) { + if (i == 0) { + session_init.set_disable_oem_crypto_api_version_reflection(true); + // Refection is diabled, expect kc09; + expected_kcb = "6b633039000000000000000000002008"; // kc09; + } else if (i == 1) { + session_init.set_disable_oem_crypto_api_version_reflection(false); + session_init.set_override_oem_crypto_api_version("kc12"); + expected_kcb = "6b633132000000000000000000002008"; // kc12 + } else if (i == 2) { + session_init.set_override_oem_crypto_api_version("kc13"); + expected_kcb = "6b633133000000000000000000002008"; // kc13 + } else if (i == 3) { + session_init.set_override_oem_crypto_api_version("kc14"); + expected_kcb = "6b633134000000000000000000002008"; // kc14 + } else if (i == 4) { + // override value is larger that supported (length of 4) + session_init.set_override_oem_crypto_api_version("123456789"); + expected_kcb = "31323334000000000000000000002008"; // 1234 + } else if (i == 5) { + // override value is smaller than expected (length of 4) + session_init.set_override_oem_crypto_api_version("12"); + expected_kcb = "3132746c000000000000000000002008"; // 12tl + } + // Return the OEM version specified in session_init. + ASSERT_TRUE(key_control_block::Generate( + key_container, duration, nonce, client_capabilities, + has_provider_session_token, &session_init, STREAMING, default_system_id, + key_container.mutable_key_control()->mutable_key_control_block())); + EXPECT_EQ( + expected_kcb, + absl::BytesToHexString(key_container.key_control().key_control_block())) + << ">> session_init test case: " << i; + } +} + +} // namespace key_control_block +} // namespace widevine diff --git a/license_server_sdk/internal/parse_content_id.cc b/license_server_sdk/internal/parse_content_id.cc new file mode 100644 index 0000000..9a9f5f7 --- /dev/null +++ b/license_server_sdk/internal/parse_content_id.cc @@ -0,0 +1,242 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "license_server_sdk/internal/parse_content_id.h" + +#include +#include +#include + +#include +#include "glog/logging.h" +#include "absl/strings/str_cat.h" +#include "util/endian/endian.h" +#include "common/error_space.h" +#include "protos/public/errors.pb.h" +#include "protos/public/license_server_sdk.pb.h" +#include "protos/public/widevine_pssh.pb.h" + +namespace widevine { + +namespace { + +const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, + 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, + 0xd5, 0x1d, 0x21, 0xed}; + +void AddKeyIdIfNotFound(const std::string& key_id, + ContentInfo::ContentInfoEntry* entry) { + for (int idx = 0; idx < entry->key_ids_size(); ++idx) + if (entry->key_ids(idx) == key_id) return; + + entry->add_key_ids(key_id); +} + +util::Status AddWidevinePsshInfo( + const std::string& pssh_data, + ContentInfo::ContentInfoEntry* content_info_entry) { + if (pssh_data.empty()) { + return util::Status(error_space, INVALID_WIDEVINE_PSSH_DATA, + "widevine-pssh-data-is-empty"); + } + + if (!content_info_entry->mutable_pssh() + ->mutable_widevine_data() + ->ParseFromString(pssh_data)) { + return util::Status(error_space, INVALID_WIDEVINE_PSSH_DATA, + "invalid-widevine-pssh-data"); + } + content_info_entry->mutable_pssh()->set_system_id( + std::string(kWidevineSystemId, kWidevineSystemId + sizeof(kWidevineSystemId))); + const widevine::WidevinePsshData& wv_pssh = + content_info_entry->pssh().widevine_data(); + for (int idx = 0; idx < wv_pssh.key_ids_size(); ++idx) + AddKeyIdIfNotFound(wv_pssh.key_ids(idx), content_info_entry); + + return util::OkStatus(); +} + +util::Status ParseCencId( + const LicenseRequest::ContentIdentification& content_id, + ContentInfo* content_info) { + content_info->set_init_data_type( + LicenseRequest::ContentIdentification::InitData::CENC); + for (int idx = 0; idx < content_id.cenc_id_deprecated().pssh_size(); ++idx) { + util::Status status = + AddWidevinePsshInfo(content_id.cenc_id_deprecated().pssh(idx), + content_info->add_content_info_entry()); + if (!status.ok()) return status; + } + return util::OkStatus(); +} + +util::Status AddWebmKeyId(const std::string& key_id, ContentInfo* content_info) { + content_info->set_init_data_type( + LicenseRequest::ContentIdentification::InitData::WEBM); + content_info->add_content_info_entry()->add_key_ids(key_id); + return util::OkStatus(); +} + +util::Status ParseIsoBmffBoxes(const std::string& boxes, ContentInfo* content_info) { + const uint32_t kPsshType = 0x70737368; + const size_t kPsshSystemIdSize = 16; + const size_t kKeyIdSize = 16; + const size_t kMinPsshSize = kPsshSystemIdSize + 8; + + if (boxes.empty()) { + return util::Status(error_space, INVALID_CENC_INIT_DATA, + "init-data-is-empty"); + } + + const char* r_ptr = boxes.data(); + const char* end_ptr = r_ptr + boxes.size(); + + while (r_ptr < end_ptr) { + ContentInfo::ContentInfoEntry content_info_entry; + + if (r_ptr + 8 > end_ptr) + return util::Status(error_space, INVALID_CENC_INIT_DATA, + "init-data-too-short"); + + const char* box_start = r_ptr; + uint64_t box_size = BigEndian::Load32(r_ptr); + r_ptr += 4; + uint32_t box_type = BigEndian::Load32(r_ptr); + r_ptr += 4; + + if (box_size == 1) { + if (r_ptr + 8 > end_ptr) { + return util::Status(error_space, INVALID_CENC_INIT_DATA, + "init-data-too-short"); + } + box_size = BigEndian::Load64(r_ptr); + r_ptr += 8; + } + + const char* box_end = box_start + box_size; + if (box_end > end_ptr) { + return util::Status(error_space, INVALID_CENC_INIT_DATA, + "init-data-too-short"); + } + if (box_end < r_ptr) { + return util::Status(error_space, INVALID_CENC_INIT_DATA, + "invalid-box-size"); + } + + if (box_type != kPsshType) { + r_ptr = box_end; + continue; + } + + if (r_ptr + kMinPsshSize > box_end) + return util::Status(error_space, INVALID_PSSH, "pssh-contents-too-short"); + + const uint32_t version_and_flags = BigEndian::Load32(r_ptr); + r_ptr += 4; + + const uint8_t version = static_cast(version_and_flags >> 24); + if (version > 1) { + return util::Status(error_space, UNSUPPORTED_PSSH_VERSION, + absl::StrCat("unsupported-pssh-version ", version)); + } + + content_info_entry.mutable_pssh()->set_system_id( + std::string(r_ptr, r_ptr + kPsshSystemIdSize)); + bool is_widevine_pssh = + !memcmp(r_ptr, kWidevineSystemId, kPsshSystemIdSize); + r_ptr += kPsshSystemIdSize; + + if (version == 1) { + if (r_ptr + 4 > box_end) { + return util::Status(error_space, INVALID_PSSH, + "pssh-contents-too-short"); + } + + const uint32_t num_key_ids = BigEndian::Load32(r_ptr); + r_ptr += 4; + + if (r_ptr + (num_key_ids * kKeyIdSize) > box_end) { + return util::Status(error_space, INVALID_PSSH, + "pssh-contents-too-short"); + } + + for (uint32_t idx = 0; idx < num_key_ids; ++idx) { + AddKeyIdIfNotFound(std::string(r_ptr, r_ptr + kKeyIdSize), + &content_info_entry); + r_ptr += kKeyIdSize; + } + } + + if (r_ptr + 4 > box_end) + return util::Status(error_space, INVALID_PSSH, "pssh-contents-too-short"); + + uint32_t data_size = BigEndian::Load32(r_ptr); + r_ptr += 4; + if (r_ptr + data_size > box_end) + return util::Status(error_space, INVALID_PSSH, "pssh-contents-too-short"); + if (r_ptr + data_size < box_end) + return util::Status(error_space, INVALID_PSSH, "pssh-contents-too-long"); + + if (is_widevine_pssh) { + util::Status status = AddWidevinePsshInfo( + std::string(r_ptr, r_ptr + data_size), &content_info_entry); + if (!status.ok()) return status; + } else { + content_info_entry.mutable_pssh()->set_raw_data( + std::string(r_ptr, r_ptr + data_size)); + } + r_ptr += data_size; + + *content_info->add_content_info_entry() = content_info_entry; + } + + return util::OkStatus(); +} + +util::Status ParseInitData( + const LicenseRequest::ContentIdentification& content_id, + ContentInfo* content_info) { + if (!content_id.init_data().has_init_data()) + return util::Status(error_space, MISSING_INIT_DATA, "missing-init-data"); + + if (content_id.init_data().init_data_type() == + LicenseRequest::ContentIdentification::InitData::CENC) { + return ParseIsoBmffBoxes(content_id.init_data().init_data(), content_info); + } + if (content_id.init_data().init_data_type() == + LicenseRequest::ContentIdentification::InitData::WEBM) { + return AddWebmKeyId(content_id.init_data().init_data(), content_info); + } + return util::Status(error_space, UNKNOWN_INIT_DATA_TYPE, + "unknown-init-data-type"); +} + +} // namespace + +util::Status ParseContentId( + const LicenseRequest::ContentIdentification& content_id, + ContentInfo* content_info) { + DCHECK(content_info); + + content_info->Clear(); + switch (content_id.content_id_variant_case()) { + case LicenseRequest_ContentIdentification::kCencIdDeprecated: + return ParseCencId(content_id, content_info); + case LicenseRequest_ContentIdentification::kWebmIdDeprecated: + return AddWebmKeyId(content_id.webm_id_deprecated().header(), + content_info); + case LicenseRequest_ContentIdentification::kInitData: + return ParseInitData(content_id, content_info); + default: + break; + } + return util::Status(error_space, INVALID_CONTENT_ID_TYPE, + "invalid-content-id-type"); +} + +} // namespace widevine diff --git a/license_server_sdk/internal/parse_content_id.h b/license_server_sdk/internal/parse_content_id.h new file mode 100644 index 0000000..6109a25 --- /dev/null +++ b/license_server_sdk/internal/parse_content_id.h @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef LICENSE_SERVER_SDK_INTERNAL_PARSE_CONTENT_ID_H__ +#define LICENSE_SERVER_SDK_INTERNAL_PARSE_CONTENT_ID_H__ + +#include "util/status.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +class ContentInfo; + +// Parse the ContentIdentification message passed into |content_id| and populate +// the ContentInfo message passed into |content_info|. This function deep parses +// PSSH boxes and the Widevine PSSH Data. |content_info| may not be NULL and the +// caller retains ownership. +util::Status ParseContentId( + const LicenseRequest::ContentIdentification& content_id, + ContentInfo* content_info); + +} // namespace widevine + +#endif // LICENSE_SERVER_SDK_INTERNAL_PARSE_CONTENT_ID_H__ diff --git a/license_server_sdk/internal/parse_content_id_test.cc b/license_server_sdk/internal/parse_content_id_test.cc new file mode 100644 index 0000000..f169d9a --- /dev/null +++ b/license_server_sdk/internal/parse_content_id_test.cc @@ -0,0 +1,302 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "license_server_sdk/internal/parse_content_id.h" + +#include +#include + +#include +#include "testing/gunit.h" +#include "util/endian/endian.h" +#include "common/error_space.h" +#include "protos/public/errors.pb.h" +#include "protos/public/license_server_sdk.pb.h" + +namespace widevine { + +namespace { + +const char kExpectedKey1[] = "0123456789abcdef"; + +const uint8_t kWvSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, + 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed}; + +const uint8_t kWvPsshV0[] = { + 0x00, 0x00, 0x00, 0x34, // Size of this box = 52. + 0x70, 0x73, 0x73, 0x68, // Type 'pssh'. + 0x00, 0x00, 0x00, 0x00, // Version and flags should be 0. + 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, + 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, // Widevine System ID. + 0x00, 0x00, 0x00, 0x14, // Size of data = 20. + 0x08, 0x01, 0x12, 0x10, 0x72, 0x65, 0x5d, 0x53, 0xc9, 0x09, + 0x57, 0xbf, 0xb0, 0x7b, 0xef, 0xd3, 0xdd, 0x54, 0xa2, 0xbd // Data. +}; +const size_t kWvPsshV0DataOffset = 32; +const uint8_t kWvPsshV0KeyId[] = {0x72, 0x65, 0x5d, 0x53, 0xc9, 0x09, 0x57, 0xbf, + 0xb0, 0x7b, 0xef, 0xd3, 0xdd, 0x54, 0xa2, 0xbd}; + +const uint8_t kInvalidWvPsshV0[] = { + 0x00, 0x00, 0x00, 0x24, // Size of this box = 36. + 0x70, 0x73, 0x73, 0x68, // Type 'pssh'. + 0x00, 0x00, 0x00, 0x00, // Version and flags should be 0. + 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, + 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, // Widevine System ID. + 0x00, 0x00, 0x00, 0x04, // Size of data = 4. + 0x08, 0x01, 0x12, 0x10}; + +const uint8_t kWvPsshV1[] = { + 0x00, 0x00, 0x00, 0x58, // Size of this box = 88. + 0x70, 0x73, 0x73, 0x68, // Type 0x70737368 == 'pssh'. + 0x01, 0x00, 0x00, 0x00, // PSSH Version 1. + 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, // + 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, // Widevine System ID. + 0x00, 0x00, 0x00, 0x02, // 2 Key IDs. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // KeyID 1. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // KeyID 2. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // + 0x00, 0x00, 0x00, 0x14, // Size of data = 20. + 0x08, 0x01, 0x12, 0x10, 0x72, 0x65, 0x5d, 0x53, 0xc9, 0x09, 0x57, // Data. + 0xbf, 0xb0, 0x7b, 0xef, 0xd3, 0xdd, 0x54, 0xa2, 0xbd}; +const size_t kWvPsshV1BoxSizeOffset = 0; +const size_t kWvPsshV1BoxVersionOffset = 8; +const size_t kWvPsshV1NumKeyIdsOffset = 28; +const size_t kWvPsshV1DataSizeOffset = 64; +const size_t kWvPsshV1DataOffset = 68; + +const uint8_t kNonWvPsshV1[] = { + 0x00, 0x00, 0x00, 0x48, // Size of this box = 72. + 0x70, 0x73, 0x73, 0x68, // Type 0x70737368 == 'pssh'. + 0x01, 0x00, 0x00, 0x00, // PSSH Version 1. + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // System ID. + 0x00, 0x00, 0x00, 0x02, // 2 Key IDs. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // KeyID 1. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // KeyID 2. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x04, // Size of data = 4. + 0x00, 0x01, 0x1a, 0x10 // Data. +}; + +const uint8_t kNonPsshBox[] = { + 0x00, 0x00, 0x00, 0x08, // Size of this box = 8. + 0x70, 0x73, 0x73, 0x74, // Type 0x70737368 == 'psst'. +}; + +void MakeDeprecatedWebmContentId( + const std::string& key_id, LicenseRequest::ContentIdentification* content_id) { + content_id->Clear(); + content_id->mutable_webm_id_deprecated()->set_header(key_id); +} + +void MakeInitDataWebmContentId( + const std::string& key_id, LicenseRequest::ContentIdentification* content_id) { + content_id->Clear(); + content_id->mutable_init_data()->set_init_data_type( + LicenseRequest::ContentIdentification::InitData::WEBM); + content_id->mutable_init_data()->set_init_data(key_id); +} + +void VerifyWebmContentId( + const LicenseRequest::ContentIdentification& content_id) { + ContentInfo content_info; + ASSERT_EQ(util::OkStatus(), ParseContentId(content_id, &content_info)); + ASSERT_EQ(LicenseRequest::ContentIdentification::InitData::WEBM, + content_info.init_data_type()); + ASSERT_EQ(1, content_info.content_info_entry_size()); + ASSERT_EQ(1, content_info.content_info_entry(0).key_ids_size()); + ASSERT_EQ(kExpectedKey1, content_info.content_info_entry(0).key_ids(0)); + ASSERT_FALSE(content_info.content_info_entry(0).has_pssh()); +} + +void MakeDeprecatedCencContentId( + const std::string& pssh_data, + LicenseRequest::ContentIdentification* content_id) { + content_id->Clear(); + *content_id->mutable_cenc_id_deprecated()->add_pssh() = pssh_data; +} + +void MakeInitDataCencContentId( + const std::string& pssh, LicenseRequest::ContentIdentification* content_id) { + content_id->Clear(); + content_id->mutable_init_data()->set_init_data_type( + LicenseRequest::ContentIdentification::InitData::CENC); + content_id->mutable_init_data()->set_init_data(pssh); +} + +void MakeExistingLicenseContentId( + LicenseRequest::ContentIdentification* content_id) { + content_id->Clear(); + content_id->mutable_existing_license(); +} + +void VerifyCencContentId( + const LicenseRequest::ContentIdentification& content_id) { + ContentInfo content_info; + ASSERT_EQ(util::OkStatus(), ParseContentId(content_id, &content_info)); + ASSERT_EQ(LicenseRequest::ContentIdentification::InitData::CENC, + content_info.init_data_type()); + ASSERT_EQ(1, content_info.content_info_entry_size()); + ASSERT_EQ(1, content_info.content_info_entry(0).key_ids_size()); + ASSERT_TRUE(content_info.content_info_entry(0).has_pssh()); + ASSERT_EQ(std::string(kWvSystemId, kWvSystemId + sizeof(kWvSystemId)), + content_info.content_info_entry(0).pssh().system_id()); + ASSERT_TRUE(content_info.content_info_entry(0).pssh().has_widevine_data()); + ASSERT_FALSE(content_info.content_info_entry(0).pssh().has_raw_data()); +} + +void MakeModifiedV1WvCencContentId( + size_t modification_offset, uint32_t modification_value, + LicenseRequest::ContentIdentification* content_id) { + std::string box_data(kWvPsshV1, kWvPsshV1 + sizeof(kWvPsshV1)); + BigEndian::Store32(&box_data[modification_offset], modification_value); + MakeInitDataCencContentId(box_data, content_id); +} + +} // namespace + +TEST(ParseContentIdTest, DeprecatedWebmContentId) { + LicenseRequest::ContentIdentification content_id; + MakeDeprecatedWebmContentId(kExpectedKey1, &content_id); + ASSERT_NO_FATAL_FAILURE(VerifyWebmContentId(content_id)); +} + +TEST(ParseContentIdTest, InitDataWebmContentId) { + LicenseRequest::ContentIdentification content_id; + MakeInitDataWebmContentId(kExpectedKey1, &content_id); + ASSERT_NO_FATAL_FAILURE(VerifyWebmContentId(content_id)); +} + +TEST(ParseContentIdTest, DeprecatedCencContentId) { + LicenseRequest::ContentIdentification content_id; + MakeDeprecatedCencContentId( + std::string(kWvPsshV0 + kWvPsshV0DataOffset, kWvPsshV0 + sizeof(kWvPsshV0)), + &content_id); + ASSERT_NO_FATAL_FAILURE(VerifyCencContentId(content_id)); +} + +TEST(ParseContentIdTest, InitDataCencContentId) { + LicenseRequest::ContentIdentification content_id; + MakeInitDataCencContentId(std::string(kWvPsshV0, kWvPsshV0 + sizeof(kWvPsshV0)), + &content_id); + ASSERT_NO_FATAL_FAILURE(VerifyCencContentId(content_id)); +} + +TEST(ParseContentIdTest, PsshV1) { + LicenseRequest::ContentIdentification content_id; + MakeInitDataCencContentId(std::string(kWvPsshV1, kWvPsshV1 + sizeof(kWvPsshV1)), + &content_id); + ContentInfo content_info; + EXPECT_EQ(util::OkStatus(), ParseContentId(content_id, &content_info)); + EXPECT_EQ(LicenseRequest::ContentIdentification::InitData::CENC, + content_info.init_data_type()); + ASSERT_EQ(1, content_info.content_info_entry_size()); + ASSERT_EQ(3, content_info.content_info_entry(0).key_ids_size()); + EXPECT_TRUE(content_info.content_info_entry(0).has_pssh()); + EXPECT_EQ(std::string(kWvSystemId, kWvSystemId + sizeof(kWvSystemId)), + content_info.content_info_entry(0).pssh().system_id()); + EXPECT_TRUE(content_info.content_info_entry(0).pssh().has_widevine_data()); + EXPECT_FALSE(content_info.content_info_entry(0).pssh().has_raw_data()); +} + +TEST(ParseContentIdTest, ExistingLicense) { + LicenseRequest::ContentIdentification content_id; + ContentInfo content_info; + MakeExistingLicenseContentId(&content_id); + EXPECT_EQ(util::Status(error_space, INVALID_CONTENT_ID_TYPE, + "invalid-content-id-type"), + ParseContentId(content_id, &content_info)); +} + +TEST(ParseContentIdTest, MultipleBoxes) { + LicenseRequest::ContentIdentification content_id; + MakeInitDataCencContentId( + std::string(kNonPsshBox, kNonPsshBox + sizeof(kNonPsshBox)) + + std::string(kWvPsshV0, kWvPsshV0 + sizeof(kWvPsshV0)) + + std::string(kNonPsshBox, kNonPsshBox + sizeof(kNonPsshBox)) + + std::string(kWvPsshV1, kWvPsshV1 + sizeof(kWvPsshV1)) + + std::string(kNonPsshBox, kNonPsshBox + sizeof(kNonPsshBox)) + + std::string(kNonWvPsshV1, kNonWvPsshV1 + sizeof(kNonWvPsshV1)) + + std::string(kNonPsshBox, kNonPsshBox + sizeof(kNonPsshBox)), + &content_id); + ContentInfo content_info; + EXPECT_EQ(util::OkStatus(), ParseContentId(content_id, &content_info)); + EXPECT_EQ(LicenseRequest::ContentIdentification::InitData::CENC, + content_info.init_data_type()); + EXPECT_EQ(3, content_info.content_info_entry_size()); +} + +TEST(ParseContentIdTest, BoxSizeTooLarge) { + LicenseRequest::ContentIdentification content_id; + MakeModifiedV1WvCencContentId(kWvPsshV1BoxSizeOffset, 89, &content_id); + ContentInfo content_info; + EXPECT_EQ("Errors::INVALID_CENC_INIT_DATA: init-data-too-short", + ParseContentId(content_id, &content_info).ToString()); +} + +TEST(ParseContentIdTest, BoxSizeTooSmall) { + LicenseRequest::ContentIdentification content_id; + MakeModifiedV1WvCencContentId(kWvPsshV1BoxSizeOffset, 87, &content_id); + ContentInfo content_info; + EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-short", + ParseContentId(content_id, &content_info).ToString()); +} + +TEST(ParseContentIdTest, UnsupportedPsshVersion) { + LicenseRequest::ContentIdentification content_id; + MakeModifiedV1WvCencContentId(kWvPsshV1BoxVersionOffset, 0x02000000, + &content_id); + ContentInfo content_info; + EXPECT_EQ("Errors::UNSUPPORTED_PSSH_VERSION: unsupported-pssh-version 2", + ParseContentId(content_id, &content_info).ToString()); +} + +TEST(ParseContentIdTest, TooManyKeyIds) { + LicenseRequest::ContentIdentification content_id; + MakeModifiedV1WvCencContentId(kWvPsshV1NumKeyIdsOffset, 3, &content_id); + ContentInfo content_info; + EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-short", + ParseContentId(content_id, &content_info).ToString()); +} + +TEST(ParseContentIdTest, TooFewKeyIds) { + LicenseRequest::ContentIdentification content_id; + MakeModifiedV1WvCencContentId(kWvPsshV1NumKeyIdsOffset, 1, &content_id); + ContentInfo content_info; + EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-long", + ParseContentId(content_id, &content_info).ToString()); +} + +TEST(ParseContentIdTest, DataSizeTooSmall) { + LicenseRequest::ContentIdentification content_id; + MakeModifiedV1WvCencContentId(kWvPsshV1DataSizeOffset, 19, &content_id); + ContentInfo content_info; + EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-long", + ParseContentId(content_id, &content_info).ToString()); +} + +TEST(ParseContentIdTest, DataSizeTooLarge) { + LicenseRequest::ContentIdentification content_id; + MakeModifiedV1WvCencContentId(kWvPsshV1DataSizeOffset, 21, &content_id); + ContentInfo content_info; + EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-short", + ParseContentId(content_id, &content_info).ToString()); +} + +TEST(ParseContentIdTest, InvalidWidevinePsshData) { + LicenseRequest::ContentIdentification content_id; + MakeInitDataCencContentId( + std::string(kInvalidWvPsshV0, kInvalidWvPsshV0 + sizeof(kInvalidWvPsshV0)), + &content_id); + ContentInfo content_info; + EXPECT_EQ("Errors::INVALID_WIDEVINE_PSSH_DATA: invalid-widevine-pssh-data", + ParseContentId(content_id, &content_info).ToString()); +} +} // namespace widevine diff --git a/license_server_sdk/internal/session_impl.cc b/license_server_sdk/internal/session_impl.cc new file mode 100644 index 0000000..abc222d --- /dev/null +++ b/license_server_sdk/internal/session_impl.cc @@ -0,0 +1,1151 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "license_server_sdk/internal/session_impl.h" + +#include +#include +#include +#include +#include +#include + +#include "glog/logging.h" +#include "base/timestamp.h" +#include "google/protobuf/util/message_differencer.h" +#include "strings/serialize.h" +#include "absl/strings/escaping.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/synchronization/mutex.h" +#include "util/endian/endian.h" +#include "util/random/global_id.h" +#include "common/aes_cbc_util.h" +#include "common/certificate_type.h" +#include "common/certificate_util.h" +#include "common/crypto_util.h" +#include "common/drm_root_certificate.h" +#include "common/drm_service_certificate.h" +#include "common/error_space.h" +#include "common/random_util.h" +#include "common/remote_attestation_verifier.h" +#include "common/rsa_key.h" +#include "common/signing_key_util.h" +#include "common/verified_media_pipeline.h" +#include "common/vmp_checker.h" +#include "license_server_sdk/internal/client_cert.h" +#include "license_server_sdk/internal/device_status_list.h" +#include "license_server_sdk/internal/generate_error_response.h" +#include "license_server_sdk/internal/key_control_block.h" +#include "license_server_sdk/internal/parse_content_id.h" +#include "license_server_sdk/internal/session_usage_report.h" +#include "protos/public/errors.pb.h" +#include "protos/public/widevine_pssh.pb.h" + +namespace widevine { + +// TODO(user): These constants are also defined in public/session.cc. Fix the +// duplicate definitions. +const char* SessionImpl::kEncryptionKeyLabel = "ENCRYPTION"; +const uint32_t SessionImpl::kEncryptionKeySizeBits = 128; +const char* SessionImpl::kSigningKeyLabel = "AUTHENTICATION"; +const uint32_t SessionImpl::kSigningKeySizeBits = 256; +bool SessionImpl::is_service_certificate_loaded_ = false; + +namespace { + +const size_t kSha1SignatureSizeBytes = 20; +const size_t kContentKeySize = 16; +const size_t kEntitlementKeySize = 32; + +} // namespace + +std::string GetProviderClientToken(const SessionInit& session_init, + const ClientIdentification& client_id) { + if (client_id.client_capabilities().client_token() && + session_init.has_provider_client_token()) { + if (session_init.override_provider_client_token()) { + return session_init.provider_client_token(); + } + if (client_id.has_provider_client_token() && + !client_id.provider_client_token().empty()) { + return client_id.provider_client_token(); + } else { + return session_init.provider_client_token(); + } + } + return ""; +} + +void SessionImpl::SetPreProvisioningKeys(const std::map& keys) { + std::multimap temp(keys.begin(), keys.end()); + KeyboxClientCert::SetPreProvisioningKeys(temp); +} + +void SessionImpl::SetPreProvisioningKeys( + const std::multimap& keys) { + KeyboxClientCert::SetPreProvisioningKeys(keys); +} + +util::Status SessionImpl::SetCertificateStatusList( + CertificateType cert_type, const std::string& certificate_status_list, + uint32_t expiration_period_seconds, bool allow_unknown_devices) { + return widevine::SetCertificateStatusList( + cert_type, certificate_status_list, expiration_period_seconds, + allow_unknown_devices); +} + +util::Status SessionImpl::AddDrmServiceCertificate( + CertificateType cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + util::Status status = widevine::AddDrmServiceCertificate( + cert_type, service_certificate, service_private_key, + service_private_key_passphrase); + if (!status.ok()) return status; + status = DrmServiceCertificate::ValidateDrmServiceCertificate(); + if (status.ok()) { + is_service_certificate_loaded_ = true; + } + return status; +} + +void SessionImpl::AllowDevelopmentClients(bool enable) { + DeviceStatusList::Instance()->set_allow_test_only_devices(enable); + VmpChecker::Instance()->set_allow_development_vmp(enable); +} + +void SessionImpl::AllowRevokedDevices(const std::string& system_id_list) { + DeviceStatusList::Instance()->AllowRevokedDevices(system_id_list); +} + +util::Status SessionImpl::Create(const std::string& signed_license_request, + SessionImpl** session) { + util::Status status(util::OkStatus()); + if (!is_service_certificate_loaded_) { + status = util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND, ""); + return status; + } + return SessionImpl::Create(signed_license_request, session, nullptr); +} + +util::Status SessionImpl::Create(const std::string& signed_license_request, + SessionImpl** session, + LicenseRequest* parsed_request_out) { + util::Status status(util::OkStatus()); + DCHECK(session); + DCHECK(*session == NULL); + + LicenseRequest* request_ptr = new LicenseRequest(); + SignedMessage* signed_message_ptr = new SignedMessage(); + status = ParseLicenseRequestFromString(signed_license_request, + signed_message_ptr, request_ptr); + std::unique_ptr signed_message(signed_message_ptr); + std::unique_ptr request(request_ptr); + + if (!status.ok()) { + return status; + } + if (parsed_request_out) { + *parsed_request_out = *request; + } + if (request->has_encrypted_client_id()) { + if (request->has_client_id()) { + status = util::Status(error_space, MULTIPLE_CLIENT_ID, ""); + return status; + } + status = DrmServiceCertificate::DecryptClientIdentification( + request->encrypted_client_id(), request->mutable_client_id()); + if (!status.ok()) { + return status; + } + } + uint32_t key_control_nonce = 0; + bool has_key_control_nonce = false; + status = + LoadKeyControlNonce(*request, &has_key_control_nonce, &key_control_nonce); + if (!status.ok()) { + return status; + } + status = CheckLicenseRequestFields(*request); + if (!status.ok()) { + return status; + } + VLOG(3) << "signed_message: " << signed_message->ShortDebugString() + << ", license_request: " << request->ShortDebugString(); + std::unique_ptr new_session( + new SessionImpl(signed_message.release(), request.release(), + has_key_control_nonce, key_control_nonce, nullptr)); + status = new_session->Init(); + if (status.ok()) { + *session = new_session.release(); + } + return status; +} + +util::Status SessionImpl::CreateForProxy( + const std::string& signed_license_request, + const PlatformVerificationStatus platform_verification_status, + const ClientIdentification* client_id, SessionImpl** session, + LicenseRequest* parsed_request_out) { + util::Status status(util::OkStatus()); + DCHECK(session); + DCHECK(*session == nullptr); + + LicenseRequest* request_ptr = new LicenseRequest(); + SignedMessage* signed_message_ptr = new SignedMessage(); + status = ParseLicenseRequestFromString(signed_license_request, + signed_message_ptr, request_ptr); + std::unique_ptr signed_message(signed_message_ptr); + std::unique_ptr request(request_ptr); + if (!status.ok()) { + return status; + } + if (parsed_request_out) { + *parsed_request_out = *request; + } + if (client_id != nullptr) { + *(*request).mutable_client_id() = *client_id; + } + + uint32_t key_control_nonce = 0; + bool has_key_control_nonce = false; + status = + LoadKeyControlNonce(*request, &has_key_control_nonce, &key_control_nonce); + if (!status.ok()) { + return status; + } + status = CheckLicenseRequestFields(*request); + if (!status.ok()) { + return status; + } + std::unique_ptr new_session( + new SessionImpl(signed_message.release(), request.release(), + has_key_control_nonce, key_control_nonce, nullptr)); + if (platform_verification_status != PLATFORM_NO_VERIFICATION) { + } + status = new_session->Init(); + if (status.ok()) { + *session = new_session.release(); + } + return status; +} + +util::Status SessionImpl::LoadKeyControlNonce(const LicenseRequest& request, + bool* has_key_control_nonce, + uint32_t* key_control_nonce) { + DCHECK(has_key_control_nonce); + DCHECK(key_control_nonce); + + util::Status status; + *has_key_control_nonce = false; + if (request.has_key_control_nonce()) { + // Newer type uint32_t nonce. + *key_control_nonce = request.key_control_nonce(); + *has_key_control_nonce = true; + } else if (request.has_key_control_nonce_deprecated()) { + // Deprecated, decimal-encoded type bytes nonce. + // Remove any null terminating characters. + std::string kc_nonce_string(request.key_control_nonce_deprecated()); + std::string::size_type nul_pos(kc_nonce_string.find('\0')); + if (nul_pos != std::string::npos) { + kc_nonce_string.resize(nul_pos); + } + if (!absl::SimpleAtoi(kc_nonce_string, key_control_nonce)) { + return util::Status(error_space, INVALID_KEY_CONTROL_NONCE, + request.key_control_nonce_deprecated()); + } + *has_key_control_nonce = true; + } + return util::OkStatus(); +} + +util::Status SessionImpl::CheckLicenseRequestFields( + const LicenseRequest& request) { + if (request.type() == LicenseRequest::NEW) { + if (!request.has_client_id()) { + return util::Status(error_space, MISSING_CLIENT_ID, + "new-license-missing-client-id"); + } + } else { + if (!request.has_content_id()) { + return util::Status(error_space, MISSING_CONTENT_ID, + "renew-release-license-missing-content-id"); + } + if (!request.content_id().has_existing_license()) { + return util::Status(error_space, MISSING_LICENSE_ID, + "renew-release-license-missing-existing-license"); + } + if (!request.content_id().existing_license().has_license_id()) { + return util::Status(error_space, MISSING_LICENSE_ID, + "renew-release-license-missing-license-id"); + } + } + return util::OkStatus(); +} + +util::Status SessionImpl::ParseLicenseRequestFromString( + const std::string& signed_license_request, SignedMessage* signed_message, + LicenseRequest* license_request) { + DCHECK(signed_message); + DCHECK(license_request); + + // Deserialize the signed message + if (!signed_message->ParseFromString(signed_license_request)) { + return util::Status(error_space, SIGNED_MESSAGE_PARSE_ERROR, ""); + } + + if (signed_message->type() == SignedMessage::SERVICE_CERTIFICATE_REQUEST) { + return util::Status(error_space, SERVICE_CERTIFICATE_REQUEST_MESSAGE, + std::string()); + } + if (signed_message->type() != SignedMessage::LICENSE_REQUEST && + signed_message->type() != SignedMessage::CAS_LICENSE_REQUEST) { + return util::Status(error_space, INVALID_MESSAGE_TYPE, std::string()); + } + if (!license_request->ParseFromString(signed_message->msg())) { + return util::Status(error_space, LICENSE_REQUEST_PARSE_ERROR, ""); + } + return util::OkStatus(); +} + +std::string SessionImpl::DeriveKey(const std::string& key, const std::string& label, + const std::string& context, const uint32_t size_bits) { + return crypto_util::DeriveKey(key, label, context, size_bits); +} + +SessionImpl::SessionImpl() {} + +SessionImpl::SessionImpl(SignedMessage* message, LicenseRequest* request, + bool has_nonce, uint32_t nonce, + ProvisionedDeviceInfo* device_info) + : signed_message_(message), + license_request_(request), + has_key_control_nonce_(has_nonce), + key_control_nonce_(nonce), + provisioned_device_info_(device_info) { + DCHECK(message); + DCHECK(request); +} + +SessionImpl::~SessionImpl() {} + +util::Status SessionImpl::Init() { + if (license_request_->has_client_id()) { + // Check the client token and verify the message signature. + ClientCert* client_cert_ptr = NULL; + + util::Status status = ClientCert::Create( + license_request_->client_id().type(), + license_request_->client_id().token(), &client_cert_ptr); + client_cert_.reset(client_cert_ptr); + if (!status.ok()) { + return status; + } + // If this cert was issued by a provisioning service other than Widevine's + // cloud service, make sure only the license service for that provider can + // process this request. + if (!client_cert_->service_id().empty() && + DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() + ->provider_id() == client_cert_->service_id()) { + status = util::Status(error_space, PROVIDER_ID_MISMATCH, + "client-cert-service-cert-id-mismatch"); + return status; + } + provisioned_device_info_.reset(new ProvisionedDeviceInfo); + status = DeviceStatusList::Instance()->GetCertStatus( + *client_cert_, provisioned_device_info_.get()); + if (!status.ok()) { + return status; + } + // For renew or release, signature check will happen in + // GenerateSignedLicense. + if (license_request_->type() == LicenseRequest::NEW) { + // Generate/Derive a new signing key if one does not previously exist. + client_cert_->GenerateSigningKey(signed_message_->msg(), + license_request_->protocol_version()); + if (!client_cert_->VerifySignature( + signed_message_->msg(), signed_message_->signature(), + license_request_->protocol_version())) { + return client_cert_->status(); + } + } + } + // Call VerifyPlatform(), if platform has already not been verified at the + // proxy. platform_verification_status_ would either be set at the proxy + // or as result of calling VerifyPlatform(). + if (platform_verification_status_ == PLATFORM_NO_VERIFICATION) { + util::Status status = VerifyPlatform(); + if (!status.ok()) { + LOG(ERROR) << "Platform verification failed. Status: " << status + << ", License Request: " + << license_request_->ShortDebugString(); + return status; + } + } + return util::OkStatus(); +} + +const std::string& SessionImpl::GetSessionId() { + absl::MutexLock lock(&session_id_lock_); + if (session_id_.empty()) { + if (license_request_->content_id().existing_license().has_license_id()) { + session_id_ = license_request_->content_id() + .existing_license() + .license_id() + .session_id(); + } else { + session_id_ = strings::EncodeUint64(util::random::NewGlobalID()); + } + } + return session_id_; +} + +bool SessionImpl::GetProvisionedDeviceInfo( + widevine::ProvisionedDeviceInfo* device_info) { + if (provisioned_device_info_.get()) { + *device_info = *provisioned_device_info_; + return true; + } + return false; +} + +util::Status SessionImpl::GetRequestId(std::string* request_id) const { + DCHECK(request_id); + DCHECK(license_request_); + + if (!license_request_->has_content_id()) + return util::Status(error_space, MISSING_CONTENT_ID, "missing-content-id"); + + const LicenseRequest::ContentIdentification& content_id = + license_request_->content_id(); + if (content_id.has_init_data()) { + *request_id = content_id.init_data().request_id(); + return util::OkStatus(); + } + if (content_id.has_cenc_id_deprecated()) { + *request_id = content_id.cenc_id_deprecated().request_id(); + return util::OkStatus(); + } + if (content_id.has_webm_id_deprecated()) { + *request_id = content_id.webm_id_deprecated().request_id(); + return util::OkStatus(); + } + return util::Status(error_space, INVALID_CONTENT_ID_TYPE, + "invalid-content-id-type"); +} + +util::Status SessionImpl::GetLicenseType(LicenseType* license_type) const { + DCHECK(license_type); + + if (!license_request_->has_content_id()) + return util::Status(error_space, MISSING_CONTENT_ID, "missing-content-id"); + + const LicenseRequest::ContentIdentification& content_id = + license_request_->content_id(); + if (content_id.has_init_data()) { + *license_type = content_id.init_data().license_type(); + return util::OkStatus(); + } + if (content_id.has_cenc_id_deprecated()) { + *license_type = content_id.cenc_id_deprecated().license_type(); + return util::OkStatus(); + } + if (content_id.has_webm_id_deprecated()) { + *license_type = content_id.webm_id_deprecated().license_type(); + return util::OkStatus(); + } + if (content_id.has_existing_license()) { + *license_type = content_id.existing_license().license_id().type(); + return util::OkStatus(); + } + return util::Status(error_space, INVALID_CONTENT_ID_TYPE, + "invalid-content-id-type"); +} + +util::Status SessionImpl::GetContentInfo(ContentInfo* content_info) const { + DCHECK(content_info); + if (!license_request_->has_content_id()) + return util::Status(error_space, MISSING_CONTENT_ID, "missing-content-id"); + + return ParseContentId(license_request_->content_id(), content_info); +} + +util::Status SessionImpl::GenerateNewLicenseInfo( + const SessionInit* session_init, LicenseIdentification* new_id, + std::string* renewal_signing_key, std::string* signing_key) { + DCHECK(new_id); + DCHECK(renewal_signing_key); + DCHECK(signing_key); + std::string request_id; + util::Status status = GetRequestId(&request_id); + if (!status.ok()) return status; + new_id->set_request_id(request_id); + + LicenseType license_type; + status = GetLicenseType(&license_type); + if (!status.ok()) return status; + new_id->set_type(license_type); + + if (session_init && session_init->has_purchase_id()) + new_id->set_purchase_id(session_init->purchase_id()); + // The logic here is that a |session_id| specified in |session_init| takes + // priority over |session_id_| that may have been generated by a prior call to + // GetSessionID(). + if (session_init && session_init->has_session_id()) { + if (!session_id_.empty() && (session_id_ != session_init->session_id())) { + status = util::Status(error_space, SESSION_ID_MISMATCH, ""); + } else { + new_id->set_session_id(session_init->session_id()); + } + } else { + new_id->set_session_id(GetSessionId()); + } + if (ShouldSetProviderSessionToken( + session_init && session_init->has_provider_session_token(), + (license_request_->client_id().has_client_capabilities() && + license_request_->client_id().client_capabilities().session_token()), + provisioned_device_info_->security_level(), + (license_request_->client_id() + .client_capabilities() + .has_oem_crypto_api_version() + ? license_request_->client_id() + .client_capabilities() + .oem_crypto_api_version() + : 0))) { + status = CheckProviderSessionToken(session_init->provider_session_token()); + if (status.ok()) { + new_id->set_provider_session_token( + session_init->provider_session_token()); + } + } + if (!status.ok()) return status; + + uint32_t signing_key_material_size_bytes = + SigningKeyMaterialSize(license_request_->protocol_version()) / 8; + if (session_init) { + if (session_init->has_signing_key()) { + if (session_init->signing_key().size() != + signing_key_material_size_bytes) { + status = util::Status(error_space, INVALID_SIGNING_KEY_SIZE, ""); + return status; + } + *renewal_signing_key = session_init->signing_key(); + if (renewal_signing_key->size() > signing_key_material_size_bytes) { + renewal_signing_key->resize(signing_key_material_size_bytes); + } + } else if (session_init->has_master_signing_key()) { + if (session_init->master_signing_key().size() != + kMasterSigningKeySizeBytes) { + status = + util::Status(error_space, INVALID_MASTER_SIGNING_KEY_SIZE, ""); + return status; + } + std::string context; + if (new_id->SerializeToString(&context)) { + *renewal_signing_key = DeriveKey( + session_init->master_signing_key(), std::string(kSigningKeyLabel), + context, + SigningKeyMaterialSize(license_request_->protocol_version())); + } + } + } + using crypto_util::kSigningKeySizeBytes; + if (!renewal_signing_key->empty() && + renewal_signing_key->size() != signing_key_material_size_bytes) { + status = util::Status(error_space, INVALID_RENEWAL_SIGNING_KEY_SIZE, ""); + } + new_id->set_version(0); + *signing_key = client_cert_->signing_key(); + return status; +} + +util::Status SessionImpl::GeneratePriorLicenseInfo( + const SessionInit* session_init, SessionState* session_state, + LicenseIdentification* new_id, std::string* signing_key) { + DCHECK(new_id); + DCHECK(signing_key); + util::Status status; + if (session_state) { + // If the session_state is provided, we expect to have the previous + // LicenseIdentification to compare against. + bool has_previous_license_id = session_state->has_license_id(); + // We do not assume the version is updated every request, so ignore. + session_state->mutable_license_id()->set_version( + license_request_->content_id() + .existing_license() + .license_id() + .version()); + // Skip this check if the session state did not provide any previous license + // identification. + if (has_previous_license_id && + !google::protobuf::util::MessageDifferencer::Equals( + license_request_->content_id().existing_license().license_id(), + session_state->license_id())) { + status = util::Status(error_space, RENEWAL_LICENSE_ID_MISMATCH, ""); + return status; + } + } + // Because this is renewal we haven't had the signing key to verify the + // signature, until now. + + // Obtain the signing key for this message. The order of preference for the + // key is as follows: + // 1) |session_state| is provided from the previous call to + // GenerateSignedLicense. + // 2) |session_init| is provided with a |signing_key| already provided. + // 3) |session_init| is provided and |signing_key| is derived from + // |master_signing_key|. + if (session_state && session_state->has_signing_key()) { + *signing_key = session_state->signing_key(); + } else if (session_init) { + if (session_init->has_signing_key()) { + uint32_t signing_key_material_size_bytes = + SigningKeyMaterialSize(license_request_->protocol_version()) / 8; + if (session_init->signing_key().size() != + signing_key_material_size_bytes) { + status = util::Status(error_space, INVALID_SIGNING_KEY_SIZE, ""); + return status; + } + *signing_key = session_init->signing_key(); + } else if (session_init->has_master_signing_key()) { + if (session_init->master_signing_key().size() != + kMasterSigningKeySizeBytes) { + status = + util::Status(error_space, INVALID_MASTER_SIGNING_KEY_SIZE, ""); + return status; + } + LicenseIdentification id; + id = license_request_->content_id().existing_license().license_id(); + id.clear_version(); + + std::string context; + if (id.SerializeToString(&context)) { + *signing_key = DeriveKey( + session_init->master_signing_key(), std::string(kSigningKeyLabel), + context, + SigningKeyMaterialSize(license_request_->protocol_version())); + } + } + } + + if (signing_key->empty()) { + status = util::Status(error_space, MISSING_RENEWAL_SIGNING_KEY, ""); + return status; + } + uint32_t signing_key_material_size_bytes = + SigningKeyMaterialSize(license_request_->protocol_version()) / 8; + if (signing_key->size() < signing_key_material_size_bytes) { + status = util::Status(error_space, INVALID_RENEWAL_SIGNING_KEY_SIZE, ""); + return status; + } + signing_key->resize(signing_key_material_size_bytes); + if (!crypto_util::VerifySignatureHmacSha256( + GetClientSigningKey(*signing_key, + license_request_->protocol_version()), + signed_message_->signature(), signed_message_->msg())) { + status = util::Status(error_space, INVALID_RENEWAL_SIGNATURE, ""); + return status; + } + + // Check the session usage table entry if present. + if (license_request_->has_content_id() && + license_request_->content_id().has_existing_license() && + license_request_->content_id() + .existing_license() + .has_session_usage_table_entry()) { + const std::string& session_usage = license_request_->content_id() + .existing_license() + .session_usage_table_entry(); + if (session_usage.size() <= kSha1SignatureSizeBytes) { + status = + util::Status(error_space, INVALID_SESSION_USAGE_TABLE_ENTRY, ""); + return status; + } + if (!crypto_util::VerifySignatureHmacSha1( + GetClientSigningKey(*signing_key, + license_request_->protocol_version()), + session_usage.substr(0, kSha1SignatureSizeBytes), + session_usage.substr(kSha1SignatureSizeBytes))) { + status = util::Status(error_space, INVALID_SESSION_USAGE_SIGNATURE, ""); + return status; + } + } + + // If the system_id is available, check if active. + // TODO(user): This is not strictly necessary, and could be problematic for + // license release if a device is revoked before release. Revisit. + uint32_t system_id = 0; + if (session_state && session_state->keybox_system_id()) { + system_id = session_state->keybox_system_id(); + } else if (client_cert_.get()) { + system_id = client_cert_->system_id(); + } + if (system_id && !DeviceStatusList::Instance()->IsSystemIdActive(system_id)) { + status = util::Status(error_space, UNSUPPORTED_SYSTEM_ID, + "system-id-for-renewal-not-active"); + return status; + } + *new_id = license_request_->content_id().existing_license().license_id(); + new_id->set_version(new_id->version() + 1); + return status; +} + +util::Status SessionImpl::GenerateSignedLicense( + const License::Policy* policy, + const std::list* key_container, + const SessionInit* session_init, SessionState* session_state, + std::string* signed_message_bytes) { + DCHECK(signed_message_bytes); + *signed_message_bytes = ""; + + util::Status status; + LicenseIdentification new_id; + std::string signing_key, renewal_signing_key; + + if (license_request_->type() != LicenseRequest::NEW) { + if (key_container && !key_container->empty()) { + status = util::Status(error_space, + RENEWAL_WITH_CONTENT_KEYS_NOT_ALLOWED, ""); + return status; + } + status = GeneratePriorLicenseInfo(session_init, session_state, &new_id, + &signing_key); + } else { + status = GenerateNewLicenseInfo(session_init, &new_id, &renewal_signing_key, + &signing_key); + } + if (!status.ok()) return status; + + if (signing_key.empty()) { + status = util::Status(error_space, MISSING_SIGNING_KEY, ""); + return status; + } + // Build up the license using the information passed in. + License license; + license.mutable_id()->Swap(&new_id); + + if (session_init && session_init->has_license_start_time()) { + license.set_license_start_time(session_init->license_start_time()); + } else { + if (license_request_->has_request_time()) { + license.set_license_start_time(license_request_->request_time()); + } else { + license.set_license_start_time(time(NULL)); + } + } + if (policy) { + *license.mutable_policy() = *policy; + } + // If this is a new license and the renewal signing key is present, then + // add it to the license. The renewal signing key is required if |can_renew| + // is true. + if (license_request_->type() == LicenseRequest::NEW) { + if (!renewal_signing_key.empty()) { + License::KeyContainer* signing_key_container = license.add_key(); + signing_key_container->set_key(renewal_signing_key); + signing_key_container->set_type(License::KeyContainer::SIGNING); + VLOG(3) << "Renewal Signing Key before encryption: " + << absl::BytesToHexString(renewal_signing_key); + } else if (license.policy().can_renew()) { + status = + util::Status(error_space, MISSING_RENEWAL_SIGNING_KEY, + "required for NEW license with can_renew = true."); + return status; + } + if ((license.id().type() == OFFLINE) && !license.policy().can_persist()) { + status = util::Status(error_space, INVALID_OFFLINE_CAN_PERSIST, ""); + return status; + } + } else if (license_request_->type() == LicenseRequest::RELEASE) { + if (license.has_policy() && license.policy().can_play()) { + // Invalid RELEASE response. can_play should be false. + status = util::Status(error_space, INVALID_RELEASE_CAN_PLAY_VALUE, ""); + return status; + } + } + + std::string encryption_key; + if (key_container && !key_container->empty()) { + if (!client_cert_.get()) { + status = util::Status(error_space, MISSING_CLIENT_CERT, ""); + return status; + } + encryption_key = DeriveKey(client_cert_->key(), std::string(kEncryptionKeyLabel), + signed_message_->msg(), kEncryptionKeySizeBits); + if (encryption_key.empty()) { + status = util::Status(error_space, MISSING_ENCRYPTION_KEY, ""); + return status; + } + for (std::list::const_iterator iter = + key_container->begin(); + iter != key_container->end(); iter++) { + *license.add_key() = *iter; + } + } + if (license_request_->type() == LicenseRequest::NEW) { + std::string provider_client_token = + GetProviderClientToken(*session_init, license_request_->client_id()); + if (!provider_client_token.empty()) { + license.set_provider_client_token(provider_client_token); + } + if (session_state) { + if (license.has_provider_client_token()) { + session_state->set_provider_client_token( + license.provider_client_token()); + } else if (license_request_->client_id().has_provider_client_token()) { + session_state->set_provider_client_token( + license_request_->client_id().provider_client_token()); + } + session_state->set_license_counter( + license_request_->client_id().license_counter()); + } + // Put the protection scheme contained in the PSSH into the license. + ContentInfo content_info; + if (ParseContentId(license_request_->content_id(), &content_info).ok()) { + for (const auto& content_info_entry : content_info.content_info_entry()) { + if (content_info_entry.has_pssh() && + content_info_entry.pssh().has_widevine_data()) { + license.set_protection_scheme( + content_info_entry.pssh().widevine_data().protection_scheme()); + break; + } + } + } + } else { + // This handles the renewal and release case where a single key control + // block is used to represent all current session keys. + if (has_key_control_nonce_) { + License::KeyContainer* kcb_key = license.add_key(); + kcb_key->set_type(License::KeyContainer::KEY_CONTROL); + } + } + uint32_t system_id = 0; + if (provisioned_device_info_) { + system_id = provisioned_device_info_->system_id(); + } + std::unique_ptr rsa_public_key; + if (client_cert_ != nullptr) { + rsa_public_key.reset(RsaPublicKey::Create(client_cert_->public_key())); + if (client_cert_->type() == ClientIdentification::DRM_DEVICE_CERTIFICATE && + rsa_public_key == nullptr) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "create-device-public-key-failed"); + } + } + for (int i = 0; i < license.key_size(); ++i) { + if (has_key_control_nonce_ && + (license.key(i).type() != License::KeyContainer::SIGNING)) { + // Add key control blocks for all key types but signing. + License::KeyContainer::KeyControl& kcb = + *license.mutable_key(i)->mutable_key_control(); + uint32_t duration = license.has_policy() && + license.policy().has_license_duration_seconds() + ? license.policy().license_duration_seconds() + : 0; + if (!key_control_block::Generate( + license.key(i), duration, key_control_nonce_, + license_request_->client_id().client_capabilities(), + license.id().has_provider_session_token(), session_init, + license.id().type(), system_id, + kcb.mutable_key_control_block())) { + status = util::Status(error_space, KEYCONTROL_GENERATION_ERROR, + "Could not generate key control block."); + return status; + } + if (license.key(i).has_key() && + (license.key(i).type() != License::KeyContainer::KEY_CONTROL)) { + // Encrypt the KCB with the corresponding content or operator session + // key. Key control blocks for renewals (type = KEY_CONTROL) are not + // encrypted. + kcb.set_iv(Random16Bytes()); + kcb.set_key_control_block( + crypto_util::EncryptAesCbc(license.key(i).key().substr(0, 16), + kcb.iv(), kcb.key_control_block())); + } + } + if (license.key(i).has_key()) { + if (license.key(i).type() == License::KeyContainer::CONTENT) { + if (license.key(i).key().size() != kContentKeySize) { + return util::Status( + error_space, INVALID_KEY_SIZE, + absl::StrCat( + "content key size in license is wrong. Key size should be ", + kContentKeySize)); + } + } else if (license.key(i).type() == License::KeyContainer::ENTITLEMENT) { + if (license.key(i).key().size() != kEntitlementKeySize) { + return util::Status(error_space, INVALID_KEY_SIZE, + "invalid-entitlement-key-size"); + } + } + license.mutable_key(i)->set_iv(Random16Bytes()); + license.mutable_key(i)->set_key(crypto_util::EncryptAesCbc( + encryption_key, license.key(i).iv(), license.key(i).key())); + if (license.key(i).key().empty()) { + return util::Status(error_space, ENCRYPT_ERROR, "key-encrypt-failed"); + } + } + } + + switch (platform_verification_status_) { + case PLATFORM_NO_VERIFICATION: + break; + case PLATFORM_HARDWARE_VERIFIED: + // TODO(b/65054419): Deprecate remote_attestation_verified altogether. + license.set_remote_attestation_verified(true); + ABSL_FALLTHROUGH_INTENDED; + default: + license.set_platform_verification_status(platform_verification_status_); + break; + } + + + SignedMessage signed_license_message; + if (signed_message_->type() == SignedMessage::CAS_LICENSE_REQUEST) { + signed_license_message.set_type(SignedMessage::CAS_LICENSE); + } else { + signed_license_message.set_type(SignedMessage::LICENSE); + } + bool success = + license.SerializeToString(signed_license_message.mutable_msg()); + if (license_request_->type() == LicenseRequest::NEW) { + DCHECK(client_cert_ != nullptr); + if (client_cert_->type() == ClientIdentification::DRM_DEVICE_CERTIFICATE) { + DCHECK(!client_cert_->encrypted_key().empty()); + signed_license_message.set_session_key(client_cert_->encrypted_key()); + } + } + signed_license_message.set_signature(crypto_util::CreateSignatureHmacSha256( + GetServerSigningKey(signing_key), signed_license_message.msg())); + + success = signed_license_message.SerializeToString(signed_message_bytes); + DCHECK(success); + + if (session_state) { + *session_state->mutable_license_id() = license.id(); + + if (!session_state->has_signing_key()) { + session_state->set_signing_key(renewal_signing_key); + } + + if (client_cert_.get()) { + session_state->set_keybox_system_id(client_cert_->system_id()); + } + } + return status; +} + +bool SessionImpl::GenerateErrorResponse(const util::Status& status, + std::string* signed_message_bytes) { + return widevine::GenerateErrorResponse(status, signed_message_bytes); +} + +std::string SessionImpl::GetRootCertificateDigest(CertificateType cert_type) { + return DrmRootCertificate::GetDigest(cert_type); +} + +std::string SessionImpl::GetSdkVersionString() { + return absl::StrCat(kMajorVersion, ".", kMinorVersion, ".", kRelease); +} + +bool SessionImpl::GetSessionUsage(SessionUsage* session_usage) const { + DCHECK(session_usage); + if (license_request_->has_content_id() && + license_request_->content_id().has_existing_license() && + license_request_->content_id() + .existing_license() + .has_session_usage_table_entry()) { + InternalSessionUsageReport usage_report; + const std::string& session_usage_table = license_request_->content_id() + .existing_license() + .session_usage_table_entry(); + memcpy(reinterpret_cast(&usage_report), session_usage_table.data(), + sizeof(usage_report)); + session_usage->set_clock_security_level( + static_cast( + usage_report.clock_security_level)); + // Values from the client are expected in network byte order. + session_usage->set_seconds_since_license_received( + gntohll(usage_report.seconds_since_license_received)); + session_usage->set_seconds_since_first_decrypt( + gntohll(usage_report.seconds_since_first_decrypt)); + session_usage->set_seconds_since_last_decrypt( + gntohll(usage_report.seconds_since_last_decrypt)); + // Client is not explicitly letting us know if the license was used. + // For now, try to determine based on first_decrypt time being large + // (initial value of 0 - now()). + if (session_usage->seconds_since_first_decrypt() <= + session_usage->seconds_since_license_received()) { + session_usage->set_license_used(true); + } else { + session_usage->set_license_used(false); + session_usage->set_seconds_since_first_decrypt(0); + session_usage->set_seconds_since_last_decrypt(0); + } + if (static_cast(usage_report.status) >= + SessionUsageStatus_Inactive_Deprecated && + static_cast(usage_report.status) <= + SessionUsageStatus_Inactive_Unused) { + session_usage->set_license_released(true); + } + std::string* pst = session_usage->mutable_provider_session_token(); + pst->assign(reinterpret_cast(usage_report.pst), + usage_report.pst_length); + return true; + } + return false; +} + +bool SessionImpl::ShouldSetProviderSessionToken( + bool pst_provided, bool client_supports_pst, + ProvisionedDeviceInfo::WvSecurityLevel level, uint32_t oem_crypto_version) { + if (pst_provided && client_supports_pst) { + // This is a workaround for a problem that prevented offline playback on L3 + // devices. See b/20956523 for details. + // TODO(user): This only affects Android devices. Can we refine this check + // based on device information. + if (level != ProvisionedDeviceInfo::LEVEL_3 || oem_crypto_version != 9) { + return true; + } + } + return false; +} + +util::Status SessionImpl::VerifyPlatform() { + if (platform_verification_status_ != PLATFORM_NO_VERIFICATION) { + return util::OkStatus(); + } + // Verify the platform only if it has not been verified at the proxy. This + // verification can be performed by content providers who host their own + // service certificate. + platform_verification_status_ = PLATFORM_UNVERIFIED; + if (provisioned_device_info_ && provisioned_device_info_->security_level() == + ProvisionedDeviceInfo::LEVEL_1) { + platform_verification_status_ = PLATFORM_HARDWARE_VERIFIED; + } else { + util::Status status = VerifyRemoteAttestation(); + if (!status.ok()) { + VLOG(1) << "Remote Attestation Failure: " << status + << ", license request: " << license_request_->ShortDebugString(); + return status; + } + } + if (platform_verification_status_ == PLATFORM_UNVERIFIED && + license_request_ && !license_request_->client_id().vmp_data().empty()) { + util::Status status = + VerifyVmpData(license_request_->client_id().vmp_data(), + &platform_verification_status_); + if (!status.ok()) { + VLOG(1) << "Platform Verification Failure: " << status + << ", license request: " << license_request_->ShortDebugString(); + return status; + } + } + + return util::OkStatus(); +} + +std::string SessionImpl::GetDrmDeviceId() const { + if (!remote_attestation_cert_serial_number_.empty()) { + return remote_attestation_cert_serial_number_; + } + if (client_cert_.get()) { + return client_cert_->serial_number(); + } + return std::string(); +} + +util::Status SessionImpl::VerifyDeviceCapabilities( + const ClientIdentification::ClientCapabilities& device_capabilities, + const License::KeyContainer::OutputProtection& output_protection) const { + util::Status status(util::OkStatus()); + if (output_protection.has_hdcp() && + device_capabilities.has_max_hdcp_version()) { + // TODO(user): Implement the HDCP use case. + } + if (output_protection.disable_analog_output()) { + // If the device has analog output and cannot disable analog output, we + // need to reject this request because analog output is not allowed. + if ((device_capabilities.analog_output_capabilities() != + ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_UNKNOWN && + device_capabilities.analog_output_capabilities() != + ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_NONE) && + !device_capabilities.can_disable_analog_output()) { + return util::Status(util::error::PERMISSION_DENIED, + "Analog output must be disabled."); + } + } + if (output_protection.cgms_flags() != + License::KeyContainer::OutputProtection::CGMS_NONE) { + if (device_capabilities.analog_output_capabilities() == + ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_SUPPORTED) { + return util::Status(util::error::PERMISSION_DENIED, + "Analog output must support CGMS-A."); + } + } + return status; +} + +util::Status SessionImpl::CheckProviderSessionToken(const std::string& pst_src) { + if (pst_src.size() > kProviderSessionTokenSizeBytes) { + return util::Status(error_space, INVALID_PROVIDER_SESSION_TOKEN_SIZE, ""); + } + return util::OkStatus(); +} + + +PlatformVerificationStatus SessionImpl::GetPlatformVerificationStatus() const { + return platform_verification_status_; +} + +util::Status SessionImpl::VerifyRemoteAttestation() { + platform_verification_status_ = PLATFORM_UNVERIFIED; + if (signed_message_ != nullptr && signed_message_->has_remote_attestation()) { + if (!signed_message_->has_msg()) { + return util::Status(error_space, INVALID_MESSAGE, + "SignedMessage-msg-missing"); + } + util::Status status = + RemoteAttestationVerifier::get().VerifyRemoteAttestation( + signed_message_->msg(), signed_message_->remote_attestation(), + &remote_attestation_cert_serial_number_); + if (status.ok()) { + platform_verification_status_ = PLATFORM_HARDWARE_VERIFIED; + } else if ((status.error_space() == error_space) && + (status.error_code() == REMOTE_ATTESTATION_FAILED)) { + platform_verification_status_ = PLATFORM_TAMPERED; + } else { + return status; + } + } + + return util::OkStatus(); +} + +// TODO(user): add check to get client_id from EncryptedClientIdentification. +bool SessionImpl::GetClientInfo(ClientIdentification* client_info) const { + if (client_info == nullptr || license_request_ == nullptr) { + return false; + } + if (license_request_->has_client_id()) { + *client_info = license_request_->client_id(); + } else { + return false; + } + return true; +} + +std::string SessionImpl::GetDrmDeviceServiceId() const { + if (client_cert_.get()) { + return client_cert_->service_id(); + } + return std::string(); +} + +} // namespace widevine diff --git a/license_server_sdk/internal/session_impl.h b/license_server_sdk/internal/session_impl.h new file mode 100644 index 0000000..d25e69b --- /dev/null +++ b/license_server_sdk/internal/session_impl.h @@ -0,0 +1,377 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef LICENSE_SERVER_SDK_INTERNAL_SESSION_IMPL_H_ +#define LICENSE_SERVER_SDK_INTERNAL_SESSION_IMPL_H_ + +#include +#include +#include +#include + +#include +#include "base/macros.h" +#include "util/status.h" +#include "common/certificate_type.h" +#include "absl/synchronization/mutex.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/license_protocol.pb.h" +#include "protos/public/license_server_sdk.pb.h" +#include "protos/public/provisioned_device_info.pb.h" + +namespace widevine { + +class ClientCert; +class ClientIdentification; +class ContentInfo; +class SessionInit; +class SessionState; +class SessionTest; +class SessionUsage; + +const uint32_t kMajorVersion = 4; +const uint32_t kMinorVersion = 5; +const uint32_t kRelease = 1; + +const uint32_t kMasterSigningKeySizeBytes = 16; +const uint32_t kSigningKeySizeBytes = 64; +const uint32_t kProviderSessionTokenSizeBytes = 255; + +// Helper function to determine which provider client token to use. +std::string GetProviderClientToken(const SessionInit& session_init, + const ClientIdentification& client_id); + +// A Session represents a lifetime for the request/response between the DRM +// client and the DRM server. During this lifetime, referred to as a DRM +// session, some security secrets are established. These secrets are used +// to wrap a license from the DRM server. A DRM session is used for each +// license request. Example usage: +// Session::SetPreProvisioningKeys(); +// Session::SetCertificateStatusList( +// widevine::sdk::kCertificateTypeProduction, +// cert_status_list, +// expiration_period_seconds, +// /* allow unknown device */ false); +// Session::AddDrmServiceCertificate( +// widevine::sdk::kCertificateTypeProduction, +// service_certificate, +// service_private_key, +// service_private_key_passphrase); +// std::string signed_license_request; +// // assign signed_license_request to incoming license request. +// Session* session = NULL; +// util::Status status = Session::Create(signed_license_request, &session); +// if (!status.ok()) { +// std::string error_license; +// if (Session::GenerateErrorResponse(status, &error_license)) { +// // Send error_license to the client. +// } else { +// // Handle error +// } +// return ... +// } +// License::Policy policies; +// License::KeyContainer key_container; +// // ... evaluate request().content_id and request().type to determine +// // ... what License::Policy and License::KeyContainer objects +// // ... are needed to fulfill this client request. +// SessionInit init; // Required if |policies| contains |can_renew| true. +// SessionState cache; // Required if caching state. +// std::string signed_license; +// Status status = session->GenerateSignedLicense(&policies, +// &key_container, +// &init, +// &cache, +// &signed_license); +// // For renewal, either SessionState may be cached and provided to the call +// // to GenerateSignedLicense. Or if it is not possible to cache the +// // SessionState, then SessionInit must be provided with either +// // |signing_key| or |master_signing_key|, in order to allow the sdk to +// // validate the signature of the renewal request, and to sign the +// // license issued. + +class SessionImpl { + public: + // Set pre-provisioning keys system-wide. Map key is system_id, value. + // Value should be human-readable hex digits suitable for passing to + // absl::HexStringToBytes(). + // Must be called before any other calls to this class. Calls are + // thread-safe, so the keys can be updated at any time. + static void SetPreProvisioningKeys(const std::map& keys); + static void SetPreProvisioningKeys(const std::multimap& keys); + + // Set the certificate status list system-wide. |cert_type| specifies + // whether to use development or production root certificates. + // |expiration_period| is the number of seconds until the + // certificate_status_list expires after its creation time + // (creation_time_seconds). If |allow_unknown_devices| is false, an error is + // returned if the device does not appear in the certificate_status_list. + static util::Status SetCertificateStatusList( + CertificateType cert_type, const std::string& certificate_status_list, + uint32_t expiration_period_seconds, bool allow_unknown_devices); + + // Add a service certificate system-wide. |cert_type| indicates the type of + // root certificate used to sign the service certificate; + // |service_certificate| is a Google-generated certificate used to + // authenticate the service provider for purposes of device privacy; + // |service_private_key| is the encrypted PKCS#8 private RSA key corresponding + // to the service certificate; and |service_private_key_passphrase| is the + // password required to decrypt |service_private_key|. + static util::Status AddDrmServiceCertificate( + CertificateType cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + // Enable delivery of licenses to client devices. This includes devices with + // TEST_ONLY status, and development platform verification certificates. + // Defaults to false. + static void AllowDevelopmentClients(bool enable); + + // Enable delivery of licenses to revoked client devices. |system_id_list| is + // a comma separated list of systems Ids to allow even if the device is in the + // revoked state. + static void AllowRevokedDevices(const std::string& system_id_list); + + // Creates a Session object. + // |signed_license_request| is the serialized SignedMessage received from the + // client. |session| points to a Session*, which must be initialized to NULL + // on entry, but |session| itself may not be NULL. The new Session object will + // be owned by the caller. This method returns util::Status::OK if successful, + // or an appropriate error status, in which case + // Session::GenerateErrorResponse should be invoked. + // Example usage: + // Session* session = NULL; + // util::Status status = Session::Create(request_from_client, &session); + // if (!status.ok()) { + // std::string error_license; + // if (Session::GenerateErrorResponse(status, &error_license)) { + // // Send error_license to the client. + // } else { + // // Handle error + // } + // return ... + // } + // // Create license, invoke GenerateSignedLicense, etc. + static util::Status Create(const std::string& signed_license_request, + SessionImpl** session); + + // Variation of Session::Create which also fills in the parsed LicenseRequest, + // for use in logging or debugging. + static util::Status Create(const std::string& signed_license_request, + SessionImpl** session, + LicenseRequest* parsed_request_out); + + // Same as Create(), but caller can specify the ClientIdentification + // message and/or PlatformVerificationStatus. If ClientIdentification is + // specified, this variation of Create() will use the specified |client_id| + // instead of what is specified in |signed_license_request|. If + // PlatformVerificationStatus is specified, this method will use the specified + // |platform_verification_status| instead of attempting to determine it by + // calling SessionImpl::VerifyPlatform(...). + // Background for this function is to support cases where the client + // identification is encrypted with the provider's service certificate in + // which case we won't be able to decrypt OR when the provider determines + // platform verification. The provider will specify the + // clear client identification in |client_id| and the platform verification + // in |platform_verification_status|. + static util::Status CreateForProxy( + const std::string& signed_license_request, + const PlatformVerificationStatus platform_verification_status, + const ClientIdentification* client_id, SessionImpl** session, + LicenseRequest* parsed_request_out); + + // Generates a SignedMessage containing a message generated in response to + // an error condition. |status| is a previous error status returned by the + // Session or util::Status(util::error::UNAVAILABLE, ...) to indicate that the + // backend is unavailable, |signed_message| points to a std::string to contain the + // serialized SignedMessage, and may not be NULL. This method returns true if + // there is an error license to be sent to the client, or false otherwise. + // Example usage in the Session::Create comments above. + static bool GenerateErrorResponse(const util::Status& status, + std::string* signed_message_bytes); + + // DeriveKey uses the NIST 800-108 KDF recommendation, using AES-CMAC PRF. + // NIST 800-108: + // http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf + // AES-CMAC: + // http://tools.ietf.org/html/rfc4493 + static std::string DeriveKey(const std::string& key, const std::string& label, + const std::string& context, const uint32_t size_bits); + + // Returns a std::string containing the hex-encoded SHA-256 digest of the root + // certificate specified by |cert_type|. Used for purposes of root certificate + // verification. + static std::string GetRootCertificateDigest(CertificateType cert_type); + + // Returns a std::string containing the Widevine License Server SDK version in the + // form .. . + static std::string GetSdkVersionString(); + + // Returns true if service certificate is loaded. + static bool is_service_certificate_loaded() { + return is_service_certificate_loaded_; + } + + static const char* kEncryptionKeyLabel; // NOLINT + static const uint32_t kEncryptionKeySizeBits; + static const char* kSigningKeyLabel; // NOLINT + static const uint32_t kSigningKeySizeBits; + + virtual ~SessionImpl(); + virtual const LicenseRequest& request() const { return *license_request_; } + virtual const std::string& GetSessionId(); + + // Returns true if a provisioned device info exists. Caller + // owns |provisioned_device_info| and it must not be null. + virtual bool GetProvisionedDeviceInfo( + widevine::ProvisionedDeviceInfo* device_info); + + // Returns true if client info exists, otherwise returns false. Populate the + // specified |client_info| structure. + virtual bool GetClientInfo(ClientIdentification* client_info) const; + + // Accessor for request_id field which may be encoded in one of multiple + // places in the liciense request protcol buffer. Use this method instead + // of accessing directly. |request_id| is a pointer to a std::string to contain + // the request ID upon successful return. + virtual util::Status GetRequestId(std::string* request_id) const; + + // Accessor for license_type field which may be encoded in one of multiple + // places in the license request protcol buffer. Use this method instead + // of accessing directly. |license_type| is a pointer to a value to contain + // the license type upon successful return. + virtual util::Status GetLicenseType(LicenseType* license_type) const; + + // Method used to get ContentIdentification in a consistent message regardless + // of the type or version of initialization data contained in the content_id + // field of the license request. Use this method instead of accessing the + // fields of ContentIdentification directly. |content_info| is a pointer to a + // message to contain the parsed values from content_id upon successful + // return. + virtual util::Status GetContentInfo(ContentInfo* content_info) const; + + // Returns the serial number of certificate associated with this device and + // content provider. + virtual std::string GetDrmDeviceId() const; + + // Copies the session usage table from license request to |usage_report|. + // Returns true if session usage exist in the license request, otherwise + // returns false. + bool GetSessionUsage(SessionUsage* usage_report) const; + + // Generates a serialized signed License response, emptying |policy| and + // |key_container|, encrypting the keys therein. |session_init| and + // |session_state| are returned to be cached and provided in subsequent + // calls to the function. If no additional PolicyItem or KeyContainer objects + // are necessary to fulfill the request (such as the case with license + // renewal), |policy| and/or |key_container| may be NULL. + // The response is expected to be sent to the Widevine CDM. + virtual util::Status GenerateSignedLicense( + /*IN*/ const License::Policy* policy, + /*IN*/ const std::list* key_container, + /*IN*/ const SessionInit* session_init, + /*INOUT*/ SessionState* session_state, + /*OUT*/ std::string* signed_message_bytes); + + // Verify the required |output_protection| can be satisified based on the + // |device_capabilities| specified by the client. + virtual util::Status VerifyDeviceCapabilities( + const ClientIdentification::ClientCapabilities& device_capabilities, + const License::KeyContainer::OutputProtection& output_protection) const; + + + virtual PlatformVerificationStatus GetPlatformVerificationStatus() const; + + // Returns the service id of the provider that owns the device certificate. + virtual std::string GetDrmDeviceServiceId() const; + + // Returns true if the license request contained a key control nonce, else + // false. + virtual bool HasKeyControlNonce() const { return has_key_control_nonce_; } + + protected: + friend class Session; + PlatformVerificationStatus platform_verification_status_ = + PLATFORM_NO_VERIFICATION; + + // For testing only. This allows unit tests to define a mock Session class. + SessionImpl(); + + private: + friend class SessionTest; + + // Takes ownership of |message|, |request|, |client_cert| and |device_info|. + SessionImpl(SignedMessage* message, LicenseRequest* request, bool has_nonce, + uint32_t nonce, widevine::ProvisionedDeviceInfo* device_info); + + // Called by the SessionImpl::Create factory to initialize a new session. + util::Status Init(); + util::Status GenerateNewLicenseInfo(/*IN*/ const SessionInit* session_init, + /*OUT*/ LicenseIdentification* new_id, + /*OUT*/ std::string* renewal_signing_key, + /*OUT*/ std::string* signing_key); + util::Status GeneratePriorLicenseInfo( + /*IN*/ const SessionInit* session_init, + /*INOUT*/ SessionState* session_state, + /*OUT*/ LicenseIdentification* new_id, + /*OUT*/ std::string* signing_key); + void SetSessionKey(const std::string& session_key); + + // Verifies remote attestation in the license request and sets + // |platform_verification_status_|. + virtual util::Status VerifyRemoteAttestation(); + // Return true if the provider session token should get included in the + // license response. + static bool ShouldSetProviderSessionToken( + bool pst_provided, bool client_supports_pst, + ProvisionedDeviceInfo::WvSecurityLevel level, uint32_t oem_crypto_version); + + // Verifies the platform by platform-specific means such as Remote Attestion + // or host code verification. Only meaningful for Widevine Level 2-3, as + // Level 1 devices are verified by default. + virtual util::Status VerifyPlatform(); + + // Extracts the nonce from |request| and populates |key_control_nonce|. Sets + // |key_control_nonce| to true if the nonce is found. + static util::Status LoadKeyControlNonce(const LicenseRequest& request, + bool* has_key_control_nonce, + uint32_t* key_control_nonce); + + // Validates required fields are set in the license |request|. + static util::Status CheckLicenseRequestFields(const LicenseRequest& request); + + // De-serialize the license request from the |signed_license_request| + // into |license_request| and |signed_message|. + static util::Status ParseLicenseRequestFromString( + const std::string& signed_license_request, SignedMessage* signed_message, + LicenseRequest* license_request); + + // Validates the Provider Session Token from |pst_src|. + static util::Status CheckProviderSessionToken(const std::string& pst_src); + + std::unique_ptr signed_message_; + std::unique_ptr license_request_; + // Client cert object used for all crypto operations. + std::unique_ptr client_cert_; + std::unique_ptr client_identification_; + std::string session_key_; + std::string session_id_; + absl::Mutex session_id_lock_; + bool has_key_control_nonce_ = false; + static bool is_service_certificate_loaded_; + uint32_t key_control_nonce_; + std::unique_ptr provisioned_device_info_; + std::string remote_attestation_cert_serial_number_; + + private: + DISALLOW_COPY_AND_ASSIGN(SessionImpl); +}; + +} // namespace widevine + +#endif // LICENSE_SERVER_SDK_INTERNAL_SESSION_IMPL_H_ diff --git a/license_server_sdk/internal/session_impl_test.cc b/license_server_sdk/internal/session_impl_test.cc new file mode 100644 index 0000000..05f5c8d --- /dev/null +++ b/license_server_sdk/internal/session_impl_test.cc @@ -0,0 +1,2888 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "license_server_sdk/internal/session_impl.h" + +#include +#include +#include +#include +#include +#include + +#include "base/googleinit.h" +#include "glog/logging.h" +#include "google/protobuf/text_format.h" +#include "google/protobuf/util/message_differencer.h" +#include "strings/serialize.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "common/aes_cbc_util.h" +#include "common/crypto_util.h" +#include "common/drm_root_certificate.h" +#include "common/error_space.h" +#include "common/remote_attestation_verifier.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" +#include "common/signing_key_util.h" +#include "common/test_certificates.h" +#include "common/test_utils.h" +#include "license_server_sdk/internal/client_cert.h" +#include "license_server_sdk/internal/key_control_block.h" +#include "license_server_sdk/internal/session_usage_report.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/device_certificate_status.pb.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/license_protocol.pb.h" +#include "protos/public/license_server_sdk.pb.h" +#include "protos/public/provisioned_device_info.pb.h" +#include "protos/public/remote_attestation.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" +#include "protos/public/widevine_pssh.pb.h" + +using google::protobuf::TextFormat; +using testing::Eq; +using testing::Return; + +namespace widevine { +namespace { +const char* kToken = + "00000002000001128e1ebfe0378280" + "96ca6538b4f6f4bcb51c2b7191cf03" + "7e98beaa24924907e128f9ff49b54a" + "165cd9c33e6547537eb4d29fb7e8df" + "3c2c1cd92517a12f4922953e"; +const char* kKey = "4071197f1f8910d9bf10c6bc4c987638"; +// This instance of the kPreProvisioningKey is the development pre-provisioning +// key used in tests by the Alcatraz team (go/alcatraz). +const char* kPreProvisioningKey = "f7538b38acc78ec68c732ac665c55c65"; + +// This constant represents a hex-encoded, serialized SignedMessage. It was +// created and serialized prior to cl/44801569. In that CL, the +// ClientIdentification proto message was refactored into a separate proto file. +// The serialized message is used to verify backward compatibility of future +// changes to the Session class. +const char* kPriorGeneratedSignedMessage = + "1290010a580800124800000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b71" + "91cf037e98beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1c" + "d92517a12f4922953e1a0a0a03666f6f1203626172122c122a0a10303132333435363738" + "3941424344454610011a146d79436f6f6c526571756573742c2044756465211801209d88" + "f78a051a20acc012accd1aac9f11aa84bfd16f01448ad500b8602e269dd2717e0b265531" + "83"; +const uint32_t kSystemId = 0x112; +const uint32_t kProtocolVersion2_1KeySizeWithPadding = 80; +const uint32_t kProtocolVersion2_0KeySizeWithPadding = 48; +const uint32_t kEncryptionKeySizeBytes = 32; +const uint32_t kEntitlementKeySizeBytes = 48; + +const uint32_t kValidSystemId = 100; +const uint32_t kRevokedSystemId = 101; +const uint32_t kAllowedRevokedSystemId = 102; +const uint32_t kUnknownSystemId = 666; +const char kAllowedRevokedSystemIdValue[] = "102"; +const char kValidSerialNumber[] = "valid-serial-number"; +const char kRevokedSerialNumber[] = "revoked-serial-number"; +const char kAllowedRevokedSerialNumber[] = "allowed-revoked-serial-number"; +const char kUnknownSerialNumber[] = "unknown-serial-number"; +const uint32_t kStatusListCreationTime = 17798001; +const uint32_t kNonce = 12345678; +const char kBadNonce[] = "THIS_IS_BAD_NONCE"; +const uint32_t kShortRequestTime = 10; +const uint32_t kUseCurrentTime = 0; +const char* kProviderSessionToken = "1122334455"; +const char* kProviderClientToken = "6677889900"; +const char* kExistingProviderClientToken = "9988776655"; + +const bool kAllowUnknownDevices(true); +const bool kDontAllowUnknownDevices(false); +const bool kDeprecatedNonce(true); +const bool kNoDeprecatedNonce(false); +const bool kIncludeProviderToken(true); +const bool kLicenseRelease(true); +const bool kNoLicenseRelease(false); +const bool kDontIncludeProviderToken(false); +const bool kDontUseDeprecatedKcbNonce(false); +const uint32_t kMinOEMVersionForEntitlementKey = 14; + +// TODO(user): Replace the RA test certs here with RA certs from +// //common/X509TestCertificates (added in CL/144397600). +// Remote attestation cert chain for a device in verified mode. +const char kVerifiedRaCertChain[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n" + "VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n" + "Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n" + "ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n" + "MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n" + "BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n" + "aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n" + "Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n" + "aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n" + "DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n" + "l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n" + "krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n" + "wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n" + "fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n" + "FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n" + "qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n" + "EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n" + "f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n" + "QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n" + "7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n" + "3rDeUI+Odg==\n" + "-----END CERTIFICATE-----\n" + "-----BEGIN CERTIFICATE-----\n" + "MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n" + "EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n" + "DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n" + "b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n" + "ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n" + "BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n" + "bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n" + "ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n" + "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n" + "f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n" + "oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n" + "UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n" + "gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n" + "9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n" + "AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n" + "FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n" + "BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n" + "xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n" + "HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n" + "ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n" + "qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n" + "6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n" + "-----END CERTIFICATE-----\n"; + +// Remote attestation cert chain for a device in developer mode. +const char kDeveloperRaCertChain[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIDrzCCApcCAQMwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n" + "VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n" + "Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n" + "ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n" + "MTMwOTAyMjMxNzQ4WhcNMzMwOTAxMjMxNzQ4WjCBmjELMAkGA1UEBhMCVVMxEzAR\n" + "BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRUwEwYDVQQKDAxu\n" + "b3QtdmVyaWZpZWQxFTATBgNVBAsMDHRlc3RpbmcudGVzdDESMBAGA1UEAwwJc3Rh\n" + "YmxlX2lkMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wggEiMA0G\n" + "CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpWw6rLTitE5S7SWThs8rIw+PGVxJ6\n" + "cLYtHEACuiXDfYbcKR3pgZcwGyLjiI3mNrX75JdriDjpzTIAIirPw6bvVW3nVwFQ\n" + "p7sRO/8q1iMP64NWZqncUFxak6PudhK7XjK7uJKzmjYEOt0hPd+MkdDEhc7km704\n" + "ExSz+4R7nmnoPPglL0EVNFqAtmpDF82NRugJFcMmP5bnwP1xEF4D24bz+u2MUfns\n" + "cAn80BqJQeYZJYirsdK5sw33CIhzDNCA2hOp7n25vASj8ZWUv0K802QK/UORjMo3\n" + "LtF0yNX5JxlaxOKWTIfIHvqJ3XICEMVwToXyShQqH2Ej+6wcbWrCvuJ3AgMBAAEw\n" + "DQYJKoZIhvcNAQEFBQADggEBAEVXxewOognbMf+RWqvCPkPrd3DWgCteKuUAEH0/\n" + "XNJ88GnL1KrqqT1f1QalO+x0NAaUhjTU0oWfYu40FisNKjBtV3mmyfPT0fpjAS/y\n" + "BnRUvaAj9ATt8b8m088fOkdR//pYBTOY02FxKRVRlzOYypuwi8SVjGEqM/1hqt4E\n" + "EI8QeGOGeVWuVVY+0OUILFUR7QlqrQk0np3ZHv6594KCs53rFJ8zFe7rKhZoLG6P\n" + "M3sxtx/ViZTRag3fy/TkTh9LOKP0NpvxODTuiVNMZTuCdoznzoVvM0ewNEFz1V5c\n" + "WE6vByvE8pWY98hIv7wkrlrZ1nhamQPiV/ObsYqqDLFpVOg=\n" + "-----END CERTIFICATE-----\n" + "-----BEGIN CERTIFICATE-----\n" + "MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n" + "EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n" + "DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n" + "b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n" + "ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n" + "BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n" + "bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n" + "ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n" + "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n" + "f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n" + "oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n" + "UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n" + "gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n" + "9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n" + "AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n" + "FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n" + "BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n" + "xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n" + "HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n" + "ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n" + "qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n" + "6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n" + "-----END CERTIFICATE-----\n"; + +// Private key for the leaf RA certificates above. +const char kDeviceRaPrivateKey[] = + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCpWw6rLTitE5S7\n" + "SWThs8rIw+PGVxJ6cLYtHEACuiXDfYbcKR3pgZcwGyLjiI3mNrX75JdriDjpzTIA\n" + "IirPw6bvVW3nVwFQp7sRO/8q1iMP64NWZqncUFxak6PudhK7XjK7uJKzmjYEOt0h\n" + "Pd+MkdDEhc7km704ExSz+4R7nmnoPPglL0EVNFqAtmpDF82NRugJFcMmP5bnwP1x\n" + "EF4D24bz+u2MUfnscAn80BqJQeYZJYirsdK5sw33CIhzDNCA2hOp7n25vASj8ZWU\n" + "v0K802QK/UORjMo3LtF0yNX5JxlaxOKWTIfIHvqJ3XICEMVwToXyShQqH2Ej+6wc\n" + "bWrCvuJ3AgMBAAECggEAN3CVlrfRC+eTEtRt7+z5f0uRLwamB+sJ2C6dTcc+QYbA\n" + "NSZIon5Hzfm0LCQxv6E9Zd5pK0yGxRLlQWHt/eGgrkybE9ZJmeuus2+ekplTFQ0W\n" + "C5zEEllEQmp+ibuhWjD2DrnXXHApKq1SYmVs3wmE/DCJkBDjmo20g07dCdLL/Lgg\n" + "atQ+CMymYlrbpr4mF25NGr4sw+7bDP4fFLVV/arQfUg4mv+SKYwUxoFFgSs1yUVr\n" + "xUgWPCPPmt4fmUnKm1qMaGu6ivoQrWqqE1zM9JFjhNkflByMzjrO6LqXe0v17faP\n" + "qhR3iYCngeEfR/Cz+nBrAVgQIj4z7s4KOArL6pInUQKBgQDWD4vTI3iL65JyzsSm\n" + "xgl9pE/U8y0adSyh/rg+DqL0gpy5/maLkqloiBl1cBkjOwAIfldt+4LlKuzjnrmT\n" + "/1esd6Ety4HOh26WvPCY5QNskDEgPDi5NRf3lbS4r1SrIYvIzzA1iz8TOG95IUvx\n" + "+cVbM4Av/4eyFCkoIHN51JT4bQKBgQDKiUfHCzaaKbutKtjJHPVWPTastm4FRHtw\n" + "9ocPGOO65JRsjaewNGybplQI2Um1F4cGSkd5bmIKUEaSSxWwklgkGbIPDpreaePS\n" + "Z/EGeH3MZodCLREK47CZiW7Yv9dVBeIwUpOB99rO6/KyDs39vPsImObw9FHH9wDH\n" + "EV8CjN1/8wKBgQC0FqAsIcto6+dpb0tCg5m4emQvY+auO6YxI3sdunKeAsSYNc9P\n" + "3TNpHhdNFeIQIbMFckVb4h1db5+cYA0A1fGMx5hyTqQGfSos5wgDmagnzaeG1yNh\n" + "ralcQjaoqdkxoCdZ6fVFFsEaCoC+B6U9DkVbVRijSPeAejWl7Kc59/7cgQKBgCw/\n" + "nczgqEc6AuSsTbxydS8oJH/LQ3InKgjd81fbCjXUppCoyg/3BBtlVUqxnibQQxGe\n" + "vMrM0OKL8z8W5GA1OMh6TKp5eGBLJhjb2I6fWGyFodtVWTnV0Mlaezsjfnn21RnV\n" + "kKB+c/FtHxLDpUFgPj/8l8fguNVFI4haRTWbbPD9AoGAYTnmjTGsLLAXfANS0Vrq\n" + "+Cmw59m70huWxaS1JsP+WTVg6oalT/8HQ7gaqKE5BPsT2QM26yykVQalmsTRPQH4\n" + "ZbVP2JNHTN36UL6oTG/aCAMmgfTrcOcfk5RDPYzwsRJ4XF/pmUHGRzKuTobVEDLu\n" + "VSz18eF+akQktW/gNMK5YOU=\n" + "-----END PRIVATE KEY-----\n"; + +// Service ID for the RA certificates above. +const char kRaServiceId[] = "testing.test"; +// Serial number for the leaf device certificates above. +const char kRaDeviceCertSerialNumber[] = "\002"; + +// Provider Id for the provisioner provider cetificate. +const char kProvisionerProviderId[] = "testing.test"; + +// The session usage report sent by the client is in network byte order +// (big-endian). +// ghtonll(6) = 432345564227567616 +// ghtonll(7) = 504403158265495552 +// ghtonll(8) = 576460752303423488 +const InternalSessionUsageReport kSampleSessionUsageReport = { + {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + 2, + 1, + 8, + 5, + 504403158265495552, + 432345564227567616, + 576460752303423488, + {48, 49, 50, 51, 52, 53, 54, 55}}; +// Session usage proto corresponding to kSampleSessionUsageReport above. +const char kSessionUsageText[] = { + "license_used: 1 " + "license_released: 1 " + "clock_security_level: 1 " + "seconds_since_license_received: 7 " + "seconds_since_first_decrypt: 6 " + "seconds_since_last_decrypt: 8 " + "provider_session_token: \'01234567\' "}; + +// Protection scheme used in the PSSH. +// 'cenc' (AES-CTR) protection_scheme = 0x63656E63 +// See widevine_pssh.proto +const uint32_t kProtectionScheme = 0x63656E63; + +const uint32_t kOutdatedSrmVersion = 1; +const uint32_t kCurrentSrmVersion = 2; +const char kCurrentSrmFile[] = "Current SRM file content"; +const char kCurrentSrmRequirement[] = "Current SRM requirement"; + +const char kGoodVmpData[] = + "0ad60a30820552308203baa00302010202081cc33c2393ed3b37300d06092a864886f70d" + "01010b050030819c310b30090603550406130255533113301106035504080c0a57617368" + "696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d060355040a0c06" + "476f6f676c653111300f060355040b0c085769646576696e65311e301c06035504030c15" + "7769646576696e652d6465762d636f64657369676e3121301f06092a864886f70d010901" + "161274696e736b697040676f6f676c652e636f6d3020170d313631303238303131343336" + "5a180f32313136313030343031313433365a3081a0310b30090603550406130255533113" + "301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b6c61" + "6e64310f300d060355040a0c06476f6f676c653111300f060355040b0c08576964657669" + "6e653122302006035504030c197769646576696e652d6465762d766d702d636f64657369" + "676e3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e636f" + "6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100b3a7" + "da87309390688388d614a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5de2f7" + "b409b1eac2dc578b2bfd8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c7" + "1d894fe6d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646bb50d" + "3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd34c99" + "9a7b35245bce2795715d8a5957a0872398473eed486029286f51d4118927e88634ad0400" + "34e061fd3f58632961761b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be2fd2" + "58331e7a86baa7394706424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb730" + "08150203010001a382010e3082010a301d0603551d0e041604147266b4ce84aafd02b115" + "9cd2fa04c2553c6c02463081bb0603551d230481b33081b0a181a2a4819f30819c310b30" + "090603550406130255533113301106035504080c0a57617368696e67746f6e3111300f06" + "035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c653111300f06" + "0355040b0c085769646576696e65311e301c06035504030c157769646576696e652d6465" + "762d636f64657369676e3121301f06092a864886f70d010901161274696e736b69704067" + "6f6f676c652e636f6d820900c5f82f038facf15830090603551d1304023000300b060355" + "1d0f04040302078030130603551d25040c300a06082b06010505070303300d06092a8648" + "86f70d01010b05000382018100718a39206b970031605f6b4729e31e8f4446863d380073" + "5b7ab8ec4ac32af1594a2070ba39da19a2c66937bd087aad040fbfc0ac57c66e5a4bb9ef" + "255534961bc5251188ed07a1fadcebcf885bf082a6881856169a4fced35435b984b6cb42" + "2298739ae161a06976c981632e610f22da694963c3fb6c7672d1b8807448321fa0eff7bd" + "7ea696207c7a07fd63d93b753213b88dcf8606b5da579867c5349bc82d9215b214476ae0" + "3f835867e11e00be8e76f51d9cc2a220b445f079a9df916eccec013142d32e913bf07a01" + "8c76ce0637b7004e21ae4350bbbace978b524ae6ad02a34cc89bf517d5e2a7e31183ced2" + "dab74cc662510d8e4f88bbabf48207a199aa93dac03d3902359552b14260da9d5ccdbdf9" + "be2dc789af492e296cd5e738979e307cbf8fcaccf5811f6979a417719e2513b66d10b3b9" + "d4a70be720d36a0834cdacb647b2c0a3c133b6eeb7ee6819e85ce0d5b66b8951513a7f4d" + "a23c0309275a5c12593ee4b267b604eda9d86cffc2e15efa1284c10cb14c1ac5351dd899" + "3d0ad60a30820552308203baa00302010202081cc33c2393ed3b37300d06092a864886f7" + "0d01010b050030819c310b30090603550406130255533113301106035504080c0a576173" + "68696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d060355040a0c" + "06476f6f676c653111300f060355040b0c085769646576696e65311e301c06035504030c" + "157769646576696e652d6465762d636f64657369676e3121301f06092a864886f70d0109" + "01161274696e736b697040676f6f676c652e636f6d3020170d3136313032383031313433" + "365a180f32313136313030343031313433365a3081a0310b300906035504061302555331" + "13301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b6c" + "616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c085769646576" + "696e653122302006035504030c197769646576696e652d6465762d766d702d636f646573" + "69676e3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e63" + "6f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100b3" + "a7da87309390688388d614a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5de2" + "f7b409b1eac2dc578b2bfd8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62" + "c71d894fe6d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646bb5" + "0d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd34c" + "999a7b35245bce2795715d8a5957a0872398473eed486029286f51d4118927e88634ad04" + "0034e061fd3f58632961761b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be2f" + "d258331e7a86baa7394706424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb7" + "3008150203010001a382010e3082010a301d0603551d0e041604147266b4ce84aafd02b1" + "159cd2fa04c2553c6c02463081bb0603551d230481b33081b0a181a2a4819f30819c310b" + "30090603550406130255533113301106035504080c0a57617368696e67746f6e3111300f" + "06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c653111300f" + "060355040b0c085769646576696e65311e301c06035504030c157769646576696e652d64" + "65762d636f64657369676e3121301f06092a864886f70d010901161274696e736b697040" + "676f6f676c652e636f6d820900c5f82f038facf15830090603551d1304023000300b0603" + "551d0f04040302078030130603551d25040c300a06082b06010505070303300d06092a86" + "4886f70d01010b05000382018100718a39206b970031605f6b4729e31e8f4446863d3800" + "735b7ab8ec4ac32af1594a2070ba39da19a2c66937bd087aad040fbfc0ac57c66e5a4bb9" + "ef255534961bc5251188ed07a1fadcebcf885bf082a6881856169a4fced35435b984b6cb" + "422298739ae161a06976c981632e610f22da694963c3fb6c7672d1b8807448321fa0eff7" + "bd7ea696207c7a07fd63d93b753213b88dcf8606b5da579867c5349bc82d9215b214476a" + "e03f835867e11e00be8e76f51d9cc2a220b445f079a9df916eccec013142d32e913bf07a" + "018c76ce0637b7004e21ae4350bbbace978b524ae6ad02a34cc89bf517d5e2a7e31183ce" + "d2dab74cc662510d8e4f88bbabf48207a199aa93dac03d3902359552b14260da9d5ccdbd" + "f9be2dc789af492e296cd5e738979e307cbf8fcaccf5811f6979a417719e2513b66d10b3" + "b9d4a70be720d36a0834cdacb647b2c0a3c133b6eeb7ee6819e85ce0d5b66b8951513a7f" + "4da23c0309275a5c12593ee4b267b604eda9d86cffc2e15efa1284c10cb14c1ac5351dd8" + "993d12b4020a0b62696e617279312e65786510001a1e3031323334353637383961626465" + "6630313233343536373839616264656620002a8002a2172ee47e269a7aa83c65d5f15a0a" + "250ecda4c860e8ba08915116055a97dad11fcdf5046d2ded79c9b0b9c39f6486ece99827" + "30a3a619289f0ce8e2a248780158be3ff383183f668d3984ee9084e24083a9c55ef9c089" + "ad401a240cf6c17c94c71277861effd30d735be9f36a17fbdbd685e2e4008742ee82861e" + "527a308ec718829756ec7777fb2c4189a7550b71bd108f151f91109eba98a7647942125a" + "1328daaa149de2e867bb7a88c49a3e9e459e3e7c918c87935c55a0e74aa08d9488eaa0c5" + "7f965ba3ef6a3055dc387d4528409493bc04186079d9230ef342555a2d2dc3e0626cd67b" + "f2efba76ae39b0e2730f54ecb0c799d9a2ba8d9d7c2cbc552b12b4020a0b62696e617279" + "322e646c6c10001a1e616161616161616161616161616161616161616161616161616161" + "61616120002a80027d10154666bc181e7fe0b66c0f9dbc707b0cce3e6c59a12d68d44dda" + "a7c4ff11b4b9800061fabdae10c72d3b452a3768408f5c04c76ad2642fcfeb6ef196ebd1" + "f5121be4993069c63f2a070f48391cc1081a852ebe3cd29660b1baa10702d55f4c6a96ed" + "224bd6e68c30b2833d0fa5d86bf58fbb638b4762e5aa19fdd684daf9cf277af089bf3c1c" + "fd2c7ac1c0a4805e6b0a87c3a1fc5731c412d4f6f50107960d4a77b41aea3d0a2b152d8b" + "61751de9c16b39a5e22e5218d8877fc021b5bcc346a221af88d9bf5dd7b94157f66a0db4" + "21a3ae62fce6de1d96b7f52786e8f8768b363a654ee56230049fa20ad8a74f4acc195ad6" + "4db295e3b486805d9380efee12b4020a0b62696e617279332e646c6c10011a1e62626262" + "626262626262626262626262626262626262626262626262626220002a800257b5bb5da0" + "9de6668e91606d0568e726d38b21ba135754517b9fc3c491b663ba623d1239350eafd132" + "c50c4858a08fbfe701e4d2b93c6dafdd9cf1f5cfe4b307dbdf1cd1e4288e58a699e6e4e4" + "ef975eb2a9d605f6531778e8353ad42207f8572b1143731f9f312fdabaebff365952a2a2" + "50999935d633a4dbbe28d5b53682fb851658469a52848be86d882b834cb48716c465cf99" + "256c18eb85731a8549d95adc1ad74db01fa17084d97fd55f17f30ddd89e1e7b003a57766" + "88403fa9e8ee572017e0dd698351452745e6da31bc293ce8df9be4ba590a1d372448e76d" + "e60cbbae8005e33510b5e6ff0a83af615f0a4f62d4052d6ef610094c966812aa61056912" + "b4020a0b62696e617279342e646c6c10011a1e6363636363636363636363636363636363" + "6363636363636363636363636320012a800239221c0c27190d3a2b346ac323e3a2e1fa1a" + "360c5d38f5759f0ed99b45d2d636019261af3c0c0a3ffd463201ab217e5b08d1cd950f58" + "50f486c146b0a32426b9ef0196113c4d295a8ebf90592319322411f27fa4163eee48895e" + "fdaf21fdaed58ab4bde5dd9948ab9ce8cab17c8aa551af84262f72196b450c7b27fa5fb6" + "04e12d73d1a077c11ff8d5c3f28754809aa5023cc76a5bb9af6b307069b8a003c19cd278" + "98e3002f1e3c67074f24f4204fabfb5e419bf58f27a07d511429ba177a02a22c1ac56cb5" + "c68c3b167b257c4a64afb2fbe7eb66544d92480ec595d1c025b335b1fba7a2a49c884900" + "c2fd7a4d5a9d820a4a4fd98624e02938195bf312d05b"; + +class MockCDPush {}; +} // anonymous namespace + +class MockSessionImpl : public SessionImpl { + public: + MOCK_METHOD0(VerifyRemoteAttestation, util::Status()); +}; + +// The REGISTER_MODULE_INITIALIZER macro does not parse the template type +// map properly. This typedef is used to allow the macro +// to compile properly. +typedef std::map SystemIdsToKeys; + +REGISTER_MODULE_INITIALIZER(KeyboxProvisioningKey, { + SystemIdsToKeys keys; + keys[kSystemId] = kPreProvisioningKey; + SessionImpl::SetPreProvisioningKeys(keys); + RsaTestKeys test_keys; + CHECK_OK(CertificateClientCert::SetDrmRootCertificatePublicKey( + test_keys.public_test_key_1_3072_bits())); +}); + +namespace { +RsaTestKeys test_keys_; + +void SetupCertificateStatusList( + bool allow_unknown_devices, + SignedDeviceCertificateStatusList* status_list) { + ASSERT_TRUE(status_list); + + DeviceCertificateStatusList cert_status_list; + DeviceCertificateStatus* cert_status; + RsaTestKeys test_keys; + + // Device cert with status RELEASED. + cert_status = cert_status_list.add_certificate_status(); + cert_status->mutable_device_info()->set_system_id(kValidSystemId); + cert_status->set_drm_serial_number(kValidSerialNumber); + cert_status->set_status(DeviceCertificateStatus::STATUS_RELEASED); + + // Device cert with status REVOKED. + cert_status = cert_status_list.add_certificate_status(); + cert_status->mutable_device_info()->set_system_id(kRevokedSystemId); + cert_status->set_drm_serial_number(kRevokedSerialNumber); + cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED); + + // Device cert with status Allowed to be REVOKED. + cert_status = cert_status_list.add_certificate_status(); + cert_status->mutable_device_info()->set_system_id(kAllowedRevokedSystemId); + cert_status->set_drm_serial_number(kAllowedRevokedSerialNumber); + cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED); + + cert_status_list.set_creation_time_seconds(kStatusListCreationTime); + cert_status_list.SerializeToString( + status_list->mutable_certificate_status_list()); + std::unique_ptr root_key( + RsaPrivateKey::Create(test_keys.private_test_key_1_3072_bits())); + ASSERT_TRUE(root_key.get()); + ASSERT_TRUE( + root_key->GenerateSignature(status_list->certificate_status_list(), + status_list->mutable_signature())); +} + +void SetCertificateStatusList(bool allow_unknown_devices) { + TestCertificates test_certs; + SignedDeviceCertificateStatusList status_list; + SetupCertificateStatusList(allow_unknown_devices, &status_list); + std::string serialized_status_list; + ASSERT_TRUE(status_list.SerializeToString(&serialized_status_list)); + uint32_t expiration_period_seconds = 0; + EXPECT_EQ(util::OkStatus(), + SessionImpl::SetCertificateStatusList( + kCertificateTypeTesting, serialized_status_list, + expiration_period_seconds, allow_unknown_devices)); +} + +// TODO(user): Add test utilizing provisioner certificates. + +SignedDrmCertificate* GenerateProvisionerCertificate( + const std::string& serial_number, uint32_t system_id) { + DrmCertificate provisioner_certificate; + provisioner_certificate.set_type(DrmCertificate::PROVISIONER); + provisioner_certificate.set_serial_number(serial_number); + // TODO(user) Use different test keys from the intermediate certificate. + provisioner_certificate.set_public_key( + test_keys_.public_test_key_2_2048_bits()); + provisioner_certificate.set_system_id(system_id); + provisioner_certificate.set_creation_time_seconds(kStatusListCreationTime - + 1); + provisioner_certificate.set_provider_id(kProvisionerProviderId); + std::unique_ptr signed_device_certificate( + new SignedDrmCertificate); + signed_device_certificate->set_drm_certificate( + provisioner_certificate.SerializeAsString()); + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); + rsa_private_key->GenerateSignature( + signed_device_certificate->drm_certificate(), + signed_device_certificate->mutable_signature()); + return signed_device_certificate.release(); +} + +SignedDrmCertificate* GenerateIntermediateCertificate( + const std::string& serial_number, uint32_t system_id, + bool use_provisioner_provider) { + DrmCertificate intermediate_certificate; + intermediate_certificate.set_type(DrmCertificate::DEVICE_MODEL); + intermediate_certificate.set_serial_number(serial_number); + intermediate_certificate.set_public_key( + test_keys_.public_test_key_2_2048_bits()); + intermediate_certificate.set_system_id(system_id); + intermediate_certificate.set_creation_time_seconds(kStatusListCreationTime - + 1); + std::unique_ptr signed_drm_certificate( + new SignedDrmCertificate); + if (use_provisioner_provider) { + signed_drm_certificate->mutable_signer()->Swap( + GenerateProvisionerCertificate(serial_number, system_id)); + } + signed_drm_certificate->set_drm_certificate( + intermediate_certificate.SerializeAsString()); + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); + rsa_private_key->GenerateSignature( + signed_drm_certificate->drm_certificate(), + signed_drm_certificate->mutable_signature()); + return signed_drm_certificate.release(); +} + +std::string GenerateSignedDrmCertificate(uint32_t system_id, + const std::string& serial_number, + bool use_provisioner_provider) { + DrmCertificate drm_certificate; + drm_certificate.set_type(DrmCertificate::DEVICE); + drm_certificate.set_serial_number(serial_number); + drm_certificate.set_system_id(system_id); + drm_certificate.set_public_key(test_keys_.public_test_key_3_2048_bits()); + + SignedDrmCertificate signed_drm_certificate; + signed_drm_certificate.set_allocated_signer(GenerateIntermediateCertificate( + serial_number, system_id, use_provisioner_provider)); + signed_drm_certificate.set_drm_certificate( + drm_certificate.SerializeAsString()); + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + rsa_private_key->GenerateSignature( + signed_drm_certificate.drm_certificate(), + signed_drm_certificate.mutable_signature()); + return signed_drm_certificate.SerializeAsString(); +} + +std::string GenerateBasicLicenseRequest( + ProtocolVersion protocol_version, bool deprecated_nonce, + LicenseType license_type, uint32_t request_time, + const ClientIdentification::ClientCapabilities& client_capabilities, + bool client_token_in_request, + const LicenseRequest::ContentIdentification::InitData::InitDataType + init_data_type) { + LicenseRequest request; + request.mutable_client_id()->set_type(ClientIdentification::KEYBOX); + request.mutable_client_id()->set_token(absl::HexStringToBytes(kToken)); + *request.mutable_client_id()->mutable_client_capabilities() = + client_capabilities; + if (init_data_type == LicenseRequest::ContentIdentification::InitData::WEBM) { + LicenseRequest::ContentIdentification::WebmDeprecated* webm_id = + request.mutable_content_id()->mutable_webm_id_deprecated(); + webm_id->set_header("0123456789ABCDEF"); + webm_id->set_license_type(license_type); + webm_id->set_request_id("myCoolRequest, Dude!"); + } else { + LicenseRequest::ContentIdentification::CencDeprecated* cenc_id = + request.mutable_content_id()->mutable_cenc_id_deprecated(); + widevine::WidevinePsshData wv_pssh; + wv_pssh.set_protection_scheme(kProtectionScheme); + std::string pssh_string; + wv_pssh.SerializeToString(&pssh_string); + cenc_id->add_pssh(pssh_string); + cenc_id->set_license_type(license_type); + cenc_id->set_request_id("myCool-CENC-Request, Dude!"); + } + if (client_token_in_request) { + request.mutable_client_id()->set_provider_client_token( + kExistingProviderClientToken); + } + request.set_type(LicenseRequest::NEW); + if (request_time == kUseCurrentTime) { + request.set_request_time(time(nullptr)); + } else { + request.set_request_time(request_time); + } + request.set_protocol_version(protocol_version); + if (deprecated_nonce) { + request.set_key_control_nonce_deprecated(absl::StrCat(kNonce)); + } else { + // Deprecated nonce should be ignored. + request.set_key_control_nonce_deprecated(kBadNonce); + request.set_key_control_nonce(kNonce); + } + + SignedMessage signed_request_message; + CHECK(request.SerializeToString(signed_request_message.mutable_msg())); + + std::string signing_key = SessionImpl::DeriveKey( + absl::HexStringToBytes(kKey), SessionImpl::kSigningKeyLabel, + signed_request_message.msg(), + SigningKeyMaterialSize(request.protocol_version())); + signed_request_message.set_signature(crypto_util::CreateSignatureHmacSha256( + GetClientSigningKey(signing_key, request.protocol_version()), + signed_request_message.msg())); + + return signed_request_message.SerializeAsString(); +} + +std::string GenerateStreamingRequestDrmCert(ProtocolVersion protocol_version, + uint32_t system_id, + const std::string& serial_number, + LicenseRequest::RequestType request_type, + bool set_key_control_nonce) { + LicenseRequest request; + request.mutable_client_id()->set_type( + ClientIdentification::DRM_DEVICE_CERTIFICATE); + request.mutable_client_id()->set_token( + GenerateSignedDrmCertificate(system_id, serial_number, false)); + + request.mutable_content_id()->mutable_webm_id_deprecated()->set_header( + "0123456789ABCDEF"); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_license_type( + STREAMING); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_request_id( + "myCoolRequest, Dude!"); + if (request_type != LicenseRequest::NEW) { + request.mutable_content_id() + ->mutable_existing_license() + ->mutable_license_id() + ->set_session_id("111"); + } + request.set_type(request_type); + request.set_request_time(time(nullptr)); + request.set_protocol_version(protocol_version); + if (set_key_control_nonce) { + request.set_key_control_nonce(kNonce); + } + + SignedMessage signed_request_message; + CHECK(request.SerializeToString(signed_request_message.mutable_msg())); + + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + rsa_private_key->GenerateSignature( + signed_request_message.msg(), signed_request_message.mutable_signature()); + + return signed_request_message.SerializeAsString(); +} + +std::string GenerateLicenseRequestWithVmp(bool tampered) { + LicenseRequest request; + + std::string vmp_data(absl::HexStringToBytes(kGoodVmpData)); + if (tampered) { + vmp_data[32]++; + } + request.mutable_client_id()->set_vmp_data(vmp_data); + + request.mutable_client_id()->set_type( + ClientIdentification::DRM_DEVICE_CERTIFICATE); + request.mutable_client_id()->set_token( + GenerateSignedDrmCertificate(kValidSystemId, kValidSerialNumber, false)); + + request.mutable_content_id()->mutable_webm_id_deprecated()->set_header( + "0123456789ABCDEF"); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_license_type( + STREAMING); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_request_id( + "myCoolRequest, Dude!"); + request.set_type(LicenseRequest::NEW); + request.set_request_time(time(nullptr)); + request.set_protocol_version(VERSION_2_1); + + SignedMessage signed_request_message; + CHECK(request.SerializeToString(signed_request_message.mutable_msg())); + + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + rsa_private_key->GenerateSignature( + signed_request_message.msg(), signed_request_message.mutable_signature()); + + return signed_request_message.SerializeAsString(); +} + +void ValidateBasicRequestResponse( + const std::string& request_msg, + const ClientIdentification::ClientCapabilities& client_capabilities, + const LicenseRequest::ContentIdentification::InitData::InitDataType + init_data_type, + MockCDPush* cdpush) { + // Parse LicenseRequest from request_msg for comparison + // the one returned from SessionImpl::Create(). + SignedMessage signed_request; + ASSERT_TRUE(signed_request.ParseFromString(request_msg)); + LicenseRequest expected_request_proto; + ASSERT_TRUE(expected_request_proto.ParseFromString(signed_request.msg())); + + LicenseRequest actual_request_proto; + SessionImpl* session_ptr = nullptr; + util::Status status = + SessionImpl::Create(request_msg, &session_ptr, &actual_request_proto); + std::unique_ptr session(session_ptr); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_TRUE(session.get()); + ASSERT_EQ(expected_request_proto.DebugString(), + actual_request_proto.DebugString()); + + if (session->request().client_id().type() == + ClientIdentification::DRM_DEVICE_CERTIFICATE) { + EXPECT_EQ(kValidSerialNumber, session->GetDrmDeviceId()); + } + if (actual_request_proto.has_key_control_nonce()) { + EXPECT_TRUE(session->HasKeyControlNonce()); + } else { + EXPECT_FALSE(session->HasKeyControlNonce()); + } + + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + + std::list keys; + // Setup Content Keys. + License::KeyContainer key; + const char* kKeyId1 = "key id-1"; + key.set_id(kKeyId1); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + key.set_track_label("SD"); + keys.push_front(key); + const char* kKeyId2 = "key id-2"; + key.set_id(kKeyId2); + key.set_key("abcdef9876543210"); + key.set_type(License::KeyContainer::CONTENT); + key.set_track_label("AUDIO"); + keys.push_front(key); + const char* kKeyId3 = "key id-3"; + key.set_id(kKeyId3); + key.set_key("9876543210fedcba"); + key.set_type(License::KeyContainer::CONTENT); + key.set_track_label("HD"); + // Current SRM requested for kKeyId3 + key.clear_required_protection(); + keys.push_front(key); + + const char* kEntitlementKeyId1 = "entitlement key id-1"; + const char* kEntitlementKeyId2 = "entitlement key id-2"; + const char* kEntitlementKeyId3 = "entitlement key id-3"; + int expected_num_entitlement_keys = 0; + if (client_capabilities.oem_crypto_api_version() >= + kMinOEMVersionForEntitlementKey) { + // Setup Entitlement Keys (introduced in OEMCrypto 14). + key.clear_required_protection(); + key.set_id(kEntitlementKeyId1); + key.set_key("deadbeefdeadbeef0011223344abcdef"); + key.set_type(License::KeyContainer::ENTITLEMENT); + key.set_track_label("SD"); + keys.push_front(key); + key.set_id(kEntitlementKeyId2); + key.set_key("deadbeefdeadbeefabcdef9988776655"); + key.set_type(License::KeyContainer::ENTITLEMENT); + key.set_track_label("AUDIO"); + keys.push_front(key); + key.set_id(kEntitlementKeyId3); + key.set_key("deadbeefdeadbeefabcdef4433221155"); + key.set_type(License::KeyContainer::ENTITLEMENT); + key.set_track_label("HD"); + keys.push_front(key); + expected_num_entitlement_keys = 3; + } + + SessionInit init; + init.set_purchase_id("Purchases!"); + init.set_provider_session_token(kProviderSessionToken); + init.set_provider_client_token(kProviderClientToken); + SessionState cache; + std::string signed_license_bytes; + // Test signing key size is enforced. Signing key size is too big. + init.set_master_signing_key("01234567890123450123456789012345"); + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + EXPECT_EQ(INVALID_MASTER_SIGNING_KEY_SIZE, status.error_code()); + // Test signing key size is enforced. Signing key size is too small. + init.set_master_signing_key("012345"); + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + EXPECT_EQ(INVALID_MASTER_SIGNING_KEY_SIZE, status.error_code()); + // Test signing key size is enforced. Signing key size is valid. + init.set_master_signing_key("0123456789012345"); + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + ASSERT_EQ(util::OkStatus(), status); + + SignedMessage signed_message; + CHECK(signed_message.ParseFromString(signed_license_bytes)); + ASSERT_EQ(signed_message.signature().size(), 32); // Yep, SHA-256 was used! + + License license; + ASSERT_TRUE(license.ParseFromString(signed_message.msg())); + + // Strip out the signing key, since it's random, but also assert we see it + // (and there's only one). + bool signing_key_seen = false; + int num_content_keys_found = 0; + int num_entitlement_keys_found = 0; + for (int i = 0; i < license.key_size(); ++i) { + if (license.key(i).type() == License::KeyContainer::CONTENT) { + if (license.key(i).id() == kKeyId1 || license.key(i).id() == kKeyId2 || + license.key(i).id() == kKeyId3) { + num_content_keys_found++; + } + ASSERT_EQ(kEncryptionKeySizeBytes, license.key(i).key().size()) + << " for content key: " << license.key(i).id(); + } else if (license.key(i).type() == License::KeyContainer::SIGNING) { + // Check signing key size. Should be 256 bits for version 2.0 and + // 512 bits for version 2.1. + if (session->request().protocol_version() >= VERSION_2_1) { + ASSERT_EQ(std::string(license.key(i).key()).size(), + kProtocolVersion2_1KeySizeWithPadding); + } else { + ASSERT_EQ(std::string(license.key(i).key()).size(), + kProtocolVersion2_0KeySizeWithPadding); + } + ASSERT_FALSE(signing_key_seen); + signing_key_seen = true; + license.mutable_key()->DeleteSubrange(i--, 1); + } else if (license.key(i).type() == License::KeyContainer::ENTITLEMENT) { + if (license.key(i).id() == kEntitlementKeyId1 || + license.key(i).id() == kEntitlementKeyId2 || + license.key(i).id() == kEntitlementKeyId3) { + num_entitlement_keys_found++; + ASSERT_EQ(kEntitlementKeySizeBytes, license.key(i).key().size()) + << " for entitlement key: " << license.key(i).id(); + } + } else { + license.mutable_key(i)->clear_iv(); + license.mutable_key(i)->clear_key(); + } + } + ASSERT_TRUE(signing_key_seen); + ASSERT_EQ(3, num_content_keys_found); + ASSERT_EQ(expected_num_entitlement_keys, num_entitlement_keys_found); + + ASSERT_TRUE(license.has_license_start_time()); + license.clear_license_start_time(); + + ASSERT_TRUE(license.has_id()); + ASSERT_TRUE(license.id().has_session_id()); + license.mutable_id()->clear_session_id(); + + if (init_data_type == LicenseRequest::ContentIdentification::InitData::WEBM) { + EXPECT_EQ("myCoolRequest, Dude!", license.id().request_id()); + EXPECT_FALSE(license.has_protection_scheme()); + } else { + EXPECT_EQ("myCool-CENC-Request, Dude!", license.id().request_id()); + EXPECT_EQ(kProtectionScheme, license.protection_scheme()); + } + EXPECT_EQ("Purchases!", license.id().purchase_id()); + EXPECT_EQ(STREAMING, license.id().type()); + EXPECT_EQ(0, license.id().version()); + EXPECT_EQ(true, license.policy().can_play()); + EXPECT_EQ(true, license.policy().can_renew()); + EXPECT_FALSE(license.remote_attestation_verified()); + EXPECT_EQ(PLATFORM_UNVERIFIED, license.platform_verification_status()); + + EXPECT_EQ(client_capabilities.client_token(), + license.has_provider_client_token()); + ASSERT_EQ(client_capabilities.session_token(), + license.id().has_provider_session_token()); + if (client_capabilities.session_token()) { + EXPECT_EQ(kProviderSessionToken, license.id().provider_session_token()); + } + if (session->request().client_id().has_provider_client_token()) { + EXPECT_EQ(session->request().client_id().provider_client_token(), + cache.provider_client_token()); + } else if (license.has_provider_client_token()) { + EXPECT_EQ(license.provider_client_token(), cache.provider_client_token()); + } + +} + +void ValidateBasicRequestResponse( + const std::string& request_msg, + const ClientIdentification::ClientCapabilities& client_capabilities, + const LicenseRequest::ContentIdentification::InitData::InitDataType + init_data_type) { + ValidateBasicRequestResponse(request_msg, client_capabilities, init_data_type, + /* cdpush= */ nullptr); +} + +enum SessionUsageTestMode { + kNoSessionUsage, + kInvalidSessionUsage, + kInvalidSessionUsageSignature, + kValidSessionUsage, +}; + +void LicenseRenewalTest(const std::string& request_msg_bytes, + bool use_valid_key_expect_success, bool release_license, + SessionUsageTestMode session_usage_mode) { + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + policies.set_playback_duration_seconds(86000); + policies.set_license_duration_seconds(240); + policies.set_renewal_delay_seconds(180); + policies.set_renewal_retry_interval_seconds(30); + policies.set_renewal_recovery_duration_seconds(600); + policies.set_renewal_server_url("http://myrenewalserver.com"); + + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + + SessionImpl* session_ptr = nullptr; + SessionImpl::AllowDevelopmentClients(true); + SessionImpl::AllowRevokedDevices(kAllowedRevokedSystemIdValue); + util::Status status = SessionImpl::Create(request_msg_bytes, &session_ptr); + ASSERT_EQ(util::OkStatus(), status); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); + + SessionInit init; + init.set_purchase_id("Buy ALL the things!"); + + if (session->request().protocol_version() == VERSION_2_0) { + init.set_signing_key("01234567890123450123456789012345"); + } else { + init.set_signing_key( + "01234567890123450123456789012345" + "01234567890123450123456789012345"); + } + init.set_provider_client_token("client token"); + + SessionState session_state; + std::string license_bytes; + status = session->GenerateSignedLicense(&policies, &keys, &init, + &session_state, &license_bytes); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_FALSE(license_bytes.empty()); + + if (!use_valid_key_expect_success) { + session_state.set_keybox_system_id(kRevokedSystemId); + } + + // Use license from original license request to create a Session instance + // for license renewal request. + SignedMessage signed_message; + CHECK(signed_message.ParseFromString(license_bytes)); + CHECK_EQ(signed_message.signature().size(), 32); + + License license; + CHECK(license.ParseFromString(signed_message.msg())); + license.ParseFromString(signed_message.msg()); + + SignedMessage request_message; + CHECK(request_message.ParseFromString(request_msg_bytes)); + + std::string device_key; + if (session->request().client_id().type() == + ClientIdentification::DRM_DEVICE_CERTIFICATE) { + ASSERT_TRUE(signed_message.has_session_key()); + std::unique_ptr rsa_session_key( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + rsa_session_key->Decrypt(signed_message.session_key(), &device_key); + } else { + device_key = absl::HexStringToBytes(kKey); + } + std::string encrypt_key = SessionImpl::DeriveKey( + device_key, SessionImpl::kEncryptionKeyLabel, request_message.msg(), + SessionImpl::kEncryptionKeySizeBits); + std::string signing_key; + for (int i = 0; i < license.key_size(); ++i) { + if (license.key(i).type() == License::KeyContainer::SIGNING) { + signing_key = crypto_util::DecryptAesCbc(encrypt_key, license.key(i).iv(), + license.key(i).key()); + uint32_t signing_key_size_bytes = SessionImpl::kSigningKeySizeBits / 8; + if (signing_key.size() > signing_key_size_bytes) { + // Protocol v2.1 signing key. Retain only server portion. + signing_key.resize(signing_key_size_bytes); + } + break; + } + } + ASSERT_FALSE(signing_key.empty()); + + LicenseRequest renewal; + renewal.mutable_content_id() + ->mutable_existing_license() + ->mutable_license_id() + ->Swap(license.mutable_id()); + if (release_license) { + renewal.set_type(LicenseRequest::RELEASE); + } else { + renewal.set_type(LicenseRequest::RENEWAL); + } + std::string session_usage_data; + switch (session_usage_mode) { + case kInvalidSessionUsage: + session_usage_data = "String is too short."; + break; + case kInvalidSessionUsageSignature: + session_usage_data.assign(21, 'x'); + break; + case kValidSessionUsage: { + std::string signed_data( + reinterpret_cast(&kSampleSessionUsageReport.status), + sizeof(kSampleSessionUsageReport)); + session_usage_data = + crypto_util::CreateSignatureHmacSha1(signing_key, signed_data); + session_usage_data += signed_data; + break; + } + default: + break; + } + if (!session_usage_data.empty()) { + renewal.mutable_content_id() + ->mutable_existing_license() + ->set_session_usage_table_entry(session_usage_data); + } + + SignedMessage signed_request_message; + CHECK(renewal.SerializeToString(signed_request_message.mutable_msg())); + signed_request_message.set_signature(crypto_util::CreateSignatureHmacSha256( + signing_key, signed_request_message.msg())); + session_ptr = nullptr; + status = SessionImpl::Create(signed_request_message.SerializeAsString(), + &session_ptr); + ASSERT_EQ(util::OkStatus(), status); + std::unique_ptr renewal_session(session_ptr); + + if (use_valid_key_expect_success && release_license) { + License::Policy policies; + policies.set_can_play(true); + status = renewal_session->GenerateSignedLicense( + &policies, nullptr, &init, &session_state, &license_bytes); + EXPECT_EQ(INVALID_RELEASE_CAN_PLAY_VALUE, status.error_code()); + } + std::list renewal_keys; + status = renewal_session->GenerateSignedLicense( + nullptr, &renewal_keys, &init, &session_state, &license_bytes); + SessionUsage expected_session_usage; + SessionUsage session_usage; + switch (session_usage_mode) { + case kInvalidSessionUsage: + EXPECT_EQ(INVALID_SESSION_USAGE_TABLE_ENTRY, status.error_code()); + return; + case kInvalidSessionUsageSignature: + EXPECT_EQ(INVALID_SESSION_USAGE_SIGNATURE, status.error_code()); + return; + case kValidSessionUsage: + EXPECT_TRUE(renewal_session->GetSessionUsage(&session_usage)); + ASSERT_TRUE(TextFormat::ParseFromString(kSessionUsageText, + &expected_session_usage)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals( + expected_session_usage, session_usage)); + EXPECT_EQ(expected_session_usage.ShortDebugString(), + session_usage.ShortDebugString()); + break; + default: + break; + } + if (!use_valid_key_expect_success) { + EXPECT_EQ(UNSUPPORTED_SYSTEM_ID, status.error_code()); + // Renewal with invalid, non-existent system ID. + ASSERT_TRUE(license_bytes.empty()); + return; + } + + ASSERT_EQ(util::OkStatus(), status); + ASSERT_FALSE(license_bytes.empty()); + + SignedMessage signed_renew_message; + CHECK(signed_renew_message.ParseFromString(license_bytes)); + + License renew_license; + ASSERT_TRUE(renew_license.ParseFromString(signed_renew_message.msg())); + + ASSERT_TRUE(renew_license.has_license_start_time()); + renew_license.clear_license_start_time(); + + ASSERT_TRUE(renew_license.id().has_session_id()); + renew_license.mutable_id()->clear_session_id(); + + // Renewals should never contain a client token. + ASSERT_FALSE(renew_license.has_provider_client_token()); + + // Renewal response messages should never contain a session key. + ASSERT_FALSE(signed_renew_message.has_session_key()); + + std::string renew_license_text; + TextFormat::PrintToString(renew_license, &renew_license_text); + EXPECT_EQ( + "id {\n" + " request_id: \"myCoolRequest, Dude!\"\n" + " purchase_id: \"Buy ALL the things!\"\n" + " type: STREAMING\n" + " version: 1\n" + "}\n" + "platform_verification_status: PLATFORM_UNVERIFIED\n", + renew_license_text); + + // Verify renewal will fail if content keys are specified. + status = renewal_session->GenerateSignedLicense( + nullptr, &keys, &init, &session_state, &license_bytes); + EXPECT_EQ(RENEWAL_WITH_CONTENT_KEYS_NOT_ALLOWED, status.error_code()); +} + +void LicenseTestFailed(const std::string& request_msg_bytes, bool release_license, + SessionUsageTestMode session_usage_mode) { + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + policies.set_playback_duration_seconds(86000); + policies.set_license_duration_seconds(240); + policies.set_renewal_delay_seconds(180); + policies.set_renewal_retry_interval_seconds(30); + policies.set_renewal_recovery_duration_seconds(600); + policies.set_renewal_server_url("http://myrenewalserver.com"); + + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + + SessionImpl* session_ptr = nullptr; + SessionImpl::AllowDevelopmentClients(true); + util::Status status = SessionImpl::Create(request_msg_bytes, &session_ptr); + ASSERT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED, status.error_code()); + std::unique_ptr session(session_ptr); + ASSERT_FALSE(session); +} +void SigningCheckTest(std::string* signed_license_request) { + SessionImpl* session_ptr = nullptr; + + util::Status status = + SessionImpl::Create(*signed_license_request, &session_ptr); + ASSERT_EQ(util::OkStatus(), status); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); + ASSERT_EQ(LicenseRequest::NEW, session->request().type()); + + // Test that invalid signed messages fail to validate. + for (size_t i = 0; i < signed_license_request->size(); ++i) { + SCOPED_TRACE("byte: " + absl::StrCat(i)); + // Test that perturbing any byte in the request invalidates it. + char tmp = (*signed_license_request)[i] ? '\0' : '\255'; + std::swap((*signed_license_request)[i], tmp); + session_ptr = nullptr; + status = SessionImpl::Create(*signed_license_request, &session_ptr); + ASSERT_NE(util::OkStatus(), status); + ASSERT_FALSE(session_ptr); + std::swap((*signed_license_request)[i], tmp); + // Test that perturbing any single bit in the request invalidates it. + for (int bit = 0; bit < 8; ++bit) { + SCOPED_TRACE("bit: " + absl::StrCat(bit)); + signed_license_request->at(i) ^= 1 << bit; + session_ptr = nullptr; + status = SessionImpl::Create(*signed_license_request, &session_ptr); + if (session_ptr && session_ptr->request().type() != LicenseRequest::NEW) { + // SessionImpl::Create() will not verify the signature for a license + // unless the type is NEW. Perturbing the bits may change the license + // type. + delete session_ptr; + } else { + ASSERT_NE(util::OkStatus(), status); + ASSERT_FALSE(session_ptr); + } + (*signed_license_request)[i] ^= 1 << bit; + } + } + // Post-facto check we remembered to reset everything. + status = SessionImpl::Create(*signed_license_request, &session_ptr); + session.reset(session_ptr); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_TRUE(session.get()); +} + +void TestKeyControlBlocks(const std::string& request_msg) { + SessionImpl* session_ptr = nullptr; + util::Status status = SessionImpl::Create(request_msg, &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_TRUE(session.get()); + License::KeyContainer key; + key.set_id("content"); + const std::string content_key("0123456789abcdef"); + key.set_key(content_key); + key.set_type(License::KeyContainer::CONTENT); + // If client supports hardware_anti_rollback, make it required for this key. + if (session->request() + .client_id() + .client_capabilities() + .anti_rollback_usage_table()) { + key.set_anti_rollback_usage_table(true); + } + std::list keys; + keys.push_back(key); + key.set_id("operator"); + const std::string operator_key("fedcba9876543210"); + key.set_key(operator_key); + key.set_type(License::KeyContainer::OPERATOR_SESSION); + key.clear_track_label(); + keys.push_back(key); + + SessionInit init; + init.set_purchase_id("Purchases!"); + License::Policy policies; + policies.set_can_persist(true); + SessionState cache; + std::string signed_license_bytes; + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + ASSERT_EQ(util::OkStatus(), status); + + SignedMessage signed_message; + CHECK(signed_message.ParseFromString(signed_license_bytes)); + License license; + ASSERT_TRUE(license.ParseFromString(signed_message.msg())); + ASSERT_EQ(2, license.key_size()); + const std::string key_list[2] = {content_key, operator_key}; + int num_content_keys = 1; + int num_operator_session_keys = 1; + for (int idx = 0; idx < num_content_keys + num_operator_session_keys; ++idx) { + const License::KeyContainer& key = license.key(idx); + ASSERT_TRUE(key.has_key_control()); + const License::KeyContainer::KeyControl& kc = key.key_control(); + ASSERT_TRUE(kc.has_key_control_block()); + ASSERT_TRUE(kc.has_iv()); + std::string kcb(crypto_util::DecryptAesCbc(key_list[idx], kc.iv(), + kc.key_control_block())); + ASSERT_EQ(16, kcb.size()); + EXPECT_EQ('k', kcb[0]); + EXPECT_EQ('c', kcb[1]); + EXPECT_EQ('t', kcb[2]); + EXPECT_EQ('l', kcb[3]); + if (key.type() == License::KeyContainer::CONTENT) { + EXPECT_EQ(kNonce, strings::KeyToUint32(kcb.substr(8, 4))); + } + // Verify control bits. + uint32_t kcb_bits = strings::KeyToUint32(kcb.substr(12, 4)); + if (cache.license_id().type() == STREAMING) { + EXPECT_EQ(key_control_block::kKeyControlFlagsNonceEnable, + kcb_bits & key_control_block::kKeyControlFlagsNonceEnable); + } else { + EXPECT_EQ(0, kcb_bits & key_control_block::kKeyControlFlagsNonceEnable); + } + if (key.type() == License::KeyContainer::CONTENT && + session->request() + .client_id() + .client_capabilities() + .anti_rollback_usage_table()) { + EXPECT_EQ( + key_control_block::kKeyControlFlagsAntiRollbackUsageTableRequired, + kcb_bits & key_control_block:: + kKeyControlFlagsAntiRollbackUsageTableRequired); + } else { + EXPECT_EQ(0, kcb_bits & + key_control_block:: + kKeyControlFlagsAntiRollbackUsageTableRequired); + } + } +} + +void TestOfflineLicense(const std::string& request_msg) { + SessionImpl* session_ptr = nullptr; + util::Status status = SessionImpl::Create(request_msg, &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_EQ(util::OkStatus(), status); + ASSERT_TRUE(session.get()); + License::KeyContainer key; + key.set_id("content"); + const std::string content_key("0123456789abcdef"); + key.set_key(content_key); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_back(key); + key.set_id("operator"); + const std::string operator_key("fedcba9876543210"); + key.set_key(operator_key); + key.set_type(License::KeyContainer::OPERATOR_SESSION); + keys.push_back(key); + SessionInit init; + init.set_purchase_id("Purchases!"); + License::Policy policies; + SessionState cache; + std::string signed_license_bytes; + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + EXPECT_EQ(INVALID_OFFLINE_CAN_PERSIST, status.error_code()); + policies.set_can_persist(true); + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + EXPECT_TRUE(status.ok()); +} + +std::string GenerateDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds) { + DrmCertificate cert; + cert.set_type(DrmCertificate::SERVICE); + cert.set_serial_number(serial_number); + cert.set_provider_id(provider_id); + cert.set_public_key(test_keys_.public_test_key_2_2048_bits()); + cert.set_creation_time_seconds(creation_time_seconds); + SignedDrmCertificate signed_cert; + cert.SerializeToString(signed_cert.mutable_drm_certificate()); + std::unique_ptr root_key( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); + if (root_key == NULL) { + return std::string(); + } + root_key->GenerateSignature(signed_cert.drm_certificate(), + signed_cert.mutable_signature()); + std::string serialized_cert; + signed_cert.SerializeToString(&serialized_cert); + return serialized_cert; +} + +void AddDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds) { + std::string passphrase("this is a passphrase"); + std::string signed_cert(GenerateDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + std::string encrypted_private_key; + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), passphrase, + &encrypted_private_key)); + ASSERT_EQ(util::OkStatus(), SessionImpl::AddDrmServiceCertificate( + kCertificateTypeTesting, signed_cert, + encrypted_private_key, passphrase)); +} + +void EncryptClientIdentification( + const ClientIdentification& client_id, const std::string& serial_number, + const std::string& provider_id, + EncryptedClientIdentification* encrypted_client_id) { + CHECK(encrypted_client_id); + + std::string privacy_key("aabbccddeeffgghh"); + std::string iv("0011223344556677"); + encrypted_client_id->set_provider_id(provider_id); + encrypted_client_id->set_service_certificate_serial_number(serial_number); + std::string serial_client_id; + client_id.SerializeToString(&serial_client_id); + encrypted_client_id->set_encrypted_client_id( + crypto_util::EncryptAesCbc(privacy_key, iv, serial_client_id)); + encrypted_client_id->set_encrypted_client_id_iv(iv); + std::unique_ptr rsa_key( + RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); + ASSERT_TRUE(rsa_key.get()); + rsa_key->Encrypt(privacy_key, + encrypted_client_id->mutable_encrypted_privacy_key()); +} + +void AddRemoteAttestation(SignedMessage* signed_message, const std::string& ra_cert, + const std::string& ra_cert_private_key, + const std::string& drm_service_cert_serial_number) { + ClientIdentification client_id; + client_id.set_type(ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE); + client_id.set_token(ra_cert); + RemoteAttestation* ra = signed_message->mutable_remote_attestation(); + EncryptClientIdentification(client_id, drm_service_cert_serial_number, + kRaServiceId, ra->mutable_certificate()); + std::string salt("Ye are the salt of the earth"); + std::string message_signed = signed_message->msg() + salt; + ASSERT_EQ(util::OkStatus(), + GenerateRsaSignatureSha256Pkcs1(ra_cert_private_key, message_signed, + ra->mutable_signature())); + ra->set_salt(salt); +} + +} // anonymous namespace + +class SessionTest : public ::testing::Test { + public: + SessionTest() {} + void SetUp() override {} + + std::string TestGetProviderClientToken(const SessionInit& session_init, + const ClientIdentification& client_id) { + return GetProviderClientToken(session_init, client_id); + } + static bool TestShouldSetProviderSessionToken( + bool pst_provided, bool client_supports_pst, + ProvisionedDeviceInfo::WvSecurityLevel level, uint32_t oem_crypto_version) { + return SessionImpl::ShouldSetProviderSessionToken( + pst_provided, client_supports_pst, level, oem_crypto_version); + } + + util::Status VerifyDeviceCapabilities( + const ClientIdentification::ClientCapabilities& device_capabilities, + const License::KeyContainer::OutputProtection& output_protection) const { + SessionImpl session; + return session.VerifyDeviceCapabilities(device_capabilities, + output_protection); + } + + util::Status CheckProviderSessionToken(const std::string& pst_src) { + return SessionImpl::CheckProviderSessionToken(pst_src); + } + + util::Status VerifyPlatform() { return session_impl_.VerifyPlatform(); } + + void RemoteAttestationSuccess() { + RemoteAttestationVerifier::get().EnableTestCertificates(true); + std::string service_cert_sn("service_cert_sn"); + AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_1, false, STREAMING, kUseCurrentTime, client_capabilities, + kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); + SignedMessage signed_message; + ASSERT_TRUE(signed_message.ParseFromString(request_msg)); + AddRemoteAttestation(&signed_message, kVerifiedRaCertChain, + kDeviceRaPrivateKey, service_cert_sn); + std::string updated_request; + ASSERT_TRUE(signed_message.SerializeToString(&updated_request)); + SessionImpl* session_ptr(nullptr); + EXPECT_OK(SessionImpl::Create(updated_request, &session_ptr)); + std::unique_ptr session(session_ptr); + EXPECT_EQ(PLATFORM_HARDWARE_VERIFIED, + session->GetPlatformVerificationStatus()); + EXPECT_EQ(kRaDeviceCertSerialNumber, session->GetDrmDeviceId()); + + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + SessionInit init; + init.set_purchase_id("Purchases!"); + init.set_master_signing_key("0123456789abcdef"); + SessionState cache; + std::string signed_license_bytes; + ASSERT_OK(session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes)); + ASSERT_TRUE(signed_message.ParseFromString(signed_license_bytes)); + License license; + ASSERT_TRUE(license.ParseFromString(signed_message.msg())); + EXPECT_TRUE(license.remote_attestation_verified()); + EXPECT_EQ(PLATFORM_HARDWARE_VERIFIED, + license.platform_verification_status()); + } + + protected: + MockSessionImpl session_impl_; +}; + +namespace { +void RunShouldSetProviderSessionTokenTests(const uint32_t oem_crypto_version) { + bool pst_provided = true; + bool client_supports_pst = true; + ProvisionedDeviceInfo::WvSecurityLevel level = ProvisionedDeviceInfo::LEVEL_1; + + EXPECT_TRUE(SessionTest::TestShouldSetProviderSessionToken( + pst_provided, client_supports_pst, level, oem_crypto_version)); + + client_supports_pst = false; + EXPECT_FALSE(SessionTest::TestShouldSetProviderSessionToken( + pst_provided, client_supports_pst, level, oem_crypto_version)); + + client_supports_pst = true; + pst_provided = false; + EXPECT_FALSE(SessionTest::TestShouldSetProviderSessionToken( + pst_provided, client_supports_pst, level, oem_crypto_version)); + + client_supports_pst = true; + pst_provided = true; + level = ProvisionedDeviceInfo::LEVEL_3; + if (oem_crypto_version == 9) { + // See b/20956523 for reason this case exist. + EXPECT_FALSE(SessionTest::TestShouldSetProviderSessionToken( + pst_provided, client_supports_pst, level, oem_crypto_version)); + } else { + EXPECT_TRUE(SessionTest::TestShouldSetProviderSessionToken( + pst_provided, client_supports_pst, level, oem_crypto_version)); + } +} +} // anonymous namespace + +TEST_F(SessionTest, BasicRequestResponse) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + bool client_token_in_request = false; + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, client_token_in_request, + LicenseRequest::ContentIdentification::InitData::WEBM)); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::WEBM); +} + +TEST_F(SessionTest, BasicRequestResponse_ClientDoesNotSupportSessionToken) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(false); + client_capabilities.set_session_token(false); + client_capabilities.set_anti_rollback_usage_table(false); + bool client_token_in_request = true; + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, client_token_in_request, + LicenseRequest::ContentIdentification::InitData::WEBM)); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::WEBM); +} + +TEST_F(SessionTest, BasicRequestDrmCertificateResponseProtocolVersion_2_0) { + std::string request_msg(GenerateStreamingRequestDrmCert( + VERSION_2_0, kValidSystemId, kValidSerialNumber, LicenseRequest::NEW, + true)); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(false); + client_capabilities.set_session_token(false); + SetCertificateStatusList(kAllowUnknownDevices); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::WEBM); +} + + +TEST_F(SessionTest, BasicRequestResponseProtocolVersion2_1) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + bool client_token_in_request = true; + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, client_token_in_request, + LicenseRequest::ContentIdentification::InitData::WEBM)); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::WEBM); +} + +TEST_F(SessionTest, BasicRequestDrmCertificateResponseProtocolVersion_2_1) { + std::string request_msg(GenerateStreamingRequestDrmCert( + VERSION_2_1, kValidSystemId, kValidSerialNumber, LicenseRequest::NEW, + true)); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(false); + client_capabilities.set_session_token(false); + client_capabilities.set_anti_rollback_usage_table(false); + SetCertificateStatusList(kAllowUnknownDevices); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::WEBM); +} + +TEST_F(SessionTest, BasicRequestDrmCertificateResponseEmptyToken) { + SessionImpl* session_ptr = nullptr; + + LicenseRequest request; + request.mutable_client_id()->set_type( + ClientIdentification::DRM_DEVICE_CERTIFICATE); + request.mutable_client_id()->set_token("invalid"); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_header( + "0123456789ABCDEF"); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_license_type( + STREAMING); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_request_id( + "myInvalidRequest, Dude!"); + request.set_type(LicenseRequest::NEW); + request.set_request_time(time(NULL)); + + SignedMessage signed_request_message; + CHECK(request.SerializeToString(signed_request_message.mutable_msg())); + util::Status status = SessionImpl::Create( + signed_request_message.SerializeAsString(), &session_ptr); + ASSERT_EQ(SERVICE_CERTIFICATE_NOT_FOUND, status.error_code()); + ASSERT_FALSE(SessionImpl::is_service_certificate_loaded()); + std::string serial_number("service_cert_sn"); + std::string provider_id("myservice.com"); + AddDrmServiceCertificate(serial_number, provider_id, 123); + session_ptr = nullptr; + status = SessionImpl::Create(signed_request_message.SerializeAsString(), + &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(SessionImpl::is_service_certificate_loaded()); + ASSERT_EQ(INVALID_DRM_CERTIFICATE, status.error_code()); +} + +TEST_F(SessionTest, BasicRequestCheckRequestTime) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + SessionImpl* session_ptr = nullptr; + bool client_token_in_request = false; + + std::string signed_request_message(GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kShortRequestTime, + client_capabilities, client_token_in_request, + LicenseRequest::ContentIdentification::InitData::WEBM)); + ASSERT_EQ(util::OkStatus(), + SessionImpl::Create(signed_request_message, &session_ptr)); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); + + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + + SessionInit init; + init.set_purchase_id("Purchases!"); + init.set_master_signing_key("0123456789012345"); + + SessionState cache; + std::string signed_license_bytes; + ASSERT_EQ(util::OkStatus(), + session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes)); + SignedMessage signed_message; + signed_message.ParseFromString(signed_license_bytes); + License license; + license.ParseFromString(signed_message.msg()); + ASSERT_TRUE(license.has_license_start_time()); + CHECK_EQ(kShortRequestTime, license.license_start_time()); +} + +TEST_F(SessionTest, BasicRequestCheckTokens) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + SessionImpl* session_ptr = nullptr; + bool client_token_in_request = true; + + std::string signed_request_message(GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kShortRequestTime, + client_capabilities, client_token_in_request, + LicenseRequest::ContentIdentification::InitData::WEBM)); + ASSERT_EQ(util::OkStatus(), + SessionImpl::Create(signed_request_message, &session_ptr)); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); + + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + + SessionInit init; + std::string client_token("client token"); + std::string session_token("session token"); + init.set_provider_client_token(client_token); + init.set_provider_session_token(session_token); + init.set_master_signing_key("0123456789012345"); + + SessionState cache; + std::string signed_license_bytes; + ASSERT_EQ(util::OkStatus(), + session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes)); + SignedMessage signed_message; + signed_message.ParseFromString(signed_license_bytes); + License license; + license.ParseFromString(signed_message.msg()); + ASSERT_TRUE(license.has_provider_client_token()); + ASSERT_TRUE(license.has_id()); + ASSERT_TRUE(license.id().has_provider_session_token()); + EXPECT_EQ(session_token, license.id().provider_session_token()); +} + +// This test ensures that a serialized request generated prior to cl/44801569 +// will still work with the session class. This message in +// kPriorGeneratedSignedMessage was created and serialized prior to the +// refactoring. +TEST_F(SessionTest, BasicRequestResponseBackwardCompatible) { + std::string request_msg(absl::HexStringToBytes(kPriorGeneratedSignedMessage)); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(false); + client_capabilities.set_session_token(false); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::WEBM); +} + +TEST_F(SessionTest, DeriveKeyTest) { + const unsigned char input[] = {0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, + 0x32, 0x9e, 0x6b, 0x3b, 0x4e, 0x29, + 0xfa, 0x3b, 0x00, 0x4b}; + + const unsigned char label[] = { + 0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1, 0x92, 0xa0, 0x34, + 0x8a, 0x8b, 0x6b, 0x77, 0x08, 0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda, + 0xfb, 0x60, 0xd1, 0xcf, 0x6a, 0x4d, 0x40, 0xa1, 0xe3, 0xfe, 0xd3, + 0xe9, 0xa6, 0x58, 0x4c, 0xd4, 0xad, 0xa4, 0xa2}; + + const unsigned char context[] = {0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d, + 0x6d, 0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5, + 0x5b, 0x80, 0x81, 0x91, 0xff}; + + const unsigned char output0[] = {0xd5, 0xad, 0x2d, 0xb1, 0x5a, 0x06, + 0xcb, 0x50, 0xf2, 0x59, 0x5a, 0xb2, + 0xb2, 0x0d, 0x44, 0x4e}; + + const unsigned char output1[] = { + 0xdf, 0x38, 0x45, 0x97, 0x5d, 0x7a, 0x81, 0xb4, 0x94, 0x86, 0xaf, 0x0c, + 0xdc, 0x4d, 0xeb, 0x62, 0x31, 0x39, 0x67, 0x8f, 0xff, 0x5d, 0x68, 0x35, + 0xdc, 0x89, 0x5f, 0x47, 0xca, 0xe0, 0x2d, 0x3a, 0x10, 0x24, 0xf8, 0x7e, + 0x5b, 0x70, 0xe1, 0xa3, 0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22}; + + std::string label_str(label, label + sizeof(label)); + std::string key_str(input, input + sizeof(input)); + std::string context_str(context, context + sizeof(context)); + std::string result = SessionImpl::DeriveKey(key_str, label_str, context_str, 128); + std::string output_128(output0, output0 + sizeof(output0)); + + ASSERT_EQ(result, output_128); + + result = SessionImpl::DeriveKey(key_str, label_str, context_str, 384); + + std::string output_384(output1, output1 + sizeof(output1)); + + ASSERT_EQ(result, output_384); +} + +TEST_F(SessionTest, SignatureCheck) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + std::string signed_message_string = GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM); + SigningCheckTest(&signed_message_string); +} + +TEST_F(SessionTest, SignatureCheckProtocolVersion2_0) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + std::string signed_message_string = GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM); + SigningCheckTest(&signed_message_string); +} + +TEST_F(SessionTest, SignatureCheckProtocolVersion2_1) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + std::string signed_message_string = GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM); + SigningCheckTest(&signed_message_string); +} + +TEST_F(SessionTest, ResultStatus) { + std::string empty_request; + SessionImpl* session_ptr = nullptr; + util::Status status = SessionImpl::Create(empty_request, &session_ptr); + EXPECT_GT(status.ToString().size(), 0); +} + +TEST_F(SessionTest, NewLicenseFailedWithRevokedSystemId) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseTestFailed(GenerateStreamingRequestDrmCert( + VERSION_2_1, kAllowedRevokedSystemId, + kAllowedRevokedSerialNumber, LicenseRequest::NEW, true), + kLicenseRelease, kNoSessionUsage); +} + +TEST_F(SessionTest, LicenseRequestWithMissingNonce) { + // This is also the default nonce setting. + std::string request_msg(GenerateStreamingRequestDrmCert( + VERSION_2_0, kValidSystemId, kValidSerialNumber, LicenseRequest::NEW, + false)); + ClientIdentification::ClientCapabilities client_capabilities; + SetCertificateStatusList(kAllowUnknownDevices); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::WEBM); +} + +class SessionTestRenewal : public ::testing::TestWithParam { + public: + void SetUp() override { SetCertificateStatusList(kAllowUnknownDevices); } +}; + +TEST_P(SessionTestRenewal, RenewalLicenseFailedWithRevokedSystemId) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseTestFailed( + GenerateStreamingRequestDrmCert(VERSION_2_1, kAllowedRevokedSystemId, + kAllowedRevokedSerialNumber, + LicenseRequest::RENEWAL, true), + kLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, ReleaseLicenseFailedWithRevokedSystemId) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseTestFailed( + GenerateStreamingRequestDrmCert(VERSION_2_1, kAllowedRevokedSystemId, + kAllowedRevokedSerialNumber, + LicenseRequest::RELEASE, true), + kLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewal) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + GetParam(), kNoLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewalProtocolVersion2_0) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest( + GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + GetParam(), kNoLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewalProtocolVersion2_1) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + GetParam(), kNoLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewalDrmCertProtocolVersion2_0) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest(GenerateStreamingRequestDrmCert( + VERSION_2_0, kValidSystemId, kValidSerialNumber, + LicenseRequest::NEW, true), + GetParam(), kNoLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewalDrmCertProtocolVersion2_1) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest(GenerateStreamingRequestDrmCert( + VERSION_2_0, kValidSystemId, kValidSerialNumber, + LicenseRequest::NEW, true), + GetParam(), kNoLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewalUnknownDevice) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest(GenerateStreamingRequestDrmCert( + VERSION_2_1, kUnknownSystemId, kUnknownSerialNumber, + LicenseRequest::NEW, true), + GetParam(), kNoLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewalAllowedDevice) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest( + GenerateStreamingRequestDrmCert(VERSION_2_1, kAllowedRevokedSystemId, + kAllowedRevokedSerialNumber, + LicenseRequest::NEW, true), + GetParam(), kNoLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRelease) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + GetParam(), kLicenseRelease, kNoSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewalInvalidSessionUsage) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + GetParam(), kNoLicenseRelease, kInvalidSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewalInvalidSessionUsageSignature) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + GetParam(), kNoLicenseRelease, kInvalidSessionUsageSignature); +} + +TEST_P(SessionTestRenewal, LicenseReleaseValidSessionUsage) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + LicenseRenewalTest( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + GetParam(), kLicenseRelease, kValidSessionUsage); +} + +TEST_P(SessionTestRenewal, LicenseRenewalWithSerialNumber) { + std::string request_msg = GenerateStreamingRequestDrmCert( + VERSION_2_0, kValidSystemId, kValidSerialNumber, LicenseRequest::NEW, + true); + LicenseRenewalTest(request_msg, GetParam(), kLicenseRelease, + kValidSessionUsage); +} + +INSTANTIATE_TEST_CASE_P(SystemIdVariations, SessionTestRenewal, + ::testing::Bool()); + +TEST_F(SessionTest, InvalidRenewalKeySigningKey_2_0) { + SessionImpl* session_ptr = nullptr; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + ASSERT_EQ(util::OkStatus(), + SessionImpl::Create( + GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + &session_ptr)); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); + + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + + SessionInit init; + init.set_purchase_id("Purchases!"); + init.set_signing_key("01234567"); + + SessionState cache; + std::string signed_license_bytes; + ASSERT_EQ(INVALID_SIGNING_KEY_SIZE, + session + ->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes) + .error_code()); +} + +TEST_F(SessionTest, InvalidRenewalKeySigningKey_2_1) { + SessionImpl* session_ptr = nullptr; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + ASSERT_EQ(util::OkStatus(), + SessionImpl::Create( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + &session_ptr)); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); + + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + + SessionInit init; + init.set_purchase_id("Purchases!"); + init.set_signing_key("00112233445566778899aabbccddeeff"); + + SessionState cache; + std::string signed_license_bytes; + ASSERT_EQ(INVALID_SIGNING_KEY_SIZE, + session + ->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes) + .error_code()); +} + +TEST_F(SessionTest, ValidRenewalLongSigningKey) { + SessionImpl* session_ptr = nullptr; + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + ASSERT_EQ(util::OkStatus(), + SessionImpl::Create( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + &session_ptr)); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); + + License::Policy policies; + policies.set_can_play(true); + policies.set_can_renew(true); + + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + + SessionInit init; + init.set_purchase_id("Purchases!"); + init.set_signing_key( + "00112233445566778899aabbccddeeff" + "00112233445566778899aabbccddeeff"); + + SessionState cache; + std::string signed_license_bytes; + ASSERT_EQ(util::OkStatus(), + session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes)); +} + +TEST_F(SessionTest, NewLicenseWithKeyControlBlock) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(false); + client_capabilities.set_session_token(false); + client_capabilities.set_anti_rollback_usage_table(false); + TestKeyControlBlocks(GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); +} + +TEST_F(SessionTest, NewLicenseWithKeyControlBlockOffline) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(false); + client_capabilities.set_session_token(false); + client_capabilities.set_anti_rollback_usage_table(false); + TestKeyControlBlocks(GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, OFFLINE, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); +} + +TEST_F(SessionTest, NewLicenseWithKeyControlBlockDeprecatedNonce) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(false); + client_capabilities.set_session_token(false); + client_capabilities.set_anti_rollback_usage_table(false); + TestKeyControlBlocks(GenerateBasicLicenseRequest( + VERSION_2_1, kDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); +} + +TEST_F(SessionTest, NewLicenseWithKeyControlBlockAntiRollback) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(false); + client_capabilities.set_session_token(false); + client_capabilities.set_anti_rollback_usage_table(true); + TestKeyControlBlocks(GenerateBasicLicenseRequest( + VERSION_2_1, kDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); +} + +TEST_F(SessionTest, OfflineLicenseCreate) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(false); + client_capabilities.set_session_token(false); + client_capabilities.set_anti_rollback_usage_table(false); + TestOfflineLicense(GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, OFFLINE, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); +} + +// TODO(user): Add test to verify license contains clear key control block. +TEST_F(SessionTest, DISABLED_RenewLicenseWithKeyControlBlock) {} + +TEST_F(SessionTest, DrmCertificateNotSupported) { + std::string request_msg(GenerateStreamingRequestDrmCert( + VERSION_2_0, kUnknownSystemId, kValidSerialNumber, LicenseRequest::NEW, + true)); + SessionImpl* session_ptr = nullptr; + SetCertificateStatusList(kDontAllowUnknownDevices); + // Cannot create a session if the certificate system id + // does not exist in the device status list and allow_unknown_device + // is false. + util::Status status = SessionImpl::Create(request_msg, &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN, status.error_code()); +} + +// The system Id is found, but the intermediate status has a different +// serial number. Perhaps the intermediate certificate was regenerated. +TEST_F(SessionTest, DrmCertificateIntermediateStatusChangedSerialNumber) { + std::string request_msg(GenerateStreamingRequestDrmCert( + VERSION_2_0, kValidSystemId, kRevokedSerialNumber, LicenseRequest::NEW, + true)); + SessionImpl* session_ptr = nullptr; + SetCertificateStatusList(kAllowUnknownDevices); + util::Status status = SessionImpl::Create(request_msg, &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_EQ(INVALID_DRM_CERTIFICATE, status.error_code()); +} + +TEST_F(SessionTest, DrmCertificateRevoked) { + std::string request_msg(GenerateStreamingRequestDrmCert( + VERSION_2_0, kRevokedSystemId, kValidSerialNumber, LicenseRequest::NEW, + true)); + SessionImpl* session_ptr = nullptr; + SetCertificateStatusList(kAllowUnknownDevices); + util::Status status = SessionImpl::Create(request_msg, &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED, status.error_code()); +} + +TEST_F(SessionTest, DrmCertificateReportingSerialNumberWithRenewal) { + std::string request_msg(GenerateStreamingRequestDrmCert( + VERSION_2_0, kValidSystemId, kValidSerialNumber, LicenseRequest::NEW, + true)); + SessionImpl* session_ptr = nullptr; + util::Status status = SessionImpl::Create(request_msg, &session_ptr); + ASSERT_EQ(util::OkStatus(), status); + std::unique_ptr session(session_ptr); + ASSERT_TRUE(session.get()); +} + +TEST_F(SessionTest, SetCertificateStatusListFail) { + SignedDeviceCertificateStatusList status_list; + TestCertificates test_certs; + RsaTestKeys test_keys; + + SetupCertificateStatusList(false, &status_list); + // Control (should succeed). + std::string serialized_status_list; + ASSERT_TRUE(status_list.SerializeToString(&serialized_status_list)); + const uint32_t expiration_seconds = 0; + EXPECT_EQ(util::OkStatus(), + SessionImpl::SetCertificateStatusList( + kCertificateTypeTesting, serialized_status_list, + expiration_seconds, kDontAllowUnknownDevices)); + // Use different certificate for validation (other than kCertificateTypeTest). + EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, + SessionImpl::SetCertificateStatusList( + kCertificateTypeDevelopment, serialized_status_list, + expiration_seconds, kDontAllowUnknownDevices) + .error_code()); + EXPECT_EQ( + "Errors::INVALID_CERTIFICATE_STATUS_LIST: invalid-status-list-signature", + SessionImpl::SetCertificateStatusList( + kCertificateTypeDevelopment, serialized_status_list, + expiration_seconds, kDontAllowUnknownDevices) + .ToString()); + // Invalid signature. + ++(*status_list.mutable_signature())[20]; + ASSERT_TRUE(status_list.SerializeToString(&serialized_status_list)); + EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, + SessionImpl::SetCertificateStatusList( + kCertificateTypeDevelopment, serialized_status_list, + expiration_seconds, kDontAllowUnknownDevices) + .error_code()); +} + +TEST_F(SessionTest, SetCertificateStatusList) { + DeviceCertificateStatusList cert_status_list; + TestCertificates test_certs; + RsaTestKeys test_keys; + + SignedDeviceCertificateStatusList status_list; + SetupCertificateStatusList(false, &status_list); + std::string serialized_status_list; + ASSERT_TRUE(status_list.SerializeToString(&serialized_status_list)); + const uint32_t expiration_seconds = 0; + SetCertificateStatusList(kAllowUnknownDevices); + EXPECT_EQ(util::OkStatus(), + SessionImpl::SetCertificateStatusList( + kCertificateTypeTesting, serialized_status_list, + expiration_seconds, kDontAllowUnknownDevices)); + // Use different certificate for validation (other than kCertificateTypeTest). + EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, + SessionImpl::SetCertificateStatusList( + kCertificateTypeDevelopment, serialized_status_list, + expiration_seconds, kAllowUnknownDevices) + .error_code()); + EXPECT_EQ( + "Errors::INVALID_CERTIFICATE_STATUS_LIST: invalid-status-list-signature", + SessionImpl::SetCertificateStatusList( + kCertificateTypeDevelopment, serialized_status_list, + expiration_seconds, kAllowUnknownDevices) + .ToString()); + // Invalid signature. + ++(*status_list.mutable_signature())[20]; + ASSERT_TRUE(status_list.SerializeToString(&serialized_status_list)); + EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, + SessionImpl::SetCertificateStatusList( + kCertificateTypeTesting, serialized_status_list, + expiration_seconds, kAllowUnknownDevices) + .error_code()); + // Empty status list. + EXPECT_EQ( + INVALID_CERTIFICATE_STATUS_LIST, + SessionImpl::SetCertificateStatusList( + kCertificateTypeTesting, "", expiration_seconds, kAllowUnknownDevices) + .error_code()); + + // Known status list from using the API: + // https://www.googleapis.com/certificateprovisioning/v1/ + // devicecertificatestatus/list + const std::string hex_signed_list = + "0ab60108ded2d58c0512540a10084d60bc65937db6ac7f992b41ecf5f21000223e089920" + "12115749444556494e455f494e5445524e414c1a06476f6f676c6522105265666572656e" + "6365204465766963652a07416e64726f696430dc0f400112580a10b4e3c32dae3982c168" + "376d3ac51f14e31000224208aa21120f616e64726f69642067656e657269631a1747656e" + "65726963204669656c642050726f766973696f6e22064c6576656c332a0443454e4330dd" + "0f3803400112800319d77451a9d88cf272ca44325eb8db3fb74134f12f86dd84b05b0e27" + "a0dba2b3e390c510a50a37a749cfbcd9765367a54b2544de2bf938d09fe045326ce31358" + "8f69be43b30071bc168916fd16028447265e6c710e53416fbbc6262f39be3db565e30cf3" + "4d1c160943509b06e71a0171d17fcb8732ae3a17b25fa71c8e6448600edde4394ee07727" + "a8d9d21cc7020d787f6c68279b38dfc2b69438548ed367112680b5bae669ccaa9658b701" + "2264402bd5ea279e182f94900aa22132b442ff7a0ec7242c17e1adbe1397795f918da27a" + "25cba6eaf68c057802a6b666807798a681ec0c5c50d08ead11124f462fc2fba0c4084acb" + "c4635fac8bf959e9163f31937bbe1cb9b5b3d13eaab9be34c6045006f4ceaeec322f525c" + "e97b26f0a74438d84578bbeb87ee55568fd3c5c9b1cc2d9d053f99e7a1616ecd80f496da" + "93d2ae8af907b14d692d32086f603420ddd9e3b4498a98726d6d7e5a1b5f2143ff4089e4" + "617423d57b2d1f1f9cb6ddfe056bdc74ea9830142cae45f3b6ba7d59ffe28e3e"; + std::string signed_list = absl::HexStringToBytes(hex_signed_list); + EXPECT_EQ(util::OkStatus(), SessionImpl::SetCertificateStatusList( + kCertificateTypeProduction, signed_list, + expiration_seconds, kAllowUnknownDevices)); +} + +void VerifyErrorLicense(const std::string& error_license, + LicenseError::Error error) { + SignedMessage signed_message; + ASSERT_TRUE(signed_message.ParseFromString(error_license)); + ASSERT_TRUE(signed_message.type() == SignedMessage::ERROR_RESPONSE); + EXPECT_FALSE(signed_message.has_signature()); + ASSERT_TRUE(signed_message.has_msg()); + LicenseError license_error; + ASSERT_TRUE(license_error.ParseFromString(signed_message.msg())); + EXPECT_EQ(error, license_error.error_code()); +} + +TEST_F(SessionTest, ErrorLicense) { + std::string error_license; + EXPECT_FALSE( + SessionImpl::GenerateErrorResponse(util::OkStatus(), &error_license)); + EXPECT_FALSE(SessionImpl::GenerateErrorResponse( + util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, ""), + &error_license)); + EXPECT_FALSE(SessionImpl::GenerateErrorResponse( + util::Status(error_space, INVALID_DRM_CERTIFICATE, ""), &error_license)); + VerifyErrorLicense(error_license, + LicenseError::INVALID_DRM_DEVICE_CERTIFICATE); + EXPECT_FALSE(SessionImpl::GenerateErrorResponse( + util::Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED, ""), + &error_license)); + VerifyErrorLicense(error_license, + LicenseError::REVOKED_DRM_DEVICE_CERTIFICATE); + EXPECT_FALSE(SessionImpl::GenerateErrorResponse( + util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST, ""), + &error_license)); + VerifyErrorLicense(error_license, LicenseError::SERVICE_UNAVAILABLE); + EXPECT_FALSE(SessionImpl::GenerateErrorResponse( + util::Status(util::error::UNAVAILABLE, ""), &error_license)); + VerifyErrorLicense(error_license, LicenseError::SERVICE_UNAVAILABLE); +} + +TEST_F(SessionTest, LogVersion) { + LOG(INFO) << "Version std::string: " << SessionImpl::GetSdkVersionString(); +} + +TEST_F(SessionTest, EncryptedClientIdentification) { + std::string serial_number("service_cert_sn"); + std::string provider_id("myservice.com"); + + SetCertificateStatusList(false); + + LicenseRequest request; + const uint32_t license_counter = 20; + ClientIdentification client_id; + client_id.set_license_counter(license_counter); + client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE); + client_id.set_token( + GenerateSignedDrmCertificate(kValidSystemId, kValidSerialNumber, false)); + EncryptClientIdentification(client_id, serial_number, provider_id, + request.mutable_encrypted_client_id()); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_header( + "0123456789ABCDEF"); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_license_type( + STREAMING); + request.mutable_content_id()->mutable_webm_id_deprecated()->set_request_id( + "request id"); + request.set_type(LicenseRequest::NEW); + request.set_request_time(54322); + request.set_protocol_version(VERSION_2_1); + *request.mutable_client_id() = client_id; + SignedMessage signed_request_message; + signed_request_message.set_type(SignedMessage::LICENSE_REQUEST); + ASSERT_TRUE(request.SerializeToString(signed_request_message.mutable_msg())); + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + rsa_private_key->GenerateSignature( + signed_request_message.msg(), signed_request_message.mutable_signature()); + std::string request_msg_bytes; + ASSERT_TRUE(signed_request_message.SerializeToString(&request_msg_bytes)); + SessionImpl* session_ptr(nullptr); + // License request cannot contain both client_id and encrypted_client_id; + EXPECT_EQ(MULTIPLE_CLIENT_ID, + SessionImpl::Create(request_msg_bytes, &session_ptr).error_code()); + request.clear_client_id(); + ASSERT_TRUE(request.SerializeToString(signed_request_message.mutable_msg())); + rsa_private_key->GenerateSignature( + signed_request_message.msg(), signed_request_message.mutable_signature()); + ASSERT_TRUE(signed_request_message.SerializeToString(&request_msg_bytes)); + AddDrmServiceCertificate(serial_number, provider_id, 123); + EXPECT_EQ(util::OkStatus(), + SessionImpl::Create(request_msg_bytes, &session_ptr)); + std::unique_ptr session(session_ptr); + EXPECT_EQ(license_counter, session->request().client_id().license_counter()); + + LicenseRequest parsed_request; + SessionImpl* session_ptr_with_client_id(nullptr); + const uint32_t updated_license_counter = license_counter + 10; + client_id.set_license_counter(updated_license_counter); + PlatformVerificationStatus platform_verification_status = + PLATFORM_NO_VERIFICATION; + EXPECT_EQ(util::OkStatus(), + SessionImpl::CreateForProxy( + request_msg_bytes, platform_verification_status, &client_id, + &session_ptr_with_client_id, &parsed_request)); + std::unique_ptr session_with_client_id( + session_ptr_with_client_id); + // Verify the license is using the client_id specified when creating a session + // using CreateForProxy() instead of what is in the request. + EXPECT_EQ(updated_license_counter, + session_with_client_id->request().client_id().license_counter()); + // SessionImpl::VerifyPlatform() was called because platform verification + // status was passed into Session::CreateForProxy(...) with the default + // value of PLATFORM_NO_VERIFICATION. + EXPECT_EQ(PLATFORM_UNVERIFIED, + session_with_client_id->GetPlatformVerificationStatus()); +} + +TEST_F(SessionTest, DrmServiceCertificateRequest) { + std::string serial_number("service_cert_sn"); + std::string provider_id("myservice.com"); + AddDrmServiceCertificate(serial_number, provider_id, 123); + + SignedMessage signed_request_message; + signed_request_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST); + std::string request_msg_bytes; + ASSERT_TRUE(signed_request_message.SerializeToString(&request_msg_bytes)); + SessionImpl* session_ptr(nullptr); + util::Status status = SessionImpl::Create(request_msg_bytes, &session_ptr); + ASSERT_EQ(SERVICE_CERTIFICATE_REQUEST_MESSAGE, status.error_code()); + std::string error_license; + ASSERT_TRUE(SessionImpl::GenerateErrorResponse(status, &error_license)); + SignedMessage signed_response; + ASSERT_TRUE(signed_response.ParseFromString(error_license)); + EXPECT_EQ(SignedMessage::SERVICE_CERTIFICATE, signed_response.type()); + SignedDrmCertificate signed_cert; + ASSERT_TRUE(signed_cert.ParseFromString(signed_response.msg())); + DrmCertificate drm_cert; + ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate())); + EXPECT_EQ(DrmCertificate::SERVICE, drm_cert.type()); + EXPECT_EQ(serial_number, drm_cert.serial_number()); + EXPECT_EQ(provider_id, drm_cert.provider_id()); +} + +TEST_F(SessionTest, InvalidMessageType) { + SignedMessage signed_request_message; + signed_request_message.set_type(SignedMessage::LICENSE); + std::string request_msg_bytes; + ASSERT_TRUE(signed_request_message.SerializeToString(&request_msg_bytes)); + SessionImpl* session_ptr(nullptr); + EXPECT_EQ(INVALID_MESSAGE_TYPE, + SessionImpl::Create(request_msg_bytes, &session_ptr).error_code()); +} + +TEST_F(SessionTest, RemoteAttestationSuccess) { RemoteAttestationSuccess(); } + +TEST_F(SessionTest, RemoteAttestationFailureDeveloperMode) { + RemoteAttestationVerifier::get().EnableTestCertificates(true); + std::string service_cert_sn("service_cert_sn"); + AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_1, false, STREAMING, kUseCurrentTime, client_capabilities, + kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); + SignedMessage signed_message; + ASSERT_TRUE(signed_message.ParseFromString(request_msg)); + AddRemoteAttestation(&signed_message, kDeveloperRaCertChain, + kDeviceRaPrivateKey, service_cert_sn); + std::string updated_request; + ASSERT_TRUE(signed_message.SerializeToString(&updated_request)); + SessionImpl* session_ptr(nullptr); + ASSERT_OK(SessionImpl::Create(updated_request, &session_ptr)); + std::unique_ptr session(session_ptr); + EXPECT_EQ(PLATFORM_TAMPERED, session_ptr->GetPlatformVerificationStatus()); +} + +TEST_F(SessionTest, RemoteAttestationFailureCertChainValidation) { + RemoteAttestationVerifier::get().EnableTestCertificates(true); + std::string service_cert_sn("service_cert_sn"); + AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_1, false, STREAMING, kUseCurrentTime, client_capabilities, + kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); + SignedMessage signed_message; + ASSERT_TRUE(signed_message.ParseFromString(request_msg)); + std::string truncated_chain(kVerifiedRaCertChain); + size_t intermediate_cert_pos = + truncated_chain.rfind("-----BEGIN CERTIFICATE-----"); + ASSERT_NE(std::string::npos, intermediate_cert_pos); + truncated_chain.resize(intermediate_cert_pos); + AddRemoteAttestation(&signed_message, truncated_chain, kDeviceRaPrivateKey, + service_cert_sn); + std::string updated_request; + ASSERT_TRUE(signed_message.SerializeToString(&updated_request)); + SessionImpl* session_ptr(nullptr); + ASSERT_OK(SessionImpl::Create(updated_request, &session_ptr)); + std::unique_ptr session(session_ptr); + EXPECT_EQ(PLATFORM_TAMPERED, session_ptr->GetPlatformVerificationStatus()); +} + +TEST_F(SessionTest, RemoteAttestationFailureSignature) { + RemoteAttestationVerifier::get().EnableTestCertificates(true); + std::string service_cert_sn("service_cert_sn"); + AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_1, false, STREAMING, kUseCurrentTime, client_capabilities, + kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); + SignedMessage good_signed_message; + ASSERT_TRUE(good_signed_message.ParseFromString(request_msg)); + AddRemoteAttestation(&good_signed_message, kVerifiedRaCertChain, + kDeviceRaPrivateKey, service_cert_sn); + + // Control. + std::string updated_request; + ASSERT_TRUE(good_signed_message.SerializeToString(&updated_request)); + SessionImpl* session_ptr(nullptr); + EXPECT_OK(SessionImpl::Create(updated_request, &session_ptr)); + std::unique_ptr session(session_ptr); + + // Invalid signature. + SignedMessage bad_signed_message; + bad_signed_message = good_signed_message; + ++(*bad_signed_message.mutable_remote_attestation()->mutable_signature())[2]; + updated_request.clear(); + ASSERT_TRUE(bad_signed_message.SerializeToString(&updated_request)); + session_ptr = nullptr; + ASSERT_OK(SessionImpl::Create(updated_request, &session_ptr)); + session.reset(session_ptr); + EXPECT_EQ(PLATFORM_TAMPERED, session_ptr->GetPlatformVerificationStatus()); + + // Invalid salt. + bad_signed_message = good_signed_message; + ++(*bad_signed_message.mutable_remote_attestation()->mutable_salt())[0]; + updated_request.clear(); + ASSERT_TRUE(bad_signed_message.SerializeToString(&updated_request)); + session_ptr = nullptr; + ASSERT_OK(SessionImpl::Create(updated_request, &session_ptr)); + session.reset(session_ptr); + EXPECT_EQ(PLATFORM_TAMPERED, session_ptr->GetPlatformVerificationStatus()); +} + +TEST_F(SessionTest, ProviderClientTokenRules_ClientSupportsClientToken) { + SessionInit session_init; + ClientIdentification client_id; + + client_id.mutable_client_capabilities()->set_client_token(true); + // Default case where nothing is specified. + EXPECT_EQ("", TestGetProviderClientToken(session_init, client_id)); + + // Client specifies a client token. + client_id.set_provider_client_token("11111111"); + EXPECT_EQ("", TestGetProviderClientToken(session_init, client_id)); + + // Both client and provider specifies a client token, client wins. + session_init.set_provider_client_token("22222222"); + EXPECT_EQ(client_id.provider_client_token(), + TestGetProviderClientToken(session_init, client_id)); + + // Both client and provider specifies a client token but override is set, + // provider wins. + session_init.set_override_provider_client_token(true); + EXPECT_EQ(session_init.provider_client_token(), + TestGetProviderClientToken(session_init, client_id)); + + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + bool client_token_in_request = true; + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, client_token_in_request, + LicenseRequest::ContentIdentification::InitData::WEBM)); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::WEBM); +} + +TEST_F(SessionTest, ProviderClientTokenRules_ClientDoesNotSupportClientToken) { + SessionInit session_init; + ClientIdentification client_id; + + client_id.mutable_client_capabilities()->set_client_token(false); + // Default case where nothing is specified. + EXPECT_EQ("", TestGetProviderClientToken(session_init, client_id)); + + // Client specifies a client token. + client_id.set_provider_client_token("11111111"); + EXPECT_EQ("", TestGetProviderClientToken(session_init, client_id)); + + // Both client and provider specifies a client token. + session_init.set_provider_client_token("22222222"); + EXPECT_EQ("", TestGetProviderClientToken(session_init, client_id)); + + // Both client and provider specifies a client token but override is set. + session_init.set_override_provider_client_token(true); + EXPECT_EQ("", TestGetProviderClientToken(session_init, client_id)); +} + +// TODO(user): Add test to return the license type for all cases. +TEST_F(SessionTest, GetLicenseType) {} + +TEST_F(SessionTest, ShouldSetProviderSessionToken) { + uint32_t oem_crypto_version = 0; + RunShouldSetProviderSessionTokenTests(oem_crypto_version); + oem_crypto_version = 9; + RunShouldSetProviderSessionTokenTests(oem_crypto_version); + oem_crypto_version = 10; + RunShouldSetProviderSessionTokenTests(oem_crypto_version); +} + +TEST_F(SessionTest, BasicRequestResponse_ProtectionSchemeInLicense) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + bool client_token_in_request = false; + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, client_token_in_request, + LicenseRequest::ContentIdentification::InitData::CENC)); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::CENC); +} + +TEST_F(SessionTest, DISABLED_VerifyDeviceHdcpCapabilities) { + ClientIdentification::ClientCapabilities device_capabilities; + License::KeyContainer::OutputProtection required; + + // Device capabilities was not specified by the client. + device_capabilities.clear_max_hdcp_version(); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_NONE); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V1); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2_1); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2_2); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp( + License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + + // Device capabilities are too low for any HDCP. Only fail if the required + // protection is HDCP_NO_DIGITAL_OUTPUT. + device_capabilities.set_max_hdcp_version( + ClientIdentification::ClientCapabilities::HDCP_NONE); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_NONE); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V1); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp( + License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT); + EXPECT_EQ( + DEVICE_CAPABILITIES_TOO_LOW, + VerifyDeviceCapabilities(device_capabilities, required).error_code()); + + // Device does not have any digital output ports. In this case, the CDM + // cannot enforce this situation. For now, allow all HDCP requests, the KCB + // will enforce the HDCP settings. + device_capabilities.set_max_hdcp_version( + ClientIdentification::ClientCapabilities::HDCP_NO_DIGITAL_OUTPUT); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_NONE); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V1); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2_1); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2_2); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); + required.set_hdcp( + License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT); + EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required)); +} + +TEST_F(SessionTest, ProviderSessionTokenSize) { + std::string pst_src; + for (int i = 0; i < kProviderSessionTokenSizeBytes; i++) { + pst_src.append("a"); + } + EXPECT_OK(CheckProviderSessionToken(pst_src)); + pst_src.append("a"); + EXPECT_EQ(INVALID_PROVIDER_SESSION_TOKEN_SIZE, + CheckProviderSessionToken(pst_src).error_code()); +} + +// TODO(user): Add test for internal session usage report to session usage. +TEST_F(SessionTest, DISABLED_SessionUsageFromInternalSessionUsageReport) {} + +class SessionVmpTest + : public SessionTest, + public ::testing::WithParamInterface {}; + +TEST_P(SessionVmpTest, VmpVerifiedAndTampered) { + const PlatformVerificationStatus kPlatformVerificationStatus(GetParam()); + + SetCertificateStatusList(kDontAllowUnknownDevices); + std::string request_msg; + if (kPlatformVerificationStatus == PLATFORM_UNVERIFIED) { + request_msg = GenerateBasicLicenseRequest( + VERSION_2_1, kDontUseDeprecatedKcbNonce, STREAMING, kUseCurrentTime, + ClientIdentification::ClientCapabilities(), kDontIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM); + } else { + request_msg = GenerateLicenseRequestWithVmp(kPlatformVerificationStatus == + PLATFORM_TAMPERED); + } + SessionImpl* session_ptr(nullptr); + if (kPlatformVerificationStatus != PLATFORM_TAMPERED) { + ASSERT_EQ(util::OkStatus(), + SessionImpl::Create(request_msg, &session_ptr)); + std::unique_ptr session(session_ptr); + if (kPlatformVerificationStatus != PLATFORM_NO_VERIFICATION) { + ASSERT_EQ(kPlatformVerificationStatus, + session->GetPlatformVerificationStatus()); + } + License::Policy policies; + policies.set_can_play(true); + License::KeyContainer key; + key.set_id("key id"); + key.set_key("0123456789abcdef"); + key.set_type(License::KeyContainer::CONTENT); + std::list keys; + keys.push_front(key); + SessionInit init; + init.set_purchase_id("Purchases!"); + init.set_master_signing_key("0123456789abcdef"); + SessionState cache; + std::string signed_license_bytes; + ASSERT_EQ(util::OkStatus(), + session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes)); + SignedMessage signed_message; + ASSERT_TRUE(signed_message.ParseFromString(signed_license_bytes)); + License license; + ASSERT_TRUE(license.ParseFromString(signed_message.msg())); + EXPECT_FALSE(license.remote_attestation_verified()); + if (kPlatformVerificationStatus != PLATFORM_NO_VERIFICATION) { + EXPECT_EQ(kPlatformVerificationStatus, + license.platform_verification_status()); + } + } else { + // Platform verification status is tampered. + util::Status status = SessionImpl::Create(request_msg, &session_ptr); + ASSERT_EQ(util::error::INTERNAL, status.error_code()); + } +} + +TEST_F(SessionTest, SessionFailureWithInvalidRemoteAttestation) { + RemoteAttestationVerifier::get().EnableTestCertificates(true); + std::string service_cert_sn("service_cert_sn"); + AddDrmServiceCertificate(service_cert_sn, kRaServiceId, 123456); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_anti_rollback_usage_table(false); + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_1, false, STREAMING, kUseCurrentTime, client_capabilities, + kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); + SignedMessage signed_message; + ASSERT_TRUE(signed_message.ParseFromString(request_msg)); + const std::string ra_cert_chain = "unverified-remote-attestation"; + AddRemoteAttestation(&signed_message, ra_cert_chain, kDeviceRaPrivateKey, + service_cert_sn); + std::string updated_request; + ASSERT_TRUE(signed_message.SerializeToString(&updated_request)); + SessionImpl* session_ptr(nullptr); + EXPECT_EQ(INVALID_MESSAGE, + SessionImpl::Create(updated_request, &session_ptr).error_code()); +} + +TEST_F(SessionTest, AnalogOutputAllowed_DeviceWithoutAnalogOutput) { + License::KeyContainer::OutputProtection output_protection; + output_protection.set_disable_analog_output(false); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_analog_output_capabilities( + ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_UNKNOWN); + EXPECT_OK(VerifyDeviceCapabilities(client_capabilities, output_protection)); +} + +TEST_F(SessionTest, AnalogOutputNotAllowed_DeviceWithoutAnalogOutput) { + License::KeyContainer::OutputProtection output_protection; + output_protection.set_disable_analog_output(true); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_analog_output_capabilities( + ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_NONE); + EXPECT_OK(VerifyDeviceCapabilities(client_capabilities, output_protection)); +} + +TEST_F(SessionTest, AnalogOutputNotAllowed_DeviceWithAnalogOutput) { + License::KeyContainer::OutputProtection output_protection; + output_protection.set_disable_analog_output(true); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_analog_output_capabilities( + ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_SUPPORTED); + client_capabilities.set_can_disable_analog_output(false); + EXPECT_EQ(util::error::PERMISSION_DENIED, + VerifyDeviceCapabilities(client_capabilities, output_protection) + .error_code()); + // Client has the ability to disable it's analog output. + client_capabilities.set_can_disable_analog_output(true); + EXPECT_OK(VerifyDeviceCapabilities(client_capabilities, output_protection)); +} + +TEST_F(SessionTest, CGMSRequired) { + License::KeyContainer::OutputProtection output_protection; + output_protection.set_disable_analog_output(false); + output_protection.set_cgms_flags( + License::KeyContainer::OutputProtection::COPY_NEVER); + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_analog_output_capabilities( + ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_SUPPORTED); + EXPECT_EQ(util::error::PERMISSION_DENIED, + VerifyDeviceCapabilities(client_capabilities, output_protection) + .error_code()); + client_capabilities.set_analog_output_capabilities( + ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A); + EXPECT_OK(VerifyDeviceCapabilities(client_capabilities, output_protection)); +} + +TEST_F(SessionTest, BasicRequestResponse_OEMVersion14) { + ClientIdentification::ClientCapabilities client_capabilities; + client_capabilities.set_client_token(true); + client_capabilities.set_session_token(true); + client_capabilities.set_oem_crypto_api_version(14); + bool client_token_in_request = false; + std::string request_msg(GenerateBasicLicenseRequest( + VERSION_2_0, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, client_token_in_request, + LicenseRequest::ContentIdentification::InitData::WEBM)); + ValidateBasicRequestResponse( + request_msg, client_capabilities, + LicenseRequest::ContentIdentification::InitData::WEBM); +} + + +INSTANTIATE_TEST_CASE_P(SessionVmpTests, SessionVmpTest, + ::testing::Values(PLATFORM_NO_VERIFICATION, + PLATFORM_UNVERIFIED, + PLATFORM_TAMPERED, + PLATFORM_SOFTWARE_VERIFIED)); + +} // namespace widevine diff --git a/license_server_sdk/internal/session_usage_report.h b/license_server_sdk/internal/session_usage_report.h new file mode 100644 index 0000000..4663289 --- /dev/null +++ b/license_server_sdk/internal/session_usage_report.h @@ -0,0 +1,53 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef LICENSE_SERVER_SDK_INTERNAL_SESSION_USAGE_REPORT_H_ +#define LICENSE_SERVER_SDK_INTERNAL_SESSION_USAGE_REPORT_H_ + +#include + +namespace widevine { + +const uint32_t kMaxProviderSessionTokenSizeBytes = 128; + +enum SessionUsageStatus { + SessionUsageStatus_Unused = 0, + // License still active on the device, has not been released yet. + SessionUsageStatus_Active = 1, + // Deprecated in OEMCrypto V13, replaced with Inactive_Used and + // Inactive_Unused. All Inactive status values indicate the license was + // relased. + SessionUsageStatus_Inactive_Deprecated = 2, + // Keys released after use. + SessionUsageStatus_Inactive_Used = 3, + // Keys released before use. + SessionUsageStatus_Inactive_Unused = 4, +}; + +// Data sent in the license release request from the client to indicate the +// license usage. +struct InternalSessionUsageReport { + // HMAC SHA1 of the rest of the report. + uint8_t signature[20]; + // Current status of status report: 0=unused, 1=active, 2=inactive. + uint8_t status; + // The clock security level are: 0=insecure clock, 1=secure timer, + // 2=secure clock. + uint8_t clock_security_level; + uint8_t pst_length; + // Make int64_t's word aligned. + uint8_t padding; + int64_t seconds_since_license_received; + int64_t seconds_since_first_decrypt; + int64_t seconds_since_last_decrypt; + uint8_t pst[kMaxProviderSessionTokenSizeBytes]; +} ABSL_ATTRIBUTE_PACKED; + +} // namespace widevine + +#endif // LICENSE_SERVER_SDK_INTERNAL_SESSION_USAGE_REPORT_H_ diff --git a/media_cas_proxy_sdk/external/common/wvpl/BUILD b/media_cas_proxy_sdk/external/common/wvpl/BUILD new file mode 100644 index 0000000..c270b08 --- /dev/null +++ b/media_cas_proxy_sdk/external/common/wvpl/BUILD @@ -0,0 +1,75 @@ +################################################################################ +# 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. +################################################################################ + +# The public interface of Widevine CAS Proxy SDK for content providers. + +package( + default_visibility = ["//visibility:public"], +) + +filegroup( + name = "binary_release_files", + srcs = glob(["*.h"]), +) + +cc_binary( + name = "libwvpl_cas_proxy.so", + linkshared = 1, + deps = [ + ":wvpl_cas_proxy_environment", + ":wvpl_cas_proxy_session", + ], +) + +cc_library( + name = "wvpl_cas_proxy_session", + srcs = [ + "wvpl_cas_proxy_session.cc", + ], + hdrs = [ + "wvpl_cas_proxy_session.h", + ], + deps = [ + "//base", + "@abseil_repo//absl/strings", + "//common:error_space", + "//sdk/external/common/wvpl:wvpl_sdk_environment", + "//sdk/external/common/wvpl:wvpl_sdk_session", + ], +) + +cc_library( + name = "wvpl_cas_proxy_environment", + srcs = [ + "wvpl_cas_proxy_environment.cc", + ], + hdrs = [ + "wvpl_cas_proxy_environment.h", + "wvpl_cas_proxy_session.h", + ], + deps = [ + "//base", + "@abseil_repo//absl/strings", + "//common:certificate_type", + "//common:error_space", + "//sdk/external/common/wvpl:wvpl_sdk_environment", + "//sdk/external/common/wvpl:wvpl_sdk_session", + ], +) + +cc_test( + name = "wvpl_cas_proxy_session_test", + srcs = ["wvpl_cas_proxy_session_test.cc"], + deps = [ + ":wvpl_cas_proxy_session", + "//testing:gunit", + "//testing:gunit_main", + "@abseil_repo//absl/memory", + "//sdk/external/common/wvpl:wvpl_types", + ], +) diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc new file mode 100644 index 0000000..dc45ed8 --- /dev/null +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.h" + +#include "absl/strings/escaping.h" +#include "common/certificate_type.h" +#include "common/error_space.h" +#include "media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h" + +namespace util = widevine::util; +using widevine::error_space; +using widevine::kCertificateTypeDevelopment; +using widevine::kCertificateTypeTesting; + +namespace widevine_server { +namespace wv_pl_sdk { + +WvPLCASProxyEnvironment::WvPLCASProxyEnvironment( + const std::map& config_values) { + std::map::const_iterator it = + config_values.find(kDrmCertificateType); + if (it != config_values.end()) { + if (it->second == "dev" || it->second == "prod" || it->second == "test") { + drm_certificate_type_ = it->second; + } + if (drm_certificate_type_ == "dev") { + certificate_type_ = kCertificateTypeDevelopment; + } else if (drm_certificate_type_ == "test") { + certificate_type_ = kCertificateTypeTesting; + } + } + it = config_values.find(kProvider); + if (it != config_values.end()) { + provider_ = (*it).second; + } + if (it != config_values.end()) { + provider_iv_ = new std::string(absl::HexStringToBytes((*it).second)); + } + it = config_values.find(kProviderKey); + if (it != config_values.end()) { + provider_key_ = new std::string(absl::HexStringToBytes((*it).second)); + } +} + +WvPLStatus WvPLCASProxyEnvironment::CreateSession( + const std::string& cas_license_request, + WvPLCASProxySession** cas_proxy_session) { + WvPLStatus status = util::OkStatus(); + if (cas_proxy_session == nullptr) { + return WvPLStatus(error_space, util::error::INTERNAL, + "proxy_session is NULL"); + } + if (*cas_proxy_session != nullptr) { + return WvPLStatus(error_space, util::error::INTERNAL, + "*proxy_session is not NULL"); + } + std::unique_ptr wvpl_cas_proxy_session( + new WvPLCASProxySession(cas_license_request)); + // TODO(user): Complete the license request parsing in WvPLCASProxySession. + // status = wvpl_cas_proxy_session->ParseLicenseRequest(); + if (status.ok()) { + *cas_proxy_session = wvpl_cas_proxy_session.release(); + } + return status; +} + +WvPLCASProxyEnvironment::~WvPLCASProxyEnvironment() {} + +} // namespace wv_pl_sdk +} // namespace widevine_server diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.h b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.h new file mode 100644 index 0000000..6bafa56 --- /dev/null +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.h @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_PROXY_SDK_EXTERNAL_COMMON_WVPL_WVPL_CAS_PROXY_ENVIRONMENT_H_ +#define MEDIA_CAS_PROXY_SDK_EXTERNAL_COMMON_WVPL_WVPL_CAS_PROXY_ENVIRONMENT_H_ + +#include +#include "sdk/external/common/wvpl/wvpl_sdk_environment.h" + +namespace widevine_server { +namespace wv_pl_sdk { + +class WvPLCASProxySession; + +class WvPLCASProxyEnvironment : public WvPLSDKEnvironment { + public: + /** + * Constructor. + * + * @param config_values + */ + explicit WvPLCASProxyEnvironment( + const std::map& config_values); + + /** + * Destructor. + */ + ~WvPLCASProxyEnvironment() override; + + /** + * Creates a session for the license request from the Widevine CDM. + * + * @param license_request_from_cdm + * @param session + * + * @return WvPLStatus enumeration + */ + virtual WvPLStatus CreateSession(const std::string& cas_license_request, + WvPLCASProxySession** cas_proxy_session); +}; + +} // namespace wv_pl_sdk +} // namespace widevine_server + +#endif // MEDIA_CAS_PROXY_SDK_EXTERNAL_COMMON_WVPL_WVPL_CAS_PROXY_ENVIRONMENT_H_ diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.cc b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.cc new file mode 100644 index 0000000..d18a000 --- /dev/null +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.cc @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h" +#include + +namespace util = widevine::util; + +namespace widevine_server { +namespace wv_pl_sdk { + +std::string WvPLCASProxySession::GetVersionString() { + return std::to_string(kMajorVersion) + "." + std::to_string(kMinorVersion) + + "." + std::to_string(kRelease); +} + +bool WvPLCASProxySession::IsChromeCDM() const { return false; } + +PlatformVerificationStatus WvPLCASProxySession::VerifyPlatform() { + return PLATFORM_NO_VERIFICATION; +} + +WvPLStatus WvPLCASProxySession::ParsePsshData( + WvPLWidevinePsshData* wvpl_widevine_pssh_data) const { + // TODO(user): Implement this functionality in WvPLSdk and remove the + // implementation in the classes derived from WvPLSdk. + return util::OkStatus(); +} + +} // namespace wv_pl_sdk +} // namespace widevine_server diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h new file mode 100644 index 0000000..7a61e20 --- /dev/null +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h @@ -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. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef MEDIA_CAS_PROXY_SDK_EXTERNAL_COMMON_WVPL_WVPL_CAS_PROXY_SESSION_H_ +#define MEDIA_CAS_PROXY_SDK_EXTERNAL_COMMON_WVPL_WVPL_CAS_PROXY_SESSION_H_ + +#include +#include "sdk/external/common/wvpl/wvpl_sdk_session.h" + +namespace widevine_server { +namespace wv_pl_sdk { + +constexpr uint32_t kMajorVersion = 1; +constexpr uint32_t kMinorVersion = 0; +constexpr uint32_t kRelease = 1; + +/** + * Models a session for Widevine CAS Proxy functionality for + * communication with the Widevine License Service. The API allows a partner to + * inspect a license request and to generate a license request. + * + * Inherits the following "public" methods. + * - WvPLStatus AddKey(const WvPLKey& key); + * - const std::vector& key() const; + * - void set_policy(const WvPLPlaybackPolicy& policy); + * - const WvPLPlaybackPolicy& policy() const; + * - void set_session_init(const WvPLSessionInit& session_init); + * - const WvPLSessionInit& session_init(); + * - WvPLStatus GetPsshData(WvPLWidevinePsshData* wvpl_widevine_pssh_data) + * const; + * - WvPLStatus GetClientInfo(WvPLClientInfo* client_info) const; + * - WvPLStatus GetClientCapabilities(WvPLClientCapabilities* + * client_capabilities) const; + * - WvPLStatus GetDeviceInfo(WvPLDeviceInfo* device_info) const; + * - MessageType message_type() const; + * + * "Implements" the following "public" methods. + * - bool IsChromeCDM() const; + * - PlatformVerificationStatus VerifyPlatform(); + */ +class WvPLCASProxySession : public WvPLSDKSession { + public: + ~WvPLCASProxySession() override {} + + /** + * Returns a std::string containing the version in the form - + * .. + */ + static std::string GetVersionString(); + + /** + * Returns whether the CAS license request originated from Widevine Chrome + * CDM. Since MediaCAS is part of the Android framework, this method would + * return "false" for requests from AndroidTV. + */ + bool IsChromeCDM() const override; + + /** + * Returns the enum PlatformVerificationStatus for the CAS license request + * from the Widevine CDM. Since CAS license requests do not require client + * platform verification currently, this method would return + * PLATFORM_NO_VERIFICATION. + */ + PlatformVerificationStatus VerifyPlatform() override; + + private: + friend class WvPLCASProxyEnvironment; + friend class WvPLCASProxyEnvironmentTest; + friend class WvPLCASProxySessionTest; + + explicit WvPLCASProxySession(const std::string& cas_license_request_from_cdm) {} + + WvPLStatus ParsePsshData( + WvPLWidevinePsshData* wvpl_widevine_pssh_data) const override; +}; + +} // namespace wv_pl_sdk +} // namespace widevine_server + +#endif // MEDIA_CAS_PROXY_SDK_EXTERNAL_COMMON_WVPL_WVPL_CAS_PROXY_SESSION_H_ diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc new file mode 100644 index 0000000..5f5ed2b --- /dev/null +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h" +#include +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/memory/memory.h" +#include "sdk/external/common/wvpl/wvpl_types.h" + +namespace widevine_server { +namespace wv_pl_sdk { + +// A test fixture is used because WvPLCASProxySession protected/private +// methods may be tested here. +class WvPLCASProxySessionTest : public testing::Test { + protected: + void SetUp() override { + // TODO(user): Fill out with valid CAS License request. + const std::string request = ""; + wvpl_cas_proxy_session_ = new WvPLCASProxySession(request); + } + + bool IsChromeCDM() { return wvpl_cas_proxy_session_->IsChromeCDM(); } + + PlatformVerificationStatus VerifyPlatform() { + return wvpl_cas_proxy_session_->VerifyPlatform(); + } + + ~WvPLCASProxySessionTest() override { delete wvpl_cas_proxy_session_; } + + private: + WvPLCASProxySession* wvpl_cas_proxy_session_; +}; + +TEST_F(WvPLCASProxySessionTest, GetVersionString) { + std::string version = std::to_string(kMajorVersion) + "." + + std::to_string(kMinorVersion) + "." + + std::to_string(kRelease); + EXPECT_EQ(version, WvPLCASProxySession::GetVersionString()); +} + +TEST_F(WvPLCASProxySessionTest, IsChromeCDM) { EXPECT_FALSE(IsChromeCDM()); } + +TEST_F(WvPLCASProxySessionTest, VerifyPlatform) { + EXPECT_EQ(PLATFORM_NO_VERIFICATION, VerifyPlatform()); +} + +} // namespace wv_pl_sdk +} // namespace widevine_server diff --git a/protos/public/BUILD b/protos/public/BUILD new file mode 100644 index 0000000..2e4cd6f --- /dev/null +++ b/protos/public/BUILD @@ -0,0 +1,243 @@ +################################################################################ +# 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. +################################################################################ + +# Protocol buffer definitions for Widevine Services Proxy SDK. + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "binary_release_files", + srcs = glob(["**"]), +) + +# TODO(user): Remove unnecessary proto targets in this file +# once cl/216707967 is submitted. +proto_library( + name = "exported_proxy_sdk_proto", + srcs = [ + "client_identification.proto", + "device_certificate_status.proto", + "drm_certificate.proto", + "errors.proto", + "sdk_stats.proto", + "license_protocol.proto", + "license_server_sdk.proto", + "provisioned_device_info.proto", + "remote_attestation.proto", + "signed_drm_certificate.proto", + "verified_media_pipeline.proto", + "widevine_pssh.proto", + "license_services.proto", + ], +) + +java_proto_library( + name = "exported_proxy_sdk_java_proto", + deps = [":exported_proxy_sdk_proto"], +) + +proto_library( + name = "license_services_proto_base", + srcs = ["license_services.proto"], + deps = [ + ":client_identification_proto_base", + ":license_protocol_proto_base", + ":license_server_sdk_proto_base", + ":errors_proto_base", + + ], +) + +cc_proto_library( + name = "license_services_proto", + deps = [":license_services_proto_base"], +) + +java_proto_library( + name = "license_services_java_proto", + deps = [":license_services_proto_base"], +) + +proto_library( + name = "client_identification_proto_base", + srcs = ["client_identification.proto"], +) + +cc_proto_library( + name = "client_identification_proto", + deps = [":client_identification_proto_base"], +) + +java_proto_library( + name = "client_identification_java_proto", + deps = [":client_identification_proto_base"], +) + +proto_library( + name = "device_certificate_status_proto_base", + srcs = ["device_certificate_status.proto"], + deps = [":provisioned_device_info_proto_base"], +) + +cc_proto_library( + name = "device_certificate_status_proto", + deps = [":device_certificate_status_proto_base"], +) + +java_proto_library( + name = "device_certificate_status_java_proto", + deps = [":device_certificate_status_proto_base"], +) + +proto_library( + name = "sdk_stats_proto_base", + srcs = ["sdk_stats.proto"], +) + +cc_proto_library( + name = "sdk_stats_proto", + deps = [":sdk_stats_proto_base"], +) + +java_proto_library( + name = "sdk_stats_java_proto", + deps = [":sdk_stats_proto_base"], +) + +proto_library( + name = "drm_certificate_proto_base", + srcs = ["drm_certificate.proto"], +) + +cc_proto_library( + name = "drm_certificate_proto", + deps = [":drm_certificate_proto_base"], +) + +java_proto_library( + name = "drm_certificate_java_proto", + deps = [":drm_certificate_proto_base"], +) + +proto_library( + name = "errors_proto_base", + srcs = ["errors.proto"], +) + +cc_proto_library( + name = "errors_proto", + deps = [":errors_proto_base"], +) + +java_proto_library( + name = "errors_java_proto", + deps = [":errors_proto_base"], +) + +proto_library( + name = "license_protocol_proto_base", + srcs = ["license_protocol.proto"], + deps = [ + ":client_identification_proto_base", + ":remote_attestation_proto_base", + ], +) + +cc_proto_library( + name = "license_protocol_proto", + deps = [":license_protocol_proto_base"], +) + +java_proto_library( + name = "license_protocol_java_proto", + deps = [":license_protocol_proto_base"], +) + +proto_library( + name = "license_server_sdk_proto_base", + srcs = ["license_server_sdk.proto"], + deps = [":license_protocol_proto_base", + ":widevine_pssh_proto_base"], +) + +cc_proto_library( + name = "license_server_sdk_proto", + deps = [":license_server_sdk_proto_base"], +) + +java_proto_library( + name = "license_server_sdk_java_proto", + deps = [":license_server_sdk_proto_base"], +) + +proto_library( + name = "provisioned_device_info_proto_base", + srcs = ["provisioned_device_info.proto"], +) + +cc_proto_library( + name = "provisioned_device_info_proto", + deps = [":provisioned_device_info_proto_base"], +) + +java_proto_library( + name = "provisioned_device_info_java_proto", + deps = [":provisioned_device_info_proto_base"], +) + +proto_library( + name = "remote_attestation_proto_base", + srcs = ["remote_attestation.proto"], + deps = [":client_identification_proto_base"], +) + +cc_proto_library( + name = "remote_attestation_proto", + deps = [":remote_attestation_proto_base"], +) + + +java_proto_library( + name = "remote_attestation_java_proto", + deps = [":remote_attestation_proto_base"], +) + +proto_library( + name = "signed_drm_certificate_proto_base", + srcs = ["signed_drm_certificate.proto"], +) + +cc_proto_library( + name = "signed_drm_certificate_proto", + deps = [":signed_drm_certificate_proto_base"], +) + +proto_library( + name = "verified_media_pipeline_proto_base", + srcs = ["verified_media_pipeline.proto"], +) + +cc_proto_library( + name = "verified_media_pipeline_proto", + deps = [":verified_media_pipeline_proto_base"], +) + +proto_library( + name = "widevine_pssh_proto_base", + srcs = ["widevine_pssh.proto"], +) + +cc_proto_library( + name = "widevine_pssh_proto", + deps = [":widevine_pssh_proto_base"], +) + +java_proto_library( + name = "widevine_pssh_java_proto", + deps = [":widevine_pssh_proto_base"], +) diff --git a/protos/public/client_identification.proto b/protos/public/client_identification.proto new file mode 100644 index 0000000..6cb0bda --- /dev/null +++ b/protos/public/client_identification.proto @@ -0,0 +1,112 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// ClientIdentification messages used by provisioning and license protocols. + +syntax = "proto2"; + +package widevine; +option java_package = "com.google.video.widevine.protos"; + +option java_outer_classname = "ClientIdentificationProtos"; + +// ClientIdentification message used to authenticate the client device. +message ClientIdentification { + enum TokenType { + KEYBOX = 0; + DRM_DEVICE_CERTIFICATE = 1; + REMOTE_ATTESTATION_CERTIFICATE = 2; + OEM_DEVICE_CERTIFICATE = 3; + } + + message NameValue { + optional string name = 1; + optional string value = 2; + } + + // Capabilities which not all clients may support. Used for the license + // exchange protocol only. + message ClientCapabilities { + enum HdcpVersion { + HDCP_NONE = 0; + HDCP_V1 = 1; + HDCP_V2 = 2; + HDCP_V2_1 = 3; + HDCP_V2_2 = 4; + HDCP_V2_3 = 5; + HDCP_NO_DIGITAL_OUTPUT = 0xff; + } + + enum CertificateKeyType { + RSA_2048 = 0; + RSA_3072 = 1; + } + + enum AnalogOutputCapabilities { + ANALOG_OUTPUT_UNKNOWN = 0; + ANALOG_OUTPUT_NONE = 1; + ANALOG_OUTPUT_SUPPORTED = 2; + ANALOG_OUTPUT_SUPPORTS_CGMS_A = 3; + } + + optional bool client_token = 1 [default = false]; + optional bool session_token = 2 [default = false]; + optional bool video_resolution_constraints = 3 [default = false]; + optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE]; + optional uint32 oem_crypto_api_version = 5; + // Client has hardware support for protecting the usage table, such as + // storing the generation number in secure memory. For Details, see: + // https://docs.google.com/document/d/1Mm8oB51SYAgry62mEuh_2OEkabikBiS61kN7HsDnh9Y/edit#heading=h.xgjl2srtytjt + optional bool anti_rollback_usage_table = 6 [default = false]; + // The client shall report |srm_version| if available. + optional uint32 srm_version = 7; + // A device may have SRM data, and report a version, but may not be capable + // of updating SRM data. + optional bool can_update_srm = 8 [default = false]; + repeated CertificateKeyType supported_certificate_key_type = 9; + optional AnalogOutputCapabilities analog_output_capabilities = 10 + [default = ANALOG_OUTPUT_UNKNOWN]; + optional bool can_disable_analog_output = 11 [default = false]; + } + + // Type of factory-provisioned device root of trust. Optional. + optional TokenType type = 1 [default = KEYBOX]; + // Factory-provisioned device root of trust. Required. + optional bytes token = 2; + // Optional client information name/value pairs. + repeated NameValue client_info = 3; + // Client token generated by the content provider. Optional. + optional bytes provider_client_token = 4; + // Number of licenses received by the client to which the token above belongs. + // Only present if client_token is specified. + optional uint32 license_counter = 5; + // List of non-baseline client capabilities. + optional ClientCapabilities client_capabilities = 6; + // Serialized VmpData message. Optional. + optional bytes vmp_data = 7; +} + +// EncryptedClientIdentification message used to hold ClientIdentification +// messages encrypted for privacy purposes. +message EncryptedClientIdentification { + // Provider ID for which the ClientIdentifcation is encrypted (owner of + // service certificate). + optional string provider_id = 1; + // Serial number for the service certificate for which ClientIdentification is + // encrypted. + optional bytes service_certificate_serial_number = 2; + // Serialized ClientIdentification message, encrypted with the privacy key using + // AES-128-CBC with PKCS#5 padding. + optional bytes encrypted_client_id = 3; + // Initialization vector needed to decrypt encrypted_client_id. + optional bytes encrypted_client_id_iv = 4; + // AES-128 privacy key, encrypted with the service public key using RSA-OAEP. + optional bytes encrypted_privacy_key = 5; +} diff --git a/protos/public/device_certificate_status.proto b/protos/public/device_certificate_status.proto new file mode 100644 index 0000000..75c9f62 --- /dev/null +++ b/protos/public/device_certificate_status.proto @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// +// Description: +// Device certificate status list object definitions. + +syntax = "proto2"; + +package widevine; + +option java_outer_classname = "DeviceCertificateStatusProtos"; +option java_package = "com.google.video.widevine.protos"; + +import "protos/public/provisioned_device_info.proto"; + +// Contains DRM and OEM certificate status and device information for a +// specific system ID. +// TODO(user): Move this to its own file. +message DeviceCertificateStatus { + enum DeprecatedStatus { + DEPRECATED_VALID = 0; + DEPRECATED_REVOKED = 1; + } + enum Status { + STATUS_UNKNOWN = 0; + STATUS_IN_TESTING = 10; // Pre-release, active device. + STATUS_RELEASED = 20; // Released, active device. + STATUS_TEST_ONLY = 30; // Development-only device. + STATUS_REVOKED = 40; // Revoked device. + } + + // Serial number of the intermediate DrmCertificate to which this + // message refers. Required. + optional bytes drm_serial_number = 1; + // Status of the certificate. Optional & deprecated in favor of |status| + // below. + optional DeprecatedStatus deprecated_status = 2 [default = DEPRECATED_VALID]; + // Device model information about the device to which the intermediate + // certificate(s) correspond. + optional ProvisionedDeviceInfo device_info = 4; + // Serial number of the OEM X.509 intermediate certificate for this type + // of device. Present only if the device is OEM-provisioned. + optional bytes oem_serial_number = 5; + // Status of the device. Optional. + optional Status status = 6 [default = STATUS_UNKNOWN]; +} + +// List of DeviceCertificateStatus. Used to propagate certificate revocation +// status and device information. +message DeviceCertificateStatusList { + // POSIX time, in seconds, when the list was created. Required. + optional uint32 creation_time_seconds = 1; + // DeviceCertificateStatus for each system ID. + repeated DeviceCertificateStatus certificate_status = 2; + // The duration for this device certificate status list in seconds. Within + // this grace period, content provider can set device certificate status list + // in the SDK. The default time is 7 days. + optional uint32 duration_time_seconds = 3; +} + +// Signed CertificateStatusList +message SignedDeviceCertificateStatusList { + // Serialized DeviceCertificateStatusList. Required. + optional bytes certificate_status_list = 1; + // Signature of certificate_status_list. Signed with root certificate private + // key using RSASSA-PSS. Required. + optional bytes signature = 2; +} + +// A signed request sent to Widevine Provisioning Server (keysmith) to retrieve +// 'DeviceCertificateStatusList'. +message SignedDeviceCertificateStatusListRequest { + // Serialized DeviceCertificateStatusListRequest. Required. + optional bytes device_certificate_status_list_request = 1; + // Signature of device_certificate_status_list_request. Signed with root + // certificate private key using RSASSA-PSS. Required. + optional bytes signature = 2; +} + +// A request sent to Widevine Provisioning Server (keysmith) to retrieve +// 'DeviceCertificateStatusList'. +message DeviceCertificateStatusListRequest { + // The version of sdk. Required. + optional string sdk_version = 1; + // POSIX time, in seconds, when this request was created. Required. + optional uint64 sdk_time_seconds = 2; +} + +// Contains response from Widevine Provisioning Server with status and +// DeviceCertificateStatusList information. +message DeviceCertificateStatusListResponse { + enum Status { + UNKNOWN = 0; + OK = 1; + SIGNATURE_FAILED = 2; + NOT_AUTHORIZED = 3; + AUTHORIZATION_EXPIRED = 4; + PROVIDER_ID_MISSING = 5; + INTERNAL_ERROR = 6; + } + // Status returned by the Widevine Provisioning Server. Required. + optional Status status = 1; + // String message returned by the Widevine Provisioning Server. + optional string status_message = 2; + // Serialized SignedDeviceCertificateStatusList. Required. + optional bytes signed_device_certificate_status_list = 3; +} diff --git a/protos/public/drm_certificate.proto b/protos/public/drm_certificate.proto new file mode 100644 index 0000000..a9122a0 --- /dev/null +++ b/protos/public/drm_certificate.proto @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// DRM certificate object definition. + +syntax = "proto2"; + +package widevine; + +option java_outer_classname = "DrmCertificateProtos"; +option java_package = "com.google.video.widevine.protos"; + +// DRM certificate definition for user devices, intermediate, service, and root +// certificates. +message DrmCertificate { + enum Type { + ROOT = 0; // ProtoBestPractices: ignore. + DEVICE_MODEL = 1; + DEVICE = 2; + SERVICE = 3; + PROVISIONER = 4; + } + + // Type of certificate. Required. + optional Type type = 1; + // 128-bit globally unique serial number of certificate. + // Value is 0 for root certificate. Required. + optional bytes serial_number = 2; + // POSIX time, in seconds, when the certificate was created. Required. + optional uint32 creation_time_seconds = 3; + // Device public key. PKCS#1 ASN.1 DER-encoded. Required. + optional bytes public_key = 4; + // Widevine system ID for the device. Required for intermediate and + // user device certificates. + optional uint32 system_id = 5; + // Deprecated field, which used to indicate whether the device was a test + // (non-production) device. The test_device field in ProvisionedDeviceInfo + // below should be observed instead. + optional bool test_device_deprecated = 6 [deprecated = true]; + // Service identifier (web origin) for the provider which owns the + // certificate. Required for service and provisioner certificates. + optional string provider_id = 7; +} diff --git a/protos/public/errors.proto b/protos/public/errors.proto new file mode 100644 index 0000000..ec14f74 --- /dev/null +++ b/protos/public/errors.proto @@ -0,0 +1,239 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Definitions of the common Widevine protocol errors. + +syntax = "proto2"; + +package widevine; +option java_package = "com.google.video.widevine.protos"; + + +enum Errors { + // Attempt to parse the signed message failed. + SIGNED_MESSAGE_PARSE_ERROR = 100; + + // Attempt to parse the license request message failed. + LICENSE_REQUEST_PARSE_ERROR = 101; + + // Attempt to parse the session state message failed. + SESSION_STATE_PARSE_ERROR = 102; + + // The license request does not contain content_id. Since client_id was + // not present, content_id was expected. + MISSING_CONTENT_ID = 103; + + // The license request does not contain license_id. Since client_id was + // not present, license_id was expected. + MISSING_LICENSE_ID = 104; + + // The license request does not contain client_id. Since this is not a + // renewal, client_id was expected. + MISSING_CLIENT_ID = 105; + + // ClientCert construction failed. + INVALID_SIGNATURE = 106; + + // Session Id from the session state does not match session Id specified. + SESSION_ID_MISMATCH = 107; + + // License Id from session state does not match license Id in the renewal + // license request. + RENEWAL_LICENSE_ID_MISMATCH = 108; + + // Signing key is missing from the session state. + MISSING_RENEWAL_SIGNING_KEY = 109; + + // Signature verification failed when using the session's state signing key. + INVALID_RENEWAL_SIGNATURE = 110; + + // System Id from the keybox is not supported. + UNSUPPORTED_SYSTEM_ID = 111; + + // Error trying to encrypt. + ENCRYPT_ERROR = 112; + + // Error trying to decrypt the keybox. + KEYBOX_DECRYPT_ERROR = 113; + + // Client Id type is not expected. + INVALID_CLIENT_CERT_TYPE = 114; + + // Error usung the keybox token. Perhaps the size is less than 72 bytes. + INVALID_KEYBOX_TOKEN = 115; + + // Unable to find a preprovisionnig key based on the system Id. Perhaps the + // device was revoked. + MISSING_PRE_PROV_KEY = 116; + + // Unable to verify the token hash. + TOKEN_HASH_MISMATCH = 117; + + // Unable to create the encryption key for the initial license. + MISSING_ENCRYPTION_KEY = 118; + + // Signing key is missing from the session state. + MISSING_SIGNING_KEY = 119; + + // Serialization failed. + UNABLE_TO_SERIALIZE_SIGNED_MESSAGE = 120; + + // Serialization failed. + UNABLE_TO_SERIALIZE_SESSION_STATE = 121; + + // Client cert is missing. Perhaps an attempt to renew with content keys. + MISSING_CLIENT_CERT = 122; + + // Attempt to use GenerateSignedLicense() for license renewal containing + // content keys. + RENEWAL_WITH_CONTENT_KEYS_NOT_ALLOWED = 123; + + // Invalid Nonce, expected as a 32 bit unsigned int. + INVALID_KEY_CONTROL_NONCE = 124; + + // Invalid renewal signing key size. For protocol version 2_0, size must be 32 + // bytes. For protocol version 2_1, size must be 64 bytes. + INVALID_RENEWAL_SIGNING_KEY_SIZE = 125; + + // Invalid Device Certificate token. Perhaps the intermediate cert was + // replaced or the device cert is corrupt. Will result in re-provisioning. + INVALID_DRM_CERTIFICATE = 126; + + // Device Certificate was revoked. + DRM_DEVICE_CERTIFICATE_REVOKED = 127; + + // Device Certificate not in the certificate status list, and unknown + // devices are not allowed. + DRM_DEVICE_CERTIFICATE_UNKNOWN = 128; + + // Invalid Certificate status list. + INVALID_CERTIFICATE_STATUS_LIST = 129; + + // Expired Certificate status list. + EXPIRED_CERTIFICATE_STATUS_LIST = 130; + + // KeyControl block generation failed. + KEYCONTROL_GENERATION_ERROR = 131; + + // The device root certificate was not set. + ROOT_CERTIFICATE_NOT_SET = 132; + + // The service certificate is invalid. + INVALID_SERVICE_CERTIFICATE = 133; + + // Service certificate not found. + SERVICE_CERTIFICATE_NOT_FOUND = 134; + + // Invalid EncryptedClientIdentification message. + INVALID_ENCRYPTED_CLIENT_IDENTIFICATION = 135; + + // No service certificates have been added. + SERVICE_CERTIFICATE_NOT_SET = 136; + + // Could not process service private key. + INVALID_SERVICE_PRIVATE_KEY = 137; + + // ClientIdentification and EncryptedClientIdentification were specified. + MULTIPLE_CLIENT_ID = 138; + + // Message is a service certificate request. + SERVICE_CERTIFICATE_REQUEST_MESSAGE = 139; + + // Invalid message type + INVALID_MESSAGE_TYPE = 140; + + // Remote attestation verification failed. + REMOTE_ATTESTATION_FAILED = 141; + + // can_play = true for license RELEASE response. + INVALID_RELEASE_CAN_PLAY_VALUE = 142; + + // can_persist = false for offline license. + INVALID_OFFLINE_CAN_PERSIST = 143; + + // Session usage table entry is malformed. + INVALID_SESSION_USAGE_TABLE_ENTRY = 144; + + // Session usage table entry signature verification failed. + INVALID_SESSION_USAGE_SIGNATURE = 145; + + // The type of ContentIdentification is unrecognized + INVALID_CONTENT_ID_TYPE = 146; + + // Unknown InitData type. + UNKNOWN_INIT_DATA_TYPE = 147; + + // InitData.init_data field is missing. + MISSING_INIT_DATA = 148; + + // InitData contains invalid ISO BMFF boxes. + INVALID_CENC_INIT_DATA = 149; + + // Malformed PSSH box. + INVALID_PSSH = 150; + + // PSSH box version not supported. + UNSUPPORTED_PSSH_VERSION = 151; + + // Widevine PSSH Data malformed. + INVALID_WIDEVINE_PSSH_DATA = 152; + + // Device capabilities are too low for the specified output protection. + DEVICE_CAPABILITIES_TOO_LOW = 153; + + // Invalid master signing key size. Must be 16 bytes. + INVALID_MASTER_SIGNING_KEY_SIZE = 154; + + // Invalid signing key size. Must be 64 bytes. + INVALID_SIGNING_KEY_SIZE = 155; + + // Keybox tokens not intialized. PreProvisioning keys not loaded. + KEYBOX_TOKEN_KEYS_NOT_INITIALIZED = 156; + + // Provider Id in device certificate does not match service Id for License + // server. Check cert used when initializing with AddDrmServiceCertificate(). + PROVIDER_ID_MISMATCH = 157; + + // Certificate chain not selected. + CERT_CHAIN_NOT_SELECTED = 158; + + // Failed to read the SRM file from specified location. + INVALID_SRM_LOCATION = 159; + + // Invalid SRM file size, HDCP2 SRM file must be at least 396 bytes. + INVALID_SRM_SIZE = 160; + + // SRM file signature validation failed. + INVALID_SRM_SIGNATURE = 161; + + // Unable to find provider. + MISSING_PROVIDER = 162; + + // Unable to find group master key id. + MISSING_GROUP_MASTER_KEY_ID = 163; + + // Unable to find group master key. + MISSING_GROUP_MASTER_KEY = 164; + + // Invalid Provider session token size. Must be less than 256 bytes. + INVALID_PROVIDER_SESSION_TOKEN_SIZE = 165; + + // Failure to decrypt data with service certificate private key. + SERVICE_PRIVATE_KEY_DECRYPT_ERROR = 166; + + // Disallowed development certificate. + DEVELOPMENT_CERTIFICATE_NOT_ALLOWED = 167; + + // Invalid message. E.g. Deserialization failed. + INVALID_MESSAGE = 168; + + // Invalid key size. + INVALID_KEY_SIZE = 169; + +} diff --git a/protos/public/license_protocol.proto b/protos/public/license_protocol.proto new file mode 100644 index 0000000..ae26cab --- /dev/null +++ b/protos/public/license_protocol.proto @@ -0,0 +1,403 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Definitions of the protocol buffer messages used in the Widevine license +// exchange protocol, described in Widevine license exchange protocol document +// TODO(user): find out a right way to strip out all the doc link. + +syntax = "proto2"; + +package widevine; +option java_package = "com.google.video.widevine.protos"; + +import "protos/public/client_identification.proto"; +import "protos/public/remote_attestation.proto"; + +// option optimize_for = LITE_RUNTIME; +enum LicenseType { + STREAMING = 1; + OFFLINE = 2; +} + +enum PlatformVerificationStatus { + // The platform is not verified. + PLATFORM_UNVERIFIED = 0; + // Tampering detected on the platform. + PLATFORM_TAMPERED = 1; + // The platform has been verified by means of software. + PLATFORM_SOFTWARE_VERIFIED = 2; + // The platform has been verified by means of hardware (e.g. secure boot). + PLATFORM_HARDWARE_VERIFIED = 3; + // Platform verification was not performed. + PLATFORM_NO_VERIFICATION = 4; + // Platform and secure storage capability have been verified by means of + // software. + PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED = 5; +} + +// LicenseIdentification is propagated from LicenseRequest to License, +// incrementing version with each iteration. +message LicenseIdentification { + optional bytes request_id = 1; + optional bytes session_id = 2; + optional bytes purchase_id = 3; + optional LicenseType type = 4; + optional int32 version = 5; + optional bytes provider_session_token = 6; +} + +message License { + // LINT.IfChange + message Policy { + // Indicates that playback of the content is allowed. + optional bool can_play = 1 [default = false]; + + // Indicates that the license may be persisted to non-volatile + // storage for offline use. + optional bool can_persist = 2 [default = false]; + + // Indicates that renewal of this license is allowed. + optional bool can_renew = 3 [default = false]; + + // For the |*duration*| fields, playback must halt when + // license_start_time (seconds since the epoch (UTC)) + + // license_duration_seconds is exceeded. A value of 0 + // indicates that there is no limit to the duration. + + // Indicates the rental window. + optional int64 rental_duration_seconds = 4 [default = 0]; + + // Indicates the viewing window, once playback has begun. + optional int64 playback_duration_seconds = 5 [default = 0]; + + // Indicates the time window for this specific license. + optional int64 license_duration_seconds = 6 [default = 0]; + + // The |renewal*| fields only apply if |can_renew| is true. + + // The window of time, in which playback is allowed to continue while + // renewal is attempted, yet unsuccessful due to backend problems with + // the license server. + optional int64 renewal_recovery_duration_seconds = 7 [default = 0]; + + // All renewal requests for this license shall be directed to the + // specified URL. + optional string renewal_server_url = 8; + + // How many seconds after license_start_time, before renewal is first + // attempted. + optional int64 renewal_delay_seconds = 9 [default = 0]; + + // Specifies the delay in seconds between subsequent license + // renewal requests, in case of failure. + optional int64 renewal_retry_interval_seconds = 10 [default = 0]; + + // Indicates that the license shall be sent for renewal when usage is + // started. + optional bool renew_with_usage = 11 [default = false]; + + // Indicates to client that license renewal and release requests ought to + // include ClientIdentification (client_id). + optional bool always_include_client_id = 12 [default = false]; + + // Duration of grace period before playback_duration_seconds (short window) + // goes into effect. Optional. + optional int64 play_start_grace_period_seconds = 13 [default = 0]; + + // Enables "soft enforcement" of playback_duration_seconds, letting the user + // finish playback even if short window expires. Optional. + optional bool soft_enforce_playback_duration = 14 [default = false]; + } + + message KeyContainer { + enum KeyType { + SIGNING = 1; // Exactly one key of this type must appear. + CONTENT = 2; // Content key. + KEY_CONTROL = 3; // Key control block for license renewals. No key. + OPERATOR_SESSION = 4; // wrapped keys for auxiliary crypto operations. + ENTITLEMENT = 5; // Entitlement keys. + } + + // The SecurityLevel enumeration allows the server to communicate the level + // of robustness required by the client, in order to use the key. + enum SecurityLevel { + // Software-based whitebox crypto is required. + SW_SECURE_CRYPTO = 1; + + // Software crypto and an obfuscated decoder is required. + SW_SECURE_DECODE = 2; + + // The key material and crypto operations must be performed within a + // hardware backed trusted execution environment. + HW_SECURE_CRYPTO = 3; + + // The crypto and decoding of content must be performed within a hardware + // backed trusted execution environment. + HW_SECURE_DECODE = 4; + + // The crypto, decoding and all handling of the media (compressed and + // uncompressed) must be handled within a hardware backed trusted + // execution environment. + HW_SECURE_ALL = 5; + } + + message KeyControl { + // If present, the key control must be communicated to the secure + // environment prior to any usage. This message is automatically generated + // by the Widevine License Server SDK. + optional bytes key_control_block = 1; + optional bytes iv = 2; + } + + message OutputProtection { + // Indicates whether HDCP is required on digital outputs, and which + // version should be used. + enum HDCP { + HDCP_NONE = 0; + HDCP_V1 = 1; + HDCP_V2 = 2; + HDCP_V2_1 = 3; + HDCP_V2_2 = 4; + HDCP_V2_3 = 5; + HDCP_NO_DIGITAL_OUTPUT = 0xff; + } + optional HDCP hdcp = 1 [default = HDCP_NONE]; + + // Indicate the CGMS setting to be inserted on analog output. + enum CGMS { + CGMS_NONE = 42; + COPY_FREE = 0; + COPY_ONCE = 2; + COPY_NEVER = 3; + } + optional CGMS cgms_flags = 2 [default = CGMS_NONE]; + + enum HdcpSrmRule { + HDCP_SRM_RULE_NONE = 0; + // In 'required_protection', this means most current SRM is required. + // Update the SRM on the device. If update cannot happen, + // do not allow the key. + // In 'requested_protection', this means most current SRM is requested. + // Update the SRM on the device. If update cannot happen, + // allow use of the key anyway. + CURRENT_SRM = 1; + } + optional HdcpSrmRule hdcp_srm_rule = 3 [default = HDCP_SRM_RULE_NONE]; + // Optional requirement to indicate analog output is not allowed. + optional bool disable_analog_output = 4 [default = false]; + } + + message VideoResolutionConstraint { + // Minimum and maximum video resolutions in the range (height x width). + optional uint32 min_resolution_pixels = 1; + optional uint32 max_resolution_pixels = 2; + // Optional output protection requirements for this range. If not + // specified, the OutputProtection in the KeyContainer applies. + optional OutputProtection required_protection = 3; + } + + message OperatorSessionKeyPermissions { + // Permissions/key usage flags for operator service keys + // (type = OPERATOR_SESSION). + optional bool allow_encrypt = 1 [default = false]; + optional bool allow_decrypt = 2 [default = false]; + optional bool allow_sign = 3 [default = false]; + optional bool allow_signature_verify = 4 [default = false]; + } + + optional bytes id = 1; + optional bytes iv = 2; + optional bytes key = 3; + optional KeyType type = 4; + optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO]; + optional OutputProtection required_protection = 6; + // NOTE: Use of requested_protection is not recommended as it is only + // supported on a small number of platforms. + optional OutputProtection requested_protection = 7; + optional KeyControl key_control = 8; + optional OperatorSessionKeyPermissions operator_session_key_permissions = 9; + // Optional video resolution constraints. If the video resolution of the + // content being decrypted/decoded falls within one of the specified ranges, + // the optional required_protections may be applied. Otherwise an error will + // be reported. + // NOTE: Use of this feature is not recommended, as it is only supported on + // a small number of platforms. + repeated VideoResolutionConstraint video_resolution_constraints = 10; + // Optional flag to indicate the key must only be used if the client + // supports anti rollback of the user table. Content provider can query the + // client capabilities to determine if the client support this feature. + optional bool anti_rollback_usage_table = 11 [default = false]; + // Optional not limited to commonly known track types such as SD, HD. + // It can be some provider defined label to identify the track. + optional string track_label = 12; + } + + optional LicenseIdentification id = 1; + optional Policy policy = 2; + repeated KeyContainer key = 3; + // Time of the request in seconds (UTC) as set in + // LicenseRequest.request_time. If this time is not set in the request, + // the local time at the license service is used in this field. + optional int64 license_start_time = 4; + // TODO(b/65054419): Deprecate remote_attestation_verified in favor of + // platform_verification_status, below. + optional bool remote_attestation_verified = 5 [default = false]; + // Client token generated by the content provider. Optional. + optional bytes provider_client_token = 6; + // 4cc code specifying the CENC protection scheme as defined in the CENC 3.0 + // specification. Propagated from Widevine PSSH box. Optional. + optional uint32 protection_scheme = 7; + // 8 byte verification field "HDCPDATA" followed by unsigned 32 bit minimum + // HDCP SRM version (whether the version is for HDCP1 SRM or HDCP2 SRM + // depends on client max_hdcp_version). + optional bytes srm_requirement = 8; + // If present this contains a signed SRM file (either HDCP1 SRM or HDCP2 SRM + // depending on client max_hdcp_version) that should be installed on the + // client device. + optional bytes srm_update = 9; + // Indicates the status of any type of platform verification performed by the + // server. + optional PlatformVerificationStatus platform_verification_status = 10 + [default = PLATFORM_NO_VERIFICATION]; + // IDs of the groups for which keys are delivered in this license, if any. + repeated bytes group_ids = 11; + +} + +enum ProtocolVersion { + VERSION_2_0 = 20; + VERSION_2_1 = 21; +} + +message LicenseRequest { + message ContentIdentification { + message CencDeprecated { + repeated bytes pssh = 1; + optional LicenseType license_type = 2; + optional bytes request_id = 3; // Opaque, client-specified. + } + + message WebmDeprecated { + optional bytes header = 1; + optional LicenseType license_type = 2; + optional bytes request_id = 3; // Opaque, client-specified. + } + + message ExistingLicense { + optional LicenseIdentification license_id = 1; + optional int64 seconds_since_started = 2; + optional int64 seconds_since_last_played = 3; + optional bytes session_usage_table_entry = 4; + } + + message InitData { + enum InitDataType { + CENC = 1; + WEBM = 2; + } + + optional InitDataType init_data_type = 1 [default = CENC]; + optional bytes init_data = 2; + optional LicenseType license_type = 3; + optional bytes request_id = 4; + } + + oneof content_id_variant { + // Exactly one of these must be present. + CencDeprecated cenc_id_deprecated = 1; + WebmDeprecated webm_id_deprecated = 2; + ExistingLicense existing_license = 3; + InitData init_data = 4; + } + } + + enum RequestType { + NEW = 1; + RENEWAL = 2; + RELEASE = 3; + } + + // The client_id provides information authenticating the calling device. It + // contains the Widevine keybox token that was installed on the device at the + // factory. This field or encrypted_client_id below is required for a valid + // license request, but both should never be present in the same request. + optional ClientIdentification client_id = 1; + optional ContentIdentification content_id = 2; + optional RequestType type = 3; + // Time of the request in seconds (UTC) as set by the client. + optional int64 request_time = 4; + // Old-style decimal-encoded string key control nonce. + optional bytes key_control_nonce_deprecated = 5; + optional ProtocolVersion protocol_version = 6 [default = VERSION_2_0]; + // New-style uint32 key control nonce, please use instead of + // key_control_nonce_deprecated. + optional uint32 key_control_nonce = 7; + // Encrypted ClientIdentification message, used for privacy purposes. + optional EncryptedClientIdentification encrypted_client_id = 8; +} + +message LicenseError { + enum Error { + // The device credentials are invalid. The device must re-provision. + INVALID_DRM_DEVICE_CERTIFICATE = 1; + // The device credentials have been revoked. Re-provisioning is not + // possible. + REVOKED_DRM_DEVICE_CERTIFICATE = 2; + // The service is currently unavailable due to the backend being down + // or similar circumstances. + SERVICE_UNAVAILABLE = 3; + } + optional Error error_code = 1; +} + +message MetricData { + enum MetricType { + // The time spent in the 'stage', specified in microseconds. + LATENCY = 1; + // The UNIX epoch timestamp at which the 'stage' was first accessed in + // microseconds. + TIMESTAMP = 2; + } + + message TypeValue { + optional MetricType type = 1; + // The value associated with 'type'. For example if type == LATENCY, the + // value would be the time in microseconds spent in this 'stage'. + optional int64 value = 2 [default = 0]; + } + + // 'stage' that is currently processing the SignedMessage. Required. + optional string stage_name = 1; + // metric and associated value. + repeated TypeValue metric_data = 2; +} + +message SignedMessage { + enum MessageType { + LICENSE_REQUEST = 1; + LICENSE = 2; + ERROR_RESPONSE = 3; + SERVICE_CERTIFICATE_REQUEST = 4; + SERVICE_CERTIFICATE = 5; + SUB_LICENSE = 6; + CAS_LICENSE_REQUEST = 7; + CAS_LICENSE = 8; + } + + optional MessageType type = 1; + optional bytes msg = 2; + optional bytes signature = 3; + optional bytes session_key = 4; + // Remote attestation data which will be present in the initial license + // request for ChromeOS client devices operating in verified mode. Remote + // attestation challenge data is |msg| field above. Optional. + optional RemoteAttestation remote_attestation = 5; + + repeated MetricData metric_data = 6; +} diff --git a/protos/public/license_server_sdk.proto b/protos/public/license_server_sdk.proto new file mode 100644 index 0000000..b089474 --- /dev/null +++ b/protos/public/license_server_sdk.proto @@ -0,0 +1,120 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// Definitions of the protocol buffer messages used in the Widevine License +// Server SDK. + +syntax = "proto2"; + +package widevine; +option java_package = "com.google.video.widevine.protos"; + +import "protos/public/license_protocol.proto"; +import "protos/public/widevine_pssh.proto"; + +// This message is used to pass optional data on initial license issuance. +// LINT.IfChange +message SessionInit { + optional bytes session_id = 1; + optional bytes purchase_id = 2; + // master_signing_key should be 128 bits in length. + optional bytes master_signing_key = 3; + // signing_key should be 512 bits in length to be split into two + // (server || client) HMAC-SHA256 keys. + optional bytes signing_key = 4; + optional int64 license_start_time = 5; + // Client token for the session. This session is for use by the license + // provider, and is akin to a client cookie. It will be copied to + // License::provider_client_token, and sent back by the client in + // ClientIdentification::provider_client_token in all license requests + // thereafter. + optional bytes provider_client_token = 6; + // Session token for the session. This token is for use by the license + // provider, and is akin to a session cookie. It will be copied to + // LicenseIdentfication::provider_session_token, and sent back in all + // license renewal and release requests for the session thereafter. + optional bytes provider_session_token = 7; + // If false and the request contains a provider_client_token, use the token + // from the request even if SessionInit.provider_client_token is specified. + // If true and the request contains a provider_client_token, use + // SessionInit.provider_client_token. + optional bool override_provider_client_token = 8 [default = false]; + // Set true if group key(s) should not be included in the license. If true, + // the result license will contain keys for the current content only, + // therefore the license cannot be used to playback other content in the same + // group. + optional bool exclude_group_key = 9 [default = false]; + // If set to true, the OEM Crypto API version will be not be reflected in the + // license response. + optional bool disable_oem_crypto_api_version_reflection = 10 + [default = false]; + // For testing use only. Service Providers can test how devices are handling + // the version reflection in KCB (key control block) by specifying the api + // version that is reflected back in the KCB. If an override is specified, + // the override value will be used in the KCB instead of the api version + // specified by the client in client_capabilities. + // Crypto API version is represented as 4 bytes, for example 'kcxx', where xx + // is the API version. Some valid values are: 'kc09', kc10', kc14'. Only the + // first 4 bytes are used and additional bytes are ignored. + optional bytes override_oem_crypto_api_version = 11; +} + +// This message is used by the server to preserve and restore session state. +message SessionState { + optional LicenseIdentification license_id = 1; + optional bytes signing_key = 2; + optional uint32 keybox_system_id = 3; + // Provider client token sent back in the license. + optional bytes provider_client_token = 4; + // License counter associated with the avove token. + optional uint32 license_counter = 5; +} + +message ContentInfo { + message ContentInfoEntry { + message Pssh { + optional bytes system_id = 1; + oneof pssh_data { + // Populated for non-Widevine PSSH boxes. + bytes raw_data = 2; + // Populated if system_id matches Widevine’s system ID. + WidevinePsshData widevine_data = 3; + } + } + + repeated bytes key_ids = 1; + // Populated if init_data_type = CENC. + optional Pssh pssh = 2; + } + + optional LicenseRequest.ContentIdentification.InitData.InitDataType + init_data_type = 1; + repeated ContentInfoEntry content_info_entry = 2; +} + +// Usage report sent in a license release. +message SessionUsage { + enum ClockSecurityLevel { + INSECURE_CLOCK = 0; + SECURE_TIMER = 1; + SECURE_CLOCK = 2; + HW_SECURE_CLOCK = 3; + } + // Set to true is the license was used. + optional bool license_used = 1; + // Set to true is the license was released. + optional bool license_released = 2; + optional ClockSecurityLevel clock_security_level = 3; + optional uint64 seconds_since_license_received = 4; + // The decrypt values are only set if the license was used. + optional uint64 seconds_since_first_decrypt = 5; + optional uint64 seconds_since_last_decrypt = 6; + optional bytes provider_session_token = 7; +} diff --git a/protos/public/license_services.proto b/protos/public/license_services.proto new file mode 100644 index 0000000..8bb4a37 --- /dev/null +++ b/protos/public/license_services.proto @@ -0,0 +1,373 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// Data and Service definitions for Widevine DRM Service. + +syntax = "proto2"; + +option java_package = "com.google.video.widevine.licensing"; +import "protos/public/client_identification.proto"; +import "protos/public/license_protocol.proto"; +import "protos/public/license_server_sdk.proto"; +package widevine; + +// TODO(user): refactor license_services.proto and sdk_stats.proto. + +enum ModularDrmType { + WIDEVINE = 0; + PLAYREADY = 1; + FAIRPLAY = 2; +} + +// LINT.IfChange +// The Modular DRM license request sent to the RPC service. +message ModularDrmLicenseRequest { + // The request payload. This is usually the HTTP Post body of a request. + // Required. + optional bytes payload = 1; + + // A ContentKeySpec identifies a content key by track type name. It also + // specifies the policy that should be used for this key. + // LINT.IfChange + message ContentKeySpec { + // A track type is used to represent a set of tracks that share the same + // content key and security level. Common values are SD, HD, UHD1, UHD2 + // and AUDIO. Content providers may use arbitrary strings for track type + // as long as they are consistent with the track types used at registration + // time. Required. + optional string track_type = 1; + + // The following settings override pre-stored settings referenced by the + // policy_name. + // Security level defines the robustness requirements on key handling at + // client side. + optional License.KeyContainer.SecurityLevel security_level = 2; + optional License.KeyContainer.OutputProtection required_output_protection = + 3; + optional License.KeyContainer.OutputProtection requested_output_protection = + 4; + // Optional content key Id for this track. If specified, this Id is used to + // identify the key for this track. If not specified, the license server + // will either generate or lookup the key Id for this track. + optional bytes key_id = 5; + // Optional content key for this track. If specified, this key is used to + // build the license. If not specified, the license server will generate + // or lookup the key for this track. + optional bytes key = 6; + + // TODO (robinconnell): iv, key_type, and operator_session_key_permissions + // where added to support OPERATION_SESSION licenses. But with the addition + // of the field the ContentKeySpec message is almost the same as the + // License.KeyContainer message. Merge the two messages needs to be + // investigated. + + // Optional iv used to encrypt the keys. If not specified random iv will be + // generated. + optional bytes iv = 7; + optional License.KeyContainer.KeyType key_type = 8; + optional License.KeyContainer.OperatorSessionKeyPermissions + operator_session_key_permissions = 9; + + // Optional video resolution constraints. If the video resolution of the + // content being decrypted/decoded falls within one of the specified ranges, + // the optional required_protections may be applied. Otherwise an error will + // be reported. + repeated License.KeyContainer.VideoResolutionConstraint + video_resolution_constraints = 10; + } + + // Specifies a list of content keys and policies to be included in a license. + repeated ContentKeySpec content_key_specs = 2; + + // A shortcut for specifying which track types should be included in a + // license. + // This field is ignored if one or more content_key_specs is specified. + // LINT.IfChange + enum CommonTrackTypeSet { + // Implies SD and HD. + SD_HD = 0; + // Implies SD only. + SD_ONLY = 1; + // Implies SD, HD, and UHD1. + SD_UHD1 = 2; + // Implies SD, HD, UHD1 and UHD2. + SD_UHD2 = 3; + } + optional CommonTrackTypeSet allowed_track_types = 3 [default = SD_UHD1]; + + // Identifier used to derive KeyId(s) and Content Key(s) + // for each content_key_specs.track_type. Required only + // when the content id isn't part of the PSSH. Used for + // backward-compatibility with existing YouTube CENC + // videos. Ignored for assets whose PSSH includes a + // content id. + optional bytes content_id = 4; + + // Used to look up Content Key(s) and policy. Required. + optional string provider = 5; + + // A blob of data to be sent to client and bounce back in subsequent heartbeat + // requests. Maps to license.license_id.prchase.id. + optional string replay_data = 6; + + // License policy information could come from various sources. For any given + // field in a policy, the final value is determined by (in the order by + // highest precedence to lowest) + // 1. request.policy // acts as policy override. + // 2. if !use_policy_overrides_exclusively, + // if request.has_policy_name + // GetStoredPolicy(request.policy_name) + // else + // GetStoredPolicy(GetRegisteredAsset(request.content_id).policy_name) + + // Policy overrides specified by a content provider proxy. + optional License.Policy policy_overrides = 7; + + // Use a previously registered policy named . + optional string policy_name = 8; + + // Use Policy attributes specified by policy_overrides and + // skip any Policy lookup from storage. + optional bool use_policy_overrides_exclusively = 9 [default = false]; + + // Indicates whether this is a Widevine, PlayReady or FairPlay license + // request. + optional ModularDrmType drm_type = 10 [default = WIDEVINE]; + + // This is now handled as a server configuration, only enabled for QA and UAT. + optional bool allow_test_device_deprecated = 11 [deprecated = true]; + + // The IP Address of the portal that is forwarding the license request from + // the device. + optional string client_ip_address = 13; + + // The client software identifier, as used by HTTP. + optional string user_agent = 14; + + // Client token owned by Content Provider. This value is added to the + // license response. + // TODO(user): Deprecated and use session_init instead. + optional bytes provider_client_token = 15; + + // Session token owned by Content Provider. This value is added to the + // license response. + // TODO(user): Deprecated and use session_init instead. + optional bytes provider_session_token = 16; + + // Pass optional data to initial license. + optional SessionInit session_init = 17; + + // Indicates whether to process the license request and return the parsed data + // only, hence not generating an actual license. + optional bool parse_only = 18 [default = false]; + + // The request identifier for this license request as specified by the + // content provider proxy. Optional. + optional string content_provider_request_id = 19 [default = "unspecified"]; + + // Indicates all key and iv values in ContentKeySpec are encrypted with this + // sesion key. This session key is encrypted with the providers AES key. If + // session_key is used, session_iv must also be specified. + optional bytes session_key = 20; + + // Indicates all key and iv values in ContentKeySpec are encrypted with this + // session IV. This session IV is encrypted with the provider's AES key. + optional bytes session_iv = 21; + + // In prod environment, normally license request from IN_TESTING devices + // will be rejected. But if test_content is true, such request from + // a IN_TESTING device will succeed. + // In all other environments, test_content has no impact, licensing for + // IN_TESTING wil be allowed regardless. + // See b/26692995 for more info. + optional bool test_content = 22 [default = false]; + + // The name of the provider making the request. This field will be + // populated by content providers who are serving content packaged by + // content owners. If the content owner and content provider are the same, + // this field will be empty. + // Example: Play is a content provider which serves content packaged and + // owned by the content owner, YouTube. + optional string requesting_provider = 23; + + // Serialized ClientIdentification protobuf message returned by the Proxy SDK + // GetClientInfoAsAtring() API. + optional bytes client_id_msg = 24; + + // If set to true and the device is L3, return AUDIO and SD keys only. + // This takes precedence over allowed_track_types. + optional bool sd_only_for_l3 = 25 [default = false]; + + // Content providers who host their own service certificate must set this + // field by using the ProxySDK(internally, RequestInspector) API. + optional PlatformVerificationStatus platform_verification_status = 26 + [default = PLATFORM_NO_VERIFICATION]; +} + + +// LINT.IfChange +// A generic protobuf used as the request format of various Modular DRM APIs, +// including GetModularDrmLicense and KeyRequest APIs. +// The following docs describe how the proto is interpreted by various APIs. +message SignedModularDrmRequest { + // For GetModularDrmLicenseExternal, request is a ModularDrmLicenseRequest in + // JSON format. + optional bytes request = 1; + optional bytes signature = 2; + // Identifies the entity sending / signing the request. + optional string signer = 3; + // The IP Address of the portal that is forwarding the request from the + // original sender. + optional string client_ip_address = 4; + // The client software identifier, as used by HTTP. + optional string user_agent = 5; + // The request identifier for this license request as specified by the + // content provider proxy. Optional. + optional string content_provider_request_id = 6 [default = "unspecified"]; + // The provider on behalf of whom, this request is made. This field will be + // populated by content providers who are serving content packaged by content + // owners ("signer"). + optional string provider = 7; +} + +// LINT.IfChange +message SignedModularDrmResponse { + // A CommonEncryptionResponse message in JSON format. + optional bytes response = 1; + optional bytes signature = 2; +} + + +message LicenseMetadata { + // DRM-specific status code from the WvmStatus enum. It's a uint32 here + // because providers can use the 'setstatus' field to supply their own + // arbitrary return values. + optional uint32 drm_status_code = 1; + // Asset Identifier that was generated by this DRM service. + optional uint64 asset_id = 2; + optional string asset_name = 4; +} + + +// LINT.IfChange +message ModularDrmLicenseResponse { + enum Status { + OK = 0; + SIGNATURE_FAILED = 1; + INVALID_LICENSE_CHALLENGE = 2; + INVALID_CONTENT_INFO = 3; + POLICY_UNKNOWN = 4; + MALFORMED_REQUEST = 5; + INTERNAL_ERROR = 6; + PROVIDER_MISSING = 7; + INVALID_REQUEST = 8; + ACCESS_DENIED = 9; + SIGNING_KEY_EXPIRED = 10; + } + optional Status status = 1; + optional string status_message = 2; + optional bytes license = 3; + message LicenseMetadata { + optional bytes content_id = 1; + optional LicenseType license_type = 2; + optional LicenseRequest.RequestType request_type = 3; + // The current SRM version sent back to client in the license. + optional uint32 srm_version = 4; + // Whether SRM file was included in the license. + optional bool srm_included = 5 [default = false]; + // Sets the value from + // ClientIdentification.ClientCapabilities.can_update_srm in the license + // request. + optional bool can_update_srm = 6 [default = false]; + } + optional LicenseMetadata license_metadata = 4; + message Track { + optional string type = 1; + optional bytes key_id = 2; + } + // A subset of data from the Widevine PSSH. + message Pssh { + repeated bytes key_id = 1; + optional bytes content_id = 2; + } + // List of tracks for which keys exist in the license response. + repeated Track supported_tracks = 5; + // Make as identified from the provisioned device info. If that is not + // available, the device make will be retrieved from the license request. + optional string make = 6; + // Model as identified from the provisioned device info. If that is not + // available, the device model will be retrieved from the license request. + optional string model = 7; + // Set to true if the license request contained remote attestation challenge + // and the challenge was verified. + optional bool remote_attestation_verified = 8; + // Widevine-defined security level. + optional uint32 security_level = 9; + // Actual SDK license status as defined in widevine/server/sdk/error.proto. + optional uint32 internal_status = 10; + // Usage report sent in a license release. + optional SessionUsage session_usage = 11; + optional SessionState session_state = 12; + // Globally unique serial number of certificate associated with this + // device. + optional bytes drm_cert_serial_number = 13; + // Whether the device (make/model) making the license request is whitelisted. + // Indicates the type of message in the license response. + optional SignedMessage.MessageType message_type = 15; + // Optional Android Id reported by the Android client. + // See go/avi-unique-device-id + optional bytes android_id = 16; + // If this is a group key license, this is the group identifier from the PSSH. + optional bytes group_id = 17; + // Platform specifies the OS or device type and perhaps other software + // information for the device receving this license response. + // Example: Android, iOS, Chrome, PC. + optional string platform = 18; + optional Pssh pssh_data = 20; + // Maximum HDCP version specified by the client. + optional ClientIdentification.ClientCapabilities.HdcpVersion + client_max_hdcp_version = 21; + // Optional client information name/value pairs from the client. + repeated ClientIdentification.NameValue client_info = 22; + // Optional number of seconds before a signing key expires. + optional int64 signature_expiration_secs = 23; + // Set to true, if license request was for "live" stream. + optional bool is_live = 24 [default = false]; + // Platform verification status + optional PlatformVerificationStatus platform_verification_status = 25 + [default = PLATFORM_UNVERIFIED]; + // The "provider" field in ModularDrmLicenseRequest. + optional string content_owner = 26; + // The "requesting_provider" in ModularDrmLicenseRequest. If + // "requesting_provider" is empty, then "provider" from + // ModularDrmLicenseRequest will be set here. + optional string content_provider = 27; + // SystemID of the requesting device. + optional uint32 system_id = 28; +} + +message LicenseResponse { + // Each license is a Base64-encoded string which is of the format + // . Multiple licenses are separated by a + // comma. Required. + optional string licenses = 2; + // License for the requested asset. This should not be returned to the + // client, but possibly evaluated by the provider. The number of + // license_metadata fields must be the same as the number of licenses. + // Required. + repeated LicenseMetadata license_metadata = 3; + // Make as specified in the license request. + optional string make = 4; + // Model as specified in the license request. + optional string model = 5; + // Platform specifies the OS or device type and perhaps other software + // information for the device receving this license response. + // Example: Android, iOS, Chrome, PC. + optional string platform = 6; +} + diff --git a/protos/public/provisioned_device_info.proto b/protos/public/provisioned_device_info.proto new file mode 100644 index 0000000..f6d4104 --- /dev/null +++ b/protos/public/provisioned_device_info.proto @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Description: +// Provisioned device info format definitions. + +syntax = "proto2"; + +package widevine; + +option java_package = "com.google.video.widevine.protos"; +option java_outer_classname = "ProvisionedDeviceInfoProto"; + +// Contains device model information for a provisioned device. +message ProvisionedDeviceInfo { + enum WvSecurityLevel { + // Defined in Widevine Security Integration Guide for DASH on Android: + // http://doc/1Zum-fcJeoIw6KG1kDP_KepIE5h9gAZg0PaMtemBvk9c/edit#heading=h.1t3h5sf + LEVEL_UNSPECIFIED = 0; + LEVEL_1 = 1; + LEVEL_2 = 2; + LEVEL_3 = 3; + } + // Widevine initial provisioning / bootstrapping method. DRM certificates are + // required for retrieving licenses, so if a DRM certificate is not initially + // provisioned, then the provisioned credentials will be used to provision + // a DRM certificate via the Widevine Provisioning Service. + enum ProvisioningMethod { + // Don't use this. + PROVISIONING_METHOD_UNSPECIFIED = 0; + // Factory-provisioned device-unique keybox. + FACTORY_KEYBOX = 1; + // Factory-provisioned device-unique OEM certificate. + FACTORY_OEM_DEVICE_CERTIFICATE = 2; + // Factory-provisioned model-group OEM certificate. + FACTORY_OEM_GROUP_CERTIFICATE = 3; + // Factory-provisioned model-group DRM certificate (Level-3 "baked in"). + FACTORY_DRM_GROUP_CERTIFICATE = 4; + // OTA-provisioned keybox (Level-1 ARC++). + OTA_KEYBOX = 5; + // OTA-provisioned device-unique OEM certificate. + OTA_OEM_DEVICE_CERTIFICATE = 6; + // OTA-provisioned model-group OEM certificate. + OTA_OEM_GROUP_CERTIFICATE = 7; + // OTA-provisioned device-unique DRM certificate (Bedrock). + OTA_DRM_DEVICE_CERTIFICATE = 8; + } + + // Widevine system ID for the device. Mandatory. + optional uint32 system_id = 1; + // Name of system-on-a-chip. Optional. + optional string soc = 2; + // Name of manufacturer. Optional. + optional string manufacturer = 3; + // Manufacturer's model name. Matches "brand" in device metadata. Optional. + optional string model = 4; + // Type of device (Phone, Tablet, TV, etc). + optional string device_type = 5; + // Device model year. Optional. + optional uint32 model_year = 6; + // Widevine-defined security level. Optional. + optional WvSecurityLevel security_level = 7 [default = LEVEL_UNSPECIFIED]; + // True if the certificate corresponds to a test (non production) device. + // Optional. + optional bool test_device = 8 [default = false]; + // Indicates the type of device root of trust which was factory provisioned. + optional ProvisioningMethod provisioning_method = 9; +} diff --git a/protos/public/remote_attestation.proto b/protos/public/remote_attestation.proto new file mode 100644 index 0000000..d8a8551 --- /dev/null +++ b/protos/public/remote_attestation.proto @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Remote attestation is used by ChromeOS device to authenticate itself +// to Widevine services for both licensing and keybox provisioning. + +syntax = "proto2"; + +package widevine; +option java_package = "com.google.video.widevine.protos"; + +import "protos/public/client_identification.proto"; + +message RemoteAttestation { + // Encrypted ClientIdentification message containing the device remote + // attestation certificate. Required. + optional EncryptedClientIdentification certificate = 1; + // Bytes of salt which were added to the remote attestation challenge prior to + // signing it. Required. + optional bytes salt = 2; + // Signed remote attestation challenge + salt. Required. + optional bytes signature = 3; +} + diff --git a/protos/public/sdk_stats.proto b/protos/public/sdk_stats.proto new file mode 100644 index 0000000..904483f --- /dev/null +++ b/protos/public/sdk_stats.proto @@ -0,0 +1,69 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// +// Main protocol buffers for Widevine external SDK +// licensing statistics. +// Design doc: https://docs.google.com/document/d/1yyt5TxApYbI0N07aH94zwnKYuzYdFcmqZtC3jCyph8k/edit# + +syntax = "proto2"; + +package widevine; + +option java_package = "com.google.video.widevine.protos"; + +option java_outer_classname = "LicenseStatsProtos"; + + +message DeviceLicenseCounterByStatus { + // The response status sent by the SDK in response to the license request. + // Required. + optional int32 license_status = 1; + // Count of licenses for this status code. Required. + optional int64 count = 2; +} + +message DeviceLicenseCounterByModel { + // The model of the device sending a license request to the Widevine SDK. Optional. + optional string device_model = 1; + // license status specific breakdown of counter data + repeated DeviceLicenseCounterByStatus counter_by_status = 2; +} + +message DeviceLicenseCounterByMake { + // The make of the device sending a license request to the Widevine SDK. Optional. + optional string device_make = 1; + // device model specific breakdown of counter data. + repeated DeviceLicenseCounterByModel counter_by_model = 2; +} + +message DeviceLicenseCounterBySystemId { + // The system identifier for the device make/model family. Optional. + optional int32 device_system_id = 1; + // device make specific breakdown of counter data. + repeated DeviceLicenseCounterByMake counter_by_make = 2; +} + +message DeviceLicenseCounterRequest { + // The provider hosting the Widevine SDK. Required. + optional string provider = 1; + // The collection start time in UTC for this counter data. Required. + optional int64 counter_utc_start_time_usec = 2; + // The collection end time in UTC for this counter data. Required. + optional int64 counter_utc_end_time_usec = 3; + // device systemId specific breakdown of counter data. + repeated DeviceLicenseCounterBySystemId counter_by_systemid = 4; +} + +message SignedDeviceLicenseCounterRequest { + // The license counter data by device. Required. + optional DeviceLicenseCounterRequest device_license_counter_request = 1; + // The signature for the provider sending the request. Required. + optional bytes signature = 2; +} + diff --git a/protos/public/signed_drm_certificate.proto b/protos/public/signed_drm_certificate.proto new file mode 100644 index 0000000..bbd0835 --- /dev/null +++ b/protos/public/signed_drm_certificate.proto @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 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. +//////////////////////////////////////////////////////////////////////////////// + +// Signed device certificate definition. + +syntax = "proto2"; + +package widevine; + +option java_outer_classname = "SignedDrmCertificateProtos"; +option java_package = "com.google.video.widevine.protos"; + +// DrmCertificate signed by a higher (CA) DRM certificate. +message SignedDrmCertificate { + // Serialized certificate. Required. + optional bytes drm_certificate = 1; + // Signature of certificate. Signed with root or intermediate + // certificate specified below. Required. + optional bytes signature = 2; + // SignedDrmCertificate used to sign this certificate. + optional SignedDrmCertificate signer = 3; +} diff --git a/protos/public/verified_media_pipeline.proto b/protos/public/verified_media_pipeline.proto new file mode 100644 index 0000000..b7ce324 --- /dev/null +++ b/protos/public/verified_media_pipeline.proto @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Protocol messages used for the Verified Media Pipeline feature of the +// Widevine CDM. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package vmp; + +message VmpData { + message SignedBinaryInfo { + // File name of the binary. Required. + optional string file_name = 1; + // Index into |certificates| for the code signing certificate used to sign + // this binary. Required if the binary is signed.. + optional uint32 certificate_index = 2; + // SHA-512 digest of signed binary. Required if the file was present. + optional bytes binary_hash = 3; + // Flags from signature file, if any. Required if signed. + optional uint32 flags = 4; + // Signature of the binary. Required if signed. + optional bytes signature = 5; + } + + // Distinct certificates used in binary code signing. No certificate should + // be present more than once. + repeated bytes certificates = 1; + // Info about each signed binary. + repeated SignedBinaryInfo signed_binary_info = 2; +} diff --git a/protos/public/widevine_pssh.proto b/protos/public/widevine_pssh.proto new file mode 100644 index 0000000..01ec1f9 --- /dev/null +++ b/protos/public/widevine_pssh.proto @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Declaration of protocol buffer which is used to encode the data stored in +// Common Encryption (CENC) 'pssh' box Data fields. + +syntax = "proto2"; + +package widevine; +option java_package = "com.google.video.widevine.protos"; + +message WidevinePsshData { + enum Type { + SINGLE = 0; // Single PSSH to be used to retrieve content keys. + ENTITLEMENT = 1; // Primary PSSH used to retrieve entitlement keys. + ENTITLED_KEY = 2; // Secondary PSSH containing entitled key(s). + } + + message EntitledKey { + // ID of entitlement key used for wrapping |key|. + optional bytes entitlement_key_id = 1; + // ID of the entitled key. + optional bytes key_id = 2; + // Wrapped key. Required. + optional bytes key = 3; + // IV used for wrapping |key|. Required. + optional bytes iv = 4; + // Size of entitlement key used for wrapping |key|. + optional uint32 entitlement_key_size_bytes = 5 [default = 32]; + } + + // Entitlement or content key IDs. Can onnly present in SINGLE or ENTITLEMENT + // PSSHs. May be repeated to facilitate delivery of multiple keys in a + // single license. Cannot be used in conjunction with content_id or + // group_ids, which are the preferred mechanism. + repeated bytes key_ids = 2; + + // Content identifier which may map to multiple entitlement or content key + // IDs to facilitate the delivery of multiple keys in a single license. + // Cannot be present in conjunction with key_ids, but if used must be in all + // PSSHs. + optional bytes content_id = 4; + + // Crypto period index, for media using key rotation. Always corresponds to + // The content key period. This means that if using entitlement licensing + // the ENTITLED_KEY PSSHs will have sequential crypto_period_index's, whereas + // the ENTITELEMENT PSSHs will have gaps in the sequence. Required if doing + // key rotation. + optional uint32 crypto_period_index = 7; + + // Protection scheme identifying the encryption algorithm. The protection + // scheme is represented as a uint32 value. The uint32 contains 4 bytes each + // representing a single ascii character in one of the 4CC protection scheme + // values. To be deprecated in favor of signaling from content. + // 'cenc' (AES-CTR) protection_scheme = 0x63656E63, + // 'cbc1' (AES-CBC) protection_scheme = 0x63626331, + // 'cens' (AES-CTR pattern encryption) protection_scheme = 0x63656E73, + // 'cbcs' (AES-CBC pattern encryption) protection_scheme = 0x63626373. + optional uint32 protection_scheme = 9; + + // Optional. For media using key rotation, this represents the duration + // of each crypto period in seconds. + optional uint32 crypto_period_seconds = 10; + + // Type of PSSH. Required if not SINGLE. + optional Type type = 11 [default = SINGLE]; + + // Key sequence for Widevine-managed keys. Optional. + optional uint32 key_sequence = 12; + + // Group identifiers for all groups to which the content belongs. This can + // be used to deliver licenses to unlock multiple titles / channels. + // Optional, and may only be present in ENTITLEMENT and ENTITLED_KEY PSSHs, and + // not in conjunction with key_ids. + repeated bytes group_ids = 13; + + // Copy/copies of the content key used to decrypt the media stream in which + // the PSSH box is embedded, each wrapped with a different entitlement key. + // May also contain sub-licenses to support devices with OEMCrypto 13 or + // older. May be repeated if using group entitlement keys. Present only in + // PSSHs of type ENTITLED_KEY. + repeated EntitledKey entitled_keys = 14; + + //////////////////////////// Deprecated Fields //////////////////////////// + enum Algorithm { + UNENCRYPTED = 0; + AESCTR = 1; + }; + optional Algorithm algorithm = 1 [deprecated = true]; + optional string provider = 3 [deprecated = true]; + optional string track_type = 5 [deprecated = true]; + optional string policy = 6 [deprecated = true]; + optional bytes grouped_license = 8 [deprecated = true]; +} diff --git a/sdk/external/common/wvpl/BUILD b/sdk/external/common/wvpl/BUILD new file mode 100644 index 0000000..0142ece --- /dev/null +++ b/sdk/external/common/wvpl/BUILD @@ -0,0 +1,117 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +# The public interface of Widevine Proxy SDK for content providers. + +package( + default_visibility = ["//visibility:public"], +) + +filegroup( + name = "binary_release_files", + srcs = glob(["*.h"]), +) + +cc_library( + name = "wvpl_types", + hdrs = [ + "wvpl_types.h", + ], + deps = [ + "//util:status", + ], +) + +cc_library( + name = "wvpl_sdk_session", + srcs = [ + "wvpl_sdk_session.cc", + ], + hdrs = [ + "wvpl_sdk_environment.h", + "wvpl_sdk_session.h", + ], + deps = [ + # TODO(user): Refactor these deps as classes that derive from WvPLSDKSession may not rely on license SDK(s). + "//base", + "//util:status", + ":wvpl_types", + "//common:certificate_type", + "//common:drm_service_certificate", + "//common:error_space", + "//common:remote_attestation_verifier", + "//common:verified_media_pipeline", + "//license_server_sdk/internal:sdk", + "//protos/public:client_identification_proto", + "//protos/public:device_certificate_status_proto", + "//protos/public:errors_proto", + "//protos/public:license_protocol_proto", + "//protos/public:license_server_sdk_proto", + "//protos/public:provisioned_device_info_proto", + ], +) + +cc_library( + name = "wvpl_sdk_environment", + srcs = [ + "wvpl_sdk_environment.cc", + ], + hdrs = [ + "wvpl_sdk_environment.h", + ], + deps = [ + # TODO(user): Refactor these deps as classes that derive from WvPLSDKEnvironment may not rely on license SDK(s). + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util:status", + ":wvpl_types", + "//common:aes_cbc_util", + "//common:certificate_type", + "//common:certificate_util", + "//common:drm_service_certificate", + "//common:error_space", + "//common:sha_util", + "//license_server_sdk/internal:sdk", + "//protos/public:device_certificate_status_proto", + "//protos/public:errors_proto", + "//protos/public:provisioned_device_info_proto", + ], +) + +cc_library( + name = "sdk", + srcs = glob( + ["*.cc"], + ), + hdrs = glob( + ["*.h"], + ), + deps = [ + ":wvpl_types", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util:status", + "//common:aes_cbc_util", + "//common:certificate_type", + "//common:certificate_util", + "//common:drm_service_certificate", + "//common:error_space", + "//common:remote_attestation_verifier", + "//common:sha_util", + "//common:verified_media_pipeline", + "//license_server_sdk/internal:sdk", + "//protos/public:client_identification_proto", + "//protos/public:device_certificate_status_proto", + "//protos/public:errors_proto", + "//protos/public:license_protocol_proto", + "//protos/public:license_server_sdk_proto", + "//protos/public:provisioned_device_info_proto", + ], +) diff --git a/sdk/external/common/wvpl/wvpl_sdk_environment.cc b/sdk/external/common/wvpl/wvpl_sdk_environment.cc new file mode 100644 index 0000000..09e2630 --- /dev/null +++ b/sdk/external/common/wvpl/wvpl_sdk_environment.cc @@ -0,0 +1,202 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "sdk/external/common/wvpl/wvpl_sdk_environment.h" +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" +#include "util/status.h" +#include "common/aes_cbc_util.h" +#include "common/certificate_type.h" +#include "common/certificate_util.h" +#include "common/drm_service_certificate.h" +#include "common/error_space.h" +#include "common/sha_util.h" +#include "license_server_sdk/internal/generate_error_response.h" +#include "protos/public/errors.pb.h" + +namespace util = widevine::util; +using widevine::AddDrmServiceCertificate; +using widevine::DeviceCertificateStatus; +using widevine::DeviceCertificateStatusList; +using widevine::DrmServiceCertificate; +using widevine::error_space; +using widevine::kCertificateTypeDevelopment; +using widevine::kCertificateTypeProduction; +using widevine::kCertificateTypeTesting; +using widevine::ProvisionedDeviceInfo; +using widevine::SetCertificateStatusList; +using widevine::crypto_util::EncryptAesCbc; + +namespace widevine_server { +namespace wv_pl_sdk { + +namespace { +// Thread safe class that keeps track of all active devices in the current +// session/environment. +class ProvisionedDeviceInfoMap { + public: + ProvisionedDeviceInfoMap() = default; + + // Not copyable or movable (move operations are implicitly disabled). + ProvisionedDeviceInfoMap(const ProvisionedDeviceInfoMap&) = delete; + ProvisionedDeviceInfoMap& operator=(const ProvisionedDeviceInfoMap&) = delete; + + const ProvisionedDeviceInfo* FindOrNull(uint32_t system_id) const { + absl::ReaderMutexLock lock(&device_info_mutex_); + const auto it = device_info_map_.find(system_id); + if (it == device_info_map_.end()) return nullptr; + return &it->second; + } + + void InsertOrUpdate(const ProvisionedDeviceInfo& device_info) { + absl::WriterMutexLock lock(&device_info_mutex_); + device_info_map_[device_info.system_id()] = device_info; + } + + private: + mutable absl::Mutex device_info_mutex_; + // Map from ProvisionedDeviceInfo.system_id to ProvisionedDeviceInfo. + std::map device_info_map_ + GUARDED_BY(device_info_mutex_); +}; + +ProvisionedDeviceInfoMap& GetProvisionedDeviceInfoMap() { + static auto* device_info_map = new ProvisionedDeviceInfoMap(); + return *device_info_map; +} +} // namespace +WvPLStatus WvPLSDKEnvironment::SetDrmServiceCertificate( + const std::string& service_certificate, const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + WvPLStatus wvpl_status = AddDrmServiceCertificate( + certificate_type_, service_certificate, service_private_key, + service_private_key_passphrase); + if (!wvpl_status.ok()) return wvpl_status; + wvpl_status = DrmServiceCertificate::ValidateDrmServiceCertificate(); + if (wvpl_status.ok()) { + is_service_certificate_loaded_ = true; + } + return wvpl_status; +} + + +bool WvPLSDKEnvironment::GenerateErrorResponse( + const WvPLStatus& create_session_status, std::string* license_response) { + return widevine::GenerateErrorResponse(create_session_status, + license_response); +} + +WvPLStatus WvPLSDKEnvironment::LookupDeviceInfo( + uint32_t system_id, ProvisionedDeviceInfo* provisioned_device_info) { + WvPLStatus status = util::OkStatus(); + if (provisioned_device_info == nullptr) { + return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, + "provisioned_device_info should not be null"); + } + const ProvisionedDeviceInfoMap& device_info_map = + GetProvisionedDeviceInfoMap(); + const ProvisionedDeviceInfo* l_provisioned_device_info = + device_info_map.FindOrNull(system_id); + if (l_provisioned_device_info == nullptr) { + return WvPLStatus( + error_space, widevine::UNSUPPORTED_SYSTEM_ID, + "Widevine SystemID does not exist because it is not found " + "in the device info map"); + } + *provisioned_device_info = *l_provisioned_device_info; + return status; +} + +WvPLStatus WvPLSDKEnvironment::UpdateProvisionedDeviceInfoMap( + const DeviceCertificateStatusList& certificate_status_list) { + WvPLStatus status = util::OkStatus(); + for (const DeviceCertificateStatus& cert_status : + certificate_status_list.certificate_status()) { + if (cert_status.has_device_info()) { + const ProvisionedDeviceInfo device_info = cert_status.device_info(); + if (device_info.has_system_id()) { + ProvisionedDeviceInfoMap& device_info_map = + GetProvisionedDeviceInfoMap(); + // TODO(user): Parse DeviceState from device to decide whether + // overwrite device_info or not. + device_info_map.InsertOrUpdate(device_info); + } else { + return WvPLStatus(error_space, + widevine::INVALID_CERTIFICATE_STATUS_LIST, + "device-info-missing-system-id"); + } + } + } + return status; +} + +void WvPLSDKEnvironment::AddDeviceInfo( + const ProvisionedDeviceInfo& provisioned_device_info) { + ProvisionedDeviceInfoMap& device_info_map = GetProvisionedDeviceInfoMap(); + device_info_map.InsertOrUpdate(provisioned_device_info); +} + +WvPLStatus WvPLSDKEnvironment::GenerateSignature(const std::string& plain_text, + std::string* signature) { + DCHECK(signature); + if (plain_text.empty()) { + return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, + "Plain_text for signature is empty."); + } + if (signature == nullptr) { + return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, + "Signature must not be null."); + } + const std::map* config_values = GetConfigValue(); + std::map::const_iterator it = + config_values->find(kDrmCertificateType); + it = config_values->find(kProviderIv); + std::string provider_iv; + std::string provider_key; + if (it != config_values->end()) { + provider_iv = absl::HexStringToBytes((*it).second); + if (provider_iv.empty()) { + return WvPLStatus(error_space, util::error::NOT_FOUND, + "Provider IV is empty."); + } + } + it = config_values->find(kProviderKey); + if (it != config_values->end()) { + provider_key = absl::HexStringToBytes((*it).second); + if (provider_key.empty()) { + return WvPLStatus(error_space, util::error::NOT_FOUND, + "Provider Key is empty."); + } + } + + std::string hashed_text = widevine::Sha1_Hash(plain_text); + if (hashed_text.empty()) { + return util::Status(error_space, util::error::INVALID_ARGUMENT, + "Hash for signature is empty."); + } else { + *signature = EncryptAesCbc(provider_key, provider_iv, hashed_text); + if (signature->empty()) { + return util::Status(error_space, util::error::INVALID_ARGUMENT, + "Generated signature failed"); + } + } + return util::OkStatus(); +} +std::map* WvPLSDKEnvironment::config_values_ = + new std::map(); +std::map* WvPLSDKEnvironment::GetConfigValue() { + return config_values_; +} + +void WvPLSDKEnvironment::SetConfigValue( + const std::map& config_values) { + config_values_->insert(config_values.begin(), config_values.end()); +} +} // namespace wv_pl_sdk +} // namespace widevine_server diff --git a/sdk/external/common/wvpl/wvpl_sdk_environment.h b/sdk/external/common/wvpl/wvpl_sdk_environment.h new file mode 100644 index 0000000..6a6d3ca --- /dev/null +++ b/sdk/external/common/wvpl/wvpl_sdk_environment.h @@ -0,0 +1,132 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_ENVIRONMENT_H_ +#define SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_ENVIRONMENT_H_ + +#include + +#include "common/certificate_type.h" +#include "sdk/external/common/wvpl/wvpl_types.h" +#include "protos/public/device_certificate_status.pb.h" +#include "protos/public/provisioned_device_info.pb.h" + +namespace widevine_server { +namespace wv_pl_sdk { + +// These fields show the configuration options that can be initialized via +// the implementation classes (WvPLEnvironment and WvPLProxyEnvironment). +const char kDeviceCertificateExpiration[] = "device_certificate_expiration"; +const char kAllowUnknownDevice[] = "allow_unknown_device"; +const char kProvider[] = "provider"; +const char kProviderIv[] = "provider_iv"; +const char kProviderKey[] = "provider_key"; +const char kApiVerInKcb[] = "api_ver_in_kcb"; +const char kLimitUsageStatsToErrorsOnly[] = "limit_usage_stats_to_errors_only"; +// Valid values are 'test' and 'prod'. +const char kDrmCertificateType[] = "drm_certificate_type"; + +/** + * Parent class of SDK environment. This class is not be instantiated directly, + * but its API can be accessed via the derived environment classes. + */ +class WvPLSDKEnvironment { + public: + virtual ~WvPLSDKEnvironment() {} + static void SetConfigValue(const std::map& config_values); + // Generates a license response containing a message generated in response to + // an error condition. |create_session_status| is a previous error status + // returned by the CreateSession(). |license_response| points to a std::string to + // contain the license response and may not be NULL. This method returns true + // if there is an error license to be sent to the client, or false + // otherwise. + static bool GenerateErrorResponse(const WvPLStatus& create_session_status, + std::string* license_response); + + /** + * Add a service certificate system-wide at the sdk. |service_certificate| + * is a Google-generated certificate used to authenticate the service + * provider. |service_private_key| is the encrypted PKCS#8 private RSA key + * corresponding to the service certificate. |service_private_key_passphrase| + * is the password required to decrypt |service_private_key|. This is a + * thread-safe call. + * + * @param service_certificate + * @param service_private_key + * @param service_private_key_passphrase + * + * @return WvPLStatus enumeration + */ + virtual WvPLStatus SetDrmServiceCertificate( + const std::string& service_certificate, const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + // Returns the DRM Root Certificate type. This would be a setting passed into + // the environment, by a derived class constructor. + virtual std::string GetDrmCertificateType() { return drm_certificate_type_; } + + protected: + // Return the signature for the provider specified in the |config_values| + // parameter in the constructor. |signature| is owned by the caller. + static WvPLStatus GenerateSignature(const std::string& plain_text, + std::string* signature); + /** + * Insert or update provisionedDeviceInfoMap with device info in + * certificate_status_list. + */ + static WvPLStatus UpdateProvisionedDeviceInfoMap( + const widevine::DeviceCertificateStatusList& + certificate_status_list); + + // Number of seconds until the certificate status list expires after its + // creation time. Default value is 604800 seconds. + uint32_t device_certificate_expiration_seconds_ = 604800; + // "config_values" setting for "kDrmCertificateType". + // Supported values are "test" and "prod". Default value is "prod". + std::string drm_certificate_type_ = "prod"; + // name of the provider hosting this service. + std::string provider_; + // value of the "iv" specified for the provider. + std::string* provider_iv_; + // value of the "key" specified for the provider. + std::string* provider_key_; + static std::map* config_values_; + // is_service_certificate_loaded_ is not thread safe. + bool is_service_certificate_loaded_ = false; + // If true, allow devices not in the certificate status list. + bool allow_unknown_device_ = false; + // DRM Certificate type. + widevine::CertificateType certificate_type_ = + widevine::kCertificateTypeProduction; + + private: + /** + * Return provisioned_device_info if the device_info_map_ contains system_id. + * + * @return WvPLStatus - Status::OK if success, else error. + */ + static WvPLStatus LookupDeviceInfo( + uint32_t system_id, + widevine::ProvisionedDeviceInfo* provisioned_device_info); + + /** + * Add a device to the current environment/session. + */ + static void AddDeviceInfo( + const widevine::ProvisionedDeviceInfo& provisioned_device_info); + + static std::map* GetConfigValue(); + friend class WvPLSDKSession; + friend class WvPLProxySessionTest; + friend class WvPLSessionTest; +}; + +} // namespace wv_pl_sdk +} // namespace widevine_server + +#endif // SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_ENVIRONMENT_H_ diff --git a/sdk/external/common/wvpl/wvpl_sdk_session.cc b/sdk/external/common/wvpl/wvpl_sdk_session.cc new file mode 100644 index 0000000..152faa0 --- /dev/null +++ b/sdk/external/common/wvpl/wvpl_sdk_session.cc @@ -0,0 +1,701 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "sdk/external/common/wvpl/wvpl_sdk_session.h" + +#include "glog/logging.h" +#include "util/status.h" +#include "absl/memory/memory.h" +#include "common/drm_service_certificate.h" +#include "common/error_space.h" +#include "common/remote_attestation_verifier.h" +#include "common/verified_media_pipeline.h" +#include "license_server_sdk/internal/client_cert.h" +#include "sdk/external/common/wvpl/wvpl_sdk_environment.h" +#include "sdk/external/common/wvpl/wvpl_types.h" +#include "protos/public/errors.pb.h" + +// TODO(user): Mark getProvisionedDeviceInfo as deprecated, move the +// implementation of isChromeCDM, getcontentid, parsePsshdata in wvpl_session +// and wvpl_proxy_session to base class. +// TODO(user): Set SerialNumber and ServiceId field of WvPLDeviceInfo in +// getDeviceInfo function. +// TODO(user): Move the related Copy* function test cases to +// wvpl_sdk_session_test.cc. +// TODO(user): Remove sdk_license_request_ and both proxy and wvpl LSDK set +// signed_message_request_from_cdm_ when create session. +namespace util = widevine::util; +using widevine::ClientIdentification; +using widevine::DrmServiceCertificate; +using widevine::error_space; +using widevine::KeyboxClientCert; +using widevine::License; +using widevine::LicenseRequest; +using widevine::ProvisionedDeviceInfo; +using widevine::RemoteAttestationVerifier; +using widevine::SessionInit; +using widevine::SessionState; +using widevine::SignedMessage; + +namespace widevine_server { +namespace wv_pl_sdk { + +WvPLSDKSession::~WvPLSDKSession() {} + +WvPLStatus WvPLSDKSession::AddKey(const WvPLKey& key) { + keys_.push_back(key); + return util::OkStatus(); +} + +void WvPLSDKSession::CopyPlaybackPolicy(const WvPLPlaybackPolicy& wvpl_policy, + License::Policy* sdk_policy) { + DCHECK(sdk_policy); + if (sdk_policy == NULL) { + return; + } + // Copy the policy information. + sdk_policy->set_can_play(wvpl_policy.can_play()); + sdk_policy->set_can_persist(wvpl_policy.can_persist()); + sdk_policy->set_can_renew(wvpl_policy.can_renew()); + sdk_policy->set_playback_duration_seconds( + wvpl_policy.playback_duration_seconds()); + sdk_policy->set_license_duration_seconds( + wvpl_policy.license_duration_seconds()); + sdk_policy->set_renewal_delay_seconds(wvpl_policy.renewal_interval_seconds()); + sdk_policy->set_renewal_recovery_duration_seconds( + wvpl_policy.renewal_recovery_duration_seconds()); + sdk_policy->set_renewal_retry_interval_seconds( + wvpl_policy.renewal_retry_interval_seconds()); + sdk_policy->set_renewal_server_url(wvpl_policy.renewal_url()); + sdk_policy->set_renew_with_usage(wvpl_policy.renew_with_usage()); +} + +void WvPLSDKSession::CopySecurityLevel( + const WvPLOutputProtection& output_protection, TrackType track_type, + License::KeyContainer* key_container) { + switch (output_protection.security_level()) { + case SECURITY_LEVEL_UNDEFINED: + if (output_protection.secure_data_path()) { + key_container->set_level(License::KeyContainer::HW_SECURE_ALL); + } else { + if (IsChromeCDM() && track_type != AUDIO) { + key_container->set_level(License::KeyContainer::SW_SECURE_DECODE); + } else { + key_container->set_level(License::KeyContainer::SW_SECURE_CRYPTO); + } + } + break; + default: + key_container->set_level( + static_cast( + output_protection.security_level())); + break; + } +} + +void WvPLSDKSession::CopySessionInit(const WvPLSessionInit& wvpl_session_init, + SessionInit* session_init) { + DCHECK(session_init); + if (session_init == NULL) { + return; + } + if (wvpl_session_init.license_start_time() > 0) { + session_init->set_license_start_time( + wvpl_session_init.license_start_time()); + } + if (!wvpl_session_init.session_id().empty()) { + session_init->set_session_id(wvpl_session_init.session_id()); + } + if (!wvpl_session_init.purchase_id().empty()) { + session_init->set_purchase_id(wvpl_session_init.purchase_id()); + } + if (!wvpl_session_init.master_signing_key().empty()) { + session_init->set_master_signing_key( + wvpl_session_init.master_signing_key()); + } + if (!wvpl_session_init.provider_client_token().empty()) { + session_init->set_provider_client_token( + wvpl_session_init.provider_client_token()); + } + if (!wvpl_session_init.provider_session_token().empty()) { + session_init->set_provider_session_token( + wvpl_session_init.provider_session_token()); + } + session_init->set_override_provider_client_token( + wvpl_session_init.override_provider_client_token()); +} + +void WvPLSDKSession::CopySessionState( + const WvPLSessionState& wvpl_session_state, + widevine::SessionState* session_state) { + DCHECK(session_state); + if (session_state == nullptr) { + return; + } + if (wvpl_session_state.license_counter() > 0) { + session_state->set_license_counter(wvpl_session_state.license_counter()); + } + if (wvpl_session_state.keybox_system_id() > 0) { + session_state->set_keybox_system_id(wvpl_session_state.keybox_system_id()); + } + if (!wvpl_session_state.signing_key().empty()) { + session_state->set_signing_key(wvpl_session_state.signing_key()); + } + if (!wvpl_session_state.provider_client_token().empty()) { + session_state->set_provider_client_token( + wvpl_session_state.provider_client_token()); + } + widevine::LicenseIdentification* license_id = + session_state->mutable_license_id(); + widevine_server::wv_pl_sdk::LicenseIdentification wvpl_license_id = + wvpl_session_state.license_id(); + LicenseType license_type = wvpl_license_id.type(); + switch (license_type) { + case STREAMING: + license_id->set_type(widevine::LicenseType::STREAMING); + break; + case OFFLINE: + license_id->set_type(widevine::LicenseType::OFFLINE); + break; + default: + break; + } + if (wvpl_license_id.version() > 0) { + license_id->set_version(wvpl_license_id.version()); + } + if (!wvpl_license_id.request_id().empty()) { + license_id->set_request_id(wvpl_license_id.request_id()); + } + if (!wvpl_license_id.session_id().empty()) { + license_id->set_session_id(wvpl_license_id.session_id()); + } + if (!wvpl_license_id.purchase_id().empty()) { + license_id->set_purchase_id(wvpl_license_id.purchase_id()); + } + if (!wvpl_license_id.provider_session_token().empty()) { + license_id->set_provider_session_token( + wvpl_license_id.provider_session_token()); + } +} + +MessageType WvPLSDKSession::message_type() const { return message_type_; } + +void WvPLSDKSession::CopyHDCP( + HDCP hdcp_value, + License::KeyContainer::OutputProtection* output_protection) { + DCHECK(output_protection); + if (!output_protection) { + return; + } + switch (hdcp_value) { + case HDCP_V1: + output_protection->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V1); + break; + case HDCP_V2: + output_protection->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V2); + break; + case HDCP_V2_1: + output_protection->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V2_1); + break; + case HDCP_V2_2: + output_protection->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V2_2); + break; + case HDCP_V2_3: + output_protection->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_V2_3); + break; + case HDCP_NONE: + output_protection->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_NONE); + break; + default: + break; + } +} + +void WvPLSDKSession::CopyHDCPToClientCapabilities( + ClientIdentification::ClientCapabilities::HdcpVersion hdcp_value, + WvPLClientCapabilities* client_capabilities) const { + DCHECK(client_capabilities); + if (!client_capabilities) { + return; + } + switch (hdcp_value) { + case ClientIdentification::ClientCapabilities::HDCP_NONE: + client_capabilities->set_max_hdcp_version(HDCP_NONE); + break; + case ClientIdentification::ClientCapabilities::HDCP_V1: + client_capabilities->set_max_hdcp_version(HDCP_V1); + break; + case ClientIdentification::ClientCapabilities::HDCP_V2: + client_capabilities->set_max_hdcp_version(HDCP_V2); + break; + case ClientIdentification::ClientCapabilities::HDCP_V2_1: + client_capabilities->set_max_hdcp_version(HDCP_V2_1); + break; + case ClientIdentification::ClientCapabilities::HDCP_V2_2: + client_capabilities->set_max_hdcp_version(HDCP_V2_2); + break; + case ClientIdentification::ClientCapabilities::HDCP_V2_3: + client_capabilities->set_max_hdcp_version(HDCP_V2_3); + break; + case ClientIdentification::ClientCapabilities::HDCP_NO_DIGITAL_OUTPUT: + client_capabilities->set_max_hdcp_version(HDCP_NO_DIGITAL_OUTPUT); + break; + default: + break; + } +} + +void WvPLSDKSession::CopyAnalogOutputCapabilities( + ClientIdentification::ClientCapabilities::AnalogOutputCapabilities + analog_output_capabilities, + WvPLClientCapabilities* client_capabilities) const { + DCHECK(client_capabilities); + if (!client_capabilities) { + return; + } + switch (analog_output_capabilities) { + case ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_UNKNOWN: + client_capabilities->set_analog_output_capabilities( + ANALOG_OUTPUT_UNKNOWN); + break; + case ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_NONE: + client_capabilities->set_analog_output_capabilities(ANALOG_OUTPUT_NONE); + break; + case ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_SUPPORTED: + client_capabilities->set_analog_output_capabilities( + ANALOG_OUTPUT_SUPPORTED); + break; + case ClientIdentification::ClientCapabilities:: + ANALOG_OUTPUT_SUPPORTS_CGMS_A: + client_capabilities->set_analog_output_capabilities( + ANALOG_OUTPUT_SUPPORTS_CGMS_A); + break; + default: + break; + } +} + +void WvPLSDKSession::CopyOutputProtection( + const WvPLKey& wvpl_key, License::KeyContainer* sdk_key_container) { + DCHECK(sdk_key_container); + if (!sdk_key_container) { + return; + } + // Transfer HDCP from required output protection value. + CopyHDCP(wvpl_key.output_protection().hdcp(), + sdk_key_container->mutable_required_protection()); + + // Transfer HDCP from requested output protection value. + CopyHDCP(wvpl_key.requested_output_protection().hdcp(), + sdk_key_container->mutable_requested_protection()); + + CopySecurityLevel(wvpl_key.output_protection(), wvpl_key.track_type(), + sdk_key_container); + + sdk_key_container->mutable_requested_protection()->set_disable_analog_output( + wvpl_key.output_protection().disable_analog_output()); + + if (wvpl_key.output_protection().disable_digital_output()) { + sdk_key_container->mutable_required_protection()->set_hdcp( + License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT); + } + CopyCGMS(wvpl_key.output_protection().cgms(), + sdk_key_container->mutable_requested_protection()); + + // Transfer Video resultion constraints. + const std::vector& video_constraints = + wvpl_key.video_resolution_constraints(); + for (std::vector::const_iterator it = + video_constraints.begin(); + it != video_constraints.end(); ++it) { + License::KeyContainer::VideoResolutionConstraint* video_constraint = + sdk_key_container->mutable_video_resolution_constraints()->Add(); + video_constraint->set_min_resolution_pixels(it->min_resolution_pixels()); + video_constraint->set_max_resolution_pixels(it->max_resolution_pixels()); + CopyHDCP(it->hdcp(), video_constraint->mutable_required_protection()); + } +} + +void WvPLSDKSession::CopyCGMS( + CGMS cgms_value, + License::KeyContainer::OutputProtection* output_protection) { + DCHECK(output_protection); + if (output_protection == nullptr) { + return; + } + switch (cgms_value) { + case CGMS_NONE: + output_protection->set_cgms_flags( + License::KeyContainer::OutputProtection::CGMS_NONE); + break; + case COPY_FREE: + output_protection->set_cgms_flags( + License::KeyContainer::OutputProtection::COPY_FREE); + break; + case COPY_ONCE: + output_protection->set_cgms_flags( + License::KeyContainer::OutputProtection::COPY_ONCE); + break; + case COPY_NEVER: + output_protection->set_cgms_flags( + License::KeyContainer::OutputProtection::COPY_NEVER); + break; + default: + break; + } +} + +void WvPLSDKSession::CopyProvisionedDeviceInfo( + const WvPLDeviceInfo& wvpl_device_info, + widevine::ProvisionedDeviceInfo* device_info) { + DCHECK(device_info); + if (device_info == nullptr) { + return; + } + device_info->set_system_id(wvpl_device_info.system_id()); + device_info->set_soc(wvpl_device_info.soc()); + device_info->set_manufacturer(wvpl_device_info.manufacturer()); + + device_info->set_model(wvpl_device_info.model()); + device_info->set_device_type(wvpl_device_info.device_type()); + + device_info->set_test_device(wvpl_device_info.test_device()); + switch (wvpl_device_info.security_level()) { + case DEVICE_LEVEL_UNSPECIFIED: + device_info->set_security_level(ProvisionedDeviceInfo::LEVEL_UNSPECIFIED); + break; + case DEVICE_LEVEL_1: + device_info->set_security_level(ProvisionedDeviceInfo::LEVEL_1); + break; + case DEVICE_LEVEL_2: + device_info->set_security_level(ProvisionedDeviceInfo::LEVEL_2); + break; + case DEVICE_LEVEL_3: + device_info->set_security_level(ProvisionedDeviceInfo::LEVEL_3); + break; + default: + break; + } +} + +WvPLStatus WvPLSDKSession::ParseLicenseRequest() { + WvPLStatus status = util::OkStatus(); + if (license_request_from_cdm_.empty() && sdk_license_request_ == nullptr) { + return WvPLStatus(error_space, widevine::SIGNED_MESSAGE_PARSE_ERROR, + "License request from cdm is empty or null"); + } + // This is used by proxy sdk only. + if (!license_request_from_cdm_.empty()) { + signed_message_request_from_cdm_ = absl::make_unique(); + if (!signed_message_request_from_cdm_->ParseFromString( + license_request_from_cdm_)) { + return WvPLStatus(error_space, widevine::SIGNED_MESSAGE_PARSE_ERROR, + "Unable to parse license request from CDM into " + "SignedMessage"); + } + } + // This is used by proxy sdk only. + if (signed_message_request_from_cdm_ != nullptr) { + switch (signed_message_request_from_cdm_->type()) { + case SignedMessage::LICENSE_REQUEST: + message_type_ = LICENSE_REQUEST; + DCHECK(signed_message_request_from_cdm_); + sdk_license_request_ = absl::make_unique(); + if (!sdk_license_request_->ParseFromString( + signed_message_request_from_cdm_->msg())) { + return WvPLStatus(error_space, + widevine::LICENSE_REQUEST_PARSE_ERROR, + "Unable to parse license request from CDM into " + "LicenseRequest"); + } + break; + case SignedMessage::SERVICE_CERTIFICATE_REQUEST: + message_type_ = SERVICE_CERTIFICATE_REQUEST; + break; + default: + break; + } + } + // This code is used by proxy and wvpl license server sdk. + if (message_type_ == LICENSE_REQUEST) { + // Parse for ClientIdentification in license request. + if (sdk_license_request_->has_client_id() && + sdk_license_request_->has_encrypted_client_id()) { + return WvPLStatus( + error_space, widevine::MULTIPLE_CLIENT_ID, + "ClientIdentification and EncryptedClientIdentification " + "are both specified in the license request"); + } + if (sdk_license_request_->has_client_id()) { + client_id_ = sdk_license_request_->client_id(); + has_client_id_ = true; + } else if (sdk_license_request_->has_encrypted_client_id()) { + if (!DrmServiceCertificate::DecryptClientIdentification( + sdk_license_request_->encrypted_client_id(), &client_id_) + .ok()) { + return WvPLStatus( + error_space, + widevine::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "Unable to decrypt EncryptedClientIdentification in " + "the license request"); + } + has_client_id_ = true; + } + if (client_id_.has_token()) { + // Get system_id from token field in ClientIdentification. + system_id_ = KeyboxClientCert::GetSystemId(client_id_.token()); + has_system_id_ = true; + } + // TODO(user): Consider enforcing missing client id here. + + // Verifies platform for license requests and sets Platform Verification + // status. + platform_verification_status_ = VerifyPlatform(); + if (sdk_license_request_->type() == LicenseRequest::NEW) { + WvPLWidevinePsshData wvpl_widevine_pssh_data; + status = GetPsshData(&wvpl_widevine_pssh_data); + if (status.ok()) { + pssh_data_ = wvpl_widevine_pssh_data; + has_pssh_data_ = true; + } + } + request_type_ = sdk_license_request_->type(); + } + return util::OkStatus(); +} + +bool WvPLSDKSession::IsChromeCDM() const { + WvPLDeviceInfo sdk_device_info; + if (GetDeviceInfo(&sdk_device_info).ok()) { + return (sdk_device_info.model().find("ChromeCDM-") != std::string::npos); + } + return false; +} + +WvPLStatus WvPLSDKSession::GetClientInfo(WvPLClientInfo* client_info) const { + WvPLStatus status = util::OkStatus(); + if (client_info == nullptr) { + return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, + "client_info is NULL"); + } + if (!has_client_id_) { + return WvPLStatus( + error_space, widevine::MISSING_CLIENT_ID, + "ClientIdentification is not found in the license request"); + } + switch (client_id_.client_capabilities().max_hdcp_version()) { + case ClientIdentification::ClientCapabilities::HDCP_NONE: + client_info->set_max_hdcp_version(HDCP_NONE); + break; + case ClientIdentification::ClientCapabilities::HDCP_V1: + client_info->set_max_hdcp_version(HDCP_V1); + break; + case ClientIdentification::ClientCapabilities::HDCP_V2: + client_info->set_max_hdcp_version(HDCP_V2); + break; + case ClientIdentification::ClientCapabilities::HDCP_V2_1: + client_info->set_max_hdcp_version(HDCP_V2_1); + break; + case ClientIdentification::ClientCapabilities::HDCP_V2_2: + client_info->set_max_hdcp_version(HDCP_V2_2); + break; + case ClientIdentification::ClientCapabilities::HDCP_V2_3: + client_info->set_max_hdcp_version(HDCP_V2_3); + break; + case ClientIdentification::ClientCapabilities::HDCP_NO_DIGITAL_OUTPUT: + client_info->set_max_hdcp_version(HDCP_NO_DIGITAL_OUTPUT); + break; + default: + break; + } + if (client_id_.client_capabilities().has_oem_crypto_api_version()) { + client_info->set_oem_crypto_api_version( + client_id_.client_capabilities().oem_crypto_api_version()); + } + if (client_id_.has_provider_client_token()) { + client_info->set_provider_client_token(client_id_.provider_client_token()); + } + for (const auto& name_value : client_id_.client_info()) { + client_info->set_name_value(name_value.name(), name_value.value()); + } + WvPLClientCapabilities client_capabilities; + status = GetClientCapabilities(&client_capabilities); + client_info->set_client_capabilities(client_capabilities); + return status; +} + +WvPLStatus WvPLSDKSession::VerifyRemoteAttestation() { + DCHECK(signed_message_request_from_cdm_); + if (!signed_message_request_from_cdm_->has_remote_attestation()) { + return WvPLStatus(error_space, util::error::INTERNAL, + "Remote Attestation not specified"); + } else if (!signed_message_request_from_cdm_->remote_attestation() + .has_certificate()) { + return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, + "Remote Attestation Certificate not specified"); + } else { + WvPLStatus ra_status = + RemoteAttestationVerifier::get().VerifyRemoteAttestation( + signed_message_request_from_cdm_->msg(), + signed_message_request_from_cdm_->remote_attestation(), + &remote_attestation_cert_serial_number_); + } + return util::OkStatus(); +} + +WvPLStatus WvPLSDKSession::GetClientCapabilities( + WvPLClientCapabilities* client_capabilities) const { + WvPLStatus status = util::OkStatus(); + if (client_capabilities == nullptr) { + return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, + "client_capabilities is NULL"); + } + if (!has_client_id_) { + return WvPLStatus( + error_space, widevine::MISSING_CLIENT_ID, + "ClientIdentification is not found in the license request"); + } + status = GetWvPLClientCapabilities(client_id_, client_capabilities); + return status; +} + +WvPLStatus WvPLSDKSession::GetWvPLClientCapabilities( + const widevine::ClientIdentification& client_id, + WvPLClientCapabilities* client_capabilities) const { + WvPLStatus status = util::OkStatus(); + if (client_capabilities == nullptr) { + return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, + "client_capabilities is NULL"); + } + + CopyHDCPToClientCapabilities( + client_id.client_capabilities().max_hdcp_version(), client_capabilities); + + if (client_id.client_capabilities().has_oem_crypto_api_version()) { + client_capabilities->set_oem_crypto_api_version( + client_id.client_capabilities().oem_crypto_api_version()); + } + if (client_id.client_capabilities().has_can_disable_analog_output()) { + client_capabilities->set_can_disable_analog_output(true); + } + if (client_id.has_provider_client_token()) { + client_capabilities->set_client_token(true); + } + if (client_id.has_token()) { + client_capabilities->set_session_token(true); + } + if (client_id.client_capabilities().has_video_resolution_constraints()) { + client_capabilities->set_video_resolution_constraints(true); + } + + if (client_id.client_capabilities().has_anti_rollback_usage_table()) { + client_capabilities->set_anti_rollback_usage_table(true); + } + if (client_id.client_capabilities().has_srm_version()) { + client_capabilities->set_srm_version( + client_id.client_capabilities().srm_version()); + } + if (client_id.client_capabilities().has_can_update_srm()) { + client_capabilities->set_can_update_srm( + client_id.client_capabilities().can_update_srm()); + } + CopyAnalogOutputCapabilities( + client_id.client_capabilities().analog_output_capabilities(), + client_capabilities); + + for (const auto& key_type : + client_id.client_capabilities().supported_certificate_key_type()) { + switch (key_type) { + case ClientIdentification::ClientCapabilities::RSA_2048: + client_capabilities->add_supported_certificate_key_type(RSA_2048); + break; + case ClientIdentification::ClientCapabilities::RSA_3072: + client_capabilities->add_supported_certificate_key_type(RSA_3072); + break; + default: + break; + } + } + return status; +} + +WvPLStatus WvPLSDKSession::GetPsshData( + WvPLWidevinePsshData* wvpl_widevine_pssh_data) const { + DCHECK(wvpl_widevine_pssh_data); + if (wvpl_widevine_pssh_data == nullptr) { + return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, + "wvpl_widevine_pssh_data is null"); + } + if (!has_pssh_data_) { + // TODO(b/115784237): Fix calling ParsePsshData within base class. + WvPLStatus status = ParsePsshData(wvpl_widevine_pssh_data); + return status; + } + *wvpl_widevine_pssh_data = pssh_data_; + return util::OkStatus(); +} + +WvPLStatus WvPLSDKSession::GetDeviceInfo(WvPLDeviceInfo* device_info) const { + WvPLStatus status = util::OkStatus(); + if (device_info == nullptr) { + return WvPLStatus(error_space, util::error::INVALID_ARGUMENT, + "device_info is NULL"); + } + if (!has_system_id_) { + return WvPLStatus( + error_space, widevine::UNSUPPORTED_SYSTEM_ID, + "Widevine SystemID does not exist because it is not found " + "in the license request"); + } + ProvisionedDeviceInfo provisioned_device_info; + status = WvPLSDKEnvironment::LookupDeviceInfo(system_id_, + &provisioned_device_info); + if (!status.ok()) { + return status; + } + device_info->set_system_id(provisioned_device_info.system_id()); + if (provisioned_device_info.has_soc()) { + device_info->set_soc(provisioned_device_info.soc()); + } + if (provisioned_device_info.has_model()) { + device_info->set_model(provisioned_device_info.model()); + } + if (provisioned_device_info.has_device_type()) { + device_info->set_device_type(provisioned_device_info.device_type()); + } + if (provisioned_device_info.has_test_device()) { + device_info->set_test_device(provisioned_device_info.test_device()); + } + if (provisioned_device_info.has_manufacturer()) { + device_info->set_manufacturer(provisioned_device_info.manufacturer()); + } + switch (provisioned_device_info.security_level()) { + case ProvisionedDeviceInfo::LEVEL_UNSPECIFIED: + device_info->set_security_level(DEVICE_LEVEL_UNSPECIFIED); + break; + case ProvisionedDeviceInfo::LEVEL_1: + device_info->set_security_level(DEVICE_LEVEL_1); + break; + case ProvisionedDeviceInfo::LEVEL_2: + device_info->set_security_level(DEVICE_LEVEL_2); + break; + case ProvisionedDeviceInfo::LEVEL_3: + device_info->set_security_level(DEVICE_LEVEL_3); + break; + default: + break; + } + return status; +} +} // namespace wv_pl_sdk +} // namespace widevine_server diff --git a/sdk/external/common/wvpl/wvpl_sdk_session.h b/sdk/external/common/wvpl/wvpl_sdk_session.h new file mode 100644 index 0000000..971a64e --- /dev/null +++ b/sdk/external/common/wvpl/wvpl_sdk_session.h @@ -0,0 +1,198 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_SESSION_H_ +#define SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_SESSION_H_ + +#include "sdk/external/common/wvpl/wvpl_types.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/device_certificate_status.pb.h" +#include "protos/public/license_protocol.pb.h" +#include "protos/public/license_server_sdk.pb.h" +#include "protos/public/provisioned_device_info.pb.h" + +namespace widevine { +class SessionInit; +} +namespace widevine_server { +namespace wv_pl_sdk { + +class WvPLSDKSession { + public: + virtual ~WvPLSDKSession() = 0; + + public: + // Add WvPLKey. + virtual WvPLStatus AddKey(const WvPLKey& key); + + // Get the WvPLKey. + virtual const std::vector& key() const { return keys_; } + + // Set the license policy. + virtual void set_policy(const WvPLPlaybackPolicy& policy) { + policy_ = policy; + } + + // Get the license policy. + virtual const WvPLPlaybackPolicy& policy() const { return policy_; } + + // Set the Session Init. + virtual void set_session_init(const WvPLSessionInit& session_init) { + session_init_ = session_init; + } + + // Get the Session Init. + virtual const WvPLSessionInit& session_init() const { return session_init_; } + + virtual bool IsChromeCDM() const; + + /** + * Returns the Widevine PSSH data for the license request handled by this + * session. + * + * @param wvpl_widevine_pssh_data. + * @return WvPLStatus - Status::OK if success, else error. + */ + virtual WvPLStatus GetPsshData( + WvPLWidevinePsshData* wvpl_widevine_pssh_data) const; + + /** + * Returns the ClientIdentification information for the license request + * handled by this session. + * + * @param client_info + * @return WvPLStatus - Status::OK if success, else error. + */ + virtual WvPLStatus GetClientInfo(WvPLClientInfo* client_info) const; + /** + * Returns the WvPL Client Capabilities information for the license request + * handled by this session. + * + * @param client_capabilities. + * @return WvPLStatus - Status::OK if success, else error. + */ + virtual WvPLStatus GetClientCapabilities( + WvPLClientCapabilities* client_capabilities) const; + + /** + * Returns the WvPLDeviceInfo information for the license request + * handled by this session. + * + * @param device_info + * @return WvPLStatus - Status::OK if success, else error. + */ + virtual WvPLStatus GetDeviceInfo(WvPLDeviceInfo* device_info) const; + + /** + * Returns the type of the message handled by this session. + * + * @return MessageType enumeration. + */ + virtual MessageType message_type() const; + + virtual PlatformVerificationStatus VerifyPlatform() = 0; + + virtual widevine::LicenseRequest::RequestType request_type() const { + return request_type_; + } + + protected: + uint32_t system_id_ = 0xFFFFFFFF; + std::string user_agent_; + std::vector keys_; + WvPLPlaybackPolicy policy_; + WvPLSessionInit session_init_; + WvPLWidevinePsshData pssh_data_; + widevine::ClientIdentification client_id_; + bool has_pssh_data_ = false; + bool has_system_id_ = false; + bool has_client_id_ = false; + MessageType message_type_ = UNKNOWN; + PlatformVerificationStatus platform_verification_status_ = + PLATFORM_NO_VERIFICATION; + std::unique_ptr + signed_message_request_from_cdm_; + std::string license_request_from_cdm_; + std::string remote_attestation_cert_serial_number_; + std::unique_ptr sdk_license_request_; + widevine::LicenseRequest::RequestType request_type_; + virtual WvPLStatus VerifyRemoteAttestation(); + + // Returns the WvPL Client Capabilities information for the license request + // handled by this session. + WvPLStatus GetWvPLClientCapabilities( + const widevine::ClientIdentification& client_id, + WvPLClientCapabilities* client_capabilities) const; + + // Copies/translates the output protection from a WvPL Key into an SDK + // key container. + void CopyOutputProtection( + const WvPLKey& wvpl_key, + widevine::License_KeyContainer* sdk_key_container); + virtual void CopySecurityLevel( + const WvPLOutputProtection& output_protection, TrackType track_type, + widevine::License::KeyContainer* key_container); + + // Copies/translates the policy from a WvPL policy into an SDK policy. A + // helper function for GenerateLicenseRequestAsJSON. + virtual void CopyPlaybackPolicy(const WvPLPlaybackPolicy& wvpl_policy, + widevine::License::Policy* sdk_policy); + + // Copy the |cgms_value| into the key container. A helper function for + // GenerateLicenseRequestAsJSON. + virtual void CopyCGMS(CGMS cgms_value, + widevine::License::KeyContainer::OutputProtection* + output_protection); + + // Copy the |hdcp_value| into the key container. + virtual void CopyHDCP(HDCP hdcp_value, + widevine::License::KeyContainer::OutputProtection* + output_protection); + + // Copy the |hdcp_value| into the client_capabilities. + virtual void CopyHDCPToClientCapabilities( + widevine::ClientIdentification::ClientCapabilities::HdcpVersion + hdcp_value, + WvPLClientCapabilities* client_capabilities) const; + + // Copy the |analog_output_capabilities| into the client_capabilities. + virtual void CopyAnalogOutputCapabilities( + widevine::ClientIdentification::ClientCapabilities:: + AnalogOutputCapabilities analog_output_capabilities, + WvPLClientCapabilities* client_capabilities) const; + + // Copy the WvPLSession Init into Session Init. + virtual void CopySessionInit(const WvPLSessionInit& wvpl_session_init, + widevine::SessionInit* session_init); + + // Copy the WvPLDeviceInfo into ProvisionedDeviceInfo. + virtual void CopyProvisionedDeviceInfo( + const WvPLDeviceInfo& wvpl_device_info, + widevine::ProvisionedDeviceInfo* device_info); + + // Populate deviceInfo, clientIdentification and psshdata for license request. + WvPLStatus ParseLicenseRequest(); + + // Copy the WvPLSessionState to SessionState. + void CopySessionState(const WvPLSessionState& wvpl_session_state, + widevine::SessionState* session_state); + + private: + /** + * Parses WvPLWidevinePsshData in the new license request. + * + * @return WvPLStatus - Status::OK if success, else error. + */ + virtual WvPLStatus ParsePsshData( + WvPLWidevinePsshData* wvpl_widevine_pssh_data) const = 0; +}; + +} // namespace wv_pl_sdk +} // namespace widevine_server + +#endif // SDK_EXTERNAL_COMMON_WVPL_WVPL_SDK_SESSION_H_ diff --git a/sdk/external/common/wvpl/wvpl_types.h b/sdk/external/common/wvpl/wvpl_types.h new file mode 100644 index 0000000..9fe5236 --- /dev/null +++ b/sdk/external/common/wvpl/wvpl_types.h @@ -0,0 +1,931 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef SDK_EXTERNAL_COMMON_WVPL_WVPL_TYPES_H_ +#define SDK_EXTERNAL_COMMON_WVPL_WVPL_TYPES_H_ + +#include +#include +#include +#include +#include + +#include "util/status.h" + +// TODO(user) Split wvpl_types.h into wvpl_common_types.h , +// wvpl_license_sdk_types.h, wvpl_proxy_sdk_types.h and +// wvpl_packager_sdk_types.h. + +namespace widevine_server { +namespace wv_pl_sdk { +typedef widevine::util::Status WvPLStatus; +typedef uint32_t uint32_t; +typedef int64_t int64_t; + +/** + * Represents the key type for the keys that are used when generating a license. + * The generated license includes a key container, which in turn, contains the + * key material, metadata and policy needed to use the key. + */ +enum KeyType { + UNKNOWN_KEY = 0, + // Key used to decrypt media content. + CONTENT = 1, + // Key used for wrapping content keys. + ENTITLEMENT = 2 +}; + +enum LicenseType { + STREAMING = 1, + OFFLINE = 2, +}; + +/** + * Represents the type of message. This struct is used by Proxy SDK. + */ +enum MessageType { + UNKNOWN = 0, + LICENSE_REQUEST = 1, + SERVICE_CERTIFICATE_REQUEST = 4 +}; + +enum CertificateKeyType { + RSA_2048 = 0, + RSA_3072 = 1, +}; + +enum PlatformVerificationStatus { + PLATFORM_UNVERIFIED = 0, + PLATFORM_TAMPERED = 1, + PLATFORM_SOFTWARE_VERIFIED = 2, + PLATFORM_HARDWARE_VERIFIED = 3, + PLATFORM_NO_VERIFICATION = 4, + PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED = 5 +}; + +// LINT.IfChange +enum HDCP { + HDCP_NONE = 0, + HDCP_V1 = 1, + HDCP_V2 = 2, + HDCP_V2_1 = 3, + HDCP_V2_2 = 4, + HDCP_V2_3 = 5, + HDCP_NO_DIGITAL_OUTPUT = 0xff +}; + +enum SecurityLevel { + SECURITY_LEVEL_UNDEFINED = 0, + SW_SECURE_CRYPTO = 1, + SW_SECURE_DECODE = 2, + HW_SECURE_CRYPTO = 3, + HW_SECURE_DECODE = 4, + HW_SECURE_ALL = 5 +}; + +enum DeviceSecurityLevel { + DEVICE_LEVEL_UNSPECIFIED = 0, + DEVICE_LEVEL_1 = 1, + DEVICE_LEVEL_2 = 2, + DEVICE_LEVEL_3 = 3 +}; + +enum CGMS { + CGMS_NONE = 42, + COPY_FREE = 0, + COPY_ONCE = 2, + COPY_NEVER = 3, +}; + +enum TrackType { + TRACK_TYPE_UNSPECIFIED = 0, + AUDIO = 1, + VIDEO_SD = 2, + VIDEO_HD = 3, + VIDEO_UHD1 = 4, + VIDEO_UHD2 = 5, +}; + +enum AnalogOutputCapabilities { + ANALOG_OUTPUT_UNKNOWN = 0, + ANALOG_OUTPUT_NONE = 1, + ANALOG_OUTPUT_SUPPORTED = 2, + ANALOG_OUTPUT_SUPPORTS_CGMS_A = 3, +}; + +struct WvPLPlaybackPolicy { + WvPLPlaybackPolicy() { + license_duration_seconds_ = 0; + playback_duration_seconds_ = 0; + renewal_interval_seconds_ = 0; + renewal_recovery_duration_seconds_ = 0; + renewal_retry_interval_seconds_ = 0; + can_play_ = true; + can_renew_ = false; + can_persist_ = false; + } + + void set_license_duration_seconds(uint32_t duration) { + license_duration_seconds_ = duration; + } + uint32_t license_duration_seconds() const { return license_duration_seconds_; } + void set_playback_duration_seconds(uint32_t duration) { + playback_duration_seconds_ = duration; + } + uint32_t playback_duration_seconds() const { + return playback_duration_seconds_; + } + void set_renewal_interval_seconds(uint32_t duration) { + renewal_interval_seconds_ = duration; + } + uint32_t renewal_interval_seconds() const { return renewal_interval_seconds_; } + void set_renewal_recovery_duration_seconds(int64_t dur) { + renewal_recovery_duration_seconds_ = dur; + } + int64_t renewal_recovery_duration_seconds() const { + return renewal_recovery_duration_seconds_; + } + void set_renewal_retry_interval_seconds(int64_t retry_interval) { + renewal_retry_interval_seconds_ = retry_interval; + } + int64_t renewal_retry_interval_seconds() const { + return renewal_retry_interval_seconds_; + } + void set_renew_with_usage(bool usage_flag) { renew_with_usage_ = usage_flag; } + bool renew_with_usage() const { return renew_with_usage_; } + void set_renewal_url(const std::string& url) { renewal_url_ = url; } + const std::string& renewal_url() const { return renewal_url_; } + void set_can_play(bool play_flag) { can_play_ = play_flag; } + bool can_play() const { return can_play_; } + void set_can_persist(bool persist_flag) { can_persist_ = persist_flag; } + bool can_persist() const { return can_persist_; } + void set_can_renew(bool renew_flag) { can_renew_ = renew_flag; } + bool can_renew() const { return can_renew_; } + + // The license window. Once a license is granted, the number of seconds to use + // this license. Default is 0, indicating unlimited license. + uint32_t license_duration_seconds_; + + // The playback window. Once initial playback starts, the number of seconds + // to use the license. Default is 0, indicating unlimited but not to exceed + // the license_duration_window. + uint32_t playback_duration_seconds_; + + // How many seconds after license_start_time, before renewal is first + // attempted. + uint32_t renewal_interval_seconds_; + + // The window of time, in which playback is allowed to continue while + // renewal is attempted, yet unsuccessful due to backend problems with + // the license server. + int64_t renewal_recovery_duration_seconds_; + + // Specifies the delay in seconds between subsequent license + // renewal requests, in case of failure. + int64_t renewal_retry_interval_seconds_; + + // Indicates that the license shall be sent for renewal when usage is + // started. + bool renew_with_usage_; + + // Renewal requests are sent to this URL. + std::string renewal_url_; + + // Indicates that playback of ths content is allowed. + bool can_play_; + + // Indicates that the license may be persisted to non-volatile storage for + // offline use. + bool can_persist_; + + // Indicates that renewal of this license is allowed. + bool can_renew_; +}; + +/** + * LicenseIdentification is propagated from LicenseRequest to License, + * incrementing version with each iteration. + * */ +struct LicenseIdentification { + LicenseIdentification() { + version_ = 0; + type_ = STREAMING; + } + + // |request_id| must be specified in bytes. + void set_request_id(const std::string& request_id) { + request_id_ = request_id; + } + + const std::string& request_id() { return request_id_; } + + // |session_id| must be specified in bytes. + void set_session_id(const std::string& session_id) { + session_id_ = session_id; + } + + const std::string& session_id() { return session_id_; } + + // |purchase_id| must be specified in bytes. + void set_purchase_id(const std::string& purchase_id) { + purchase_id_ = purchase_id; + } + + const std::string& purchase_id() { return purchase_id_; } + + void set_type(LicenseType type) { type_ = type; } + + LicenseType type() { return type_; } + + uint32_t version() { return version_; } + + void set_version(uint32_t version) { version_ = version; } + + // |provider_session_token| must be specified in bytes. + void set_provider_session_token(const std::string& provider_session_token) { + provider_session_token_ = provider_session_token; + } + + const std::string& provider_session_token() { + return provider_session_token_; + } + + // License request id. + std::string request_id_; // NOLINT + // Session identifier for license request. + std::string session_id_; // NOLINT + // Session token for the session. This token is for use by the license + // provider, and is akin to a session cookie. It will be copied to + // LicenseIdentfication::provider_session_token, and sent back in all + std::string provider_session_token_; // NOLINT + // Purchase identifier. + std::string purchase_id_; // NOLINT + // Type of the license, default value is Streaming. + LicenseType type_; // NOLINT + // Version of license request. + uint32_t version_; // NOLINT +}; + +/** + * This message is used by the server to preserve and restore session state. + * */ +struct WvPLSessionState { + WvPLSessionState() { + keybox_system_id_ = 0; + license_counter_ = 0; + } + + void set_license_id(const LicenseIdentification& license_id) { + license_id_ = license_id; + } + + const LicenseIdentification& license_id() const { return license_id_; } + + LicenseIdentification* mutable_license_id() { return &license_id_; } + + const std::string& signing_key() const { return signing_key_; } + + // |signing_key| must be specified in bytes. + void set_signing_key(const std::string& signing_key) { + signing_key_ = signing_key; + } + + uint32_t keybox_system_id() const { return keybox_system_id_; } + + void set_keybox_system_id(uint32_t keybox_system_id) { + keybox_system_id_ = keybox_system_id; + } + + uint32_t license_counter() const { return license_counter_; } + + void set_license_counter(uint32_t license_counter) { + license_counter_ = license_counter; + } + + const std::string& provider_client_token() const { + return provider_client_token_; + } + + // |provider_client_token| must be specified in bytes. + void set_provider_client_token(const std::string& provider_client_token) { + provider_client_token_ = provider_client_token; + } + + // License information propagated from license request to the license. + LicenseIdentification license_id_; // NOLINT + // Provider client token sent back in the license. + std::string provider_client_token_; // NOLINT + // Signing_key should be 512 bits in length to be split into two + // (server || client) HMAC-SHA256 keys. + std::string signing_key_; // NOLINT + // System_id in keybox. + uint32_t keybox_system_id_; // NOLINT + // License counter associated with the |provider_client_token|. + uint32_t license_counter_; // NOLINT +}; + +struct WvPLVideoResolutionConstraint { + WvPLVideoResolutionConstraint() { + min_resolution_pixels_ = 0; + max_resolution_pixels_ = 0; + hdcp_ = HDCP_NONE; + } + + void set_min_resolution_pixels(uint32_t pixels) { + min_resolution_pixels_ = pixels; + } + + void set_max_resolution_pixels(uint32_t pixels) { + max_resolution_pixels_ = pixels; + } + + void set_hdcp(HDCP hdcp_value) { hdcp_ = hdcp_value; } + + HDCP hdcp() const { return hdcp_; } + + uint32_t min_resolution_pixels() const { return min_resolution_pixels_; } + + uint32_t max_resolution_pixels() const { return max_resolution_pixels_; } + + // Minimum and maximum video resolutions in the range (height x width). + uint32_t min_resolution_pixels_; + uint32_t max_resolution_pixels_; + // Optional output protection requirements for this range. If not + // specified, the output protection in WvPLKey applies. + HDCP hdcp_; +}; + +struct WvPLOutputProtection { + WvPLOutputProtection() { + hdcp_ = HDCP_NONE; + secure_data_path_ = false; + security_level_ = SECURITY_LEVEL_UNDEFINED; + disable_analog_output_ = false; + disable_digital_output_ = false; + cgms_ = CGMS_NONE; + } + + void set_hdcp(HDCP hdcp_value) { hdcp_ = hdcp_value; } + + HDCP hdcp() const { return hdcp_; } + + // Setting true indicates security_level is HW_SECURE_ALL, otherwise the + // default is false which maps to security_level of SW_SECURE_CRYPTO. This + // value is ignored if security_level is set directly. + void set_secure_data_path(bool secure_flag) { + secure_data_path_ = secure_flag; + } + + bool secure_data_path() const { return secure_data_path_; } + + // Setting security_level directly takes precedence over setting the + // |secure_data_path|. For most applications, there is no need to set the + // security_level directly. Using |secure_data_path| is sufficient. + void set_security_level(SecurityLevel level) { security_level_ = level; } + + SecurityLevel security_level() const { return security_level_; } + + // Set to true to disable analog output. + void set_disable_analog_output(bool flag) { disable_analog_output_ = flag; } + + bool disable_analog_output() const { return disable_analog_output_; } + + // Set to true to disable digital output. + void set_disable_digital_output(bool flag) { disable_digital_output_ = flag; } + + bool disable_digital_output() const { return disable_digital_output_; } + + // CGMS-A setting + void set_cgms(CGMS cgms_value) { cgms_ = cgms_value; } + + CGMS cgms() const { return cgms_; } + + // Indicates whether HDCP is required on digital outputs. Default is None. + HDCP hdcp_; + + // Crypto operations and handling of the media must be performed within a + // hardware backed trusted environment. Default is false. + bool secure_data_path_; + + // Security level robustness defined by Widevine. Default is UNDEFINED in + // which case secure_data_path_ is used. If secure_data_path_ is false, + // security_level_ is set to SW_SECURE_CRYPTO. If secure_data_path_ is + // true, security_level_ is set to HW_SECURE_ALL; + SecurityLevel security_level_; + + // Set to disable analog output. + bool disable_analog_output_; + + // Set to disable digital output. + bool disable_digital_output_; + + // CGMS-A analog output. + CGMS cgms_; +}; + +struct WvPLKey { + WvPLKey() {} + + // |key_id| must be specified in bytes. + void set_key_id(const std::string& key_id) { key_id_ = key_id; } + + const std::string& key_id() const { return key_id_; } + + // |key_bytes| must be specified in bytes. + void set_key_bytes(const std::string& key_bytes) { key_bytes_ = key_bytes; } + + const std::string& key_bytes() const { return key_bytes_; } + + void set_output_protection(const WvPLOutputProtection& out_prot) { + output_protection_ = out_prot; + } + + void set_requested_output_protection(const WvPLOutputProtection& out_prot) { + requested_output_protection_ = out_prot; + } + + const WvPLOutputProtection& output_protection() const { + return output_protection_; + } + + const WvPLOutputProtection& requested_output_protection() const { + return requested_output_protection_; + } + + WvPLOutputProtection* mutable_output_protection() { + return &output_protection_; + } + + WvPLOutputProtection* mutable_requested_output_protection() { + return &requested_output_protection_; + } + + const std::vector + video_resolution_constraints() const { + return video_resolution_constraints_; + } + + void AddVideoResolutionConstraint( + const WvPLVideoResolutionConstraint& constraint) { + video_resolution_constraints_.push_back(constraint); + } + + void set_track_type(const TrackType track_type) { track_type_ = track_type; } + + TrackType track_type() const { return track_type_; } + + void set_key_type(const KeyType key_type) { key_type_ = key_type; } + + KeyType key_type() const { return key_type_; } + + std::string key_id_; + std::string key_bytes_; + WvPLOutputProtection output_protection_; + WvPLOutputProtection requested_output_protection_; + // If the video resolution of the content being decrypted/decoded falls + // within one of the specified ranges, the optional output protections may + // be applied. + std::vector video_resolution_constraints_; + TrackType track_type_ = TRACK_TYPE_UNSPECIFIED; + KeyType key_type_ = CONTENT; +}; + +struct WvPLDeviceInfo { + WvPLDeviceInfo() { + security_level_ = DEVICE_LEVEL_UNSPECIFIED; + system_id_ = 0; + test_device_ = false; + } + void set_system_id(uint32_t system_id) { system_id_ = system_id; } + uint32_t system_id() const { return system_id_; } + void set_soc(const std::string& soc) { soc_ = soc; } + const std::string& soc() const { return soc_; } + + void set_manufacturer(const std::string& manufacturer) { + manufacturer_ = manufacturer; + } + const std::string& manufacturer() const { return manufacturer_; } + + void set_model(const std::string& model) { model_ = model; } + const std::string& model() const { return model_; } + + void set_device_type(const std::string& device_type) { + device_type_ = device_type; + } + const std::string& device_type() const { return device_type_; } + + void set_security_level(DeviceSecurityLevel level) { + security_level_ = level; + } + DeviceSecurityLevel security_level() const { return security_level_; } + + void set_test_device(bool test_device) { test_device_ = test_device; } + bool test_device() const { return test_device_; } + + void set_serial_number(const std::string& serial_number) { + serial_number_ = serial_number; + } + const std::string& serial_number() const { return serial_number_; } + + void set_service_id(const std::string& service_id) { + service_id_ = service_id; + } + const std::string& service_id() const { return service_id_; } + + // Widevine system ID for this device family. + uint32_t system_id_; + // Name of system-on-a-chip. + std::string soc_; + // Name of manufacturer. + std::string manufacturer_; + // Manufacturer's model name. + std::string model_; + // Type of device (Phone, Tablet, TV, etc). + std::string device_type_; + // Widevine-defined security level. + DeviceSecurityLevel security_level_; + // True if the certificate corresponds to a test (non production) device. + bool test_device_; + // 128-bit globally unique serial number of certificate. + std::string serial_number_; + // Service identifier (web origin) for the provider which owns the + // certificate. + std::string service_id_; +}; + +// This message is used to hold usage data for each Widevine system Id. +struct WvPLLicenseStatusCounterData { + // Initialize members + WvPLLicenseStatusCounterData() : license_status_(0), status_count_(0) {} + uint32_t license_status_; + // Count of occurences of the above status; + uint32_t status_count_; +}; + +// This message is used to hold usage data for each device model. +struct WvPLDeviceModelCounterData { + std::string device_model_; + // Key is each status we returned associated with the above model; + std::map status_map_; +}; + +// This message is used to hold usage data for each device make. +struct WvPLDeviceMakeCounterData { + std::string device_make_; + // Key is each model associated with this device make. + std::map model_map_; +}; + +// This message is used to hold usage data for each Widevine system Id. +struct WvPLSystemIdCounterData { + // Initialize members + WvPLSystemIdCounterData() : system_id_(0) {} + uint32_t system_id_; + // Key is each make we handled associated with the above system Id. + std::map make_map_; +}; + +// This top level message contains aggregated usage data for license requests. +struct WvPLCounterData { + // Initialize members + WvPLCounterData() : start_time_utc_(0), end_time_utc_(0) {} + // Time of the first license in this structure, represented as seconds since + // Unix epoch. + time_t start_time_utc_; + // Time of the last license in this structure, represented as seconds since + // Unix epoch. + time_t end_time_utc_; + // Key is each system Id we handled. + std::map system_id_map_; +}; + +struct WvPLSessionInit { + WvPLSessionInit() + : license_start_time_(0), override_provider_client_token_(false) {} + // Accessors + void set_session_id(const std::string& session_id) { + session_id_ = session_id; + } + std::string session_id() const { return session_id_; } + + void set_purchase_id(const std::string& purchase_id) { + purchase_id_ = purchase_id; + } + std::string purchase_id() const { return purchase_id_; } + + void set_master_signing_key(const std::string& master_signing_key) { + master_signing_key_ = master_signing_key; + } + std::string master_signing_key() const { return master_signing_key_; } + + void set_license_start_time(const int64_t license_start_time) { + license_start_time_ = license_start_time; + } + int64_t license_start_time() const { return license_start_time_; } + + void set_provider_client_token(const std::string& provider_client_token) { + provider_client_token_ = provider_client_token; + } + std::string provider_client_token() const { return provider_client_token_; } + + void set_provider_session_token(const std::string& provider_session_token) { + provider_session_token_ = provider_session_token; + } + std::string provider_session_token() const { return provider_session_token_; } + + void set_override_provider_client_token(bool override_provider_client_token) { + override_provider_client_token_ = override_provider_client_token; + } + bool override_provider_client_token() const { + return override_provider_client_token_; + } + + // Identifier for the session. + std::string session_id_; + // Identifier for the purchased content. + std::string purchase_id_; + // Master signing key should be 128 bits in length. + std::string master_signing_key_; + // signing_key should be 512 bits in length to be split into two + // (server || client) HMAC-SHA256 keys. + std::string signing_key_; + // The reference time in UTC as calculated by the client clock. + int64_t license_start_time_; + // Client token for the session. + std::string provider_client_token_; + // Session token for the session. + std::string provider_session_token_; + // If false and the license request contains a |provider_client_token_|, + // use the token from that request even if |provider_client_token_| is + // specified. + // If true and the license request contains a |provider_client_token_|, + // use |provider_client_token_|. + bool override_provider_client_token_; +}; + +struct WvPLClientCapabilities { + WvPLClientCapabilities() {} + + // Accessors + void set_client_token(bool client_token) { client_token_ = client_token; } + + bool client_token() const { return client_token_; } + + void set_session_token(bool session_token) { session_token_ = session_token; } + + bool session_token() const { return session_token_; } + + void set_video_resolution_constraints(bool video_resolution_constraints) { + video_resolution_constraints_ = video_resolution_constraints; + } + + bool video_resolution_constraints() const { + return video_resolution_constraints_; + } + + void set_max_hdcp_version(HDCP max_hdcp_version) { + max_hdcp_version_ = max_hdcp_version; + } + + HDCP max_hdcp_version() const { return max_hdcp_version_; } + + void set_oem_crypto_api_version(uint32_t oem_crypto_api_version) { + oem_crypto_api_version_ = oem_crypto_api_version; + } + + uint32_t oem_crypto_api_version() const { return oem_crypto_api_version_; } + + void set_anti_rollback_usage_table(bool anti_rollback_usage_table) { + anti_rollback_usage_table_ = anti_rollback_usage_table; + } + + bool anti_rollback_usage_table() const { return anti_rollback_usage_table_; } + + void set_srm_version(uint32_t srm_version) { srm_version_ = srm_version; } + + uint32_t srm_version() const { return srm_version_; } + + void set_can_update_srm(bool can_update_srm) { + can_update_srm_ = can_update_srm; + } + + bool can_update_srm() const { return can_update_srm_; } + + void add_supported_certificate_key_type(const CertificateKeyType key_type) { + supported_certificate_key_type_.push_back(key_type); + } + const std::list supported_certificate_key_type() const { + return supported_certificate_key_type_; + } + + void set_analog_output_capabilities( + const AnalogOutputCapabilities analog_output_capabilities) { + analog_output_capabilities_ = analog_output_capabilities; + } + + const AnalogOutputCapabilities analog_output_capabilities() const { + return analog_output_capabilities_; + } + + void set_can_disable_analog_output(bool can_disable_analog_output) { + can_disable_analog_output_ = can_disable_analog_output; + } + + bool can_disable_analog_output() const { return can_disable_analog_output_; } + + // Member variables + // Client token generated by the content provider. + bool client_token_ = false; + // A token associated with the client session. + bool session_token_ = false; + // Video resolution constraints. If the video resolution of the + // content being decrypted/decoded falls within one of the specified ranges, + // the required_protections may be applied. Otherwise an error will be + // reported. + // NOTE: Use of this feature is not recommended, as it is only supported on + // a small number of platforms. + bool video_resolution_constraints_ = false; + // The max "High-bandwidth Digital Content Protection" (HDCP) version + // supported by the client. + HDCP max_hdcp_version_ = HDCP_NONE; + // The OEM Crypto API version supported by the client. + uint32_t oem_crypto_api_version_; + // Flag to indicate the key must only be used if the client + // supports anti rollback of the user table. Content provider can query the + // client capabilities to determine if the client support this feature. + bool anti_rollback_usage_table_ = false; + // The client shall report |srm_version| if available. + uint32_t srm_version_; + // A device may have SRM data, and report a version, but may not be capable + // of updating SRM data. + bool can_update_srm_ = false; + // A list of CertificateKeyTypes. + std::list supported_certificate_key_type_; + // Analog capabilities of the device. + AnalogOutputCapabilities analog_output_capabilities_ = ANALOG_OUTPUT_UNKNOWN; + // Indicates if the device can disable it's analog output. + bool can_disable_analog_output_ = false; +}; + +/** + * Represents the Client Identification information. In the License Proxy + * SDK, this structure is populated as a result of parsing the license + * request from the CDM. This struct is used by Proxy SDK. + */ +struct WvPLClientInfo { + WvPLClientInfo() + : max_hdcp_version_(HDCP_NONE), + oem_crypto_api_version_(0), + analog_output_capabilities_(ANALOG_OUTPUT_UNKNOWN), + can_disable_analog_output_(false) {} + + // Accessors + void set_max_hdcp_version(HDCP max_hdcp_version) { + max_hdcp_version_ = max_hdcp_version; + } + HDCP max_hdcp_version() const { return max_hdcp_version_; } + + void set_oem_crypto_api_version(uint32_t oem_crypto_api_version) { + oem_crypto_api_version_ = oem_crypto_api_version; + } + uint32_t oem_crypto_api_version() const { return oem_crypto_api_version_; } + + void set_provider_client_token(const std::string& provider_client_token) { + provider_client_token_ = provider_client_token; + } + const std::string& provider_client_token() const { + return provider_client_token_; + } + + void set_cdm_version(const std::string& cdm_version) { + cdm_version_ = cdm_version; + } + const std::string& cdm_version() const { return cdm_version_; } + + void set_name_value(const std::string& name, const std::string& value) { + names_values_.insert(std::make_pair(name, value)); + } + const std::map& names_values() { + return names_values_; + } + void set_analog_output_capabilities(AnalogOutputCapabilities capability) { + analog_output_capabilities_ = capability; + } + const AnalogOutputCapabilities analog_output_capabilities() const { + return analog_output_capabilities_; + } + void set_can_disable_analog_output(bool can_disable) { + can_disable_analog_output_ = can_disable; + } + bool can_disable_analog_output() const { return can_disable_analog_output_; } + + const WvPLClientCapabilities client_capabilities() const { + return client_capabilities_; + } + + void set_client_capabilities( + const WvPLClientCapabilities& client_capabilities) { + client_capabilities_ = client_capabilities; + } + + // Member variables + // The max "High-bandwidth Digital Content Protection" (HDCP) version + // supported by the device. + HDCP max_hdcp_version_; + // The OEM Crypto API version supported by the client. + uint32_t oem_crypto_api_version_; + // The client CDM version. + std::string cdm_version_; + // The client token generated by the content provider. + std::string provider_client_token_; + // Map of other fields specified as name-value pairs representing the + // client. + std::map names_values_; + // Analog capabilities of the device. + AnalogOutputCapabilities analog_output_capabilities_; + // Indicates if the device can disable it's analog output. + bool can_disable_analog_output_; + // Capabilities which not all clients may support. Used for the license + // exchange protocol only. + WvPLClientCapabilities client_capabilities_; +}; + +struct WvPLEntitledKey { + WvPLEntitledKey() {} + // Accessors. + void set_entitlement_key_id(const std::string& entitlement_key_id) { + entitlement_key_id_ = entitlement_key_id; + } + const std::string& entitlement_key_id() const { return entitlement_key_id_; } + + void set_key_id(const std::string& key_id) { key_id_ = key_id; } + const std::string& key_id() const { return key_id_; } + + void set_key_bytes(const std::string& key_bytes) { key_bytes_ = key_bytes; } + const std::string& key_bytes() const { return key_bytes_; } + + void set_entitlement_key_iv(const std::string& entitlement_key_iv) { + entitlement_key_iv_ = entitlement_key_iv; + } + const std::string& entitlement_key_iv() const { return entitlement_key_iv_; } + + void set_entitlement_key_size_bytes(uint32_t entitlement_key_size_bytes) { + entitlement_key_size_bytes_ = entitlement_key_size_bytes; + } + uint32_t entitlement_key_size_bytes() const { + return entitlement_key_size_bytes_; + } + + // Member variables. + // ID of entitlement key used for wrapping |key|. + std::string entitlement_key_id_; + // ID of the entitled key. + std::string key_id_; + // Wrapped key. + std::string key_bytes_; + // IV used for wrapping |key|. + std::string entitlement_key_iv_; + // Size of entitlement key used for wrapping |key|. + uint32_t entitlement_key_size_bytes_ = 32; +}; + +/* + * Defines the 'pssh' box for Common Encryption (CENC). + */ +struct WvPLWidevinePsshData { + WvPLWidevinePsshData() {} + + // Accessors + void set_content_id(const std::string& content_id) { + content_id_ = content_id; + } + const std::string content_id() const { return content_id_; } + + void set_key_id(const std::string& key_id) { key_ids_.push_back(key_id); } + const std::list& key_ids() const { return key_ids_; } + + void add_entitled_key(const WvPLEntitledKey& entitled_key) { + entitled_keys_.push_back(entitled_key); + } + const std::list& entitled_keys() const { + return entitled_keys_; + } + + // Member variables + // A list of key identifiers, for entitlement keys or content keys. + std::list key_ids_; + // The content identifier. + std::string content_id_; + // A list of wrapped keys. + std::list entitled_keys_; +}; + +} // namespace wv_pl_sdk +} // namespace widevine_server + +#endif // SDK_EXTERNAL_COMMON_WVPL_WVPL_TYPES_H_ diff --git a/strings/BUILD b/strings/BUILD new file mode 100644 index 0000000..5009d10 --- /dev/null +++ b/strings/BUILD @@ -0,0 +1,34 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "strings", + srcs = [ + "serialize.cc", + ], + hdrs = [ + "serialize.h", + ], + deps = [ + "//base", + ], +) + +cc_test( + name = "serialize_test", + srcs = ["serialize_test.cc"], + deps = [ + ":strings", + "//base", + "//testing:gunit_main", + ], +) diff --git a/strings/serialize.cc b/strings/serialize.cc new file mode 100644 index 0000000..1aaa836 --- /dev/null +++ b/strings/serialize.cc @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "strings/serialize.h" + +#include + +#include "glog/logging.h" + +namespace widevine { +namespace strings { + +uint32_t KeyToUint32(const std::string& key) { + CHECK_GE(key.size(), 4); + return static_cast(key[0]) << 24 | + static_cast(key[1]) << 16 | + static_cast(key[2]) << 8 | static_cast(key[3]); +} + +std::string EncodeUint64(uint64_t value) { + std::string s; + s.resize(sizeof(value)); + memcpy(&s[0], &value, sizeof(value)); + return s; +} + +} // namespace strings +} // namespace widevine diff --git a/strings/serialize.h b/strings/serialize.h new file mode 100644 index 0000000..b161cc5 --- /dev/null +++ b/strings/serialize.h @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef STRINGS_SERIALIZE_H_ +#define STRINGS_SERIALIZE_H_ + +#include + +namespace widevine { +namespace strings { + +// Convert a 4-byte key in network byte order to uint32_t +uint32_t KeyToUint32(const std::string& key); +// Encode a unit64 value to a string. +std::string EncodeUint64(uint64_t value); + +} // namespace strings +} // namespace widevine + +#endif // STRINGS_SERIALIZE_H_ diff --git a/strings/serialize_test.cc b/strings/serialize_test.cc new file mode 100644 index 0000000..38e5347 --- /dev/null +++ b/strings/serialize_test.cc @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Contains the test cases for EncodeUint64 and KeyToUint32. + +#include "strings/serialize.h" + +#include "testing/gunit.h" + +namespace widevine { +namespace strings { + +TEST(SerializeTest, KeyToUint32) { + std::string val = "\x12\x34\x56\x78"; + EXPECT_EQ(0x12345678, KeyToUint32(val)); + val = "\x87\x65\x43\x21"; + EXPECT_EQ(0x87654321, KeyToUint32(val)); +} + +TEST(SerializeTest, EncodeUint64) { + uint64_t q1 = 0x4142434441424344; + std::string uint64_str = EncodeUint64(q1); + EXPECT_TRUE(uint64_str == "DCBADCBA" || uint64_str == "ABCDABCD"); +} + +} // namespace strings +} // namespace widevine diff --git a/testing/BUILD b/testing/BUILD new file mode 100644 index 0000000..bad30ce --- /dev/null +++ b/testing/BUILD @@ -0,0 +1,31 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "gunit", + hdrs = [ + "gmock.h", + "gunit.h", + ], + deps = [ + "//external:gtest", + "//util:status", + ], +) + +cc_library( + name = "gunit_main", + deps = [ + ":gunit", + "//external:gtest_main", + ], +) diff --git a/testing/gmock.h b/testing/gmock.h new file mode 100644 index 0000000..bdc95c0 --- /dev/null +++ b/testing/gmock.h @@ -0,0 +1,14 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TESTING_GMOCK_H_ +#define TESTING_GMOCK_H_ + +#include + +#endif // TESTING_GMOCK_H_ diff --git a/testing/gunit.h b/testing/gunit.h new file mode 100644 index 0000000..671dd49 --- /dev/null +++ b/testing/gunit.h @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TESTING_GUNIT_H_ +#define TESTING_GUNIT_H_ + +#include + +#define EXPECT_OK(expression) \ + EXPECT_EQ(util::error::OK, expression.error_code()) +#define ASSERT_OK(expression) \ + ASSERT_EQ(util::error::OK, expression.error_code()) + +#endif // TESTING_GUNIT_H_ diff --git a/util/BUILD b/util/BUILD new file mode 100644 index 0000000..714b6ef --- /dev/null +++ b/util/BUILD @@ -0,0 +1,68 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + + +filegroup( + name = "binary_release_files", + srcs = [ + "error_space.h", + "status.h", + ], +) + +cc_library( + name = "status", + srcs = [ + "status.cc", + ], + hdrs = [ + "status.h", + ], + deps = [ + ":error_space", + "//base", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "error_space", + hdrs = ["error_space.h"], +) + +cc_library( + name = "proto_status", + hdrs = ["proto_status.h"], + deps = [ + ":status", + "//external:protobuf", + ], +) + +cc_test( + name = "status_test", + srcs = ["status_test.cc"], + deps = [ + ":status", + "//testing:gunit_main", + ], +) + +cc_test( + name = "error_space_test", + srcs = ["error_space_test.cc"], + deps = [ + ":error_space", + "//testing:gunit_main", + ], +) + diff --git a/util/endian/BUILD b/util/endian/BUILD new file mode 100644 index 0000000..fbe70d4 --- /dev/null +++ b/util/endian/BUILD @@ -0,0 +1,19 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + + +cc_library( + name = "endian", + hdrs = [ + "endian.h", + ], +) diff --git a/util/endian/endian.h b/util/endian/endian.h new file mode 100644 index 0000000..df04c32 --- /dev/null +++ b/util/endian/endian.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_ENDIAN_ENDIAN_H_ +#define UTIL_ENDIAN_ENDIAN_H_ + +#include +#include + + +namespace widevine { + +// Utilities to convert numbers between the current hosts's native byte +// order and big-endian byte order (same as network byte order) +class BigEndian { + public: + static uint32_t Load32(const char* data) { + return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + } + + static uint64_t Load64(const char* data) { + return ((static_cast(Load32(data)) << 32) | Load32(data + 4)); + } + + static void Store32(void* p, uint32_t data) { + for (int i = 0; i < 4; ++i) + (reinterpret_cast(p))[i] = (data >> ((3 - i) * 8)) & 0xFF; + } +}; // BigEndian + +inline uint64_t gntohll(uint64_t n) { + uint64_t retval; +#if __BYTE_ORDER == __BIG_ENDIAN + retval = n; +#else + retval = ((uint64_t) ntohl(n & 0xFFFFFFFFLLU)) << 32; + retval |= ntohl((n & 0xFFFFFFFF00000000LLU) >> 32); +#endif + return(retval); +} + +} // namespace widevine + +#endif // UTIL_ENDIAN_ENDIAN_H_ diff --git a/util/error_space.h b/util/error_space.h new file mode 100644 index 0000000..68c093a --- /dev/null +++ b/util/error_space.h @@ -0,0 +1,83 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_ERROR_SPACE_H_ +#define UTIL_ERROR_SPACE_H_ + +#include + +namespace widevine { +namespace util { + +class ErrorSpace { + public: + std::string SpaceName() const { return space_name_func_(this); } + std::string String(int code) const { return code_to_string_func_(this, code); } + + protected: + // typedef instead of using statements for SWIG compatibility. + typedef std::string (*SpaceNameFunc)(const ErrorSpace* space); + typedef std::string (*CodeToStringFunc)(const ErrorSpace* space, int code); + constexpr ErrorSpace(SpaceNameFunc space_name_func, + CodeToStringFunc code_to_string_func) + : space_name_func_(space_name_func), + code_to_string_func_(code_to_string_func) {} + + private: + const SpaceNameFunc space_name_func_; + const CodeToStringFunc code_to_string_func_; +}; + +// Manages creation of error space subclasses. +template +class ErrorSpaceImpl : public ErrorSpace { + public: + constexpr ErrorSpaceImpl() + : ErrorSpace(&ErrorSpaceImpl::SpaceNameImpl, + &ErrorSpaceImpl::CodeToStringImpl) {} + + // Returns the canonical instance of the `T` error space. + static constexpr const T* Get(); + + private: + // These functions adapt the stateful implementation that takes a space + // pointer to stateless static methods, so that clients of ErrorSpaceImpl are + // safe to have constexpr global instances. + static std::string SpaceNameImpl(const ErrorSpace* /*space*/) { + return T::SpaceName(); + } + + static std::string CodeToStringImpl(const ErrorSpace* /*space*/, int code) { + return T::CodeToString(code); + } +}; + +namespace internal { + +// Provides a global constexpr instance of the error space `T`. +// We need the indirection because ErrorSpaceImpl can't declare constexpr +// instances of T since it is not yet fully declared. +template +struct ErrorSpaceInstance { + static constexpr T value = {}; +}; + +template +constexpr T ErrorSpaceInstance::value; + +} // namespace internal + +template +constexpr const T* ErrorSpaceImpl::Get() { + return &internal::ErrorSpaceInstance::value; +} + +} // namespace util +} // namespace widevine + +#endif // UTIL_ERROR_SPACE_H_ diff --git a/util/error_space_test.cc b/util/error_space_test.cc new file mode 100644 index 0000000..a32d339 --- /dev/null +++ b/util/error_space_test.cc @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "util/error_space.h" + +#include "testing/gunit.h" + +namespace widevine { +namespace util { +namespace { + +class Space1 : public util::ErrorSpaceImpl { + public: + static std::string SpaceName() { return "Space1"; } + static std::string CodeToString(int code) { return "Test" + std::to_string(code); } +}; + +TEST(ErrorSpaceTest, Basic) { + const ErrorSpace* space1 = Space1::Get(); + EXPECT_EQ("Space1", space1->SpaceName()); + EXPECT_EQ(Space1::CodeToString(23), space1->String(23)); +} + +} // namespace +} // namespace util +} // namespace widevine diff --git a/util/gtl/BUILD b/util/gtl/BUILD new file mode 100644 index 0000000..37a9667 --- /dev/null +++ b/util/gtl/BUILD @@ -0,0 +1,19 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + + +cc_library( + name = "map_util", + hdrs = [ + "map_util.h", + ], +) diff --git a/util/gtl/map_util.h b/util/gtl/map_util.h new file mode 100644 index 0000000..1fec8b7 --- /dev/null +++ b/util/gtl/map_util.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_GTL_MAP_UTIL_H_ +#define UTIL_GTL_MAP_UTIL_H_ + +namespace widevine { +namespace gtl { + +template +const typename Collection::value_type::second_type& FindWithDefault( + const Collection& collection, + const typename Collection::value_type::first_type& key, + const typename Collection::value_type::second_type& value) { + typename Collection::const_iterator it = collection.find(key); + if (it == collection.end()) { + return value; + } + return it->second; +} + +template +typename Collection::value_type::second_type* FindOrNull( + const Collection& collection, + const typename Collection::value_type::first_type& key) { + typename Collection::iterator it = collection.find(key); + if (it == collection.end()) { + return nullptr; + } + return &it->second; +} + +template +typename Collection::value_type::second_type* FindOrNull( + Collection& collection, // NOLINT + const typename Collection::value_type::first_type& key) { + typename Collection::iterator it = collection.find(key); + if (it == collection.end()) { + return nullptr; + } + return &it->second; +} + +template +bool ContainsKey(const C& collection, const K& key) { + typename C::const_iterator it = collection.find(key); + return it != collection.end(); +} + +} // namespace gtl +} // namespace widevine + +#endif // UTIL_GTL_MAP_UTIL_H_ diff --git a/util/proto_status.h b/util/proto_status.h new file mode 100644 index 0000000..2205db4 --- /dev/null +++ b/util/proto_status.h @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_PROTO_STATUS_H_ +#define UTIL_PROTO_STATUS_H_ + +#include "google/protobuf/descriptor.h" +#include "google/protobuf/generated_enum_reflection.h" +#include "util/status.h" + +namespace widevine { +namespace util { + +template +class ProtoEnumErrorSpace : public ErrorSpaceImpl> { + public: + static std::string SpaceName() { + return google::protobuf::GetEnumDescriptor()->full_name(); + } + + static std::string CodeToString(int code) { + const google::protobuf::EnumValueDescriptor* v = + google::protobuf::GetEnumDescriptor()->FindValueByNumber(code); + if (v) return v->name(); + return std::to_string(code); + } +}; + +} // namespace util +} // namespace widevine + +#endif // UTIL_PROTO_STATUS_H_ diff --git a/util/proto_status_test.cc b/util/proto_status_test.cc new file mode 100644 index 0000000..ea84e04 --- /dev/null +++ b/util/proto_status_test.cc @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2007 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 "util/proto_status.h" + +#include "testing/gunit.h" +#include "protos/public/errors.pb.h" + +namespace widevine { +namespace util { + +TEST(StatusTest, PROVIDER_ID_MISMATCH_Status) { + Status status(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "provider_id_mismatch"); + EXPECT_EQ("Errors::PROVIDER_ID_MISMATCH: provider_id_mismatch", + status.ToString()); +} + +TEST(StatusTest, PROVIDER_ID_MISMATCH_Status2) { + Status status(static_cast(PROVIDER_ID_MISMATCH), + "provider_id_mismatch"); + EXPECT_EQ("Errors::157: provider_id_mismatch", status.ToString()); +} + +TEST(StatusTest, Same) { + Status status1(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "provider_id_mismatch"); + Status status2(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "this is a provider_id_mismatch error"); + EXPECT_EQ(status1, status2); +} + +TEST(StatusTest, NotTheSameStatus) { + Status status1(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "provider_id_mismatch"); + Status status2(ProtoEnumErrorSpace::Get(), MISSING_CLIENT_ID, + "missing client id"); + EXPECT_NE(status1, status2); +} + +TEST(StatusTest, NotTheSameErrorSpace) { + Status status1(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "provider_id_mismatch"); + Status status2(static_cast(PROVIDER_ID_MISMATCH), + "this is a provider_id_mismatch error"); + EXPECT_NE(status1, status2); +} + +} // namespace util +} // namespace widevine diff --git a/util/random/BUILD b/util/random/BUILD new file mode 100644 index 0000000..c3c968f --- /dev/null +++ b/util/random/BUILD @@ -0,0 +1,31 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "global_id", + hdrs = [ + "global_id.h", + ], + deps = [ + "//external:openssl", + ], +) + +cc_test( + name = "global_id_test", + srcs = ["global_id_test.cc"], + deps = [ + ":global_id", + "//base", + "//testing:gunit_main", + ], +) diff --git a/util/random/global_id.h b/util/random/global_id.h new file mode 100644 index 0000000..b91da81 --- /dev/null +++ b/util/random/global_id.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_RANDOM_GLOBAL_ID_H_ +#define UTIL_RANDOM_GLOBAL_ID_H_ + +#include "openssl/rand.h" + +namespace widevine { +namespace util { +namespace random { + +static uint64_t NewGlobalID() { + static __thread uint64_t val = 0; + if (val == 0) { + RAND_bytes(reinterpret_cast(&val), sizeof(val)); + } + return val++; +} + +} // namespace random +} // namespace util +} // namespace widevine + +#endif // UTIL_RANDOM_GLOBAL_ID_H_ diff --git a/util/random/global_id_test.cc b/util/random/global_id_test.cc new file mode 100644 index 0000000..6aea7b7 --- /dev/null +++ b/util/random/global_id_test.cc @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "util/random/global_id.h" + +#include "glog/logging.h" +#include "testing/gunit.h" + +namespace widevine { +namespace util { +namespace random { + +TEST(GlobalIDTest, NewGlobalID) { + uint64_t id = NewGlobalID(); + LOG(INFO) << "NewGlobalID " << id; + EXPECT_EQ(id + 1, NewGlobalID()); + EXPECT_EQ(id + 2, NewGlobalID()); +} + +} // namespace random +} // namespace util +} // namespace widevine diff --git a/util/status.cc b/util/status.cc new file mode 100644 index 0000000..2936549 --- /dev/null +++ b/util/status.cc @@ -0,0 +1,62 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include + +#include "base/macros.h" +#include "absl/strings/str_cat.h" +#include "util/status.h" + +namespace widevine { +namespace util { +namespace { + +const char* kLicenseServerStatusMessage[] = {"OK", + "UNKNOWN_ERROR", + "UNKNOWN_ERROR", + "INVALID_ARGUMENT", + "UNKNOWN_ERROR", + "NOT_FOUND", + "ALREADY_EXISTS", + "PERMISSION_DENIED", + "UNKNOWN_ERROR", + "UNKNOWN_ERROR", + "UNKNOWN_ERROR", + "UNKNOWN_ERROR", + "UNIMPLEMENTED", + "INTERNAL", + "UNAVAILABLE"}; + +} // namespace + +std::string GenericErrorSpace::SpaceName() { return "generic"; } + +std::string GenericErrorSpace::CodeToString(int code) { + static_assert( + arraysize(kLicenseServerStatusMessage) == error::NUM_ERRORS, + "mismatching license_server_sdk status message and license_server_sdk " + "status."); + + if (code >= 0 && code < error::NUM_ERRORS) + return kLicenseServerStatusMessage[code]; + return std::to_string(code); +} + +std::string Status::ToString() const { + if (status_code_ == error::OK) return "OK"; + return absl::StrCat("Errors::", error_space_->String(status_code_), ": ", + error_message_); +} + +std::ostream& operator<<(std::ostream& os, const Status& x) { + os << x.ToString(); + return os; +} + +} // namespace util +} // namespace widevine diff --git a/util/status.h b/util/status.h new file mode 100644 index 0000000..5e3b15b --- /dev/null +++ b/util/status.h @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_STATUS_H_ +#define UTIL_STATUS_H_ + +#include + +#include "util/error_space.h" + +namespace widevine { +namespace util { +namespace error { + +enum StatusCode { + // Success. + OK = 0, + + // Client specified an invalid argument. + INVALID_ARGUMENT = 3, + + // Some requested entity (e.g., file or directory) was not found. + NOT_FOUND = 5, + + // Some entity that we attempted to create (e.g., file or directory) + // already exists. + ALREADY_EXISTS = 6, + + // The caller does not have permission to execute the specified + // operation. PERMISSION_DENIED must not be used for rejections + // caused by exhausting some resource (use RESOURCE_EXHAUSTED + // instead for those errors). + PERMISSION_DENIED = 7, + + // Operation is not implemented or not supported/enabled in this service. + UNIMPLEMENTED = 12, + + // Internal errors. Means some invariants expected by underlying + // system has been broken. If you see one of these errors, + // something is very broken. + INTERNAL = 13, + + // Operation is not implemented or not supported/enabled in this service. + UNAVAILABLE = 14, + + // Number of generic (non license related) errors. + NUM_ERRORS, +}; + +} // namespace error + +class GenericErrorSpace : public ErrorSpaceImpl { + public: + static std::string SpaceName(); + static std::string CodeToString(int code); +}; + +class Status { + public: + Status() : status_code_(error::OK) {} + ~Status() {} + explicit Status(error::StatusCode c) : status_code_(c) {} + Status(error::StatusCode c, const std::string& error_message) + : status_code_(c), error_message_(error_message) {} + Status(const ErrorSpace* e, error::StatusCode c, + const std::string& error_message) { + SetError(e, c, error_message); + } + Status(const ErrorSpace* e, int error, const std::string& error_message) { + SetError(e, error, error_message); + } + void SetError(const ErrorSpace* e, int c, const std::string& error_message) { + error_space_ = e; + status_code_ = c; + error_message_ = error_message; + } + + bool ok() const { return status_code_ == error::OK; } + const ErrorSpace* error_space() const { return error_space_; } + static const ErrorSpace* canonical_space() { + return GenericErrorSpace::Get(); + } + std::string ToString() const; + std::string error_message() const { return error_message_; } + int error_code() const { return status_code_; } + + private: + const ErrorSpace* error_space_ = GenericErrorSpace::Get(); + int status_code_; + std::string error_message_; +}; // class Status + +inline Status OkStatus() { return Status(); } + +// Here error_message_ is ignored during comparison. +inline bool operator==(const Status& s1, const Status& s2) { + return s1.error_space() == s2.error_space() && + s1.error_code() == s2.error_code(); +} +inline bool operator!=(const Status& s1, const Status& s2) { + return s1.error_space() != s2.error_space() || + s1.error_code() != s2.error_code(); +} + +// Prints a human-readable representation of 'x' to 'os'. +std::ostream& operator<<(std::ostream& os, const Status& x); + +#define CHECK_OK(expression) CHECK_EQ(util::error::OK, expression.error_code()) + + +} // namespace util +} // namespace widevine + +#endif // UTIL_STATUS_H_ diff --git a/util/status_test.cc b/util/status_test.cc new file mode 100644 index 0000000..a91259a --- /dev/null +++ b/util/status_test.cc @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2007 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 "util/status.h" + +#include "testing/gunit.h" + +namespace widevine { +namespace util { + +TEST(StatusTest, OK_Status) { + // test case for ok status. + Status status(error::OK); + EXPECT_EQ("OK", status.ToString()); +} + +TEST(StatusTest, OK_Status2) { + // test case for ok status. + Status status; + EXPECT_EQ("OK", status.ToString()); +} + +TEST(StatusTest, ALREADY_EXISTS_Status) { + Status status(error::ALREADY_EXISTS, "it is already exist"); + EXPECT_EQ("Errors::ALREADY_EXISTS: it is already exist", status.ToString()); +} + +// test case for status in boundary cases. +TEST(StatusTest, UNAVAILABLE_Status) { + Status status(error::UNAVAILABLE, "unavailable"); + EXPECT_EQ("Errors::UNAVAILABLE: unavailable", status.ToString()); +} + +TEST(StatusTest, NoNameCode) { + Status status(static_cast(101), "Unknown error"); + EXPECT_EQ("Errors::101: Unknown error", status.ToString()); +} + +TEST(StatusTest, EQUAL_OPERATOR) { + Status status1(error::ALREADY_EXISTS, "already exists 1"); + Status status2(error::ALREADY_EXISTS, "already exists 2"); + EXPECT_EQ(status1, status2); +} + +TEST(StatusTest, NOT_EQUAL_OPERATOR) { + Status status1(error::ALREADY_EXISTS, "already exists"); + Status status2(error::UNAVAILABLE, "unavailable"); + EXPECT_NE(status1, status2); +} + +TEST(StatusTest, NOT_EQUAL_OPERATOR_NONE_MSG) { + Status status1(error::ALREADY_EXISTS); + Status status2(error::UNAVAILABLE); + EXPECT_NE(status1, status2); +} + +} // namespace util +} // namespace widevine