From 93265ab9d126fb4f9796305c6246b9ac491bf11e Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Wed, 23 Jan 2019 15:16:31 -0800 Subject: [PATCH] Refactor and cleanup codes. No functional changes. --- BUILD | 2 +- WORKSPACE | 39 +- base/BUILD | 5 +- base/macros.h | 88 +- base/mutex.h | 91 -- base/thread_annotations.h | 202 +-- base/timestamp.h | 21 + common/BUILD | 562 ++++++- common/aes_cbc_util.cc | 76 +- common/aes_cbc_util.h | 13 +- common/aes_cbc_util_test.cc | 32 +- .../public => common}/certificate_type.h | 14 +- common/client_cert.cc | 262 +++ common/client_cert.h | 187 +++ common/client_cert_test.cc | 617 ++++++++ common/client_id_util.cc | 89 ++ common/client_id_util.h | 61 + common/crypto_util.cc | 181 +++ common/crypto_util.h | 89 ++ common/crypto_util_test.cc | 236 +++ common/device_status_list.cc | 362 +++++ common/device_status_list.h | 122 ++ common/device_status_list_test.cc | 376 +++++ common/drm_root_certificate.cc | 532 +++++++ common/drm_root_certificate.h | 106 ++ common/drm_root_certificate_test.cc | 262 +++ common/drm_service_certificate.cc | 286 ++++ common/drm_service_certificate.h | 132 ++ common/drm_service_certificate_test.cc | 364 +++++ common/ecb_util.cc | 113 ++ common/ecb_util.h | 59 + common/ecb_util_test.cc | 90 ++ common/error_space.cc | 19 + common/error_space.h | 20 + common/file_util.cc | 4 +- common/file_util.h | 2 +- common/file_util_test.cc | 8 +- common/mock_rsa_key.h | 20 +- common/openssl_util.h | 11 +- common/python/BUILD | 105 ++ common/python/aes_cbc_util.clif | 11 + common/python/certificate_type.clif | 11 + common/python/crypto_util.clif | 15 + common/python/drm_root_certificate.clif | 23 + common/python/drm_service_certificate.clif | 18 + common/python/rsa_key.clif | 15 + common/python/rsa_test_keys.clif | 20 + common/python/signing_key_util.clif | 14 + common/python/test_drm_certificates.clif | 15 + common/random_util.cc | 2 +- common/random_util.h | 2 +- common/random_util_test.cc | 5 +- common/remote_attestation_verifier.cc | 260 +++ common/remote_attestation_verifier.h | 92 ++ common/rsa_key.cc | 42 +- common/rsa_key.h | 19 +- common/rsa_key_test.cc | 4 +- common/rsa_test_keys.cc | 1410 +++++++++-------- common/rsa_test_keys.h | 30 +- common/rsa_util.cc | 296 +++- common/rsa_util.h | 52 +- common/rsa_util_test.cc | 134 +- common/sha_util.cc | 41 +- common/sha_util.h | 12 +- common/sha_util_test.cc | 40 +- common/signature_util.cc | 61 + common/signature_util.h | 34 + common/signing_key_util.cc | 44 + common/signing_key_util.h | 55 + common/signing_key_util_test.cc | 64 + common/status.cc | 74 + common/status.h | 108 ++ common/status_test.cc | 61 + common/string_util.cc | 36 + common/string_util.h | 25 + common/string_util_test.cc | 26 + common/test_drm_certificates.cc | 328 ++++ common/test_drm_certificates.h | 54 + common/test_utils.cc | 71 + common/test_utils.h | 32 + common/verified_media_pipeline.cc | 43 + common/verified_media_pipeline.h | 27 + common/vmp_checker.cc | 358 +++++ common/vmp_checker.h | 57 + common/vmp_checker_test.cc | 321 ++++ common/widevine_system_id.cc | 19 + common/widevine_system_id.h | 22 + common/wvm_test_keys.cc | 86 + common/wvm_test_keys.h | 58 + common/wvm_token_handler.cc | 311 ++++ common/wvm_token_handler.h | 125 ++ common/wvm_token_handler_test.cc | 229 +++ common/x509_cert.cc | 458 ++++++ common/x509_cert.h | 176 ++ common/x509_cert_test.cc | 531 +++++++ example/BUILD | 2 +- example/example_data/certificate_list | Bin 456 -> 433 bytes example/example_data/device.private | Bin 0 -> 1217 bytes example/example_data/device.public | Bin 0 -> 270 bytes example/example_data/intermediate.cert | Bin 0 -> 689 bytes .../intermediate.encrypted.private | Bin 1315 -> 1315 bytes example/example_data/intermediate.public | Bin 270 -> 270 bytes example/example_data/message | Bin 2695 -> 2791 bytes example/example_data/provisioner.cert | Bin 685 -> 703 bytes .../provisioner.encrypted.private | Bin 1315 -> 1315 bytes example/example_data/provisioner.public | Bin 0 -> 270 bytes example/example_data/service.cert | Bin 685 -> 703 bytes .../example_data/service.encrypted.private | Bin 1315 -> 1315 bytes example/example_data/service.public | Bin 270 -> 270 bytes example/example_data/user.private | Bin 1217 -> 0 bytes example/example_data/user.public | Bin 270 -> 0 bytes example/provisioning_example.cc | 16 +- example/provisioning_message_generator.cc | 15 +- glog.BUILD | 2 +- gtest.BUILD | 37 - oem_certificate_generator/BUILD | 8 +- oem_certificate_generator/oem_certificate.py | 67 +- .../oem_certificate_test.py | 246 ++- ...lper.py => oem_certificate_test_helper.py} | 40 +- protos/public/BUILD | 70 +- protos/public/certificate_provisioning.proto | 92 +- protos/public/client_identification.proto | 21 +- protos/public/device_certificate.proto | 91 -- protos/public/device_certificate_status.proto | 113 ++ protos/public/drm_certificate.proto | 55 + protos/public/errors.proto | 242 +++ protos/public/provisioned_device_info.proto | 28 +- protos/public/remote_attestation.proto | 30 + ...ate.proto => signed_drm_certificate.proto} | 12 +- provisioning_sdk/internal/BUILD | 85 +- provisioning_sdk/internal/certificates/BUILD | 38 +- .../certificates/drm_ca_root_dev.cert | Bin 802 -> 0 bytes .../certificates/drm_ca_root_prod.cert | Bin 802 -> 0 bytes .../certificates/drm_ca_root_test.cert | Bin 799 -> 0 bytes .../certificates/root_certificates.cc | 38 - .../internal/certificates/root_certificates.h | 56 - .../certificates/root_oem_certificates.cc | 26 + .../certificates/root_oem_certificates.h | 40 + .../certificates/sysid-2001-chain.der | Bin 2130 -> 2233 bytes .../certificates/sysid-2001-public-key.der | Bin 270 -> 270 bytes ...rtificates.cc => test_oem_certificates.cc} | 8 +- ...certificates.h => test_oem_certificates.h} | 20 +- provisioning_sdk/internal/oem_device_cert.cc | 16 +- provisioning_sdk/internal/oem_device_cert.h | 4 +- .../internal/oem_device_cert_test.cc | 25 +- .../internal/provisioning30_session_impl.cc | 235 +++ .../internal/provisioning30_session_impl.h | 78 + .../provisioning30_session_impl_test.cc | 406 +++++ .../internal/provisioning_engine_impl.cc | 299 ++-- .../internal/provisioning_engine_impl.h | 63 +- .../internal/provisioning_engine_impl_test.cc | 633 ++++---- .../internal/provisioning_session_impl.cc | 232 +-- .../internal/provisioning_session_impl.h | 61 +- .../provisioning_session_impl_test.cc | 367 +---- provisioning_sdk/public/BUILD | 24 +- .../public/provisioning_engine.cc | 92 +- provisioning_sdk/public/provisioning_engine.h | 82 +- .../public/provisioning_engine_test.cc | 4 +- .../public/provisioning_session.cc | 19 +- .../public/provisioning_session.h | 5 +- .../public/provisioning_status.cc | 19 +- provisioning_sdk/public/provisioning_status.h | 23 +- provisioning_sdk/public/python/BUILD | 6 +- provisioning_sdk/public/python/base.i | 27 - .../public/python/certificate_type.i | 28 - .../public/python/certificate_type_setup.py | 36 + .../public/python/crypto_utility.py | 3 +- .../drm_intermediate_certificate_test.py | 14 +- .../engine_generate_certificate_test.py | 29 +- .../public/python/init_engine_test.py | 91 +- .../public/python/new_session_test.py | 65 +- .../public/python/provisioning_engine.clif | 46 + .../public/python/provisioning_engine.i | 46 - .../python/provisioning_engine_setup.py | 47 + .../public/python/provisioning_session.clif | 16 + .../public/python/provisioning_session.i | 37 - .../python/provisioning_session_setup.py | 38 + .../public/python/provisioning_status.clif | 12 + .../public/python/provisioning_status.i | 44 - .../python/provisioning_status_setup.py | 36 + .../set_certificate_status_list_test.py | 10 +- provisioning_sdk/public/python/setup.py | 62 - .../public/python/setup_common.py | 34 + .../public/python/test_data_provider.py | 29 +- .../public/python/test_data_utility.py | 95 +- provisioning_sdk/public/python/unique_ptr.i | 51 - run_tests.sh | 106 +- six.BUILD | 15 + strings/BUILD | 34 + strings/serialize.cc | 33 + strings/serialize.h | 25 + strings/serialize_test.cc | 32 + testing/BUILD | 30 + testing/gmock.h | 14 + testing/gunit.h | 17 + util/BUILD | 43 + util/endian/BUILD | 19 + util/endian/endian.h | 49 + util/error_space.h | 83 + util/error_space_test.cc | 33 + util/gtl/BUILD | 19 + util/gtl/map_util.h | 58 + util/proto_status.h | 39 + util/proto_status_test.cc | 64 + util/random/BUILD | 31 + util/random/global_id.h | 30 + util/random/global_id_test.cc | 27 + 207 files changed, 14893 insertions(+), 3332 deletions(-) delete mode 100644 base/mutex.h create mode 100644 base/timestamp.h rename {provisioning_sdk/public => common}/certificate_type.h (62%) create mode 100644 common/client_cert.cc create mode 100644 common/client_cert.h create mode 100644 common/client_cert_test.cc create mode 100644 common/client_id_util.cc create mode 100644 common/client_id_util.h create mode 100644 common/crypto_util.cc create mode 100644 common/crypto_util.h create mode 100644 common/crypto_util_test.cc create mode 100644 common/device_status_list.cc create mode 100644 common/device_status_list.h create mode 100644 common/device_status_list_test.cc create mode 100644 common/drm_root_certificate.cc create mode 100644 common/drm_root_certificate.h create mode 100644 common/drm_root_certificate_test.cc create mode 100644 common/drm_service_certificate.cc create mode 100644 common/drm_service_certificate.h create mode 100644 common/drm_service_certificate_test.cc create mode 100644 common/ecb_util.cc create mode 100644 common/ecb_util.h create mode 100644 common/ecb_util_test.cc create mode 100644 common/error_space.cc create mode 100644 common/error_space.h create mode 100644 common/python/BUILD create mode 100644 common/python/aes_cbc_util.clif create mode 100644 common/python/certificate_type.clif create mode 100644 common/python/crypto_util.clif create mode 100644 common/python/drm_root_certificate.clif create mode 100644 common/python/drm_service_certificate.clif create mode 100644 common/python/rsa_key.clif create mode 100644 common/python/rsa_test_keys.clif create mode 100644 common/python/signing_key_util.clif create mode 100644 common/python/test_drm_certificates.clif create mode 100644 common/remote_attestation_verifier.cc create mode 100644 common/remote_attestation_verifier.h create mode 100644 common/signature_util.cc create mode 100644 common/signature_util.h create mode 100644 common/signing_key_util.cc create mode 100644 common/signing_key_util.h create mode 100644 common/signing_key_util_test.cc create mode 100644 common/status.cc create mode 100644 common/status.h create mode 100644 common/status_test.cc create mode 100644 common/string_util.cc create mode 100644 common/string_util.h create mode 100644 common/string_util_test.cc create mode 100644 common/test_drm_certificates.cc create mode 100644 common/test_drm_certificates.h create mode 100644 common/test_utils.cc create mode 100644 common/test_utils.h create mode 100644 common/verified_media_pipeline.cc create mode 100644 common/verified_media_pipeline.h create mode 100644 common/vmp_checker.cc create mode 100644 common/vmp_checker.h create mode 100644 common/vmp_checker_test.cc create mode 100644 common/widevine_system_id.cc create mode 100644 common/widevine_system_id.h create mode 100644 common/wvm_test_keys.cc create mode 100644 common/wvm_test_keys.h create mode 100644 common/wvm_token_handler.cc create mode 100644 common/wvm_token_handler.h create mode 100644 common/wvm_token_handler_test.cc create mode 100644 common/x509_cert.cc create mode 100644 common/x509_cert.h create mode 100644 common/x509_cert_test.cc create mode 100644 example/example_data/device.private create mode 100644 example/example_data/device.public create mode 100644 example/example_data/intermediate.cert create mode 100644 example/example_data/provisioner.public delete mode 100644 example/example_data/user.private delete mode 100644 example/example_data/user.public delete mode 100644 gtest.BUILD rename oem_certificate_generator/{oem_certificate_generator_helper.py => oem_certificate_test_helper.py} (82%) delete mode 100644 protos/public/device_certificate.proto create mode 100644 protos/public/device_certificate_status.proto create mode 100644 protos/public/drm_certificate.proto create mode 100644 protos/public/errors.proto create mode 100644 protos/public/remote_attestation.proto rename protos/public/{signed_device_certificate.proto => signed_drm_certificate.proto} (69%) delete mode 100644 provisioning_sdk/internal/certificates/drm_ca_root_dev.cert delete mode 100644 provisioning_sdk/internal/certificates/drm_ca_root_prod.cert delete mode 100644 provisioning_sdk/internal/certificates/drm_ca_root_test.cert delete mode 100644 provisioning_sdk/internal/certificates/root_certificates.cc delete mode 100644 provisioning_sdk/internal/certificates/root_certificates.h create mode 100644 provisioning_sdk/internal/certificates/root_oem_certificates.cc create mode 100644 provisioning_sdk/internal/certificates/root_oem_certificates.h rename provisioning_sdk/internal/certificates/{test_certificates.cc => test_oem_certificates.cc} (89%) rename provisioning_sdk/internal/certificates/{test_certificates.h => test_oem_certificates.h} (73%) create mode 100644 provisioning_sdk/internal/provisioning30_session_impl.cc create mode 100644 provisioning_sdk/internal/provisioning30_session_impl.h create mode 100644 provisioning_sdk/internal/provisioning30_session_impl_test.cc delete mode 100644 provisioning_sdk/public/python/base.i delete mode 100644 provisioning_sdk/public/python/certificate_type.i create mode 100644 provisioning_sdk/public/python/certificate_type_setup.py create mode 100644 provisioning_sdk/public/python/provisioning_engine.clif delete mode 100644 provisioning_sdk/public/python/provisioning_engine.i create mode 100644 provisioning_sdk/public/python/provisioning_engine_setup.py create mode 100644 provisioning_sdk/public/python/provisioning_session.clif delete mode 100644 provisioning_sdk/public/python/provisioning_session.i create mode 100644 provisioning_sdk/public/python/provisioning_session_setup.py create mode 100644 provisioning_sdk/public/python/provisioning_status.clif delete mode 100644 provisioning_sdk/public/python/provisioning_status.i create mode 100644 provisioning_sdk/public/python/provisioning_status_setup.py delete mode 100644 provisioning_sdk/public/python/setup.py create mode 100644 provisioning_sdk/public/python/setup_common.py delete mode 100644 provisioning_sdk/public/python/unique_ptr.i create mode 100644 six.BUILD create mode 100644 strings/BUILD create mode 100644 strings/serialize.cc create mode 100644 strings/serialize.h create mode 100644 strings/serialize_test.cc create mode 100644 testing/BUILD create mode 100644 testing/gmock.h create mode 100644 testing/gunit.h create mode 100644 util/BUILD create mode 100644 util/endian/BUILD create mode 100644 util/endian/endian.h create mode 100644 util/error_space.h create mode 100644 util/error_space_test.cc create mode 100644 util/gtl/BUILD create mode 100644 util/gtl/map_util.h create mode 100644 util/proto_status.h create mode 100644 util/proto_status_test.cc create mode 100644 util/random/BUILD create mode 100644 util/random/global_id.h create mode 100644 util/random/global_id_test.cc diff --git a/BUILD b/BUILD index 3f63a4c..66d06b2 100644 --- a/BUILD +++ b/BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2017 Google Inc. +# 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 diff --git a/WORKSPACE b/WORKSPACE index e19656e..b147b7c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,49 +1,62 @@ workspace(name = "provisioning_sdk") +load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository", "git_repository") + +# 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", +) git_repository( name = "protobuf_repo", remote = "https://github.com/google/protobuf.git", - tag = "v3.2.0", + tag = "v3.6.1.3", ) git_repository( name = "boringssl_repo", - commit = "bbcaa15b0647816b9a1a9b9e0d209cd6712f0105", # 2016-07-11 + commit = "14164f6fef47b7ebd97cdb0cea1624eabd6fe6b8", # 2018-11-26 remote = "https://github.com/google/boringssl.git", ) git_repository( name = "gflags_repo", - commit = "fe57e5af4db74ab298523f06d2c43aa895ba9f98", # 2016-07-20 + commit = "e171aa2d15ed9eb17054558e0b3a6a413bb01067", # 2018-11-11 remote = "https://github.com/gflags/gflags.git", ) -new_git_repository( +git_repository( name = "googletest_repo", - build_file = "gtest.BUILD", - commit = "ec44c6c1675c25b9827aacd08c02433cccde7780", # 2016-07-26 + commit = "b6cd405286ed8635ece71c72f118e659f4ade3fb", # 2019-01-04 remote = "https://github.com/google/googletest.git", ) new_git_repository( name = "glog_repo", - build_file = "glog.BUILD", + build_file = "@//:glog.BUILD", commit = "0472b91c5defdf90cff7292e3bf7bd86770a9a0a", # 2016-07-13 remote = "https://github.com/google/glog.git", ) # py_proto_library depends on six indirectly (py_proto_library uses # protobuf_python as its default runtime which depends on six). -new_http_archive( - name = "six_archive", - build_file = "@protobuf_repo//:six.BUILD", - sha256 = "105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a", - url = "https://pypi.python.org/packages/source/s/six/six-1.10.0.tar.gz#md5=34eed507548117b2ab523ab14b2f8b55", +new_git_repository( + name = "six_repo", + build_file = "@//:six.BUILD", + remote = "https://github.com/benjaminp/six.git", + tag = "1.10.0", ) bind( name = "six", - actual = "@six_archive//:six", + actual = "@six_repo//:six", ) bind( diff --git a/base/BUILD b/base/BUILD index 2af5a31..0612c2d 100644 --- a/base/BUILD +++ b/base/BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -14,10 +14,11 @@ cc_library( name = "base", hdrs = [ "macros.h", - "mutex.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 index 5aff849..e2ec936 100644 --- a/base/macros.h +++ b/base/macros.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -9,7 +9,7 @@ #ifndef BASE_MACROS_H_ #define BASE_MACROS_H_ -#include // For size_t +#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, @@ -26,88 +26,6 @@ TypeName() = delete; \ DISALLOW_COPY_AND_ASSIGN(TypeName) -// The arraysize(arr) macro returns the # of elements in an array arr. -// The expression is a compile-time constant, and therefore can be -// used in defining new arrays, for example. If you use arraysize on -// a pointer by mistake, you will get a compile-time error. -// -// This template function declaration is used in defining arraysize. -// Note that the function doesn't need an implementation, as we only -// use its type. -template -char (&ArraySizeHelper(T (&array)[N]))[N]; - -// That gcc wants both of these prototypes seems mysterious. VC, for -// its part, can't decide which to use (another mystery). Matching of -// template overloads: the final frontier. -#ifndef COMPILER_MSVC -template -char (&ArraySizeHelper(const T (&array)[N]))[N]; -#endif - -#define arraysize(array) (sizeof(ArraySizeHelper(array))) - -// A macro to turn a symbol into a std::string -#define AS_STRING(x) AS_STRING_INTERNAL(x) -#define AS_STRING_INTERNAL(x) #x - -// The following enum should be used only as a constructor argument to indicate -// that the variable has static storage class, and that the constructor should -// do nothing to its state. It indicates to the reader that it is legal to -// declare a static instance of the class, provided the constructor is given -// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a -// static variable that has a constructor or a destructor because invocation -// order is undefined. However, IF the type can be initialized by filling with -// zeroes (which the loader does for static variables), AND the type's -// destructor does nothing to the storage, then a constructor for static -// initialization can be declared as -// explicit MyClass(base::LinkerInitialized x) {} -// and invoked as -// static MyClass my_variable_name(base::LINKER_INITIALIZED); -namespace base { -enum LinkerInitialized { LINKER_INITIALIZED }; -} - -// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through -// between switch labels: -// switch (x) { -// case 40: -// case 41: -// if (truth_is_out_there) { -// ++x; -// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in -// // comments. -// } else { -// return x; -// } -// case 42: -// ... -// -// As shown in the example above, the FALLTHROUGH_INTENDED macro should be -// followed by a semicolon. It is designed to mimic control-flow statements -// like 'break;', so it can be placed in most places where 'break;' can, but -// only if there are no statements on the execution path between it and the -// next switch label. -// -// When compiled with clang in C++11 mode, the FALLTHROUGH_INTENDED macro is -// expanded to [[clang::fallthrough]] attribute, which is analysed when -// performing switch labels fall-through diagnostic ('-Wimplicit-fallthrough'). -// See clang documentation on language extensions for details: -// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough -// -// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no -// effect on diagnostics. -// -// In either case this macro has no effect on runtime behavior and performance -// of code. -#if defined(__clang__) && defined(LANG_CXX11) && defined(__has_warning) -#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") -#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT -#endif -#endif - -#ifndef FALLTHROUGH_INTENDED -#define FALLTHROUGH_INTENDED do { } while (0) -#endif +#define arraysize(array) ABSL_ARRAYSIZE(array) #endif // BASE_MACROS_H_ diff --git a/base/mutex.h b/base/mutex.h deleted file mode 100644 index 168ea1c..0000000 --- a/base/mutex.h +++ /dev/null @@ -1,91 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 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. -//////////////////////////////////////////////////////////////////////////////// - -#ifndef BASE_MUTEX_H_ -#define BASE_MUTEX_H_ - -#include -#include - -#include "glog/logging.h" -#include "base/macros.h" -#include "base/thread_annotations.h" - -// Basic mutex wrapper around a pthread RW lock. -class LOCKABLE Mutex { - public: - inline Mutex() { CHECK_EQ(pthread_rwlock_init(&lock_, nullptr), 0); } - inline Mutex(base::LinkerInitialized) { // NOLINT - CHECK_EQ(pthread_rwlock_init(&lock_, nullptr), 0); - } - inline ~Mutex() { CHECK_EQ(pthread_rwlock_destroy(&lock_), 0); } - inline void Lock() EXCLUSIVE_LOCK_FUNCTION() { WriterLock(); } - inline void Unlock() UNLOCK_FUNCTION() { WriterUnlock(); } - inline void ReaderLock() SHARED_LOCK_FUNCTION() { - CHECK_EQ(pthread_rwlock_rdlock(&lock_), 0); - } - inline void ReaderUnlock() UNLOCK_FUNCTION() { - CHECK_EQ(pthread_rwlock_unlock(&lock_), 0); - } - inline void WriterLock() EXCLUSIVE_LOCK_FUNCTION() { - CHECK_EQ(pthread_rwlock_wrlock(&lock_), 0); - } - inline void WriterUnlock() UNLOCK_FUNCTION() { - CHECK_EQ(pthread_rwlock_unlock(&lock_), 0); - } - - private: - pthread_rwlock_t lock_; - - DISALLOW_COPY_AND_ASSIGN(Mutex); -}; - -// ----------------------------------------------------------------------------- -// MutexLock(mu) acquires mu when constructed and releases it when destroyed. -class SCOPED_LOCKABLE MutexLock { - public: - explicit MutexLock(Mutex* mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { - this->mu_->Lock(); - } - ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } - - private: - Mutex* const mu_; - - DISALLOW_COPY_AND_ASSIGN(MutexLock); -}; - -// The ReaderMutexLock and WriterMutexLock classes work like MutexLock -// to acquire/release read and write locks on reader/writer locks. -class SCOPED_LOCKABLE ReaderMutexLock { - public: - explicit ReaderMutexLock(Mutex* mu) SHARED_LOCK_FUNCTION(mu) : mu_(mu) { - mu->ReaderLock(); - } - ~ReaderMutexLock() UNLOCK_FUNCTION() { this->mu_->ReaderUnlock(); } - - private: - Mutex* const mu_; - - DISALLOW_COPY_AND_ASSIGN(ReaderMutexLock); -}; - -class SCOPED_LOCKABLE WriterMutexLock { - public: - explicit WriterMutexLock(Mutex* mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { - mu->WriterLock(); - } - ~WriterMutexLock() UNLOCK_FUNCTION() { this->mu_->WriterUnlock(); } - - private: - Mutex* const mu_; - - DISALLOW_COPY_AND_ASSIGN(WriterMutexLock); -}; - -#endif // BASE_MUTEX_H_ diff --git a/base/thread_annotations.h b/base/thread_annotations.h index 1bb959d..ce3535a 100644 --- a/base/thread_annotations.h +++ b/base/thread_annotations.h @@ -1,212 +1,14 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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. //////////////////////////////////////////////////////////////////////////////// -// This header file contains the macro definitions for thread safety -// annotations that allow the developers to document the locking policies -// of their multi-threaded code. The annotations can also help program -// analysis tools to identify potential thread safety issues. -// -// -// The annotations are implemented using GCC's "attributes" extension. -// Using the macros defined here instead of the raw GCC attributes allows -// for portability and future compatibility. -// - #ifndef BASE_THREAD_ANNOTATIONS_H_ #define BASE_THREAD_ANNOTATIONS_H_ -#if defined(__GNUC__) && defined(__SUPPORT_TS_ANNOTATION__) && !defined(SWIG) -#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) -#else -#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op -#endif - -#if defined(__GNUC__) && !defined(__clang__) - -// Document if a shared variable/field needs to be protected by a lock. -// GUARDED_BY allows the user to specify a particular lock that should be -// held when accessing the annotated variable, while GUARDED_VAR only -// indicates a shared variable should be guarded (by any lock). GUARDED_VAR -// is primarily used when the client cannot express the name of the lock. -#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) -#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded) - -// Document if the memory location pointed to by a pointer should be guarded -// by a lock when dereferencing the pointer. Similar to GUARDED_VAR, -// PT_GUARDED_VAR is primarily used when the client cannot express the name -// of the lock. Note that a pointer variable to a shared memory location -// could itself be a shared variable. For example, if a shared global pointer -// q, which is guarded by mu1, points to a shared memory location that is -// guarded by mu2, q should be annotated as follows: -// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2); -#define PT_GUARDED_BY(x) \ - THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded_by(x)) -#define PT_GUARDED_VAR \ - THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded) - -// Document the acquisition order between locks that can be held -// simultaneously by a thread. For any two locks that need to be annotated -// to establish an acquisition order, only one of them needs the annotation. -// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER -// and ACQUIRED_BEFORE.) -#define ACQUIRED_AFTER(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) -#define ACQUIRED_BEFORE(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) - -// The following three annotations document the lock requirements for -// functions/methods. - -// Document if a function expects certain locks to be held before it is called -#define EXCLUSIVE_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) - -#define SHARED_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) - -// Document the locks acquired in the body of the function. These locks -// non-reentrant). -#define LOCKS_EXCLUDED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) - -// Document the lock the annotated function returns without acquiring it. -#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) - -// Document if a class/type is a lockable type (such as the Mutex class). -#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable) - -// Document if a class is a scoped lockable type (such as the MutexLock class). -#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) - -// The following annotations specify lock and unlock primitives. -#define EXCLUSIVE_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock(__VA_ARGS__)) - -#define SHARED_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_lock(__VA_ARGS__)) - -#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock(__VA_ARGS__)) - -#define SHARED_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock(__VA_ARGS__)) - -#define UNLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(unlock(__VA_ARGS__)) - -// An escape hatch for thread safety analysis to ignore the annotated function. -#define NO_THREAD_SAFETY_ANALYSIS \ - THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -// Used to mark functions that need to be fixed, because they are producing -// thread safety warnings. This macro is intended primarily for use by the -// compiler team; it allows new thread safety warnings to be rolled out -// without breaking existing code. Code which triggers the new warnings are -// marked with a FIXME, and referred back to the code owners to fix. -#define NO_THREAD_SAFETY_ANALYSIS_FIXME \ - THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -// NO_THREAD_SAFETY_ANALYSIS_OPT turns off thread-safety checking in the -// annotated function in opt (NDEBUG) mode. It is for use specifically when -// the thread-safety checker is failing in opt mode on an otherwise correct -// piece of code. -#ifdef NDEBUG -#define NO_THREAD_SAFETY_ANALYSIS_OPT NO_THREAD_SAFETY_ANALYSIS -#else -#define NO_THREAD_SAFETY_ANALYSIS_OPT -#endif - -// TS_UNCHECKED should be placed around lock expressions that are not valid -// C++ syntax, but which are present for documentation purposes. The -// expressions are passed unchanged to gcc, which will usually treat them -// as the universal lock. -#define TS_UNCHECKED(x) x - -// TS_FIXME is used to mark lock expressions that are not valid C++ syntax. -// This annotation should eventually be either fixed, or changed to -// TS_UNCHECKED. -#define TS_FIXME(x) x - -// This is used to pass different annotations to gcc and clang, in cases where -// gcc would reject a lock expression (e.g. &MyClass::mu_) that is accepted -// by clang. This is seldom needed, since GCC usually ignores invalid lock -// expressions except in certain cases, such as LOCK_RETURNED. -#define TS_CLANG_ONLY(CLANG_EXPR, GCC_EXPR) GCC_EXPR - -// Clang Attributes -// The names of attributes in the clang analysis are slightly different -#else - -#define GUARDED_BY(x) \ - THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) - -#define GUARDED_VAR \ - THREAD_ANNOTATION_ATTRIBUTE__(guarded) - -#define PT_GUARDED_BY(x) \ - THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) - -#define PT_GUARDED_VAR \ - THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded) - -#define ACQUIRED_AFTER(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) - -#define ACQUIRED_BEFORE(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) - -#define EXCLUSIVE_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) - -#define SHARED_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) - -#define LOCKS_EXCLUDED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) - -#define LOCK_RETURNED(x) \ - THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) - -#define LOCKABLE \ - THREAD_ANNOTATION_ATTRIBUTE__(lockable) - -#define SCOPED_LOCKABLE \ - THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) - -#define EXCLUSIVE_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) - -#define SHARED_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) - -#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) - -#define SHARED_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) - -#define UNLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) - -#define NO_THREAD_SAFETY_ANALYSIS \ - THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -#define NO_THREAD_SAFETY_ANALYSIS_OPT - -#define NO_THREAD_SAFETY_ANALYSIS_FIXME \ - THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -#define TS_UNCHECKED(x) "" - -#define TS_FIXME(x) "" - -#define TS_CLANG_ONLY(CLANG_EXPR, GCC_EXPR) CLANG_EXPR - -#endif // defined(__clang__) +#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 index b17a18b..cb07fbc 100644 --- a/common/BUILD +++ b/common/BUILD @@ -1,16 +1,209 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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: -# Build file for code common to multiple Widevine services. +# Constants, data structures, util classes for Widevine libraries. -package(default_visibility = ["//visibility:public"]) +package( + default_visibility = ["//visibility:public"], +) + +filegroup( + name = "binary_release_files", + srcs = [ + "certificate_type.h", + "status.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 = "status", + srcs = ["status.cc"], + hdrs = ["status.h"], + deps = [ + "//base", + "@abseil_repo//absl/base:core_headers", + "@abseil_repo//absl/strings", + "//util:error_space", + ], +) + +cc_test( + name = "status_test", + srcs = ["status_test.cc"], + deps = [ + ":status", + "//testing:gunit_main", + ], +) + +cc_library( + name = "client_cert", + srcs = ["client_cert.cc"], + hdrs = ["client_cert.h"], + deps = [ + ":crypto_util", + ":drm_root_certificate", + ":error_space", + ":random_util", + ":rsa_key", + ":sha_util", + ":signing_key_util", + ":status", + ":wvm_token_handler", + "//base", + "//strings", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "@abseil_repo//absl/time", + "//util/gtl:map_util", + "//protos/public:client_identification_proto", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:license_protocol_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_test( + name = "client_cert_test", + srcs = ["client_cert_test.cc"], + deps = [ + ":client_cert", + ":drm_root_certificate", + ":error_space", + ":sha_util", + ":test_drm_certificates", + ":wvm_test_keys", + "//base", + "//strings", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "@abseil_repo//absl/time", + "//common:rsa_key", + "//common:rsa_test_keys", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_library( + name = "device_status_list", + srcs = ["device_status_list.cc"], + hdrs = ["device_status_list.h"], + deps = [ + ":client_cert", + ":crypto_util", + ":drm_root_certificate", + ":drm_service_certificate", + ":error_space", + ":random_util", + ":rsa_key", + ":signing_key_util", + ":status", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util/gtl:map_util", + "//protos/public:client_identification_proto", + "//protos/public:device_certificate_status_proto", + "//protos/public:errors_proto", + "//protos/public:provisioned_device_info_proto", + ], +) + +cc_test( + name = "device_status_list_test", + timeout = "short", + srcs = ["device_status_list_test.cc"], + deps = [ + ":client_cert", + ":device_status_list", + "//base", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//common:rsa_key", + "//common:rsa_test_keys", + "//protos/public:client_identification_proto", + "//protos/public:errors_proto", + "//protos/public:provisioned_device_info_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_library( + name = "drm_root_certificate", + srcs = ["drm_root_certificate.cc"], + hdrs = ["drm_root_certificate.h"], + deps = [ + ":certificate_type", + ":error_space", + ":rsa_key", + ":sha_util", + ":status", + "//base", + "@abseil_repo//absl/memory", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//external:openssl", + "//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", + ":error_space", + ":rsa_key", + ":rsa_test_keys", + ":test_drm_certificates", + "//base", + "@protobuf_repo//:protobuf", + "//testing:gunit_main", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +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", + ":status", + "//base", + "@abseil_repo//absl/strings", + "//protos/public:client_identification_proto", + "//protos/public:errors_proto", + ], +) cc_library( name = "rsa_util", @@ -30,8 +223,10 @@ cc_test( deps = [ ":rsa_test_keys", ":rsa_util", - "//external:gtest", - "//external:gtest_main", + "//base", + "//testing:gunit", + "//testing:gunit_main", + "//external:openssl", ], ) @@ -63,8 +258,9 @@ cc_test( deps = [ ":rsa_key", ":rsa_test_keys", - "//external:gtest", - "//external:gtest_main", + ":rsa_util", + "//testing:gunit", + "//testing:gunit_main", ], ) @@ -84,7 +280,7 @@ cc_library( hdrs = ["mock_rsa_key.h"], deps = [ ":rsa_key", - "//external:gtest", + "//testing:gunit", ], ) @@ -104,7 +300,75 @@ cc_test( srcs = ["aes_cbc_util_test.cc"], deps = [ ":aes_cbc_util", - "//external:gtest_main", + "//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", ], ) @@ -123,7 +387,7 @@ cc_test( srcs = ["random_util_test.cc"], deps = [ ":random_util", - "//external:gtest_main", + "//testing:gunit_main", ], ) @@ -142,24 +406,282 @@ cc_test( srcs = ["sha_util_test.cc"], deps = [ ":sha_util", - "//external:gtest_main", + "//testing:gunit_main", + "@abseil_repo//absl/strings", ], ) cc_library( - name = "file_util", - srcs = ["file_util.cc"], - hdrs = ["file_util.h"], + name = "signature_util", + srcs = ["signature_util.cc"], + hdrs = ["signature_util.h"], deps = [ + ":aes_cbc_util", + ":rsa_key", + ":sha_util", + ":status", + "//base", + ], +) + +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_drm_certificates", + testonly = 1, + srcs = ["test_drm_certificates.cc"], + hdrs = ["test_drm_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", + ":status", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util/endian", + "//util/gtl:map_util", + ], +) + +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:error_space", + "//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", + ":status", + ":x509_cert", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//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", + ":status", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util/gtl:map_util", + "//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_root_certificate", + ":drm_service_certificate", + ":rsa_key", + ":rsa_test_keys", + ":rsa_util", + ":test_drm_certificates", + "//base", + "@protobuf_repo//: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 = [ + ":status", + ":vmp_checker", + "//base", + "@abseil_repo//absl/strings", + "//protos/public:license_protocol_proto", + ], +) + +cc_library( + name = "x509_cert", + srcs = ["x509_cert.cc"], + hdrs = ["x509_cert.h"], + deps = [ + ":error_space", + ":openssl_util", + ":rsa_key", + ":status", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//external:openssl", + ], +) + +cc_library( + name = "test_utils", + testonly = 1, + srcs = ["test_utils.cc"], + hdrs = ["test_utils.h"], + deps = [ + ":status", + "//base", + "//external:openssl", + ], +) + +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", + ":status", + ":x509_cert", + "//base", + "//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 = [ + ":status", "//base", ], ) cc_test( - name = "file_util_test", - srcs = ["file_util_test.cc"], + name = "string_util_test", + srcs = ["string_util_test.cc"], deps = [ - ":file_util", - "//external:gtest_main", + ":string_util", + "//base", + "//testing:gunit_main", ], ) diff --git a/common/aes_cbc_util.cc b/common/aes_cbc_util.cc index eeed72f..66e6552 100644 --- a/common/aes_cbc_util.cc +++ b/common/aes_cbc_util.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -20,23 +20,35 @@ 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) { - if (iv.size() != AES_BLOCK_SIZE) return ""; - 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) { - return ""; + LOG(WARNING) << "Invalid AES key."; + return std::string(); } - std::string encrypted(padded_text); + std::string encrypted(plaintext.size(), 0); std::vector local_iv(iv.begin(), iv.end()); - AES_cbc_encrypt(reinterpret_cast(padded_text.data()), - reinterpret_cast(&encrypted[0]), padded_text.size(), + AES_cbc_encrypt(reinterpret_cast(plaintext.data()), + reinterpret_cast(&encrypted[0]), plaintext.size(), &aes_key, &local_iv[0], AES_ENCRYPT); return encrypted; } @@ -45,26 +57,43 @@ std::string EncryptAesCbc(const std::string& key, const std::string& iv, // the plaintext on success. std::string DecryptAesCbc(const std::string& key, const std::string& iv, const std::string& ciphertext) { - if (ciphertext.empty()) return ""; - if (iv.size() != AES_BLOCK_SIZE) return ""; - if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) return ""; + 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) { - return ""; + LOG(WARNING) << "Invalid AES key."; + return std::string(); } - std::string cleartext(ciphertext); + 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) return ""; + 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) return ""; + 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; @@ -73,17 +102,26 @@ std::string DecryptAesCbc(const std::string& key, const std::string& iv, 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) return ""; - if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) return ""; + 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) { - return ""; + LOG(WARNING) << "Invalid AES key."; + return std::string(); } - std::string cleartext(ciphertext); + 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); diff --git a/common/aes_cbc_util.h b/common/aes_cbc_util.h index e4bffc8..a1ad59b 100644 --- a/common/aes_cbc_util.h +++ b/common/aes_cbc_util.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -14,12 +14,18 @@ namespace widevine { namespace crypto_util { -// Helper for wrapping AES CBC encryption. Uses PKCS5 padding. +// 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. Uses PKCS5 padding. +// 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); @@ -35,4 +41,3 @@ std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv, } // namespace widevine #endif // COMMON_AES_CBC_UTIL_H_ - diff --git a/common/aes_cbc_util_test.cc b/common/aes_cbc_util_test.cc index 34d25fb..1eded76 100644 --- a/common/aes_cbc_util_test.cc +++ b/common/aes_cbc_util_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -7,9 +7,8 @@ //////////////////////////////////////////////////////////////////////////////// #include "common/aes_cbc_util.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" +#include "testing/gmock.h" +#include "testing/gunit.h" namespace { @@ -97,6 +96,31 @@ TEST(CryptoUtilTest, TestFailedEncrypt) { 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"); diff --git a/provisioning_sdk/public/certificate_type.h b/common/certificate_type.h similarity index 62% rename from provisioning_sdk/public/certificate_type.h rename to common/certificate_type.h index f4640d7..b63a639 100644 --- a/provisioning_sdk/public/certificate_type.h +++ b/common/certificate_type.h @@ -1,22 +1,22 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_ -#define PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_ +#ifndef COMMON_CERTIFICATE_TYPE_H_ +#define COMMON_CERTIFICATE_TYPE_H_ namespace widevine { enum CertificateType { - kCertTesting = 0, - kCertDevelopment, - kCertProduction, + kCertificateTypeTesting, + kCertificateTypeDevelopment, + kCertificateTypeProduction, }; } // namespace widevine -#endif // PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_ +#endif // COMMON_CERTIFICATE_TYPE_H_ diff --git a/common/client_cert.cc b/common/client_cert.cc new file mode 100644 index 0000000..4940614 --- /dev/null +++ b/common/client_cert.cc @@ -0,0 +1,262 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/client_cert.h" + +#include +#include +#include + +#include "glog/logging.h" +#include "strings/serialize.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" +#include "util/gtl/map_util.h" +#include "common/crypto_util.h" +#include "common/drm_root_certificate.h" +#include "common/error_space.h" +#include "common/random_util.h" +#include "common/sha_util.h" +#include "common/signing_key_util.h" +#include "common/status.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 kKeyboxSizeBytes = 72; + +} // namespace + +// instead of ClientCert** to explicitly assigning ownership of the created +// object to the caller. + +Status ClientCert::Create(const DrmRootCertificate* root_certificate, + ClientIdentification::TokenType token_type, + const std::string& token, ClientCert** client_cert) { + DCHECK(client_cert); + if (token_type == ClientIdentification::KEYBOX) { + *client_cert = nullptr; + if (token.size() < kKeyboxSizeBytes) { + return Status(error_space, INVALID_KEYBOX_TOKEN, + "keybox-token-is-too-short"); + } + return ClientCert::CreateWithKeybox(token, client_cert); + } else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) { + return CreateWithDrmCertificate(root_certificate, token, client_cert); + } else { + return Status(error_space, error::UNIMPLEMENTED, + "client-type-not-implemented"); + } +} + +Status ClientCert::CreateWithKeybox(const std::string& keybox_token, + ClientCert** client_cert) { + CHECK(client_cert); + *client_cert = nullptr; + + std::unique_ptr new_client_cert(new KeyboxClientCert); + Status status = new_client_cert->Initialize(keybox_token); + if (!status.ok()) { + return status; + } + + *client_cert = new_client_cert.release(); + return OkStatus(); +} + +Status ClientCert::CreateWithDrmCertificate( + const DrmRootCertificate* root_certificate, const std::string& drm_certificate, + ClientCert** client_cert) { + CHECK(client_cert); + *client_cert = nullptr; + + std::unique_ptr new_client_cert( + new CertificateClientCert); + Status status = + new_client_cert->Initialize(root_certificate, drm_certificate); + if (!status.ok()) { + return status; + } + + *client_cert = new_client_cert.release(); + return 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, + protocol_version < VERSION_2_2 ? message : Sha512_Hash(message), + SigningKeyMaterialSizeBits(protocol_version))); +} + +KeyboxClientCert::KeyboxClientCert() {} + +KeyboxClientCert::~KeyboxClientCert() {} + +void KeyboxClientCert::SetPreProvisioningKeys( + const std::multimap& keymap) { + std::vector keyvector; + keyvector.reserve(keymap.size()); + for (std::multimap::const_iterator it = keymap.begin(); + it != keymap.end(); ++it) { + std::string key = absl::HexStringToBytes(it->second); + DCHECK_EQ(key.size(), 16); + keyvector.push_back(WvmTokenHandler::PreprovKey(it->first, key)); + } + WvmTokenHandler::SetPreprovKeys(keyvector); +} + +bool KeyboxClientCert::IsSystemIdKnown(const uint32_t system_id) { + return WvmTokenHandler::IsSystemIdKnown(system_id); +} + +uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) { + return WvmTokenHandler::GetSystemId(keybox_bytes); +} + +Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) { + if (keybox_bytes.size() < kKeyboxSizeBytes) { + return Status(error_space, INVALID_KEYBOX_TOKEN, + "keybox-token-is-too-short"); + } + + set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes)); + set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes)); + bool insecure_keybox = false; + Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &device_key_, + nullptr, &insecure_keybox); + if (!status.ok()) { + Errors new_code = status.error_code() == error::NOT_FOUND + ? MISSING_PRE_PROV_KEY + : KEYBOX_DECRYPT_ERROR; + return Status(error_space, new_code, status.error_message()); + } + return OkStatus(); +} + +Status KeyboxClientCert::VerifySignature(const std::string& message, + const std::string& signature, + ProtocolVersion protocol_version) { + DCHECK(!signing_key().empty()); + using crypto_util::VerifySignatureHmacSha256; + if (!VerifySignatureHmacSha256( + GetClientSigningKey(signing_key(), protocol_version), signature, + message)) { + return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac"); + } + return OkStatus(); +} + +CertificateClientCert::CertificateClientCert() {} + +CertificateClientCert::~CertificateClientCert() {} + +Status CertificateClientCert::Initialize( + const DrmRootCertificate* drm_root_certificate, + const std::string& serialized_certificate) { + CHECK(drm_root_certificate); + + SignedDrmCertificate signed_device_cert; + DrmCertificate device_cert; + Status status = drm_root_certificate->VerifyCertificate( + serialized_certificate, &signed_device_cert, &device_cert); + if (!status.ok()) { + return status; + } + + const SignedDrmCertificate& signer = signed_device_cert.signer(); + DrmCertificate model_certificate; + if (!model_certificate.ParseFromString(signer.drm_certificate())) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "drm-certificate-invalid-signer"); + } + if (!model_certificate.has_serial_number()) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-signer-serial-number"); + } + // Check to see if this model certificate is signed by a + // provisioner (entity using Widevine Provisioning Server SDK). + if (signer.has_signer()) { + DrmCertificate provisioner_certificate; + if (!provisioner_certificate.ParseFromString( + signer.signer().drm_certificate())) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "model-certificate-invalid-signer"); + } + if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) { + set_signed_by_provisioner(true); + } else { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "expected-provisioning-provider-certificate-type"); + } + if (!provisioner_certificate.has_provider_id() || + provisioner_certificate.provider_id().empty()) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-provisioning-service-id"); + } + set_service_id(provisioner_certificate.provider_id()); + } + set_signer_serial_number(model_certificate.serial_number()); + set_signer_creation_time_seconds(model_certificate.creation_time_seconds()); + if (!model_certificate.has_system_id()) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "model-certificate-missing-system-id"); + } + set_system_id(model_certificate.system_id()); + set_serial_number(device_cert.serial_number()); + set_public_key(device_cert.public_key()); + rsa_public_key_.reset(RsaPublicKey::Create(public_key())); + if (rsa_public_key_ == nullptr) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "drm-certificate-public-key-failed"); + } + + // TODO(user): Move this somewhere else. It is license protocol. + set_key(Random16Bytes()); + if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) { + return Status(error_space, ENCRYPT_ERROR, + "drm-certificate-failed-encrypt-session-key"); + } + + return OkStatus(); +} + +Status CertificateClientCert::VerifySignature( + const std::string& message, const std::string& signature, + ProtocolVersion protocol_version) { + CHECK(rsa_public_key_); + + if (!rsa_public_key_->VerifySignature( + protocol_version < VERSION_2_2 ? message : Sha512_Hash(message), + signature)) { + return Status(error_space, INVALID_SIGNATURE, ""); + } + return OkStatus(); +} + +} // namespace widevine diff --git a/common/client_cert.h b/common/client_cert.h new file mode 100644 index 0000000..b2dc786 --- /dev/null +++ b/common/client_cert.h @@ -0,0 +1,187 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_CLIENT_CERT_H__ +#define COMMON_CLIENT_CERT_H__ + +#include +#include +#include + +#include "common/rsa_key.h" +#include "common/status.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +class DrmRootCertificate; +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 Status Create( + const DrmRootCertificate* root_certificate, + widevine::ClientIdentification::TokenType token_type, + const std::string& token, ClientCert** client_cert); + // Creates a Keybox based ClientCert. + static Status CreateWithKeybox(const std::string& keybox_token, + ClientCert** client_cert); + // Creates a Device Certificate based ClientCert. + static Status CreateWithDrmCertificate( + const DrmRootCertificate* root_certificate, const std::string& drm_certificate, + ClientCert** client_cert); + // Creates a HMAC SHA256 signature based on the message and the key(). + // signature is owned by the caller and can not be NULL. + virtual void CreateSignature(const std::string& message, std::string* signature); + // Checks the passed in signature against a signature created used the + // classes information and the passed in message. Returns OK if signature + // is valid. + virtual Status VerifySignature(const std::string& message, const std::string& signature, + ProtocolVersion protocol_version) = 0; + // Creates a signing_key that is accessible using signing_key(). Signing_key + // is constructed by doing a key derivation using the key() and message. + virtual void GenerateSigningKey(const std::string& message, + 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 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_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_; + 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); + + Status Initialize(const std::string& keybox_bytes); + + Status VerifySignature(const std::string& message, const std::string& signature, + ProtocolVersion protocol_version) override; + const std::string& key() const override { return device_key_; } + void set_key(const std::string& key) override { device_key_ = key; } + const std::string& encrypted_key() const override { return encrypted_device_key_; } + widevine::ClientIdentification::TokenType type() const override { + return widevine::ClientIdentification::KEYBOX; + } + + private: + KeyboxClientCert(); + + friend class ClientCert; + friend class MockKeyboxClientCert; + + std::string device_key_; + std::string encrypted_device_key_; + + DISALLOW_COPY_AND_ASSIGN(KeyboxClientCert); +}; +// This class implements the device certificate operations based on RSA keys. +// It will unpack token and perform all the crypto operations for securing +// the key material in the license response. +using widevine::RsaPublicKey; +class CertificateClientCert : public ClientCert { + public: + ~CertificateClientCert() override; + + Status VerifySignature(const std::string& message, const std::string& signature, + ProtocolVersion protocol_version) override; + const std::string& key() const override { return session_key_; } + void set_key(const std::string& key) override { session_key_ = key; } + const std::string& encrypted_key() const override { + return encrypted_session_key_; + } + widevine::ClientIdentification::TokenType type() const override { + return widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE; + } + + protected: + friend class ClientCert; + friend class MockCertificateClientCert; + Status Initialize(const DrmRootCertificate* drm_root_certificate, + const std::string& serialized_certificate); + virtual void set_public_key(const std::string& public_key) { + public_key_ = public_key; + } + 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; + } + + std::string session_key_; + std::string encrypted_session_key_; + std::unique_ptr rsa_public_key_; + + private: + CertificateClientCert(); + + DISALLOW_COPY_AND_ASSIGN(CertificateClientCert); +}; + +} // namespace widevine +#endif // COMMON_CLIENT_CERT_H__ diff --git a/common/client_cert_test.cc b/common/client_cert_test.cc new file mode 100644 index 0000000..7a4b579 --- /dev/null +++ b/common/client_cert_test.cc @@ -0,0 +1,617 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/client_cert.h" + +#include +#include +#include +#include + +#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/sha_util.h" +#include "common/test_drm_certificates.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) 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; + } + ASSERT_OK( + DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_)); + } + + 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_; + Status expected_status_; + TestCertificateAndData(const std::string& certificate, + const std::string& expected_serial_number, + uint32_t expected_system_id, Status expected_status) + : certificate_(certificate), + expected_serial_number_(expected_serial_number), + expected_system_id_(expected_system_id), + expected_status_(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_rsa_keys_; + TestDrmCertificates test_drm_certs_; + std::unique_ptr root_cert_; + 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. + 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(root_cert_.get(), ClientIdentification::KEYBOX, + expectation.token_, &client_cert_ptr); + } else { + status = + ClientCert::CreateWithKeybox(expectation.token_, &client_cert_ptr); + } + std::unique_ptr keybox_cert(client_cert_ptr); + if (expect_success) { + ASSERT_EQ(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(OkStatus(), status); + EXPECT_FALSE(keybox_cert); + } + } +} + +void ClientCertTest::TestBasicValidationDrmCertificate( + const TestCertificateAndData& expectation, const bool compare_data) { + // Reset DRM certificate signature cache since some certificates get + // re-generated. + ASSERT_OK( + DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_)); + + // Test validation of a valid request. + Status status; + ClientCert* client_cert_ptr = nullptr; + status = ClientCert::Create(root_cert_.get(), + ClientIdentification::DRM_DEVICE_CERTIFICATE, + expectation.certificate_, &client_cert_ptr); + std::unique_ptr drm_certificate_cert(client_cert_ptr); + ASSERT_EQ(expectation.expected_status_, status); + 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_rsa_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_rsa_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_rsa_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_rsa_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_rsa_keys_.public_test_key_1_3072_bits()); + provisioner_certificate->set_system_id(system_id); + provisioner_certificate->set_provider_id(provider_id); + provisioner_certificate->set_creation_time_seconds(1234); + 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_rsa_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, 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_rsa_keys_.private_test_key_2_2048_bits(), + invalid_drm_cert->mutable_signature()); + invalid_drm_cert->set_allocated_signer( + GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn)); + // 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_rsa_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_rsa_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_rsa_keys_.private_test_key_2_2048_bits())); + // Invalid signer public key. + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); + signer_cert->set_public_key("bad-signer-public-key"); + std::unique_ptr bad_signer_public_key(SignCertificate( + *dev_cert, + SignCertificate(*signer_cert, nullptr, + test_rsa_keys_.private_test_key_1_3072_bits()), + test_rsa_keys_.private_test_key_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 model system ID. + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); + signer_cert->clear_system_id(); + std::unique_ptr missing_model_sn(SignCertificate( + *dev_cert, + SignCertificate(*signer_cert, nullptr, + test_rsa_keys_.private_test_key_1_3072_bits()), + test_rsa_keys_.private_test_key_2_2048_bits())); + // Missing signer serial number. + dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); + signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn)); + signer_cert->clear_serial_number(); + std::unique_ptr missing_signer_sn(SignCertificate( + *dev_cert, + SignCertificate(*signer_cert, nullptr, + test_rsa_keys_.private_test_key_1_3072_bits()), + test_rsa_keys_.private_test_key_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_rsa_keys_.private_test_key_2_2048_bits())); + + const TestCertificateAndData kInvalidCertificate[] = { + TestCertificateAndData("f", "", 0, + Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signed-drm-certificate")), + TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0, + Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-drm-certificate")), + TestCertificateAndData(bad_device_public_key->SerializeAsString(), "", 0, + Status(error_space, INVALID_DRM_CERTIFICATE, + "drm-certificate-public-key-failed")), + TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0, + Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signer-certificate")), + TestCertificateAndData(bad_signer_public_key->SerializeAsString(), "", 0, + Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-leaf-signer-public-key")), + TestCertificateAndData(bad_device_signature->SerializeAsString(), "", 0, + Status(error_space, INVALID_SIGNATURE, + "cache-miss-invalid-signature")), + TestCertificateAndData(missing_model_sn->SerializeAsString(), "", 0, + Status(error_space, INVALID_DRM_CERTIFICATE, + "model-certificate-missing-system-id")), + TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0, + Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-signer-serial-number")), + TestCertificateAndData(bad_signer_signature->SerializeAsString(), "", 0, + Status(error_space, INVALID_SIGNATURE, + "cache-miss-invalid-signature")), + }; + + for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) { + TestBasicValidationDrmCertificate(kInvalidCertificate[i], false); + } +} + +TEST_F(ClientCertTest, MissingPreProvKey) { + // system ID in token is 0x01234567 + const std::string token(absl::HexStringToBytes( + "00000002012345678e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e")); + ClientCert* client_cert_ptr = nullptr; + Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr); + ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code()); +} + +TEST_F(ClientCertTest, ValidProvisionerDeviceCert) { + const uint32_t system_id = 5000; + const std::string service_id("widevine_test.com"); + const std::string device_serial_number("device-serial-number"); + const std::string intermediate_serial_number("intermediate-serial-number"); + 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(root_cert_.get(), + ClientIdentification::DRM_DEVICE_CERTIFICATE, + serialized_cert, &client_cert_ptr)); + ASSERT_TRUE(client_cert_ptr != nullptr); + std::unique_ptr drm_cert(client_cert_ptr); + + 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(root_cert_.get(), + 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; + + // TODO(user): Fix this test. It is failing for the right reasons, but the + // certificate chain is broken (intermediate signature does not match signer). + ASSERT_EQ("cache-miss-invalid-signature", + ClientCert::Create(root_cert_.get(), + ClientIdentification::DRM_DEVICE_CERTIFICATE, + serialized_cert, &client_cert_ptr) + .error_message()); + EXPECT_FALSE(client_cert_ptr); +} + +TEST_F(ClientCertTest, Protocol21WithDrmCert) { + const char message[] = "A weekend wasted is a weekend well spent."; + + ClientCert* client_cert_ptr = nullptr; + ASSERT_OK(ClientCert::Create( + root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE, + test_drm_certs_.test_user_device_certificate(), &client_cert_ptr)); + std::unique_ptr client_cert(client_cert_ptr); + + std::unique_ptr private_key( + RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits())); + ASSERT_TRUE(private_key); + + // Success + std::string signature; + ASSERT_TRUE(private_key->GenerateSignature(message, &signature)); + EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_1)); + + // Failure + ASSERT_EQ(256, signature.size()); + ++signature[127]; + EXPECT_FALSE( + client_cert->VerifySignature(message, signature, VERSION_2_1).ok()); +} + +TEST_F(ClientCertTest, Protocol22WithDrmCert) { + const char message[] = "There is nothing permanent except change."; + const std::string message_hash(Sha512_Hash(message)); + + ClientCert* client_cert_ptr = nullptr; + ASSERT_OK(ClientCert::Create( + root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE, + test_drm_certs_.test_user_device_certificate(), &client_cert_ptr)); + std::unique_ptr client_cert(client_cert_ptr); + + std::unique_ptr private_key( + RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits())); + ASSERT_TRUE(private_key); + + // Success + std::string signature; + ASSERT_TRUE(private_key->GenerateSignature(message_hash, &signature)); + EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_2)); + + // Failure + ASSERT_EQ(256, signature.size()); + ++signature[127]; + EXPECT_FALSE( + client_cert->VerifySignature(message, signature, VERSION_2_2).ok()); +} + +} // namespace widevine diff --git a/common/client_id_util.cc b/common/client_id_util.cc new file mode 100644 index 0000000..327a3bf --- /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; +} + +Status DecryptEncryptedClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id) { + return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id, + client_id); +} + +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 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 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 Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-decryption-failed"); + } + if (!client_id->ParseFromString(serialized_client_id)) { + return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-parse-failed"); + } + return OkStatus(); +} + +} // namespace widevine diff --git a/common/client_id_util.h b/common/client_id_util.h new file mode 100644 index 0000000..6aef13d --- /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 "common/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 Status::OK, if successful, else an error. +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 Status::OK, if successful, else an error. +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..2a1ab2a --- /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 kWrappingKeyLabel[] = "ENCRYPTION"; +const int kWrappingKeySizeBits = 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..75e8dc0 --- /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 kWrappingKeyLabel[]; +extern const int kWrappingKeySizeBits; +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/device_status_list.cc b/common/device_status_list.cc new file mode 100644 index 0000000..da97d3f --- /dev/null +++ b/common/device_status_list.cc @@ -0,0 +1,362 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/device_status_list.h" + +#include +#include + +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "util/gtl/map_util.h" +#include "common/client_cert.h" +#include "common/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 { + +namespace { +const char kSignedListTerminator[] = "}"; +const char kSignedList[] = "signedList\":"; +const std::size_t kSignedListLen = strlen(kSignedList); +} // namespace + +DeviceStatusList* DeviceStatusList::Instance() { + // TODO(user): This is "ok" according to Google's Coding for Dummies, but + // we should inject the status list into the sessions. This will require + // 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() {} + +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 Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "signed-certificate-status-list-parse-error"); + } + if (!signed_certificate_status_list.has_certificate_status_list()) { + return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "missing-status-list"); + } + if (!signed_certificate_status_list.has_signature()) { + return 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 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 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 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 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 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 OkStatus(); +} + +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 Status(error_space, UNSUPPORTED_SYSTEM_ID, + "keybox-unsupported-system-id"); + } + // Get device information from certificate status list if available. + if (!GetDeviceInfo(client_cert, device_info)) { + device_info->Clear(); + } + return OkStatus(); + } + + // DRM certificate checks. + if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "device-certificate-unsupported-token-type"); + } + absl::ReaderMutexLock lock(&status_map_lock_); + if (expiration_period_seconds_ && + (GetCurrentTime() > + (creation_time_seconds_ + expiration_period_seconds_))) { + return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST, + "certificate-status-list-expired"); + } + DeviceCertificateStatus* device_cert_status = + gtl::FindOrNull(device_status_map_, client_cert.system_id()); + if (device_cert_status) { + *device_info = device_cert_status->device_info(); + if (device_cert_status->status() == + DeviceCertificateStatus::STATUS_REVOKED) { + if (IsRevokedSystemIdAllowed(client_cert.system_id())) { + LOG(WARNING) << "Allowing REVOKED device: " + << device_info->ShortDebugString(); + } else { + return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED, + "device-certificate-revoked"); + } + } + if ((device_cert_status->status() == + DeviceCertificateStatus::STATUS_TEST_ONLY) && + !allow_test_only_devices_) { + return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, + "test-only-drm-certificate-not-allowed"); + } + if (!client_cert.signed_by_provisioner() && + (client_cert.signer_serial_number() != + device_cert_status->drm_serial_number())) { + // Widevine-provisioned device, and the intermediate certificate serial + // number does not match that in the status list. If the status list is + // newer than the certificate, indicate an invalid certificate, so that + // the device re-provisions. If, on the other hand, the certificate status + // list is older than the certificate, the certificate is for all purposes + // unknown. + if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "intermediate-certificate-serial-number-mismatch"); + } + return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, + "device-certificate-status-unknown"); + } + } else { + if (!allow_unknown_devices_) { + return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, + "device-certificate-status-unknown"); + } + device_info->Clear(); + } + + return 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; +} + +Status DeviceStatusList::ExtractFromProvisioningServiceResponse( + const std::string& certificate_provisioning_service_response, + std::string* signed_certificate_status_list, std::string* certificate_status_list) { + Status status = OkStatus(); + size_t signed_list_start = + certificate_provisioning_service_response.find(kSignedList); + if (signed_list_start != std::string::npos) { + size_t signed_list_end = certificate_provisioning_service_response.find( + kSignedListTerminator, signed_list_start); + if (signed_list_end == std::string::npos) { + return Status( + error_space, error::INVALID_ARGUMENT, + "Unable to parse the certificate_provisioning_service_response. " + "SignedList not terminated."); + } + std::string signed_list( + certificate_provisioning_service_response.begin() + signed_list_start + + kSignedListLen, + certificate_provisioning_service_response.begin() + signed_list_end); + + // Strip off quotes. + signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\"'), + signed_list.end()); + // Strip off spaces. + signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), ' '), + signed_list.end()); + + // Strip off newlines. + signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\n'), + signed_list.end()); + + // Strip off carriage return (the control-M character) + signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\r'), + signed_list.end()); + if (!absl::WebSafeBase64Unescape(signed_list, + signed_certificate_status_list)) { + if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) { + return Status(error_space, error::INVALID_ARGUMENT, + "Base64 decode of signedlist failed."); + } + } + } else { + // certificate_provisioning_service_response is the signed list and not a + // JSON message. + if (!absl::WebSafeBase64Unescape(certificate_provisioning_service_response, + signed_certificate_status_list)) { + if (!absl::Base64Unescape(certificate_provisioning_service_response, + signed_certificate_status_list)) { + return Status(error_space, error::INVALID_ARGUMENT, + "Base64 decode of certList failed."); + } + } + } + SignedDeviceCertificateStatusList signed_status_list; + if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) { + return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "signed-certificate-status-list-parse-error"); + } + if (!signed_status_list.has_certificate_status_list()) { + return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "missing-status-list"); + } + DeviceCertificateStatusList device_certificate_status_list; + if (!device_certificate_status_list.ParseFromString( + signed_status_list.certificate_status_list())) { + return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "certificate-status-list-parse-error"); + } + *certificate_status_list = signed_status_list.certificate_status_list(); + return OkStatus(); +} + +Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest( + const std::string& version, + std::string* signed_device_certificate_status_list_request) { + if (version.empty()) { + return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty"); + } + DCHECK(signed_device_certificate_status_list_request); + if (signed_device_certificate_status_list_request == nullptr) { + return Status(error_space, error::INVALID_ARGUMENT, + "Signed_device_certificate_status_list_request is empty"); + } + // Construct SignedDeviceCertificateStatusListRequest. + DeviceCertificateStatusListRequest request; + request.set_sdk_version(version); + request.set_sdk_time_seconds(DeviceStatusList::Instance()->GetCurrentTime()); + std::string device_certificate_status_list_request; + request.SerializeToString(&device_certificate_status_list_request); + SignedDeviceCertificateStatusListRequest signed_request; + signed_request.set_device_certificate_status_list_request( + device_certificate_status_list_request); + const DrmServiceCertificate* sc = + DrmServiceCertificate::GetDefaultDrmServiceCertificate(); + if (sc == nullptr) { + signed_device_certificate_status_list_request->clear(); + return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE, + "Drm service certificate is not loaded."); + } + const RsaPrivateKey* private_key = sc->private_key(); + if (private_key == nullptr) { + return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE, + "Private key in the service certificate is null."); + } + std::string signature; + private_key->GenerateSignature(device_certificate_status_list_request, + &signature); + signed_request.set_signature(signature); + signed_request.SerializeToString( + signed_device_certificate_status_list_request); + return OkStatus(); +} +} // namespace widevine diff --git a/common/device_status_list.h b/common/device_status_list.h new file mode 100644 index 0000000..13782c7 --- /dev/null +++ b/common/device_status_list.h @@ -0,0 +1,122 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 COMMON_DEVICE_STATUS_LIST_H__ +#define COMMON_DEVICE_STATUS_LIST_H__ + +#include +#include + +#include "base/macros.h" +#include "absl/synchronization/mutex.h" +#include "common/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 |serialized_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. + Status UpdateStatusList(const std::string& root_certificate_public_key, + const std::string& serialized_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. + 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); + + /** + * Parses signed device certificate status list and certificate status list + * from certificateProvisoningServer response. + * + * @param certificate_provisioning_service_response + * @param signed_certificate_status_list + * @param certificate_status_list + * @return WvPLStatus - Status::OK if success, else error. + */ + static Status ExtractFromProvisioningServiceResponse( + const std::string& certificate_provisioning_service_response, + std::string* signed_certificate_status_list, std::string* certificate_status_list); + /** + * Constructs signed device certificate status list request string. + * + * @param signed_device_certificate_status_list_request + * @param version + * @return Status - Status::OK if success, else error. + */ + static Status GenerateSignedDeviceCertificateStatusListRequest( + const std::string& version, + std::string* signed_device_certificate_status_list_request); + + 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 // COMMON_DEVICE_STATUS_LIST_H__ diff --git a/common/device_status_list_test.cc b/common/device_status_list_test.cc new file mode 100644 index 0000000..73d4683 --- /dev/null +++ b/common/device_status_list_test.cc @@ -0,0 +1,376 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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/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/client_cert.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.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: + MockCertificateClientCert() {} + MOCK_CONST_METHOD0(system_id, uint32_t()); + MOCK_CONST_METHOD0(signer_serial_number, std::string &()); + MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t()); + MOCK_CONST_METHOD0(type, ClientIdentification::TokenType()); + MOCK_CONST_METHOD0(signed_by_provisioner, bool()); +}; + +class MockKeyboxClientCert : public KeyboxClientCert { + public: + MockKeyboxClientCert() {} + 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(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(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(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(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(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(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(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(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/common/drm_root_certificate.cc b/common/drm_root_certificate.cc new file mode 100644 index 0000000..b211a96 --- /dev/null +++ b/common/drm_root_certificate.cc @@ -0,0 +1,532 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +// common_typos_disable. Successful / successfull. + +#include "common/drm_root_certificate.h" + +#include + +#include "glog/logging.h" +#include "absl/memory/memory.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" +#include "common/error_space.h" +#include "common/rsa_key.h" +#include "common/sha_util.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +namespace { + +const char kDevelopmentString[] = "dev"; // QA systems. +const char kProductionString[] = "prod"; // Production. +const char kTestingString[] = "test"; // Code development / unit tests. + +const bool kUseCache = true; + +// From common::TestDrmCertificates. +// TODO(user): common::test_certificates is a testonly target, consider +// how to use instead of dupliciating the test cert here. +static const unsigned char kTestRootCertificate[] = { + 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}; +} // namespace + +// Caches an individual signature for a certificate with a specific serial +// number (signer). +struct VerifiedCertSignature { + VerifiedCertSignature(const std::string& cert, const std::string& sig, + const std::string& signer_sn) + : signed_cert(cert), signature(sig), signer_serial(signer_sn) {} + + std::string signed_cert; + std::string signature; + std::string signer_serial; +}; + +// Map of certificate serial number to its signature. +typedef std::map VerifiedCertSignatures; +class VerifiedCertSignatureCache { + public: + explicit VerifiedCertSignatureCache(const RsaKeyFactory* key_factory) + : key_factory_(key_factory) {} + + // Checks cache, on miss, uses public key. If successful, adds to + // cache. + Status VerifySignature(const std::string& cert, const std::string& serial_number, + const std::string& signature, + const std::string& signer_public_key, + const std::string& signer_serial_number) { + { + VerifiedCertSignatures::iterator cached_signature; + absl::ReaderMutexLock read_lock(&signature_cache_mutex_); + cached_signature = signature_cache_.find(serial_number); + if (cached_signature != signature_cache_.end()) { + // TODO(user): Log which of the following three conditions occurs. + if ((cert != cached_signature->second.signed_cert) || + (signature != cached_signature->second.signature) || + (signer_serial_number != cached_signature->second.signer_serial)) { + // Cached signature mismatch. + return Status(error_space, INVALID_SIGNATURE, + "cached-signature-mismatch"); + } + // Cached signature match. + return OkStatus(); + } + } + + // Cache miss. Verify signature. + std::unique_ptr signer_key( + key_factory_->CreateFromPkcs1PublicKey(signer_public_key)); + if (!signer_key) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signer-public-key"); + } + if (!signer_key->VerifySignature(cert, signature)) { + return Status(error_space, INVALID_SIGNATURE, + "cache-miss-invalid-signature"); + } + + // Add signature to cache. + absl::WriterMutexLock write_lock(&signature_cache_mutex_); + signature_cache_.emplace( + serial_number, + VerifiedCertSignature(cert, signature, signer_serial_number)); + return OkStatus(); + } + + private: + VerifiedCertSignatures signature_cache_ GUARDED_BY(&signature_cache_mutex_); + absl::Mutex signature_cache_mutex_; + const RsaKeyFactory* key_factory_; +}; + +Status DrmRootCertificate::CreateByType( + CertificateType cert_type, std::unique_ptr* cert) { + CHECK(cert); + + return Create(cert_type, absl::make_unique(), cert); +} + +std::unique_ptr DrmRootCertificate::CreateByType( + CertificateType cert_type, Status* status) { + CHECK(status); + + std::unique_ptr new_root_cert; + *status = CreateByType(cert_type, &new_root_cert); + return new_root_cert; +} + +Status DrmRootCertificate::CreateByTypeString( + const std::string& cert_type_string, std::unique_ptr* cert) { + CHECK(cert); + + CertificateType cert_type; + if (cert_type_string == kDevelopmentString) { + cert_type = kCertificateTypeDevelopment; + } else if (cert_type_string == kProductionString) { + cert_type = kCertificateTypeProduction; + } else if (cert_type_string == kTestingString) { + cert_type = kCertificateTypeTesting; + } else { + return Status(error_space, INVALID_PARAMETER, + absl::StrCat("invalid-certificate-type ", cert_type_string)); + } + + return CreateByType(cert_type, cert); +} + +Status DrmRootCertificate::Create(CertificateType cert_type, + std::unique_ptr key_factory, + std::unique_ptr* cert) { + DCHECK(cert); + + std::string serialized_certificate; + switch (cert_type) { + case kCertificateTypeProduction: { + serialized_certificate.assign( + kProdRootCertificate, + kProdRootCertificate + sizeof(kProdRootCertificate)); + break; + } + case kCertificateTypeDevelopment: { + serialized_certificate.assign( + kDevRootCertificate, + kDevRootCertificate + sizeof(kDevRootCertificate)); + break; + } + case kCertificateTypeTesting: { + serialized_certificate.assign( + kTestRootCertificate, + kTestRootCertificate + sizeof(kTestRootCertificate)); + break; + } + default: + return Status(error_space, INVALID_PARAMETER, "invalid-certificate-type"); + } + + SignedDrmCertificate signed_root_cert; + if (!signed_root_cert.ParseFromString(serialized_certificate)) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "signed-root-cert-deserialize-fail"); + } + DrmCertificate root_cert; + if (!signed_root_cert.has_drm_certificate()) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-root-device-certificate"); + } + if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "root-cert-deserialize-fail"); + } + if (!root_cert.has_public_key()) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-root-cert-public-key"); + } + if (!signed_root_cert.has_signature()) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-root-certificate-signature"); + } + + std::unique_ptr public_key( + key_factory->CreateFromPkcs1PublicKey(root_cert.public_key())); + if (!public_key) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-root-public-key"); + } + if (!public_key->VerifySignature(signed_root_cert.drm_certificate(), + signed_root_cert.signature())) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-root-certificate-signature"); + } + + cert->reset(new DrmRootCertificate( + cert_type, serialized_certificate, root_cert.serial_number(), + root_cert.public_key(), std::move(key_factory))); + return OkStatus(); +} + +DrmRootCertificate::DrmRootCertificate( + CertificateType type, const std::string& serialized_certificate, + const std::string& serial_number, const std::string& public_key, + std::unique_ptr key_factory) + : type_(type), + serialized_certificate_(serialized_certificate), + serial_number_(serial_number), + public_key_(public_key), + key_factory_(std::move(key_factory)), + signature_cache_(new VerifiedCertSignatureCache(key_factory_.get())) {} + +DrmRootCertificate::~DrmRootCertificate() {} + +std::string DrmRootCertificate::GetDigest() const { + return absl::BytesToHexString(Sha256_Hash(serialized_certificate_)); +} + +Status DrmRootCertificate::VerifyCertificate( + const std::string& serialized_certificate, + SignedDrmCertificate* signed_certificate, + DrmCertificate* certificate) const { + std::unique_ptr local_signed_certificate; + if (!signed_certificate) { + local_signed_certificate = absl::make_unique(); + signed_certificate = local_signed_certificate.get(); + } + if (!signed_certificate->ParseFromString(serialized_certificate)) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signed-drm-certificate"); + } + + std::unique_ptr local_certificate; + if (!certificate) { + local_certificate = absl::make_unique(); + certificate = local_certificate.get(); + } + if (signed_certificate->drm_certificate().empty() || + !certificate->ParseFromString(signed_certificate->drm_certificate())) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-drm-certificate"); + } + if (certificate->serial_number().empty()) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-serial-number"); + } + if (!certificate->has_creation_time_seconds()) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-creation-time"); + } + if (certificate->public_key().empty()) { + return Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"); + } + + // Verify signature chain, but do not use cache for leaf certificates. + return VerifySignatures(*signed_certificate, certificate->serial_number(), + !kUseCache); +} + +// Recursively verifies certificates with their signing certs or the root. +// use_cache should be false when initially called so that signatures do not +// cached leaf certificates not signed with the root certificate, such as for +// the case of device-unique device certificates. +// Signatures for root-signed certificates are always cached, even if they are +// leaf certificates. For example service, and provisioner certificates. +Status DrmRootCertificate::VerifySignatures( + const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number, + bool use_cache) const { + if (!signed_cert.has_signer()) { + // Always use cache for root-signed certificates. + return signature_cache_->VerifySignature( + signed_cert.drm_certificate(), cert_serial_number, + signed_cert.signature(), public_key(), serial_number_); + } + + DrmCertificate signer; + if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signer-certificate"); + } + + // Verify the signer before verifying signed_cert. + Status status = + VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache); + if (!status.ok()) { + return status; + } + + if (use_cache) { + status = signature_cache_->VerifySignature( + signed_cert.drm_certificate(), cert_serial_number, + signed_cert.signature(), signer.public_key(), signer.serial_number()); + if (!status.ok()) { + return status; + } + } else { + std::unique_ptr signer_public_key( + key_factory_->CreateFromPkcs1PublicKey(signer.public_key())); + if (!signer_public_key) { + return Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-leaf-signer-public-key"); + } + if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(), + signed_cert.signature())) { + return Status(error_space, INVALID_SIGNATURE, + "cache-miss-invalid-signature"); + } + } + + return OkStatus(); +} + +} // namespace widevine diff --git a/common/drm_root_certificate.h b/common/drm_root_certificate.h new file mode 100644 index 0000000..e786351 --- /dev/null +++ b/common/drm_root_certificate.h @@ -0,0 +1,106 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_ + +// common_typos_disable. Successful / successfull. + +#include +#include + +#include "base/macros.h" +#include "common/status.h" + +#include "common/certificate_type.h" + +namespace widevine { + +class DrmCertificate; +class RsaKeyFactory; +class RsaPublicKey; +class SignedDrmCertificate; +class VerifiedCertSignatureCache; + +// Root certificate and certificate chain verifier with internal caching. +// This object is thread-safe. +class DrmRootCertificate { + public: + virtual ~DrmRootCertificate(); + + // 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 const DrmRootCertificate* if successful. The caller assumes + // ownership of the new DrmRootCertificate. This method returns + // Status::OK on success, or appropriate error status otherwise. + static Status CreateByType(CertificateType cert_type, + std::unique_ptr* cert); + + // Variant on the method above to make CLIF happy until b/110539622 is fixed. + static std::unique_ptr CreateByType( + CertificateType cert_type, Status* status); + + // Creates a DrmRootCertificate object given a certificate type std::string, which + // must be one of "prod", "qa", or "test". + // |cert| may not be nullptr, and it points to a + // std::unique_ptr which will be used to return a newly + // created const DrmRootCertificate* if successful. The caller assumes + // ownership of the new DrmRootCertificate. This method returns + // Status::OK on success, or appropriate error status otherwise. + static Status CreateByTypeString(const std::string& cert_type_string, + std::unique_ptr* cert); + + // |certificate| will contgain the DRM certificate upon successful return. + // May be null. + // Returns Status::OK if successful, or an appropriate error code otherwise. + virtual Status VerifyCertificate(const std::string& serialized_certificate, + SignedDrmCertificate* signed_certificate, + DrmCertificate* certificate) const; + + // Returns the hex-encoded SHA-256 digest for this certificate. + virtual std::string GetDigest() const; + + const CertificateType type() const { return type_; } + + const std::string& public_key() const { return public_key_; } + + protected: + DrmRootCertificate(CertificateType cert_type, + const std::string& serialized_certificate, + const std::string& serial_number, const std::string& public_key, + std::unique_ptr key_factory); + + private: + friend class DrmRootCertificateTest; + + static Status Create(CertificateType cert_type, + std::unique_ptr key_factory, + std::unique_ptr* cert); + + Status VerifySignatures(const SignedDrmCertificate& signed_cert, + const std::string& cert_serial_number, + bool use_cache) const; + + CertificateType type_; + std::string serialized_certificate_; + std::string serial_number_; + std::string public_key_; + std::unique_ptr key_factory_; + mutable std::unique_ptr signature_cache_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DrmRootCertificate); +}; + +} // 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..24c3509 --- /dev/null +++ b/common/drm_root_certificate_test.cc @@ -0,0 +1,262 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/drm_root_certificate.h" + +#include + +#include "google/protobuf/util/message_differencer.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "common/error_space.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "common/test_drm_certificates.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +using google::protobuf::util::MessageDifferencer; + +namespace widevine { + +TEST(DrmRootCertificateCreateTest, TestCertificate) { + const std::string kTestCertificateHash( + "49f917b1bdfed78002a58e799a58e940" + "1fffaaed9d8d80752782b066757e2c8c"); + std::unique_ptr root_cert; + ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeTesting, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(kTestCertificateHash, root_cert->GetDigest()); +} + +TEST(DrmRootCertificateCreateTest, DevCertificate) { + const std::string kDevelopmentCertificateHash( + "0e25ee95476a770f30b98ac5ef778b3f" + "137b66c29385b84f547a361b4724b17d"); + std::unique_ptr root_cert; + ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeDevelopment, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(kDevelopmentCertificateHash, root_cert->GetDigest()); +} + +TEST(DrmRootCertificateCreateTest, ProdCertificate) { + const std::string kProductionCertificateHash( + "d62fdabc9286648a81f7d3bedaf2f5a5" + "27bbad39bc38da034ba98a21569adb9b"); + std::unique_ptr root_cert; + ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeProduction, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(kProductionCertificateHash, root_cert->GetDigest()); +} + +TEST(DrmRootCertificateTestCertificatesTest, Success) { + TestDrmCertificates test_certs; + std::unique_ptr root_cert; + ASSERT_TRUE( + DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert) + .ok()); + EXPECT_TRUE(root_cert + ->VerifyCertificate(test_certs.test_root_certificate(), + nullptr, nullptr) + .ok()); + EXPECT_TRUE( + root_cert + ->VerifyCertificate(test_certs.test_intermediate_certificate(), + nullptr, nullptr) + .ok()); + EXPECT_TRUE(root_cert + ->VerifyCertificate(test_certs.test_user_device_certificate(), + nullptr, nullptr) + .ok()); + EXPECT_TRUE(root_cert + ->VerifyCertificate(test_certs.test_service_certificate(), + nullptr, nullptr) + .ok()); +} + +class DrmRootCertificateTest : public testing::Test { + protected: + DrmRootCertificateTest() { + private_keys_.emplace_back( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); + private_keys_.emplace_back( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + private_keys_.emplace_back( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + } + + void SetUp() override { + drm_certificates_[0].set_serial_number("level 0"); + drm_certificates_[0].set_creation_time_seconds(0); + drm_certificates_[0].set_public_key( + test_keys_.public_test_key_1_3072_bits()); + drm_certificates_[1].set_serial_number("level 1"); + drm_certificates_[1].set_creation_time_seconds(1); + drm_certificates_[1].set_public_key( + test_keys_.public_test_key_2_2048_bits()); + drm_certificates_[2].set_serial_number("level 2"); + drm_certificates_[2].set_creation_time_seconds(2); + drm_certificates_[2].set_public_key( + test_keys_.public_test_key_3_2048_bits()); + + ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeTesting, &root_cert_)); + } + + void GenerateSignedDrmCertificate() { + SignedDrmCertificate* current_sc(&signed_drm_certificate_); + ASSERT_TRUE(drm_certificates_[2].SerializeToString( + current_sc->mutable_drm_certificate())); + ASSERT_TRUE(private_keys_[1]->GenerateSignature( + current_sc->drm_certificate(), current_sc->mutable_signature())); + + current_sc = current_sc->mutable_signer(); + ASSERT_TRUE(drm_certificates_[1].SerializeToString( + current_sc->mutable_drm_certificate())); + ASSERT_TRUE(private_keys_[0]->GenerateSignature( + current_sc->drm_certificate(), current_sc->mutable_signature())); + + current_sc = current_sc->mutable_signer(); + ASSERT_TRUE(drm_certificates_[0].SerializeToString( + current_sc->mutable_drm_certificate())); + ASSERT_TRUE(private_keys_[0]->GenerateSignature( + current_sc->drm_certificate(), current_sc->mutable_signature())); + } + + RsaTestKeys test_keys_; + std::vector> private_keys_; + SignedDrmCertificate signed_drm_certificate_; + DrmCertificate drm_certificates_[3]; + std::unique_ptr root_cert_; +}; + +TEST_F(DrmRootCertificateTest, SuccessNoOutput) { + GenerateSignedDrmCertificate(); + ASSERT_EQ(OkStatus(), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, SuccessWithOutput) { + GenerateSignedDrmCertificate(); + SignedDrmCertificate out_signed_cert; + DrmCertificate out_cert; + ASSERT_EQ(OkStatus(), root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), + &out_signed_cert, &out_cert)); + EXPECT_TRUE( + MessageDifferencer::Equals(out_signed_cert, signed_drm_certificate_)); + EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2])); +} + +TEST_F(DrmRootCertificateTest, InvalidSignedDrmCertificate) { + EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signed-drm-certificate"), + root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) { + GenerateSignedDrmCertificate(); + signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage"); + EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-signer-certificate"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, MissingDrmCertificate) { + GenerateSignedDrmCertificate(); + signed_drm_certificate_.clear_drm_certificate(); + EXPECT_EQ( + Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"), + root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), + nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) { + GenerateSignedDrmCertificate(); + signed_drm_certificate_.set_drm_certificate("junk"); + EXPECT_EQ( + Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"), + root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), + nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidPublicKey) { + drm_certificates_[0].set_public_key("rubbish"); + GenerateSignedDrmCertificate(); + EXPECT_EQ( + Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-signer-public-key"), + root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), + nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, MissingPublicKey) { + drm_certificates_[2].clear_public_key(); + GenerateSignedDrmCertificate(); + EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, MissingCreationTime) { + drm_certificates_[2].clear_creation_time_seconds(); + GenerateSignedDrmCertificate(); + EXPECT_EQ( + Status(error_space, INVALID_DRM_CERTIFICATE, "missing-creation-time"), + root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), + nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, MissingSerialNumber) { + drm_certificates_[2].set_serial_number(""); + GenerateSignedDrmCertificate(); + EXPECT_EQ( + Status(error_space, INVALID_DRM_CERTIFICATE, "missing-serial-number"), + root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), + nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) { + GenerateSignedDrmCertificate(); + signed_drm_certificate_.mutable_signer()->set_signature( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + EXPECT_EQ( + Status(error_space, INVALID_SIGNATURE, "cache-miss-invalid-signature"), + root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), + nullptr, nullptr)); +} + +TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) { + GenerateSignedDrmCertificate(); + // Verify and cache. + ASSERT_EQ(OkStatus(), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); + + // Verify success using cache. + ASSERT_EQ(OkStatus(), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); + + // Verify failure using cache. + signed_drm_certificate_.mutable_signer()->set_signature( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + EXPECT_EQ(Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"), + root_cert_->VerifyCertificate( + signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); +} + +} // namespace widevine diff --git a/common/drm_service_certificate.cc b/common/drm_service_certificate.cc new file mode 100644 index 0000000..9229859 --- /dev/null +++ b/common/drm_service_certificate.cc @@ -0,0 +1,286 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 + +Status DrmServiceCertificate::AddDrmServiceCertificate( + const DrmRootCertificate* root_drm_cert, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + DrmCertificate drm_cert; + Status status = + root_drm_cert->VerifyCertificate(service_certificate, nullptr, &drm_cert); + if (!status.ok()) { + return status; + } + + if (drm_cert.type() != DrmCertificate::SERVICE) { + return Status(error_space, INVALID_SERVICE_CERTIFICATE, + "not-service-certificate"); + } + if (drm_cert.provider_id().empty()) { + return Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing-certificate-service-id"); + } + std::unique_ptr public_key( + RsaPublicKey::Create(drm_cert.public_key())); + if (!public_key) { + return 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 Status(error_space, INVALID_SERVICE_PRIVATE_KEY, + "key-decryption-failed"); + } + std::unique_ptr private_key(RsaPrivateKey::Create(pkcs1_key)); + if (private_key == nullptr) { + return 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 OkStatus(); +} + +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); +} + +Status DrmServiceCertificate::SetDefaultDrmServiceCertificate( + const DrmRootCertificate* root_drm_cert, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + DrmServiceCertificateMap::GetInstance()->ClearDefaultDrmServiceCertificate(); + return AddDrmServiceCertificate(root_drm_cert, service_certificate, + service_private_key, + service_private_key_passphrase); +} + +Status DrmServiceCertificate::DecryptClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id) { + DCHECK(client_id); + if (encrypted_client_id.service_certificate_serial_number().empty()) { + return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-service-certificate-serial-number"); + } + if (encrypted_client_id.provider_id().empty()) { + return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-service-id"); + } + if (encrypted_client_id.encrypted_client_id().empty()) { + return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id"); + } + if (encrypted_client_id.encrypted_client_id_iv().empty()) { + return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id-iv"); + } + if (encrypted_client_id.encrypted_privacy_key().empty()) { + return 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 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 Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "privacy-key-decryption-failed"); + } + if (cert->provider_id() != encrypted_client_id.provider_id()) { + return 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 Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-decryption-failed"); + } + if (!client_id->ParseFromString(serialized_client_id)) { + return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-parse-failed"); + } + return OkStatus(); +} + +void DrmServiceCertificate::ResetServiceCertificates() { + DrmServiceCertificateMap::GetInstance()->Reset(); +} + +Status DrmServiceCertificate::ValidateDrmServiceCertificate() { + const DrmServiceCertificate* service_certificate = + GetDefaultDrmServiceCertificate(); + if (!service_certificate) { + return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND, + "drm service certificate is not found."); + } + SignedDrmCertificate signed_cert; + if (!signed_cert.ParseFromString(service_certificate->certificate())) { + return 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 Status(error_space, INVALID_DRM_CERTIFICATE, + "Drm service certificate is failed to parse."); + } + if (!drm_cert.has_creation_time_seconds()) { + return 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 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..fec6055 --- /dev/null +++ b/common/drm_service_certificate.h @@ -0,0 +1,132 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/certificate_type.h" +#include "common/rsa_key.h" +#include "common/status.h" + +namespace widevine { +class RequestInspectorTest; +} // namespace widevine + +namespace widevine { + +class ClientIdentification; +class DrmRootCertificate; +class EncryptedClientIdentification; + +// TODO(user): Add a DrmCertificateList class to provide the static method +// functionality. +class DrmServiceCertificate { + public: + // Create a new DrmServiceCertificate object and add it to the list of valid + // service certificates. |drm_root_cert| is the root certificate for the type + // of certifiate being added. |service_certificate| is a + // Google-generated certificate used to authenticate the service provider for + // purposes of device privacy, |service_private_key| is the encrypted PKCS#8 + // private RSA key corresponding to the service certificate, + // |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 Status AddDrmServiceCertificate( + const DrmRootCertificate* root_drm_cert, + const std::string& service_certificate, const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + // Same as AddDrmServiceCertificate(), but will clear the default service + // certificate if it's set. This will result in this service certificate + // being set as the default service certificate. + static Status SetDefaultDrmServiceCertificate( + const DrmRootCertificate* root_drm_cert, + const std::string& service_certificate, const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + // Returns the default service certificate. Will return null if no default + // 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 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 Status ValidateDrmServiceCertificate(); + + private: + friend class DrmServiceCertificateTest; + friend class widevine::RequestInspectorTest; + + static 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 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..b5eef32 --- /dev/null +++ b/common/drm_service_certificate_test.cc @@ -0,0 +1,364 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "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_root_certificate.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" +#include "common/test_drm_certificates.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" // IWYU pragma: keep +#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())) { + EXPECT_TRUE(root_private_key_); + EXPECT_OK( + DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_)); + client_id_.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE); + client_id_.set_token(test_certs_.test_user_device_certificate()); + } + + 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; + } + + 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 Status(error::INTERNAL, ""); + } + return DrmServiceCertificate::SetDefaultDrmServiceCertificate( + root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase); + } + + 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 Status(error::INTERNAL, ""); + } + return DrmServiceCertificate::AddDrmServiceCertificate( + root_cert_.get(), 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_; + TestDrmCertificates test_certs_; + std::string privacy_key_; + std::string iv_; + std::unique_ptr root_private_key_; + std::unique_ptr root_cert_; + 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(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 serial_number4("serial_number4"); + 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(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(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(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_number4, provider_id, + creation_time_seconds)); + default_cert = DrmServiceCertificate::GetDefaultDrmServiceCertificate(); + ASSERT_TRUE(default_cert); + ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate())); + ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate())); + EXPECT_EQ(serial_number4, 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(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(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(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(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 index 99a7b32..49147f4 100644 --- a/common/file_util.cc +++ b/common/file_util.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -39,7 +39,7 @@ bool GetContents(const std::string& file_name, std::string* contents) { LOG(WARNING) << "Failed to read all file contents."; return false; } - return true;; + return true; } bool SetContents(const std::string& file_name, const std::string& contents) { diff --git a/common/file_util.h b/common/file_util.h index 8e09bae..42c03aa 100644 --- a/common/file_util.h +++ b/common/file_util.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 diff --git a/common/file_util_test.cc b/common/file_util_test.cc index 02b38df..33399c9 100644 --- a/common/file_util_test.cc +++ b/common/file_util_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -7,8 +7,8 @@ //////////////////////////////////////////////////////////////////////////////// #include "common/file_util.h" - -#include "gtest/gtest.h" +#include "testing/gunit.h" +#include "absl/strings/str_cat.h" namespace widevine { @@ -19,7 +19,7 @@ TEST(FileUtilTest, EmptyFileName) { } TEST(FileUtilTest, BasicTest) { - const std::string file_path = FLAGS_test_tmpdir + "/file_util_test"; + 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)); diff --git a/common/mock_rsa_key.h b/common/mock_rsa_key.h index a0d7d72..e221785 100644 --- a/common/mock_rsa_key.h +++ b/common/mock_rsa_key.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -10,7 +10,7 @@ #define COMMON_MOCK_RSA_KEY_H_ #include -#include "gmock/gmock.h" +#include "testing/gmock.h" #include "common/rsa_key.h" namespace widevine { @@ -39,8 +39,8 @@ class MockRsaPublicKey : public RsaPublicKey { 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_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)); @@ -49,19 +49,19 @@ class MockRsaPublicKey : public RsaPublicKey { MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete; }; -class MockRsaKeyFactory : public RsaKeyFactory{ +class MockRsaKeyFactory : public RsaKeyFactory { public: MockRsaKeyFactory() {} ~MockRsaKeyFactory() override {} - MOCK_METHOD1(CreateFromPkcs1PrivateKey, - std::unique_ptr(const std::string& private_key)); - MOCK_METHOD2( + MOCK_CONST_METHOD1(CreateFromPkcs1PrivateKey, + std::unique_ptr(const std::string& private_key)); + MOCK_CONST_METHOD2( CreateFromPkcs8PrivateKey, std::unique_ptr(const std::string& private_key, const std::string& private_key_passphrase)); - MOCK_METHOD1(CreateFromPkcs1PublicKey, - std::unique_ptr(const std::string& public_key)); + MOCK_CONST_METHOD1(CreateFromPkcs1PublicKey, + std::unique_ptr(const std::string& public_key)); private: MockRsaKeyFactory(const MockRsaKeyFactory&) = delete; diff --git a/common/openssl_util.h b/common/openssl_util.h index 12d48ee..824aa45 100644 --- a/common/openssl_util.h +++ b/common/openssl_util.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -9,11 +9,12 @@ // RAII wrapper classes for cleaning up various OpenSSL dynamically allocated // structures. -#ifndef COMMON_OPENSSL_UTIL_H__ -#define COMMON_OPENSSL_UTIL_H__ +#ifndef COMMON_OPENSSL_UTIL_H_ +#define COMMON_OPENSSL_UTIL_H_ #include "openssl/bio.h" #include "openssl/evp.h" +#include "openssl/pkcs7.h" #include "openssl/rsa.h" #include "openssl/x509v3.h" @@ -46,6 +47,7 @@ using ScopedOpenSSLStackOnly = using ScopedBIGNUM = ScopedOpenSSLType; using ScopedBIO = ScopedOpenSSLType; +using ScopedPKCS7 = ScopedOpenSSLType; using ScopedPKEY = ScopedOpenSSLType; using ScopedRSA = ScopedOpenSSLType; using ScopedX509 = ScopedOpenSSLType; @@ -59,6 +61,7 @@ using ScopedX509StoreCtx = ScopedOpenSSLType; using ScopedX509Req = ScopedOpenSSLType; using ScopedAsn1UtcTime = ScopedOpenSSLType; +using ScopedAsn1Time = ScopedOpenSSLType; using ScopedAsn1Utc8String = ScopedOpenSSLType; using ScopedAsn1Integer = ScopedOpenSSLType; @@ -74,4 +77,4 @@ using ScopedX509InfoStack = ScopedOpenSSLStack; using ScopedX509InfoStackOnly = ScopedOpenSSLStackOnly; -#endif // COMMON_OPENSSL_UTIL_H__ +#endif // COMMON_OPENSSL_UTIL_H_ diff --git a/common/python/BUILD b/common/python/BUILD new file mode 100644 index 0000000..eb433f2 --- /dev/null +++ b/common/python/BUILD @@ -0,0 +1,105 @@ +################################################################################ +# 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: +# Build file for CLIF wrappers of Widevine common modules. + +package(default_visibility = ["//visibility:public"]) + +load("//devtools/clif/python:clif_build_rule.bzl", "py_clif_cc") + +py_clif_cc( + name = "aes_cbc_util", + srcs = ["aes_cbc_util.clif"], + deps = [ + "//common:aes_cbc_util", + ], +) + +py_clif_cc( + name = "certificate_type", + srcs = ["certificate_type.clif"], + deps = [ + "//common:certificate_type", + ], +) + +py_clif_cc( + name = "crypto_util", + srcs = ["crypto_util.clif"], + deps = [ + "//common:crypto_util", + ], +) + +py_clif_cc( + name = "drm_root_certificate", + srcs = ["drm_root_certificate.clif"], + clif_deps = [ + ":certificate_type", + ], + pyclif_deps = [ + "//protos/public:drm_certificate_pyclif", + "//protos/public:signed_drm_certificate_pyclif", + ], + deps = [ + "//util/task/python:status_clif", + "//common:drm_root_certificate", + ], +) + +py_clif_cc( + name = "rsa_test_keys", + testonly = 1, + srcs = ["rsa_test_keys.clif"], + deps = [ + "//common:rsa_test_keys", + ], +) + +py_clif_cc( + name = "rsa_key", + srcs = ["rsa_key.clif"], + deps = [ + "//common:rsa_key", + ], +) + +py_clif_cc( + name = "drm_service_certificate", + srcs = ["drm_service_certificate.clif"], + clif_deps = [ + ":certificate_type", + ":drm_root_certificate", + ], + deps = [ + "//util/task/python:status_clif", + "//common:drm_service_certificate", + ], +) + +py_clif_cc( + name = "signing_key_util", + srcs = ["signing_key_util.clif"], + pyclif_deps = [ + "//protos/public:license_protocol_pyclif", + ], + deps = [ + "//common:signing_key_util", + ], +) + +py_clif_cc( + name = "test_drm_certificates", + testonly = 1, + srcs = ["test_drm_certificates.clif"], + deps = [ + "//common:test_drm_certificates", + ], +) diff --git a/common/python/aes_cbc_util.clif b/common/python/aes_cbc_util.clif new file mode 100644 index 0000000..c4f3c77 --- /dev/null +++ b/common/python/aes_cbc_util.clif @@ -0,0 +1,11 @@ +################################################################################ +# 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. +################################################################################ + +from "common/aes_cbc_util.h": + namespace `widevine::crypto_util`: + def `EncryptAesCbc` as Encrypt(key: bytes, iv: bytes, plaintext: bytes) -> bytes diff --git a/common/python/certificate_type.clif b/common/python/certificate_type.clif new file mode 100644 index 0000000..fc009e9 --- /dev/null +++ b/common/python/certificate_type.clif @@ -0,0 +1,11 @@ +################################################################################ +# 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. +################################################################################ + +from "common/certificate_type.h": + namespace `widevine`: + enum CertificateType diff --git a/common/python/crypto_util.clif b/common/python/crypto_util.clif new file mode 100644 index 0000000..f58d497 --- /dev/null +++ b/common/python/crypto_util.clif @@ -0,0 +1,15 @@ +################################################################################ +# 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. +################################################################################ + +from "common/crypto_util.h": + namespace `widevine::crypto_util`: + const `kSigningKeyLabel` as SIGNING_KEY_LABEL: bytes + const `kSigningKeySizeBits` as SIGNING_KEY_SIZE_BITS: int + + def DeriveKey(key: bytes, label: bytes, context: bytes, size_bits: uint32_t) -> bytes + def CreateSignatureHmacSha256(key: bytes, message: bytes) -> bytes diff --git a/common/python/drm_root_certificate.clif b/common/python/drm_root_certificate.clif new file mode 100644 index 0000000..44038f8 --- /dev/null +++ b/common/python/drm_root_certificate.clif @@ -0,0 +1,23 @@ +################################################################################ +# 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. +################################################################################ + +from "util/task/python/clif.h" import * +from "common/python/certificate_type.h" import * +from "protos/public/drm_certificate_pyclif.h" import * +from "protos/public/signed_drm_certificate_pyclif.h" import * + +from "common/drm_root_certificate.h": + namespace `widevine`: + class DrmRootCertificate: + def VerifyCertificate(self, + serialized_certificate: bytes) -> (status: Status, + signed_certificate: SignedDrmCertificate, + certificate: DrmCertificate) + staticmethods from `DrmRootCertificate`: + def CreateByType(cert_type: CertificateType) -> (new_root_cert: DrmRootCertificate, + status: Status) diff --git a/common/python/drm_service_certificate.clif b/common/python/drm_service_certificate.clif new file mode 100644 index 0000000..aae1bf7 --- /dev/null +++ b/common/python/drm_service_certificate.clif @@ -0,0 +1,18 @@ +################################################################################ +# 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. +################################################################################ + +from "util/task/python/clif.h" import * +from "common/python/drm_root_certificate.h" import * +from "common/drm_service_certificate.h": + namespace `widevine`: + staticmethods from `DrmServiceCertificate`: + def AddDrmServiceCertificate( + root_certificate: DrmRootCertificate, + service_certificate: bytes, + service_private_key: bytes, + service_private_key_passphrase: bytes) -> Status diff --git a/common/python/rsa_key.clif b/common/python/rsa_key.clif new file mode 100644 index 0000000..14b5bd4 --- /dev/null +++ b/common/python/rsa_key.clif @@ -0,0 +1,15 @@ +################################################################################ +# 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. +################################################################################ + +from "common/rsa_key.h": + namespace `widevine`: + class RsaPublicKey: + def VerifySignature(self, message: bytes, signature: bytes) -> bool + + staticmethods from `RsaPublicKey`: + def Create(serialized_key: bytes) -> RsaPublicKey diff --git a/common/python/rsa_test_keys.clif b/common/python/rsa_test_keys.clif new file mode 100644 index 0000000..2d54207 --- /dev/null +++ b/common/python/rsa_test_keys.clif @@ -0,0 +1,20 @@ +################################################################################ +# 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. +################################################################################ + +from "common/rsa_test_keys.h": + namespace `widevine`: + class RsaTestKeys: + def private_test_key_1_3072_bits(self) -> bytes + def public_test_key_1_3072_bits(self) -> bytes + def private_test_key_2_2048_bits(self) -> bytes + def public_test_key_2_2048_bits(self) -> bytes + def private_test_key_3_2048_bits(self) -> bytes + def public_test_key_3_2048_bits(self) -> bytes + def private_test_key_2_carmichael_totient_2048_bits(self) -> bytes + def private_test_key_3_carmichael_totient_2048_bits(self) -> bytes + def private_test_key_4_carmichael_totient_2048_bits(self) -> bytes diff --git a/common/python/signing_key_util.clif b/common/python/signing_key_util.clif new file mode 100644 index 0000000..5ea011c --- /dev/null +++ b/common/python/signing_key_util.clif @@ -0,0 +1,14 @@ +################################################################################ +# 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. +################################################################################ + +from "protos/public/license_protocol_pyclif.h" import * + +from "common/signing_key_util.h": + namespace `widevine`: + def SigningKeyMaterialSizeBits(protocol_version: ProtocolVersion) -> uint32_t + def GetClientSigningKey(derived_key: bytes, protocol_version: ProtocolVersion) -> bytes diff --git a/common/python/test_drm_certificates.clif b/common/python/test_drm_certificates.clif new file mode 100644 index 0000000..7b3bde8 --- /dev/null +++ b/common/python/test_drm_certificates.clif @@ -0,0 +1,15 @@ +################################################################################ +# 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. +################################################################################ + +from "common/test_drm_certificates.h": + namespace `widevine`: + class TestDrmCertificates: + def test_root_certificate(self) -> bytes + def test_intermediate_certificate(self) -> bytes + def test_user_device_certificate(self) -> bytes + def test_service_certificate(self) -> bytes diff --git a/common/random_util.cc b/common/random_util.cc index 25bafc1..286ab3d 100644 --- a/common/random_util.cc +++ b/common/random_util.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 diff --git a/common/random_util.h b/common/random_util.h index 4dbff5f..90eb37e 100644 --- a/common/random_util.h +++ b/common/random_util.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 diff --git a/common/random_util_test.cc b/common/random_util_test.cc index 76bc17f..1a52d6f 100644 --- a/common/random_util_test.cc +++ b/common/random_util_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -7,8 +7,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "common/random_util.h" - -#include "gtest/gtest.h" +#include "testing/gunit.h" namespace widevine { diff --git a/common/remote_attestation_verifier.cc b/common/remote_attestation_verifier.cc new file mode 100644 index 0000000..5d9f4c0 --- /dev/null +++ b/common/remote_attestation_verifier.cc @@ -0,0 +1,260 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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::EnableTestDrmCertificates(bool enable) { + absl::WriterMutexLock lock(&ca_mutex_); + enable_test_certificates_ = enable; + ca_.reset(); +} + +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 (Status(error_space, INVALID_MESSAGE, + "remote-attestation-certificate-missing")); + } + if (!remote_attestation.has_salt()) { + return (Status(error_space, INVALID_MESSAGE, + "remote-attestation-salt-missing")); + } + if (!remote_attestation.has_signature()) { + return (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; + Status status = DrmServiceCertificate::DecryptClientIdentification( + remote_attestation.certificate(), &client_id); + if (!status.ok()) return status; + + if (client_id.type() != + ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) { + return (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); +} + +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 (Status(error_space, INVALID_MESSAGE, + "remote-attestation-certificate-missing")); + } + if (!remote_attestation.has_salt()) { + return (Status(error_space, INVALID_MESSAGE, + "remote-attestation-salt-missing")); + } + if (!remote_attestation.has_signature()) { + return (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; + Status status = DecryptEncryptedClientIdentification( + remote_attestation.certificate(), privacy_key, &client_id); + if (!status.ok()) return status; + + if (client_id.type() != + ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) { + return (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); +} + +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 (Status(error_space, INVALID_MESSAGE, + "remote-attestation-token-missing")); + } + // Load and verify the certificate chain. + std::unique_ptr cert_chain(new X509CertChain); + Status status = cert_chain->LoadPem(client_id.token()); + if (!status.ok()) return status; + + if (cert_chain->GetNumCerts() < 1) { + return (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 (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 (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 Status(error_space, REMOTE_ATTESTATION_FAILED, + "remote-attestation-cert-chain-no-leaf"); + } + + if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt, + remote_attestation.signature())) { + return (Status(error_space, REMOTE_ATTESTATION_FAILED, + "remote-attestation-signature-verification-failed: ")); + } + + *remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber(); + return OkStatus(); +} + +Status RemoteAttestationVerifier::LoadCa() { + absl::WriterMutexLock lock(&ca_mutex_); + std::unique_ptr ca_cert(new X509Cert); + 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 OkStatus(); +} + +} // namespace widevine diff --git a/common/remote_attestation_verifier.h b/common/remote_attestation_verifier.h new file mode 100644 index 0000000..40462ce --- /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 "common/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 EnableTestDrmCertificates(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. + 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. + 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. + Status VerifyRemoteAttestation(const std::string& message, + const RemoteAttestation& remote_attestation, + const ClientIdentification& client_id, + std::string* remote_attestation_cert_sn); + + 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 index 9cd02da..6a14527 100644 --- a/common/rsa_key.cc +++ b/common/rsa_key.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -45,14 +45,22 @@ bool RsaKeyMatch(const RSA* key1, const RSA* key2) { 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_(CHECK_NOTNULL(key)) {} +RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); } RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key) - : key_(CHECK_NOTNULL(RSAPrivateKey_dup(rsa_key.key_))) {} + : key_(RSAPrivateKey_dup(rsa_key.key_)) { + CHECK(key_ != nullptr); +} RsaPrivateKey::~RsaPrivateKey() { RSA_free(key_); } @@ -61,7 +69,7 @@ RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) { if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key)) return nullptr; if (RSA_check_key(key) != 1) { LOG(ERROR) << "Invalid private RSA key: " - << ERR_error_string(ERR_get_error(), nullptr); + << OpenSSLErrorString(ERR_get_error()); RSA_free(key); } return new RsaPrivateKey(key); @@ -86,7 +94,7 @@ bool RsaPrivateKey::Decrypt(const std::string& encrypted_message, RSA_PKCS1_OAEP_PADDING); if (decrypted_size == -1) { LOG(ERROR) << "RSA private decrypt failure: " - << ERR_error_string(ERR_get_error(), nullptr); + << OpenSSLErrorString(ERR_get_error()); return false; } decrypted_message->resize(decrypted_size); @@ -112,7 +120,7 @@ bool RsaPrivateKey::GenerateSignature(const std::string& message, reinterpret_cast(&message_digest[0]), EVP_sha1(), EVP_sha1(), kPssSaltLength)) { LOG(ERROR) << "RSA padding failure: " - << ERR_error_string(ERR_get_error(), nullptr); + << OpenSSLErrorString(ERR_get_error()); return false; } // Encrypt PSS padded digest. @@ -123,7 +131,7 @@ bool RsaPrivateKey::GenerateSignature(const std::string& message, key_, RSA_NO_PADDING) != static_cast(signature->size())) { LOG(ERROR) << "RSA private encrypt failure: " - << ERR_error_string(ERR_get_error(), nullptr); + << OpenSSLErrorString(ERR_get_error()); return false; } return true; @@ -157,10 +165,12 @@ bool RsaPrivateKey::MatchesPublicKey(const RsaPublicKey& public_key) const { uint32_t RsaPrivateKey::KeySize() const { return RSA_size(key_); } -RsaPublicKey::RsaPublicKey(RSA* key) : key_(CHECK_NOTNULL(key)) {} +RsaPublicKey::RsaPublicKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); } RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key) - : key_(CHECK_NOTNULL(RSAPublicKey_dup(rsa_key.key_))) {} + : key_(RSAPublicKey_dup(rsa_key.key_)) { + CHECK(key_ != nullptr); +} RsaPublicKey::~RsaPublicKey() { RSA_free(key_); } @@ -169,7 +179,7 @@ RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) { if (!rsa_util::DeserializeRsaPublicKey(serialized_key, &key)) return nullptr; if (RSA_size(key) == 0) { LOG(ERROR) << "Invalid public RSA key: " - << ERR_error_string(ERR_get_error(), nullptr); + << OpenSSLErrorString(ERR_get_error()); RSA_free(key); } return new RsaPublicKey(key); @@ -192,7 +202,7 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message, reinterpret_cast(&(*encrypted_message)[0]), key_, RSA_PKCS1_OAEP_PADDING) != static_cast(rsa_size)) { LOG(ERROR) << "RSA public encrypt failure: " - << ERR_error_string(ERR_get_error(), nullptr); + << OpenSSLErrorString(ERR_get_error()); return false; } return true; @@ -219,7 +229,7 @@ bool RsaPublicKey::VerifySignature(const std::string& message, reinterpret_cast(&padded_digest[0]), key_, RSA_NO_PADDING) != static_cast(rsa_size)) { LOG(ERROR) << "RSA public decrypt failure: " - << ERR_error_string(ERR_get_error(), nullptr); + << OpenSSLErrorString(ERR_get_error()); return false; } // Hash the message using SHA1. @@ -232,7 +242,7 @@ bool RsaPublicKey::VerifySignature(const std::string& message, reinterpret_cast(&padded_digest[0]), kPssSaltLength) == 0) { LOG(ERROR) << "RSA Verify PSS padding failure: " - << ERR_error_string(ERR_get_error(), nullptr); + << OpenSSLErrorString(ERR_get_error()); return false; } return true; @@ -276,12 +286,12 @@ RsaKeyFactory::RsaKeyFactory() {} RsaKeyFactory::~RsaKeyFactory() {} std::unique_ptr RsaKeyFactory::CreateFromPkcs1PrivateKey( - const std::string& private_key) { + const std::string& private_key) const { return std::unique_ptr(RsaPrivateKey::Create(private_key)); } std::unique_ptr RsaKeyFactory::CreateFromPkcs8PrivateKey( - const std::string& private_key, const std::string& private_key_passphrase) { + const std::string& private_key, const std::string& private_key_passphrase) const { std::string pkcs1_key; const bool result = private_key_passphrase.empty() @@ -296,7 +306,7 @@ std::unique_ptr RsaKeyFactory::CreateFromPkcs8PrivateKey( } std::unique_ptr RsaKeyFactory::CreateFromPkcs1PublicKey( - const std::string& public_key) { + const std::string& public_key) const { return std::unique_ptr(RsaPublicKey::Create(public_key)); } diff --git a/common/rsa_key.h b/common/rsa_key.h index dc6a526..cb15ff5 100644 --- a/common/rsa_key.h +++ b/common/rsa_key.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -60,9 +60,12 @@ class RsaPrivateKey { // Returns the RSA key size (modulus) in bytes. virtual uint32_t KeySize() const; + private: + friend class RsaPublicKey; + friend class X509CertificateBuilder; // TODO(user): Get rid of this. + const RSA* key() const { return key_; } - private: RSA* key_; // SWIG appears to think this declaration is a syntax error. Excluding it for @@ -93,7 +96,6 @@ class RsaPublicKey { 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 @@ -111,9 +113,12 @@ class RsaPublicKey { // Returns the RSA key size (modulus) in bytes. virtual uint32_t KeySize() const; + private: + friend class RsaPrivateKey; + friend class X509CertificateBuilder; // TODO(user): Get rid of this. + const RSA* key() const { return key_; } - private: RSA* key_; // SWIG appears to think this declaration is a syntax error. Excluding it for @@ -131,16 +136,16 @@ class RsaKeyFactory { // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. virtual std::unique_ptr CreateFromPkcs1PrivateKey( - const std::string& private_key); + const std::string& private_key) const; // Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or // EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty). virtual std::unique_ptr CreateFromPkcs8PrivateKey( - const std::string& private_key, const std::string& private_key_passphrase); + const std::string& private_key, const std::string& private_key_passphrase) const; // Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey. virtual std::unique_ptr CreateFromPkcs1PublicKey( - const std::string& public_key); + const std::string& public_key) const; private: DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory); diff --git a/common/rsa_key_test.cc b/common/rsa_key_test.cc index 48f1911..f4b716a 100644 --- a/common/rsa_key_test.cc +++ b/common/rsa_key_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -12,7 +12,7 @@ #include -#include "gtest/gtest.h" +#include "testing/gunit.h" #include "common/rsa_key.h" #include "common/rsa_test_keys.h" #include "common/rsa_util.h" diff --git a/common/rsa_test_keys.cc b/common/rsa_test_keys.cc index fbed989..315ae45 100644 --- a/common/rsa_test_keys.cc +++ b/common/rsa_test_keys.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -12,671 +12,775 @@ #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 }; + 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 }; + 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 }; + 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 }; + 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 }; + 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 }; + 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}; -RsaTestKeys::RsaTestKeys() : - private_key_1_3072_bits_(kTestRsaPrivateKey1_3072, kTestRsaPrivateKey1_3072 - + sizeof(kTestRsaPrivateKey1_3072)), - public_key_1_3072_bits_(kTestRsaPublicKey1_3072, kTestRsaPublicKey1_3072 - + sizeof(kTestRsaPublicKey1_3072)), - private_key_2_2048_bits_(kTestRsaPrivateKey2_2048, kTestRsaPrivateKey2_2048 - + sizeof(kTestRsaPrivateKey2_2048)), - public_key_2_2048_bits_(kTestRsaPublicKey2_2048, kTestRsaPublicKey2_2048 - + sizeof(kTestRsaPublicKey2_2048)), - private_key_3_2048_bits_(kTestRsaPrivateKey3_2048, kTestRsaPrivateKey3_2048 - + sizeof(kTestRsaPrivateKey3_2048)), - public_key_3_2048_bits_(kTestRsaPublicKey3_2048, kTestRsaPublicKey3_2048 - + sizeof(kTestRsaPublicKey3_2048)) { -} +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 index 1d75a86..68d98a4 100644 --- a/common/rsa_test_keys.h +++ b/common/rsa_test_keys.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -18,6 +18,10 @@ 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(); @@ -47,6 +51,25 @@ class RsaTestKeys { 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; @@ -57,6 +80,11 @@ class RsaTestKeys { 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 diff --git a/common/rsa_util.cc b/common/rsa_util.cc index d9c2e30..d9a4b5c 100644 --- a/common/rsa_util.cc +++ b/common/rsa_util.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -13,38 +13,49 @@ #include "common/rsa_util.h" +#include #include +#include #include "glog/logging.h" -#include "openssl/bio.h" -#include "openssl/evp.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 == NULL) { + if (key == nullptr) { LOG(ERROR) << (serialize_private_key ? "Private" : "Public") - << " RSA key is NULL."; + << " RSA key is nullptr."; return false; } - if (serialized_key == NULL) { - LOG(ERROR) << "Pointer to hold serialized RSA" << (serialize_private_key ? - "Private" : "Public") << "Key is NULL."; + 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 == NULL) { - LOG(ERROR) << "BIO_new returned NULL"; + 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) { + 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) == @@ -54,8 +65,8 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key, LOG(ERROR) << "BIO_read failure"; } } else { - LOG(ERROR) << (serialize_private_key ? "Private" : "Public") << - " key serialization failure"; + LOG(ERROR) << (serialize_private_key ? "Private" : "Public") + << " key serialization failure"; } BIO_free(bio); return success; @@ -64,29 +75,31 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key, 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."; + LOG(ERROR) << "Serialized RSA" + << (deserialize_private_key ? "Private" : "Public") + << "Key is empty."; return false; } - if (key == NULL) { - LOG(ERROR) << "Pointer to hold new RSA " << (deserialize_private_key ? - "private" : "public") << " key is NULL."; + 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 == NULL) { - LOG(ERROR) << "BIO_new_mem_buf returned NULL"; + if (bio == nullptr) { + LOG(ERROR) << "BIO_new_mem_buf returned nullptr"; return false; } - *key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, NULL) : - d2i_RSAPublicKey_bio(bio, NULL); + *key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, nullptr) + : d2i_RSAPublicKey_bio(bio, nullptr); BIO_free(bio); - if (*key == NULL) { - LOG(ERROR) << (deserialize_private_key ? "Private" : "Public") << - " RSA key deserialization failure"; + if (*key == nullptr) { + LOG(ERROR) << (deserialize_private_key ? "Private" : "Public") + << " RSA key deserialization failure"; } - return *key != NULL; + return *key != nullptr; } bool SerializeRsaPrivateKey(const RSA* private_key, @@ -111,12 +124,12 @@ bool DeserializeRsaPublicKey(const std::string& serialized_public_key, bool SerializePrivateKeyInfo(const RSA* private_key, std::string* serialized_private_key) { - if (private_key == NULL) { - LOG(ERROR) << "Private RSA key is NULL."; + if (private_key == nullptr) { + LOG(ERROR) << "Private RSA key is nullptr."; return false; } - if (serialized_private_key == NULL) { - LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is NULL."; + 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 @@ -124,25 +137,25 @@ bool SerializePrivateKeyInfo(const RSA* private_key, // 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 == NULL) { - LOG(ERROR) << "EVP_PKEY_new returned NULL."; + if (evp == nullptr) { + LOG(ERROR) << "EVP_PKEY_new returned nullptr."; return false; } bool success = false; - PKCS8_PRIV_KEY_INFO *pkcs8_pki = NULL; - BIO* bio = NULL; + 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 == NULL) { - LOG(ERROR) << "EVP_PKEY2PKCS8 returned NULL."; + if (pkcs8_pki == nullptr) { + LOG(ERROR) << "EVP_PKEY2PKCS8 returned nullptr."; goto cleanup; } bio = BIO_new(BIO_s_mem()); - if (bio == NULL) { - LOG(ERROR) << "BIO_new returned NULL."; + if (bio == nullptr) { + LOG(ERROR) << "BIO_new returned nullptr."; goto cleanup; } if (i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8_pki) == 0) { @@ -161,10 +174,10 @@ bool SerializePrivateKeyInfo(const RSA* private_key, success = true; cleanup: - if (bio != NULL) { + if (bio != nullptr) { BIO_free(bio); } - if (pkcs8_pki != NULL) { + if (pkcs8_pki != nullptr) { PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); } EVP_PKEY_free(evp); @@ -177,8 +190,8 @@ bool DeserializePrivateKeyInfo(const std::string& serialized_private_key, LOG(ERROR) << "Serialized PrivateKeyInfo is empty."; return false; } - if (private_key == NULL) { - LOG(ERROR) << "Pointer to hold new RSA private key is NULL."; + 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 @@ -187,34 +200,34 @@ bool DeserializePrivateKeyInfo(const std::string& serialized_private_key, // 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 == NULL) { - LOG(ERROR) << "BIO_new_mem_buf returned NULL"; + if (bio == nullptr) { + LOG(ERROR) << "BIO_new_mem_buf returned nullptr"; return false; } bool success = false; - EVP_PKEY* evp = NULL; - PKCS8_PRIV_KEY_INFO *pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL); - if (pkcs8_pki == NULL) { - LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL."; + 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 == NULL) { - LOG(ERROR) << "EVP_PKCS82PKEY returned NULL."; + if (evp == nullptr) { + LOG(ERROR) << "EVP_PKCS82PKEY returned nullptr."; goto cleanup; } *private_key = EVP_PKEY_get1_RSA(evp); - if (*private_key == NULL) { + if (*private_key == nullptr) { LOG(ERROR) << "PrivateKeyInfo did not contain an RSA key."; goto cleanup; } success = true; cleanup: - if (evp != NULL) { + if (evp != nullptr) { EVP_PKEY_free(evp); } - if (pkcs8_pki != NULL) { + if (pkcs8_pki != nullptr) { PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); } BIO_free(bio); @@ -223,7 +236,7 @@ cleanup: bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key, std::string* private_key_info) { - RSA* key = NULL; + RSA* key = nullptr; if (DeserializeRsaPrivateKey(rsa_private_key, &key)) { bool success = SerializePrivateKeyInfo(key, private_key_info); RSA_free(key); @@ -234,7 +247,7 @@ bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key, bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, std::string* rsa_private_key) { - RSA* key = NULL; + RSA* key = nullptr; if (DeserializePrivateKeyInfo(private_key_info, &key)) { bool success = SerializeRsaPrivateKey(key, rsa_private_key); RSA_free(key); @@ -246,37 +259,38 @@ bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key, const std::string& passphrase, std::string* serialized_private_key) { - if (private_key == NULL) { - LOG(ERROR) << "Private RSA key is NULL."; + 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 == NULL) { - LOG(ERROR) << "Pointer to hold serialized EncryptedPrivateKeyInfo is NULL."; + if (serialized_private_key == nullptr) { + LOG(ERROR) + << "Pointer to hold serialized EncryptedPrivateKeyInfo is nullptr."; return false; } EVP_PKEY* evp = EVP_PKEY_new(); - if (evp == NULL) { - LOG(ERROR) << "EVP_PKEY_new returned NULL."; + if (evp == nullptr) { + LOG(ERROR) << "EVP_PKEY_new returned nullptr."; return false; } bool success = false; - BIO* bio = NULL; + 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 == NULL) { - LOG(ERROR) << "BIO_new returned NULL."; + 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(), NULL, NULL) == 0) { + passphrase.size(), nullptr, nullptr) == 0) { LOG(ERROR) << "i2d_PKCS8PrivateKey_bio failed."; goto cleanup; } @@ -292,7 +306,7 @@ bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key, success = true; cleanup: - if (bio != NULL) { + if (bio != nullptr) { BIO_free(bio); } EVP_PKEY_free(evp); @@ -301,7 +315,7 @@ cleanup: namespace { // Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below. -int get_password(char *buf, int size, int rwflag, void *u) { +int get_password(char* buf, int size, int rwflag, void* u) { CHECK(buf); CHECK(u); const std::string* pass(static_cast(u)); @@ -324,32 +338,32 @@ bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_ke LOG(ERROR) << "Passphrase for RSA key decryption is empty."; return false; } - if (private_key == NULL) { - LOG(ERROR) << "Pointer to hold new RSA private key is NULL."; + 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 == NULL) { - LOG(ERROR) << "BIO_new_mem_buf returned NULL"; + if (bio == nullptr) { + LOG(ERROR) << "BIO_new_mem_buf returned nullptr"; return false; } bool success = false; - EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, NULL, get_password, + EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, nullptr, get_password, const_cast(&passphrase)); - if (evp == NULL) { - LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned NULL."; + if (evp == nullptr) { + LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned nullptr."; goto cleanup; } *private_key = EVP_PKEY_get1_RSA(evp); - if (*private_key == NULL) { + if (*private_key == nullptr) { LOG(ERROR) << "EncryptedPrivateKeyInfo did not contain an RSA key."; goto cleanup; } success = true; cleanup: - if (evp != NULL) { + if (evp != nullptr) { EVP_PKEY_free(evp); } BIO_free(bio); @@ -359,11 +373,10 @@ cleanup: bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key, const std::string& passphrase, std::string* private_key_info) { - RSA* key = NULL; + RSA* key = nullptr; if (DeserializeRsaPrivateKey(rsa_private_key, &key)) { - bool success = SerializeEncryptedPrivateKeyInfo(key, - passphrase, - private_key_info); + bool success = + SerializeEncryptedPrivateKeyInfo(key, passphrase, private_key_info); RSA_free(key); return success; } @@ -373,7 +386,7 @@ bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key, bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, const std::string& passphrase, std::string* rsa_private_key) { - RSA* key = NULL; + RSA* key = nullptr; if (DeserializeEncryptedPrivateKeyInfo(private_key_info, passphrase, &key)) { bool success = SerializeRsaPrivateKey(key, rsa_private_key); RSA_free(key); @@ -382,5 +395,122 @@ bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, 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 index 77c5921..63e91f7 100644 --- a/common/rsa_util.h +++ b/common/rsa_util.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -99,7 +99,6 @@ bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key, 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. @@ -147,6 +146,55 @@ 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 diff --git a/common/rsa_util_test.cc b/common/rsa_util_test.cc index 1856b2e..23cdf4b 100644 --- a/common/rsa_util_test.cc +++ b/common/rsa_util_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -10,11 +10,24 @@ // Description: // Unit test for rsa_util RSA utilties library. -#include "gtest/gtest.h" -#include "testing/base/public/test_utils.h" +#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 { @@ -221,5 +234,120 @@ TEST_F(RsaUtilTest, Pkcs8FailOnInvalidPassword) { 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 index fc7a7e6..5f3d5b2 100644 --- a/common/sha_util.cc +++ b/common/sha_util.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -29,4 +29,43 @@ std::string Sha256_Hash(const std::string& message) { return digest; } +std::string Sha512_Hash(const std::string& message) { + std::string digest; + digest.resize(SHA512_DIGEST_LENGTH); + SHA512(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 index fe703ae..1e2b70a 100644 --- a/common/sha_util.h +++ b/common/sha_util.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -11,6 +11,8 @@ #include +#include + namespace widevine { // Calculates SHA1 hash. @@ -19,6 +21,14 @@ std::string Sha1_Hash(const std::string& message); // Calculates SHA256 hash. std::string Sha256_Hash(const std::string& message); +// Calculate SHA512 hash. +std::string Sha512_Hash(const std::string& message); + +// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based, +// 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 index b765c11..8b3eb7c 100644 --- a/common/sha_util_test.cc +++ b/common/sha_util_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -7,8 +7,8 @@ //////////////////////////////////////////////////////////////////////////////// #include "common/sha_util.h" - -#include "gtest/gtest.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" namespace widevine { @@ -31,21 +31,41 @@ TEST(ShaUtilTest, Sha256Empty) { TEST(ShaUtilTest, Sha1) { const uint8_t kExpected[] = { - 0x5f, 0xfd, 0x3b, 0x85, 0x2c, 0xcd, 0xd4, 0x48, 0x80, 0x9a, - 0xbb, 0x17, 0x2e, 0x19, 0xbb, 0xb9, 0xc0, 0x1a, 0x43, 0xa4, + 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 std::string")); + Sha1_Hash("random data")); } TEST(ShaUtilTest, Sha256) { const uint8_t kExpected[] = { - 0xa8, 0x2a, 0xf4, 0xe9, 0x6b, 0x5b, 0x8d, 0x82, 0x5a, 0x69, 0x10, - 0xb9, 0x08, 0xec, 0xcd, 0xc4, 0x40, 0x1a, 0xf1, 0xe6, 0x63, 0xdb, - 0x5e, 0xdf, 0x2d, 0x7d, 0xfb, 0x71, 0x2d, 0xb9, 0x65, 0x29, + 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 std::string")); + Sha256_Hash("random data")); +} + +TEST(ShaUtilTest, Sha512) { + const uint8_t kExpected[] = { + 0x8f, 0x49, 0x93, 0x1f, 0x4d, 0x4a, 0x67, 0x6f, 0x9a, 0x7e, 0x62, + 0x60, 0xea, 0xe1, 0x54, 0xfe, 0xe2, 0x75, 0x3c, 0xec, 0x3b, 0xb2, + 0x2e, 0xd7, 0x51, 0xcc, 0x39, 0xf9, 0x89, 0x69, 0xad, 0xd0, 0x14, + 0xaa, 0xbe, 0x40, 0xce, 0xe3, 0xab, 0xef, 0x10, 0x2f, 0x24, 0x0e, + 0xd8, 0x26, 0x7b, 0xb5, 0x7d, 0x86, 0xce, 0xbd, 0xd7, 0x32, 0x86, + 0x3a, 0x5e, 0x9e, 0x8d, 0x23, 0x77, 0x10, 0x80, 0x0c}; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), + Sha512_Hash("random data")); +} + +TEST(ShaUtilTest, GenerateSha1Uuid) { + 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..1e80e09 --- /dev/null +++ b/common/signature_util.cc @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/aes_cbc_util.h" +#include "common/rsa_key.h" +#include "common/sha_util.h" +#include "common/status.h" + +namespace widevine { +namespace signature_util { + +Status GenerateAesSignature(const std::string& message, const std::string& aes_key, + const std::string& aes_iv, std::string* signature) { + if (signature == nullptr) { + return Status(error::INVALID_ARGUMENT, "signature is nullptr"); + } + std::string hash = Sha1_Hash(message); + if (hash.empty()) { + return Status(error::INTERNAL, "Computed hash is empty"); + } + std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash); + if (sig.empty()) { + return Status(error::INTERNAL, "Computed AES signature is empty"); + } + *signature = sig; + return OkStatus(); +} + +Status GenerateRsaSignature(const std::string& message, const std::string& private_key, + std::string* signature) { + if (signature == nullptr) { + return Status(error::INVALID_ARGUMENT, "signature is nullptr"); + } + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(private_key)); + if (rsa_private_key == nullptr) { + return Status(error::INTERNAL, "Failed to construct a RsaPrivateKey"); + } + std::string sig; + if (!rsa_private_key->GenerateSignature(message, &sig)) { + return Status(error::INTERNAL, "Failed to generate a RSA signature"); + } + if (sig.empty()) { + return Status(error::INTERNAL, "Computed RSA signature is empty"); + } + *signature = sig; + return OkStatus(); +} + +} // namespace signature_util +} // namespace widevine diff --git a/common/signature_util.h b/common/signature_util.h new file mode 100644 index 0000000..25238fb --- /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 "common/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. +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. +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..6b7b596 --- /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 SigningKeyMaterialSizeBits(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..34179cd --- /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, +// SigningKeyMaterialSizeBits(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 SigningKeyMaterialSizeBits(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..6c887d2 --- /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, SigningKeyMaterialSizeBitsProtocolVersion_2_0) { + ASSERT_EQ(crypto_util::kSigningKeySizeBits, + SigningKeyMaterialSizeBits(VERSION_2_0)); +} + +TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_1) { + ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2, + SigningKeyMaterialSizeBits(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/status.cc b/common/status.cc new file mode 100644 index 0000000..94513ef --- /dev/null +++ b/common/status.cc @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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/status.h" + +#include + +#include "absl/base/macros.h" +#include "absl/strings/str_cat.h" +#include "util/error_space.h" + +namespace widevine { + +namespace { + +const char* kGenericErrorStatusMessage[] = {"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 + +class GenericErrorSpace : public util::ErrorSpaceImpl { + public: + static std::string space_name(); + static std::string code_to_string(int code); +}; + +std::string GenericErrorSpace::space_name() { return "generic"; } + +std::string GenericErrorSpace::code_to_string(int code) { + static_assert( + ABSL_ARRAYSIZE(kGenericErrorStatusMessage) == error::NUM_ERRORS, + "mismatching generic error status message and generic error status."); + + if (code >= 0 && code < error::NUM_ERRORS) + return kGenericErrorStatusMessage[code]; + return std::to_string(code); +} + + + +const util::ErrorSpace* Status::canonical_space() { + return GenericErrorSpace::Get(); +} + +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 widevine diff --git a/common/status.h b/common/status.h new file mode 100644 index 0000000..1d6597b --- /dev/null +++ b/common/status.h @@ -0,0 +1,108 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_STATUS_H_ +#define COMMON_STATUS_H_ + +#include + +#include "util/error_space.h" + +namespace widevine { +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 Status { + public: + + Status() = default; + + ~Status() = default; + + 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 util::ErrorSpace* e, error::StatusCode c, + const std::string& error_message) + : error_space_(e), status_code_(c), error_message_(error_message) {} + + Status(const util::ErrorSpace* e, int error, const std::string& error_message) + : error_space_(e), status_code_(error), error_message_(error_message) {} + + bool ok() const { return status_code_ == error::OK; } + const util::ErrorSpace* error_space() const { return error_space_; } + static const util::ErrorSpace* canonical_space(); + std::string ToString() const; + std::string error_message() const { return error_message_; } + int error_code() const { return status_code_; } + + private: + const util::ErrorSpace* error_space_ = canonical_space(); + int status_code_ = error::OK; + std::string error_message_; +}; + +inline Status OkStatus() { return Status(); } + +inline bool operator==(const Status& s1, const Status& s2) { + return s1.error_space() == s2.error_space() && + s1.error_code() == s2.error_code() && + s1.error_message() == s2.error_message(); +} +inline bool operator!=(const Status& s1, const Status& s2) { + return !(s1 == s2); +} + + +// Prints a human-readable representation of 'x' to 'os'. +std::ostream& operator<<(std::ostream& os, const Status& x); + +#define CHECK_OK(expression) CHECK(expression.ok()) << expression.ToString() + + +} // namespace widevine + +#endif // COMMON_STATUS_H_ diff --git a/common/status_test.cc b/common/status_test.cc new file mode 100644 index 0000000..e3fda01 --- /dev/null +++ b/common/status_test.cc @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/status.h" +#include "testing/gunit.h" + +namespace widevine { + +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 1"); + 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 widevine diff --git a/common/string_util.cc b/common/string_util.cc new file mode 100644 index 0000000..84031bb --- /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 "common/status.h" + +namespace widevine { +namespace string_util { + +Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) { + if (output == nullptr) { + return Status(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 OkStatus(); +} + +} // namespace string_util +} // namespace widevine diff --git a/common/string_util.h b/common/string_util.h new file mode 100644 index 0000000..9c9922d --- /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 "common/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". +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_drm_certificates.cc b/common/test_drm_certificates.cc new file mode 100644 index 0000000..69faa48 --- /dev/null +++ b/common/test_drm_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_drm_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}; + +TestDrmCertificates::TestDrmCertificates() + : 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_drm_certificates.h b/common/test_drm_certificates.h new file mode 100644 index 0000000..666d693 --- /dev/null +++ b/common/test_drm_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_DRM_CERTIFICATES_H_ +#define COMMON_TEST_DRM_CERTIFICATES_H_ + +#include +#include "base/macros.h" + +namespace widevine { + +class TestDrmCertificates { + public: + TestDrmCertificates(); + virtual ~TestDrmCertificates() {} + + // 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(TestDrmCertificates); +}; + +} // namespace widevine +#endif // COMMON_TEST_DRM_CERTIFICATES_H_ diff --git a/common/test_utils.cc b/common/test_utils.cc new file mode 100644 index 0000000..5dfce0b --- /dev/null +++ b/common/test_utils.cc @@ -0,0 +1,71 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 { + +Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key, + const std::string& message, + std::string* signature) { + CHECK(signature); + if (pem_private_key.empty()) { + return Status(error::INVALID_ARGUMENT, "Empty PEM private key"); + } + if (message.empty()) { + return Status(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 Status(error::INTERNAL, "BIO allocation failed"); + } + 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 = Status(Status::canonical_space(), 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 = Status(Status::canonical_space(), 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..9252f93 --- /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 "common/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. +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..a3a9d96 --- /dev/null +++ b/common/verified_media_pipeline.cc @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 { +Status VerifyVmpData(const std::string& vmp_data, + PlatformVerificationStatus* platform_verification_status) { + *platform_verification_status = PLATFORM_UNVERIFIED; + VmpChecker::Result vmp_result; + 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..a6372ce --- /dev/null +++ b/common/verified_media_pipeline.h @@ -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. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Helper methods for verifying VMP (Verified Media Pipeline) data. + +#ifndef COMMON_VERIFIED_MEDIA_PIPELINE_H_ +#define COMMON_VERIFIED_MEDIA_PIPELINE_H_ + +#include +#include "common/status.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +// Retrieve the PlatformVerificationStatus for |vmp_data|. The +// PlatformVerificationStatus is defined at +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..a6d15c8 --- /dev/null +++ b/common/vmp_checker.cc @@ -0,0 +1,358 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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() {} + +Status VmpChecker::SelectCertificateType(CertificateType cert_type) { + std::unique_ptr ca_cert(new X509Cert); + 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 OkStatus(); +} + +VmpChecker* VmpChecker::Instance() { + static VmpChecker instance; + return &instance; +} + +// Verify VMP data and return appropriate result. +Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) { + DCHECK(!vmp_data.empty()); + DCHECK(result); + + if (!ca_) return 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 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); + 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 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 OkStatus(); + } + if (binary_info.certificate_index() >= code_signing_certs.size()) { + LOG(INFO) << "Invalid code signing certificate index."; + *result = kTampered; + return 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 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 OkStatus(); + } + + VLOG(2) << "VMP verification success. Secure storage: " + << secure_storage_verified; + *result = secure_storage_verified ? kSecureStorageVerified : kVerified; + return OkStatus(); +} + +} // namespace widevine diff --git a/common/vmp_checker.h b/common/vmp_checker.h new file mode 100644 index 0000000..54caf6f --- /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 "common/certificate_type.h" +#include "common/status.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 Status SelectCertificateType(CertificateType cert_type); + + // Verify VMP data and return appropriate result. + virtual 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..005dc86 --- /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()->SelectCertificateType(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..188adc1 --- /dev/null +++ b/common/wvm_token_handler.cc @@ -0,0 +1,311 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/aes_cbc_util.h" +#include "common/ecb_util.h" +#include "common/sha_util.h" +#include "common/status.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); +} + +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); +} + +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 Status(error::INVALID_ARGUMENT, "Keybox token is too short."); + } + if (PreprovKeysMap::GetSingleton()->IsEmpty()) { + return Status(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); + + 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 Status(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 Status( + 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. +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; + 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; +} + +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); +} + +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 Status(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 Status(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 Status(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 Status(error::PERMISSION_DENIED, "Keybox validation failed."); + } + *device_key_out = std::string(device_key); + if (insecure_out) { + *insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0; + } + return OkStatus(); +} + +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 Status(error::INVALID_ARGUMENT, "Invalid device key: size != 16"); + } + if (raw_asset_key.size() < 16) { + return Status(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 Status(error::INTERNAL, "Error encrypting asset key with 3DES."); + } + return OkStatus(); + case AES: + if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) { + return Status(error::INTERNAL, "Error encrypting asset key with AES."); + } + return OkStatus(); + case PASS_THRU: + result->assign(raw_asset_key.data(), raw_asset_key.size()); + return OkStatus(); + default: + return Status(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..e300928 --- /dev/null +++ b/common/wvm_token_handler.h @@ -0,0 +1,125 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/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 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 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 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 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 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 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..b3f0bf9 --- /dev/null +++ b/common/wvm_token_handler_test.cc @@ -0,0 +1,229 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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; + +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) { + 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(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. + Status status; + std::string device_key; + status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex), + &device_key, nullptr, nullptr); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(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) { + 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(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(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; + 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 + 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) { + 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(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..5699486 --- /dev/null +++ b/common/x509_cert.cc @@ -0,0 +1,458 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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) {} + +Status X509Cert::LoadPem(const std::string& pem_cert) { + if (pem_cert.empty()) { + return Status(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 Status(error::INTERNAL, "BIO allocation failed"); + } + Status status; + new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); + if (new_cert == NULL) { + status = Status(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; +} + +Status X509Cert::LoadDer(const std::string& der_cert) { + if (der_cert.empty()) { + return Status(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 Status(error::INVALID_ARGUMENT, "DER certificate load failed"); + } + if (openssl_cert_ != NULL) { + X509_free(openssl_cert_); + } + openssl_cert_ = new_cert; + return 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::GetNotBeforeSeconds(int64_t* valid_start_seconds) const { + if (openssl_cert_ == nullptr) { + return false; + } + return Asn1TimeToEpochSeconds(X509_get0_notBefore(openssl_cert_), + valid_start_seconds) + .ok(); +} + +bool X509Cert::GetNotAfterSeconds(int64_t* valid_end_seconds) const { + if (openssl_cert_ == nullptr) { + return false; + } + return Asn1TimeToEpochSeconds(X509_get0_notAfter(openssl_cert_), + valid_end_seconds) + .ok(); +} + +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; +} + +Status X509Cert::Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time, + int64_t* epoch_seconds) const { + if (asn1_time == nullptr) { + // This code is exported to shared source. The exported code does not yet + // support MakeStatus. + // NOLINTNEXTLINE + return Status(error::INVALID_ARGUMENT, "asn1_time cannot be null."); + } + + if (epoch_seconds == nullptr) { + // NOLINTNEXTLINE + return Status(error::INVALID_ARGUMENT, "epoch_seconds cannot be null."); + } + + ScopedAsn1Time epoch_time(ASN1_TIME_new()); + if (!ASN1_TIME_set(epoch_time.get(), 0)) { + // NOLINTNEXTLINE + return Status(error::INTERNAL, "Failed to set epoch time."); + } + + int day = 0; + int seconds = 0; + if (!ASN1_TIME_diff(&day, &seconds, epoch_time.get(), asn1_time)) { + // NOLINTNEXTLINE + return Status(error::INTERNAL, + "Failed to convert asn1 time to epoch time."); + } + + *epoch_seconds = 24L * 3600L * day + seconds; + return OkStatus(); +} + +X509CertChain::~X509CertChain() { Reset(); } + +void X509CertChain::Reset() { + for (auto certp : cert_chain_) { + delete certp; + } + cert_chain_.clear(); +} + +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); + 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 OkStatus(); +} + +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 Status(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 OkStatus(); +} + +std::string X509CertChain::GetPkcs7() { + std::string pkcs7_cert; + ScopedX509Stack cert_stack(sk_X509_new_null()); + for (X509Cert* cert : cert_chain_) { + // X509 stack takes ownership of certificates. Copy certificates to retain + // |cert_chain_|. + X509Cert cert_copy; + if (!cert_copy.LoadPem(cert->GetPem()).ok()) { + LOG(WARNING) << "Certificate chain serialization failed"; + return ""; + } + X509* openssl_cert_copy = const_cast(cert_copy.openssl_cert()); + cert_copy.openssl_cert_ = nullptr; + sk_X509_push(cert_stack.get(), openssl_cert_copy); + } + ScopedPKCS7 pkcs7( + PKCS7_sign(nullptr, nullptr, cert_stack.get(), nullptr, PKCS7_DETACHED)); + if (!pkcs7) { + LOG(WARNING) << "Could not convert certificate chain to PKCS7"; + return ""; + } + ScopedBIO bio(BIO_new(BIO_s_mem())); + if (bio.get() == nullptr || !i2d_PKCS7_bio(bio.get(), pkcs7.get())) { + LOG(WARNING) << "Failed writing PKCS7 to bio"; + return ""; + } + int cert_size = BIO_pending(bio.get()); + pkcs7_cert.resize(cert_size); + if (BIO_read(bio.get(), &pkcs7_cert[0], cert_size) != cert_size) { + LOG(WARNING) << "BIO_read failure"; + return ""; + } + return pkcs7_cert; +} + +X509Cert* X509CertChain::GetCert(size_t cert_index) const { + if (cert_index >= cert_chain_.size()) { + return NULL; + } + 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_); + } +} + +Status X509CA::InitializeStore() { + absl::WriterMutexLock lock(&openssl_store_mutex_); + if (openssl_store_ == NULL) { + if (ca_cert_ == NULL) { + return Status(error::INTERNAL, "CA X.509Cert is NULL"); + } + openssl_store_ = X509_STORE_new(); + if (openssl_store_ == NULL) { + return Status(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 Status(error::INTERNAL, + "Failed to add X.509 CA certificate to store"); + } + } + return OkStatus(); +} + +Status X509CA::VerifyCert(const X509Cert& cert) { + return OpenSslX509Verify(cert.openssl_cert(), nullptr); +} + +Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) { + if (cert_chain.GetNumCerts() < 1) { + return Status(error::INVALID_ARGUMENT, + "Cannot verify empty certificate chain"); + } + + ScopedX509StackOnly intermediates(sk_X509_new_null()); + if (!intermediates) { + return Status(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 Status(error::INVALID_ARGUMENT, + "X.509 certificate chain without leaf certificate."); + } + return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get()); +} + +Status X509CA::VerifyCertWithChain(const X509Cert& cert, + const X509CertChain& cert_chain) { + ScopedX509StackOnly intermediates(sk_X509_new_null()); + if (!intermediates) { + // MakeStatus is now preferred. But we don't support it in the exported + // version, yet. So, ignore lint here. + // NOLINTNEXTLINE + return Status(error::INTERNAL, + "Failed to allocate X.509 intermediate certificate stack"); + } + for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) { + sk_X509_push(intermediates.get(), + const_cast(cert_chain.GetCert(idx)->openssl_cert())); + } + + return OpenSslX509Verify(cert.openssl_cert(), intermediates.get()); +} + +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(); + Status status = InitializeStore(); + if (!status.ok()) { + return status; + } + openssl_store_mutex_.ReaderLock(); + } + ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new()); + if (!store_ctx) { + return Status(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 Status(error::INTERNAL, "Failed to initialize X.509 store context"); + } + int x509_status = X509_verify_cert(store_ctx.get()); + if (x509_status != 1) { + return Status(error::INTERNAL, + std::string("X.509 certificate chain validation failed: ") + + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get()))); + } + + return OkStatus(); +} + +} // namespace widevine diff --git a/common/x509_cert.h b/common/x509_cert.h new file mode 100644 index 0000000..d773cba --- /dev/null +++ b/common/x509_cert.h @@ -0,0 +1,176 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/openssl_util.h" +#include "common/rsa_key.h" +#include "common/status.h" + +namespace widevine { + +// NOTE: All 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. + Status LoadPem(const std::string& pem_cert); + + // Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is + // a DER-encoded certificate. + 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; + + // Gets the start of the validity period for the certificate in seconds + // since the epoch. |valid_start_seconds| must not be null. Returns true on + // success, false otherwise. + bool GetNotBeforeSeconds(int64_t* valid_start_seconds) const; + + // Gets the end of the validity period for the certificate in seconds + // since the epoch. |valid_end_seconds| must not be null. Returns true on + // success, false otherwise. + bool GetNotAfterSeconds(int64_t* valid_end_seconds) 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); + Status Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time, + int64_t* epoch_seconds) const; + + 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. + 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. + Status LoadPkcs7(const std::string& pk7_cert_chain); + + // Writes the |cert_chain_| to a DER-encoded PKCS#7 X.509 cryptographic + // message. The final message does not include signed data. + std::string GetPkcs7(); + + // Returns the number of certificates in the chain. + size_t GetNumCerts() const { return cert_chain_.size(); } + + // 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. + 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. + Status VerifyCertChain(const X509CertChain& cert_chain); + + // Does X.509 PKI validation of |cert| using the |cert_chain| + // certificates. This method allows |cert| to be an ICA. This method is + // thread-safe. + Status VerifyCertWithChain(const X509Cert& cert, + const X509CertChain& cert_chain); + + private: + Status InitializeStore(); + Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * intermediates); + + 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..efc9ee3 --- /dev/null +++ b/common/x509_cert_test.cc @@ -0,0 +1,531 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 kTestRootCaPemCert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIEAzCCAuugAwIBAgIJAKJPlK965oMfMA0GCSqGSIb3DQEBBQUAMIGXMQswCQYD\n" + "VQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQx\n" + "EzARBgNVBAoMCkdvb2dsZSBJbmMxETAPBgNVBAsMCFdpZGV2aW5lMRUwEwYDVQQD\n" + "DAxUZXN0IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNv\n" + "bTAeFw0xMzA4MTYwMDU3MTBaFw0zMzA4MTUwMDU3MTBaMIGXMQswCQYDVQQGEwJV\n" + "UzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxEzARBgNV\n" + "BAoMCkdvb2dsZSBJbmMxETAPBgNVBAsMCFdpZGV2aW5lMRUwEwYDVQQDDAxUZXN0\n" + "IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n" + "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbu5inZn3c2LbVUXtHW37NhbHQs\n" + "YX1f1I8vv8s/LsQKCAvQTVUc5RlHGou07Fwsdb+KLSyvP4XZDp45OR372q5oBRMZ\n" + "DacbGyrkgpoVxEvBsZsXE0hEuUxvBtkhYzMjZXTz8RsNEMPGIUEOQmMMV86ekBBX\n" + "7aXDwiA+4q2AWg2TUvqR2kWm9IdbRSTBk8Qv2QSKECBOWyyCA0Arp2Dn4bQSbD4q\n" + "tCWPK/KM0xcN6Mc4pqH0z8wGSfqV8UFP2dCd1PURvAqb86WESjNNngpLlSXSeJvm\n" + "q6/i0MwgedzwMP+pvorj/iyrTr36SU1IqoxjJk0x4iCKnCj3PgEDzhZGg78CAwEA\n" + "AaNQME4wHQYDVR0OBBYEFE0w/xgaxPENqZ5qEsAeAqzK34QKMB8GA1UdIwQYMBaA\n" + "FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n" + "BQADggEBAHeem5jT7AZvKYYpA6AOnJglnZh8BLnmoubDOB7lnsHdDX3uedphLk36\n" + "o0ZciRaZPtet67JzQN4gyhAQZ/g0KyEk7A1dtTEne0ZTw7xysqja6uEg5TSOGjOP\n" + "bmjnEpQ2Am54Ak8E12axMiUuwVJALc7CgXQ0aqC6mX1/GvFA/wJb7IQfgDm6ENfM\n" + "CYzyRVT4y7KqMYdSBcZ98vBTDYeE+vY8T5ReYto3TK1hVeauRPWXvP9FZuoqrEJY\n" + "5K6BVpwO3dHfaSlTK0U4vSBLL/WEfLRqxzg8lv6C0i3poTxQksksKXAhxRoqClJQ\n" + "zybCcf8mLyWnc4rkwnDYcZHBOu/dF3s=\n" + "-----END CERTIFICATE-----\n"; + +const char kTestPemCert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n" + "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 int64_t kTestPemCertNotBeforeSeconds = 1376689440; +const int64_t kTestPemCertNotAfterSeconds = 2007755040; + +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 kTestPemIca[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n" + "EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n" + "DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n" + "b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n" + "ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n" + "BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n" + "bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n" + "ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n" + "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n" + "f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n" + "oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n" + "UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n" + "gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n" + "9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n" + "AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n" + "FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n" + "BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n" + "xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n" + "HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n" + "ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n" + "qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n" + "6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n" + "-----END CERTIFICATE-----\n"; + +const char kTestPk7CertChain[] = + "308207fb06092a864886f70d010702a08207ec308207e80201013100300b" + "06092a864886f70d010701a08207ce308203c3308202ab020102300d0609" + "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(OkStatus(), + test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); + EXPECT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert)); + // TODO(user): Add more specific status checks to failure tests. + EXPECT_NE(OkStatus(), test_cert.LoadDer("bad cert")); + EXPECT_NE(OkStatus(), test_cert.LoadPem("bad cert")); + EXPECT_NE(OkStatus(), test_cert.LoadDer("")); + EXPECT_NE(OkStatus(), test_cert.LoadPem("")); +} + +TEST(X509CertTest, VerifySignature) { + X509Cert test_cert; + ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert)); + std::string message(absl::HexStringToBytes(kTestMessage)); + std::string signature; + ASSERT_EQ(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(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(OkStatus(), test_cert.LoadPem(kTestPemCert)); + EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber()); +} + +TEST(X509CertTest, GetNotBeforeSeconds) { + X509Cert test_cert; + ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert)); + int64_t not_before_seconds = 0; + ASSERT_TRUE(test_cert.GetNotBeforeSeconds(¬_before_seconds)); + EXPECT_EQ(kTestPemCertNotBeforeSeconds, not_before_seconds); +} + +TEST(X509CertTest, GetNotAfterSeconds) { + X509Cert test_cert; + ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert)); + int64_t not_after_seconds = 0; + ASSERT_TRUE(test_cert.GetNotAfterSeconds(¬_after_seconds)); + EXPECT_EQ(kTestPemCertNotAfterSeconds, not_after_seconds); +} + +TEST(X509CertTest, CertChain) { + X509CertChain test_chain; + ASSERT_EQ(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(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(OkStatus(), + ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); + X509CA ca(ca_cert.release()); + X509CertChain test_chain; + ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain)); +} + +TEST(X509CertTest, ChainVerificationPkcs7) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(OkStatus(), + ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); + X509CA ca(ca_cert.release()); + X509CertChain test_chain; + ASSERT_EQ(OkStatus(), + test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain))); + EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain)); +} + +TEST(X509CertTest, VerifyCertWithChainIca) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert)); + X509CA ca(ca_cert.release()); + + // Verify the ICA with the root succeeds. + X509CertChain test_chain; + ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + X509Cert ica_cert; + ASSERT_EQ(OkStatus(), ica_cert.LoadPem(kTestPemIca)); + EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(ica_cert, test_chain)); +} + +TEST(X509CertTest, VerifyCertWithChainLeaf) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert)); + X509CA ca(ca_cert.release()); + + // Verify the leaf with the root and ICA succeeds. + X509CertChain test_chain; + ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemIca)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + X509Cert leaf_cert; + ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert)); + EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain)); +} + +TEST(X509CertTest, VerifyCertWithChainLeafMissincIca) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert)); + X509CA ca(ca_cert.release()); + + // Verify the leaf with only the root fails (ICA missing). + X509CertChain test_chain; + ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + X509Cert leaf_cert; + ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert)); + EXPECT_NE(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain)); +} + +TEST(X509CertTest, GetPkcs7) { + X509CertChain test_chain; + ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + std::string pkcs7_certificate = test_chain.GetPkcs7(); + ASSERT_NE(pkcs7_certificate.size(), 0); + X509CertChain new_test_chain; + ASSERT_EQ(OkStatus(), new_test_chain.LoadPkcs7(pkcs7_certificate)); + ASSERT_EQ(test_chain.GetNumCerts(), new_test_chain.GetNumCerts()); + for (int i = 0; i < test_chain.GetNumCerts(); i++) { + ASSERT_EQ(test_chain.GetCert(i)->GetPem(), + new_test_chain.GetCert(i)->GetPem()); + } +} + +TEST(X509CertTest, BooleanExtension) { + std::unique_ptr cert1(new X509Cert); + ASSERT_EQ(OkStatus(), cert1->LoadPem(kTestPemCert)); + bool extension_value; + EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value)); + + std::unique_ptr cert2(new X509Cert); + ASSERT_EQ(OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert)); + ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value)); + EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value); +} + +} // namespace widevine diff --git a/example/BUILD b/example/BUILD index 6e37862..0f14de4 100644 --- a/example/BUILD +++ b/example/BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 diff --git a/example/example_data/certificate_list b/example/example_data/certificate_list index 17beb583119f1498046f84b9fa0b2ee6f35f2180..985216d3e1fc61578cf755a483e4fe44b088075b 100644 GIT binary patch literal 433 zcmV;i0Z#r3EC{>Z_umB)5h4!=&<`RCZEs{{Y+o`kFfbAkA`b}B4_q)A`1+rVd zlDp^7lx!US6dysw2!ldb6MyLQ-x?I~dG`<40E6nPN93kQJrf|L^#GoNh@f&xcW~M(h?e zBMWs6ouQCPKaTv@9WhY*Mv>&@#^OOnx$1M2N?I&*kR$HDk57ht5lvuP^}|8m{K zqZs+iKj@Ux5!%W6Ymop=PfGsT)FX&EEBtMEfL@X{xVHJCzMlMRaZ6N-f5&$E$%CZg b)Ivo=^j9$>3N=_jV?mXb6N*yK{HN>nCDYDC literal 456 zcmV;(0XO~%LkPzC(8C215h4!=(GMaDZEs{{Y+o`kFfbAm5CI|&2+|KC3TWo%zE zFfcI^6Cx1^(+?sFZEs{{Y+o`kFfu>^5`Y5(r2O{JMM(p?{HH=Rm+_LC1}cBqGi?fs zKcbz8sCWYxpEeB-j+l^%HtI){y!N&Gsox`>IS zX9=F@f*;Ims&d3bt)=RKU_f#IVsRr4Fd_y4HLO==&WI_A#5~P?9JlXfOXfZ5SGWCzZ~-)C-$hQmSNx8txs$C%6NAmAa+is;5b#GD(C+!P yr#L5D@HP`w;W)S8i5k4A-q7q;+-bBqht>)I)cBj&I#t83fAZI?b3XLmEavaMyW9H! diff --git a/example/example_data/device.private b/example/example_data/device.private new file mode 100644 index 0000000000000000000000000000000000000000..6de1b835da2bd6f2cccf1afa278eb301c072b5a8 GIT binary patch literal 1217 zcmV;y1U~yPf&{$+0RS)!1_>&LNQUrr!ay9qXGc{0)hbn0H;SJ@$Bb+im?mQSmh;1FwJiQ2}dzew`VXQ5(mYz;X4wd}0)7xeUyT4yPVz%ka=-XZ! zMgl&F-955kQ3x*hVlRV39fOdl#}Kys$BMR>I2a+K^Xo+CcZ3ZVGcw*mtJ009Dm0RR~W zs`@J@8oPnW+~7pYo+HRf^PM$U-ccGY4K(yJ7|*(uaKp87Bs3M(aM*{P@afUl1$z6ajLxW4MP1#>yN85Yj)QZ`U=4)h*Zt$TFXmT`ZZJ#hFh1T=-f0^1OfFyC=a+nABjuV70ML9wmqqwE)++k{xrwh<{Q9j#qy`H2F7fdJNn zqS@xBdxLvOGR@7&-bgioSc1Oe=B0jpe>#bkw;Epep{QC+{Vd+y0)O5=@_^RT2j8jP z1T8Ypx|=wF@7wyYC*&WHyXcJ|zsN4pynW{Ty(ef_u|^nVNxD3;YlBF^vZWlN(3hug z&nc+au=>C>dT~|Zy2p9x1&7PoAPEA2fdI##@ZrX}! zrJg;JX<#&zMuEN645F}^0)>J{KAe5%6Y@`8m%6Djh9;fh2@X9J*{u%ghRGRZ7>Q!GuA- zs{(<57ATg~PxtLsjHBx97rMBUB4bL5w(b{0Y!8lDBJgiD_@gtoMmI(;_{EU}IBf}m zDLc>jpeTdSm|jD@8qpA>5AlbjSRR?t=rI^Yfo+;GFTSK-=-l|Smsj5yV>ZWaBdlzw fq4Gz(O literal 0 HcmV?d00001 diff --git a/example/example_data/device.public b/example/example_data/device.public new file mode 100644 index 0000000000000000000000000000000000000000..0b18ee68de853a5dcacb1d710f47728f5eed1136 GIT binary patch literal 270 zcmV+p0rCDYf&mHwf&l>lr$;35^Sn|lkgSmtu;k0%ypi9 z(hr8Jfm37DMHIO*dK8VIpn1O^7q+id%-;1QoH9P{JQzZVZ5`1(y$Ggzm{ICstSz#Z zo=!*(mHf2R+hz#6zh6FLw&~00+g=++0zQb{J+fd?2rxWa*^SiOBPT3OD4WY5ceA8B z*dvQlG)GCttLxDo-iJpf{l4=o@0l$L|4=hzf68fO9a(SRo7@4w}%+|CNlT`7!9U-76jg?;h$o_~us-d>WBT4+1J4vJ*9 zhV6d8xK{-$4eM)55M(pdUuTGd^pZO}9E3D#FD{R`6M>`p?NTcOlK%R#eD6 zAOLnqNiL~9&&gNS;>zvO1WsLj<`6hz^&1t7#4y4vsP8^2&TVJ%bp$Nsi%VcopPPS+ z3r8tSy!~tt^*XaNEd|uh}OLxt^?=|CP(500#`ioF2o(Yd`%P}iMV9} z-|o2ve3CHdV7=D_eV(uZYqJUl95%86I^5gi&GgLik;MQ*bm~j%lE-(kMLW||kMz(Q X6CxaHbje^izcIGW!DmW($vTro{C`n0 literal 0 HcmV?d00001 diff --git a/example/example_data/intermediate.encrypted.private b/example/example_data/intermediate.encrypted.private index 1bab10afb895c82465fa5d1732675529c4dea0d0..316a1e33f611c257b1895160385ca86d16f03ac0 100644 GIT binary patch delta 1296 zcmV+r1@HQ!3Zn{;B!9=A`fRxDZzKW&2mml01_@w>NC9O71OX}p5Nj{QLK4&rCr0^p zZ~ksN_XL6j&_q#~vH!cpYMOm?-?T;27oCYQSnA=O-)jEy{V9c@)Un@I+|XIz%UP9% zQ&8KyN;MD|sk|74X8?_B3|Y8sRdT-~7faedBU$J>-sGq(6MqjD7}sg-7bcWCsmtKWK^Bmb$kq}aqX(^bG$t?77a%gp)Dzx#e&?5%R=h4_#LM4QHV1{; z6PjvP(8tZSLfC>P=a}>w8^zx7j)=oD-loq6MeoqIT8}Fmo+(s&P;XZ7&Js6qQ*#kA z&^*{YZtw-ajpZL(ZYJ7lK`_U;X!7hVaFx?FACu#z9e)X$DcW1&-6>>o=I)WCd3P9Q z0O~N4XCc8Vznp)GialAQFL>5Na6H#9*NHjQ{#Q0t(n7bcJSYHoQGpHF{!q!jf6r zmR`=bZ7c7IdFmgPX$b^iVp@-2l12tF*1;vkCV#-=StTUF=XuRn?j~n?AOXJB*GzY^ z66hy5-uXC(3V$&fJ62Vh>&=X|e=thE-wOLUce+w@uZ5AcD8i@EwPe-rZrd6LbAE?a z824@`$Ik9FydKekOg3OY@*Bt}>XBnPu?7SN(7=nK7-q^(rD1@31)i1kz&DGTW>dP} zn1Aw71w=A-I6_1_3QAH^KwKpV z!LGa#eq;4|n61r#Je;h8fB%5-h!T=XVB~8aJJLI08{vYB_|tqj7asCy74kFI%gLu( zDdj1%%L;@zL~-k9)R^t>c>uP515EidUVrCX4ujF;_$Br(eEGOoy3w##lg@wy8We#q zHgRYt#jD6?Gd@EWiZEX#ggRD>2Z5IEs>!E88_UP9xO26#&mBniJdDl_xe1+DK6-T& zP5JVmjHX*r8hQ=?-V(egv5lcgQ01GmbX^c@MA|)?eBhqvs%{#+&oJ?H&4)c&`+vQ< z{gNdy_)W~4w=}+;M1cxI6;~kCS*BHw4AI^`2no}vP&=|x GETyDmX>|_( delta 1296 zcmV+r1@HQ!3Zn{;B!BpxN^X>NKK%j$2mml01_@w>NC9O71OX}p5DZw|_Ap$$M&Z;u z*(LhcfdqmC&}uxd>=6uVzk%N#5o-O(6x(>%Hsq4b|J;M|fAo~WJFU1xt5K;`Dj3?zK=nlST}R=Ud>nyr$mutYr8W*siK|JfnvKO9>cMl0eW%dWO3k|9Y)tX2 z?{LsCK+6>nTs|!0y3DHx9D@W$a6U(|M?byd;qquqv050=TLvFL9e7M6; z?tq`+b#=lUAO7D{i~@{2%@&F^9cZ>3RiovARyw`J;_er48`ABoS&>WtJ<1DfJFNe{ zRo)4)vNm%(Dk`TM$M%^cMS2wp96>UnwY@VVDY2?D2Y;!bO7Mm7o0xIQry||YcbZoW zio+C*+wdstn+@G6`=wk4Gf}-jwe1AYFI*rkYSuuYJ`&)s@yi)&=Pmk^q_gqatEoWjbjz_UbDj}cQOw`fEsF^C6 zw`4!V(Z|l$OH1E*vq^c5tcw1^*glTBM=e1a*g}fxn7=-yFs2rzoWbuuDR(yKLC+EX z3hHno@r54kI{VTfc7v{4UDTpqAkIS&6;qT6AAj6aPp`UE{U$lg=kaCv#%T_?Y;ayc zr4pqoS}#<1`KKcR^e9*yBtt=5_I%YBO3su6mP%46`-BZYKXZE5%a3S4_qulJi)FTV${v@#JNYt3(CZ zZ+|Lng8*k-kfjaOn7jxr%coOio5c`)mF(m%-FO7(lV_xWn^$%He6{cZ(KKJiyv2LC znnhR_J|1)bO*o`ZoTQ;y;$uzSmKWGAPng~#LPw|B>8lz+WZq{Qw0c0otqHL*2hvSWauPYFbH_(=q# zVu#w#zY|(mAo2U4m!{Ev_m(rpuzeiX0&9DjR& zl7yJzqz{9lwy@?(PTj)$#zj0upBA*TEBLAm6Q1`=1Lc?XGdD#hhd-LurnQ&1)Q1IS zMxQE+V&bPo-|(d0+16I&Jm0~c1OEUbUF)A=!NSQ3JIlL|K7)zfgYdlX6 zBMY8$Zfevdr(dVWd-as`MfRW;4}Z`pxwAv*nJ+#R3Q9R0v&=jZU|BT?%CLH$8n|DR z=7t1eL_8XNsK;Qblw=hx_M!B#JSQdhb$6ETZ~6rl;*0UBxl$L|4=hzf68fO9a(SRo7@4w}%+|CNlT`7!9U-76j zg?;h$o_~us-d>WBT4+1J4vJ*9hV6d8xK{-$4eM)55M(lr%X}*2iKm8s(Q7YiWzsxG^hg?%XB#1MX6<`!;FC^B}6#I zw^{ZUb8EJ|wvA%7nrvVNOsHx>E;%wV*%8=NdlVl` Ufe_1Uwr`P|3#oMn0s{d60Y9vLfdBvi diff --git a/example/example_data/message b/example/example_data/message index df77986be19b580a185e6fa09bd182a192b6b6fc..a9378c8c319f5f6ca9255e85d256ebf06eee9c88 100644 GIT binary patch literal 2791 zcmVHJYPE{WNokoRPNmoBO-&n`cdDA^#Zehggih|9VW5C+^-LdgPryq=;!@Y=Pxzv3c zlniG+X|S5P~e%0!g6vvR@~ZQAe#Ac#ma(VRyKZ{ z-b3VvcqpHCUAN-DUpDc5x39b;7rc`niz@lLUqiFNL@8l)0gv#!;>S!ijkz^` zx&qsupOA(NSDAm;o^fC0aP2ipxc_X(02auj`A%bQqBT7_YyHn554KKPhAv?W*)X1V zq3{imfz840aR>M4EP`>}hh?5(D=Li>XC2`Agfhmc&leB(Xdy&-OI-$5-#A=>)zH4bf+Bz;$rZF(WG}QiciLomS#$#A}?jjO%mg4~C!uO%4EZ z0O>Mf38#hQ(?5wpEv@sC14Hr>$=4Bzt6{B}@*WMO&5NLd@&EPHNld@fE0DB~b?zzR zc}>4|E?vt})|GI3*N8Bh;?cMHp3#VaF4vuFb5shI7lI7ff)k0@SBD5#aU{ zY&RvAkuuOe51_^^$;=RIW6)qlnLWKY@==nTd42#Gb`YVLNd^mNW1XCx>%kD5-Y{*n zMK*T)V|jX&i?lA*RC0JYx4jQ$32)$7&WM#Nbi1dX2~L7zU%qKPEd%L!40Lj<$E6hQ z*e#i<;gm+tGiA(TP>SeS zl*xdfMwFn?S}e;(v@J9`L#~}F)KyL0mWM1rB7@&+_@8bJ0_7>u;-*p9W=vJ$*0P$X zfR-%9DOQGzGgxKW`2vx0zyR1OH_LX;Wm~d0!OJ4iELT0vpjv-V`9l%=dzYY9YMvUx zotrwlA)jUdi#NY(Mh8?&ni6bZlzVpL%;*>Nm3!fJ@`;Q=C(hL(SrUd)BO?SJ9r*=D zsTEk8&N}4?z4iqu%$_LYY?}8Z??gC4)s|t5yOje)jwsz(ob`y?PW0{)ye62qP`TBvQUX2 zLIMohbcbDP2t=DzYI7MfNhF1+HYT>rHVkVEYw7efz2p;k4re+24CLXhDpziQT|-K4 z(+_|)1ed?S$W&0bRv+mP^n*Sm-$|xK*YARko}*pj%xp$qo}=hl+XY~4^xI^s0K+{f(xIB(jR zEuD}va<#%uBWux0lQ0_s4w@!Opus8CvK7Y2tix*hh_8YF1i(cmdV=`xa~C#XhtEfs zt<~()p3lT-6?yWd051bDgF7dg10unidcq!aW}_W4Sr{NQn6fB{W|p%_*nOM3m58v7 z_JZ!`)V{{1dh0IV&QJgE!FbEbF#e^yRjW~2>3ilhvT|7*kB3Dm+OKRj#qZ*w-6TFf zj^A<3uTrXzv08VmaY#W*7chOTq1tfVv0VZiEIvl3kjd@fx@|M*PMqf)&60IR_A04Y zyxlzz)McHb?2Q38wS}H&5nJL6oxzo5yn@}M$143fSLqHK39xu{51LA~X|79u#ntk7 zEsr&F*(bzOm;Fk$n&Fwm_J^3MlNlVCQ{?P`H;W4RZE}d0n}wLCI(@O@j&O;Bm&4Ut zR;?Pyt4)Q~UbocF#Jx*i@JLjm3u~DWlv*?kMpr|#M|+sxU{O-hmM1S0V;l%>Vb*8P zTj+atO~@&EjgDsbJv_)@)Mx!5miTyq7vx2O2dnjaD_eDWbUSb?9|hz>%;Pp`Ve_SkAj- zt!TPAp`*uP{>6O%YcpN#I*|xfc-na*C-Jh5lNDn;JkX3q8UR z3oPhcBSlKD?{OK|e5Rl`oW1%`My4T|u~7i`J~=q)v`e!m7D8w1k(SkSxD~+N5K(HK z2hoS?Shb3~?k8CWi9FdMH=c#EuVCL$-In49!FxsE57k$?)i@xJz}lDi&EKxGhnY|5vLN_lD2Xg!O-bmBMzPr+gci0+-f=> z;i@!|DSUZmQP)nQmqWUG1-QBCTdkY@Urnu$0&3RcKfMGTXV4q##Y265QoCe+BBEmn z^jBFmKT?3RU3sd<{yPoNu#CCyb?P+cw`hvsWnS2Do;IX-jT$_jsGYsxB>q{`dl#Mi z8OW~;q>S2uIzykiR{i!w@@G{HlmeS54bTO$#8XvLQ|T=enKnSWqeZ}=_rRtd02mW{ zv-W1^nm%FS7K#fZ-X%5G>aiBh$t$c$Sb638^Qw|NS5gqhU9V6>?dGQz9UyW4^nmD& zT~N^2lq#~T7}QuOjKL;`pgiww(6njpP9bj`HAz;(N5}7dJdw3xT_921xKZSrD8!TW zNJ#0WfDrMBvo(%$aOq?5WH(%(D@hr%p;;mj@`E_w!HZw~?hkCi<6GOgDu4o!4?hkO z{R(F6V)#q$uYtu8C@1}(niXF?Bf(q|xQG1$DRt1upvuZNCzUjSyRsMCP=^k>20FG~ zH`#rG+v*@q@{gExW@-YXEt_XYqIg?&jV*~ZF{Tl9?B;Ya2!PtQO>J2Gn=puCq}H|FCY<Os(bJBx6(W-h`~%PT%xZB`OdfzrgIFj)pZ>gnO1v zg6?z~E)oM4?$4ROHEAyM^9O{(T^`GQr>@q!7oiw{|Ni&klpQstK5b39^Pl611e+|>RE51%_c7(-I9sqHvV`4TBV z-M{J=w*?cGPb~#bx-no0M-ac$-(7pVDu@N;$2k7OzxF3He5 zFtuU+$0=o~QsQdf^AKP~muIo2P5g`ciQ(c}b8L^z0Q(<;!Yel;rq<6)S=!oF+LyABwGWe4@euxL4^c+ z9#f-OckVd!&NeB}dQ4P1sF2dWwzYo*&H+6?uYhuT zJ*==_{PPpjt>;>mb<55Ie;Ad{LX!fo=pO#tD{jj3a`fhs^y)~!@c5dI9vV_Ev1=W$ z*rJ-c`ePxWD=n_SnxGJ>5hm=fUgM%^i(DU51Y4E^%F?;KmaQ~E@I7K z0C-H8UjM@3ZWxJqm7>tBy+$hS#Rkb1{nAw~`-~M9SBN%c`cstoy0~+FI{QdwrI`G~ z1gcl~>KG7cRJTzgh>RaVY^p=yV@i>zVC#xo>pisC-`<#Qnc;-)o~9vcE#_`q0jBJ! z2DVeQ!^Nbm8ph!p(|>-t7xGaUVufpFRyI$B=mS=EJi<)uyR3#Wrk*N;QmP`&TVBLy zHjB%XEd4A!ux2pZhAhs3OK8FmmA%~Ag=uDOfJdnpp$g>7QuVakJ9WSg)9X&t2++u~4OkY1)ZG^gD!yR2|UZ}^IUbAh-EwVwz} zh3uO-Ip-3)Lk}bh4@(2!P+WttIi75ET;sc8cF-eO=qaSUd;ApE@hRikFQBh3R1mmh zZ;Bm8UnjVB>wa&mS1fq5Q~>JTai;x6&4cCgUQ!TqzK|5Fh-QR!+l7ew*s`YZq@lz~ zd!ZMo17qXw>}!H8aCH`V=F45Wd%=-i{|nZqAHoo%B5UnI)ibGD>8NtL-kta$QIPw1 z;%^O5Yd@a2gabZz@xdszz!(w*15)43b(OfvX;dWds2BFYh)!{Vjqe|FSnNP z;j$#Ik9E=>c}8YlAV$+hkbzSSg1Uoyv@*2w*z&c2t0+fTWxaqSXWYEo;~gk=@d6-IH5E{4FAV3#ZTjBm!fDFhC*II69d{bIpAe9rk*o``i7T1y zmoARmX0c4li+I4e+<>?8Pr_o~TcW@q|K_hM^^KL%JGF&+Pwt1Q)+DI0=IA>OP&DLt zf-u&v#jycuf{{-lP0uwWenX_UthtZwx3qVx6gMiGhmrF_yj4Nx^4Jnv;ly^4gm8~%bLBdsnqXp=LR3e>3aW4=x8kC(U~p*fuH_gb zQgeMU4q0CXVrXJUoLvzSN$v58epmF=EUMNK;sI$*>WP(v<2Q$?$-ii_ZCL?%ZKW5M zd8VZI>H{+g?!%vrvyum9V-Y!#P-5jAm&VD5K^_{Htf<1Iqsa+0QNEs|IxA_uPLLNV z1~6Ukg6ktHNv+MrnJbpAfI@gml)X(AJnyg2!yoc4CVA7<%{{`99r@Po9VsNH-LOmR z4aw-46r2Y%MAZ9PP*T=27tske49Nz_AWbkM%|Gv=LA`63vw3h@nM}@7F!C@{ZnUr0 zj~(rp`-sA{rGvGw`k&nA_n>Vs9=9i9TS{3*5FD zG=d$6R6wK~PaK$L7RswtrxsvFB=?6ZM^h$dIY%|J zq*$DDa7x(z0BuE~@lV@Swb4-cqjKO2!Tyt8{f!cO7%XiAjGXqq-`_?twc+ROd(uW6 z1AiDRqU*$~%n)^nY1cV=E2LfP1Uon{+-xv=)@Wp=$siF4cha}o+(ACObUwN|j|7$+ BPDcO$ diff --git a/example/example_data/provisioner.cert b/example/example_data/provisioner.cert index e98d6ff01f7d44b39033654569583d6a14eba8ec..aac9844497222d4d251540e5749b96c639ed58f5 100644 GIT binary patch literal 703 zcmV;w0zmx=xdI3T5)iEJ^4I042<_`=zV4}@jCmNl-S^)GB8~zuf&mHwf&l>libAN3 z87-XqS6+~pIK0IZR7=?Qto=x0AlDs&x;)){tEpA*#}No9HyceF$QJ5iWdv%wwB@z{ zqrC%J07IZw<2dfqgkTkmzizAi4=;;QV);>9Pm3;QJw!migGIC13HTZZ{*x|61cVpY zvp7D>d0xuzcL>jNRK)Zg<8>0-6d8bUxX`k7(C(|NJsOU=qL88ME z8ZTOzXbR0&j!}MDFvL_FxdN~WvDi(xwTf3mfFN^KgnDzo_a!s(1@O90VJw^R+^^0n z4^Q7K6KZXiy6S0`;YxbpK(2C_AeVFjyn1$zbC`M#&cn>=uNTO)1e>830s{d60XhtD za&LBNWMy(LV{dH|fCEPC#dp7%Db8UyYc^Ri?J&ZcrJw%gfm@|J80g6N6#qK6w zaXJ`9<3}gm9%2*8*&1ftqC9u;C&Cvhgs| zAu4E?W;&kvr^|-Nz>rMWKdMs-5J10f=--s%2cb=IV*5P519JqyI#h?v=zQNO zXA96p_ns?1wZgHNuz^ozXAs4pOC@~Rh@7q&P~H%8K2i1cDbOdnAa7Ufr=-htP;j%Y z0%b)$TUGw;fkshqStwN{Spw7V;zE8nWy2;KMF#7x70$iCol%N=m)kxE4Gt}kJ_^Yu lx-aMP>mVb~g39(JbBZ&K2+r32Z|9oMe|-XOYGG63=SdLLN~Hh* literal 685 zcmV;e0#f}7rveBB7{S-j!v!La0x*IB3Ic)w0RXptoeKJ(6Lx)sKG2){^X0=}3Q}tS zEmbz{pED0^o&)7oWOA%gc{>@WhJM4piMjmmm}A&$%i~>m2Vhiu@S}zEAgC}6Hrz%F zcM*Mcu@B-QL2mkd7uwqk2!Ao__wxxSma$os8 zS6D)0c0Pq_yh0++*kXucGYnLk6~2G*m9$XZG)Sm%_`eC2_$bvPqJ)%OAe0RRCy3~+L9c4=f~axP+>wB}R9?VJEmT-3{YUHqC-3XS4YM{_QHR#<5AI!;epunf zcMpRD1j;)3x_$Z5&CL!T%vwqs3}*PbZq?3>PjRmwi-lx6b8^%n%$i0Eaeu_HPs43e zL^X%bxH9CJ--uKCW}OwEhNBztjmvn&lH^MRiWSXXD{LJ(L11kiBY}sh5WgP$O=Ewb zXv-Zj+r#=ZGZ{@lO!MHjNHdRm-7hM`PAEiO9l diff --git a/example/example_data/provisioner.encrypted.private b/example/example_data/provisioner.encrypted.private index b174a3e91c926e057d37125fe6a526688c944b8b..221b1395ba48c2ba748e5bfcd7b6f0c959633b91 100644 GIT binary patch delta 1296 zcmV+r1@HQ!3Zn{;B!5_({;(PLB(nkn2mml01_@w>NC9O71OX}p5I*`D@HQ!{|5TH# z@Mj*U-vojL&}YHro>oll5>O5aR{m`qBDoz@|Ctj8hag@jJM!7+rZDu-@@XAEyr_x_ zr&|3)HB{dyfFD{j$9uH9*Dz>)YV;f}3GN@Y^ZOiIH!zXO>!Y>yGt!A4*l z62IyDGFAP9gn#?*R}qIuM8qZe_e0yBUq{!i#cS{18R_fKslf_#2Q}zN)vk!G1yB8# z9Ri{W(~uh@w-LGQ|M#t%=DXot0-@z8KjGrdb0ze z|Ju&V!vpe~1;`0bcT#{A!bO1y{>92bb$fADm`7lJUR3Cfc z_rm-`s&=;8$f%Ze8y9fn9WKJIYm5Ng7^~NX7Jq_fR6-pXO5*{C3&Mn*+%DXpa}9qD zYtraE8P3TPMNGL!m(V;v0a{BL(yYcNi`;hFO##dnU9S@j0ky&XnXCR_p}nm~)knoU zpPDGmU-Vuw{uc2~Lz{x4RdG0j8aE9Hi-+KzVfVh;Y|1E=J?LLw<6lTNV2tv+4 z#(%J*)$pJIjA-nb5unZfSO|#B!#FFoVOb@VckB{4c6WkgN-v<%+@DEz05d`cZzUe@ zxV75J6ja9SLm>Yzkh3P?3<%1mMwT%Q=WN0U_~xJ8N0= z?4-6x>q4+o z8V8@fd)0IMd+kd-$-*0Ll0MkjcYlBdiL35-jd+xr8!mOaCRRUw)MDldR!{i5w7oak z4q$IK(D(Rfxkc?tqE+aLunnPMx{cHJc&1<4Kl1=-NON(mg8DZF(vXtPU~Qg*I)7nL z#`@a)G)JGheY*IPY_T=)_8~S?LRBQrHDvrbK*^M8KEj2qL+1C_+#498)D&9mn;|!+ z>1EL(bKd`khOlpC^_!QHz2PCWXc7|ZdzI~JCrxHx6i5L8w*FV`E4{;8N;1~5)+*57 zfkN>2j8bjm4BWf4u>SUR)}e`kiGKy)TMjBG-eEO1la6u`$NCEj?CFhJ-CS;ZSD}QM z5Mt=1If5=Wu~bF-J%?DcO`jKBd`g3vh~Y6e{?`o~nhEe!-ibFb Gue!wu%YY~V delta 1296 zcmV+r1@HQ!3Zn{;B!8rD0i6isKW7302mml01_@w>NC9O71OX}p5OPdv&PST%$RS~` zZLvWlCIo^6(47!|q3{!E7GiMpSBnonb2u@7xLNgR%q5pHmh>8gHYiUt&G5zxS~msK z)*h>Fi`M%8z1_wL@SluPxieNR0JAAB{_pSkC0h8>xQBG+RewsEj}>{AgDX8YSZ5mT zRd|79D7a@8ev8CcQ0|2QXAV$L4${sdD(g?yeKEvL!BhSw*1E;8kalM0BlkL&@$yP#Hsy=6@iZ<9-(Ti z@wAC7Fg&_|!$()m^~#`)4#CQUtBqPfFfhTxP9!oWq<>DjRvVZO=bMTqy#TQ+bUMfe+%ETVfZG4!inqQ8-_fJ42ydA4%Q_CvvV02x9NHK6B+wG> z)y!@J79o1jkbI8y_lv)mKDweQIDo^C3^rZd>x|IuCrU@ei@)5%6!t1kWb4bSXG2Vt zXI-b<(RN_ix91}}9;cg#SJ(R&-Y&Hn?6+WW*nc*PZLnE>`5vn9CnCU?(|86%R`xd6 zg3GlwXx9a&vgN;qw|imqnOf_nzd2<|Y)7l$O*&CrJ0|#dOb-OuQE+zyqb#{UG0NSN zWC*kejpzy%>GIc__0QhB@CS7xxUq}bMdjA*n6j#{+{~qh^i&NL!B;`(+WcWhu-i2U zCx0pKvdvb70Hrc#Y%e&Z_F*>5enB=Khb1RRqH*o^)x)3Y@m6d#<>L#&RYgNI0 zESkTfeokD{TwHiDc3WS7WZZcReVgro%_}Z74*3)ub8f6@~ zfux}L?cq`4ZJGE)NSE(wH3>-vjdDvBxaOZH+ju{J>rJYCVYn2B;P=)`QVF}Lh8eXZ GBZ_ZR#dvT4 diff --git a/example/example_data/provisioner.public b/example/example_data/provisioner.public new file mode 100644 index 0000000000000000000000000000000000000000..8ef54752b2ff5f34cf37b6cfe02e2879d1e78212 GIT binary patch literal 270 zcmV+p0rCDYf&mHwf&l>libAN387-XqS6+~pIK0IZR7=?Qto=x0AlDs&x;)){tEpA* z#}No9HyceF$QJ5iWdv%wwB@z{qrC%J07IZw<2dfqgkTkmzizAi4=;;QV);>9Pm3;Q zJw!migGIC13HTZZ{*x|61cVpYvp7D>d0xuzcL>jNRK)Zg<8>0-6d8bUxX`k7(C(|< zDi(*xDZRu3=n>NJsOU=qL88ME8ZTOzXbR0&j!}MDFvL_FxdN~WvDi(xwTf3mfFN^K zgnDzo_a!s(1@O90VJw^R+^^0n4^Q7K6KZXiy6S0`;YxbpK(2C_AeVFjyn1$zbC`M# U&cn>=uNTO)1e>830s{d60m8_9X8-^I literal 0 HcmV?d00001 diff --git a/example/example_data/service.cert b/example/example_data/service.cert index b17b7d4625e310bc9c700acf1493ad015db60579..916a1b18b15af80e673dbde804afacac393a9e1d 100644 GIT binary patch literal 703 zcmV;w0zmx=xdI3S5)hQil5;nzXiL5P2Lj)7a8DSz-S^)GB8~zuf&mHwf&l>l(IhG^ zp}A1mU;)C`#c1W`Oc43h##Z|t)g0NPFc}_Z+kw|ACuU@` z=sQqxWlky|1FxCC{RNgRyiL zDMo02nB{aOo6xpc))0?RH~4-?wBSH%sdW6fd~Q>2s>>$e4f;@;sp|91qjpI>7;3_s zT_xuL>Th>uw+4%Q@C4P&(x|W(KAgX<6;;Lmp8x26XKvrN1a7^BM)ns80s{d60XhtD za&LBNWMy(LV{dH|fCD1TriFs?!DeBY9Ny*OJZmH5LrJ(N#zXa+^M_Ia(@49g2JaOm z!9b?F6YSale+z*kS40|G=bv0YxW|pCC)CHfI)&SS{|xXRv3CsI%Ttiz^@!$AGRvOk z0$sv4`2w{w`6G4sY1XzJxUzd~L{#b3JflDK1NoZc?j;gfp-XGr&n{)0-{Du=z=~X6 znJfguKcyxjj}-sDDSEueHk3U{F=mYNxGMq@djCX|6}zdFunlInBoN*_$*XcH`k*3; zeN+gT94?hh`8(;4_gw+N?;&8^GkHxDfeJA$!1g(b;zY|^udiiA-}fnsMl2yaXB~UQ zQ-8!$eqfr@l*#y;C<}=z=?hfn;aUe@jp?_&179q^h3Zd|=&j_pcmh+r+laGH7)w6f zSDq;F*J&@{{ddig(9HCyGdu8w(Wc(hm{A}Ii#_TYH;H5-*}SCjw% literal 685 zcmV;e0#f}7rveBA7{S-j!v!La0x*IB3Ic)w0RZ5@V^X?aoCZz&1RBz44@|N5;~Lv( z4VRp+FL?zjQDchH!yA!rX5hJkj~9fzOGQh{%o{5oW;~`g#X9*!=O*wWB$o=jR9 z9+D60R?Yge7EV;^$U}5+TG*(GY^sTD0EGQOlvUBJi_%1p-Y2pb+~BV1o#)Ws6*?3J z93)cM`pAL1oB_Rx>9e>AI80Gzok|$9M@4J+My#9|$-vSMJhyG>>ML@lq*mPQ!wvn> zH^L;?sUZ>CQP<5dYRbtELo+52gJCoixBDKydaWBImxj8U3{izz@II@9QGC|E)Q(qp zvwMphP-R5R&|5pM70RRCy3~+L9c4=f~axPp4r+3NHZDu1Y4>%*E{#n2$BVYzm397(w zek~t_9|(>4#Cd?(E25<=GzJwB4ngSixynG2hew?B50Th>sTN>twM7ayyA$N85;95{ z*%&t{_r?#~c&K(A;fJERN8r5EJsT!Og)}ArQgxkZlW69P!c^RKi()pLbEsO+4AlnV z`yiGiK$74+Vg_xnQj+r$m0T!;Bh(K`#79hcOyL`JEzpE&z;TI@`>`tEbP&Kc)YyEL z2U|56vxUIgN{l=>L^VAu%KhQBx3Fxgl_psL4QqD--{y;5%lVYa^2tG3n17?KZfGts zf-M}BgVa9&gQ+Jg$1`?22l>elCA#Tfrff3IEEc>1-c95)jwSI}0UO6O&1 TqX;^Hw?GE>p`$(VnHEbXm-0Rf diff --git a/example/example_data/service.encrypted.private b/example/example_data/service.encrypted.private index 2615dbee6dcef2846611988b19b7068a611433fd..23dccdd93cede39e757c925f23a149b334b4a987 100644 GIT binary patch delta 1296 zcmV+r1@HQ!3Zn{;B!73(6NOKhnZN=92mml01_@w>NC9O71OX}p5dOt|n}zPml@pNW z=-Cz>nFN9a(0{fwXLnegf&HPgw2g>%lRAkuZ1~VunbRj^@RbJ6XOW2%-Dle}5D?m< zgKTym&k+>OqDFGr4m(4tWyIn4U|2}{mhP`W45W*P7zc4E#eagdg_wkc;*PF1iL*D{ zn1kgvn{S4OPub)^IXtKsX^@>hYS3u#Vv?;|bG&0Pd=(#s1Lf8+aP%>cuMZtc&h(^U zuyCMesf^+Kco&40CKgxA@N2dlOS{Bh z<0kTL+|VfxZhs9|50d0x5F+F7xpwwb)fwW z+Qi$;5C1lT+OYiBxR`EXGIl}NnsS-(k)zQt<*7Zz99>XKzxEH8Z9%dUY4&??x^zAe zUkDo4L2TFx8%ejo4cH&nATLc5ZAcluQIrO%!F`Xglz;C_x$G;Byq{6;p2~v%W9igU zL`k&7eFVQ4HTo;?_&jm9CFwKqkg%l=Qj0|&0zCJ0<#`~)_-q2{Ipm`2Uv{Iy3VTG&kI4c?0*o*kc^@%^Jy8*>|C#Tlx zGw@tHl7C!M=8J9u!SkQ47rigr!uP%{QO-Y&lO1!PdYI0#<5$g8tqn-MUgqe?2Wz2?JDAU4L@U1dLOFyoX@`=9a-IdKVX!sx5qD&m zOr*M++RSL!zHb{9$H*1w)lDVX60X6TBv^H3Qh${Y6xh6tX+pLLDuy#q87$AePxQgc z-nIQPW!ohs7S`dZyCCQ^C5=zTn5u|$L;)Dvbx;x^*vjLzJyMdp)|5{aVTXw!1c1?n|)OC5hDefLa}k!W1X@i>FtBnp(?#1gxw7dI0~%9 zu;B!VKz5Sb<>~C*7}v!d#1u`T3bP;3A-DGZkIhVxAZwI9KwK#)6?pn#751ArJAB3&_&OIx7eDCZXJUe8N#DA7IWp?;(89sqMgyUgQi-5J;0Hpc)d{4Lw z-u|FT-%+_fc9Yi$?6(Z_bPyVZ2f@gjjYW3NdJjOs>a`c*BK179V;Q0x4YUJ7Rl5%! zH8g-k=k&L(#xKB$3z_Psoi0O$u=8W1ZqFM!0PWKgd#)5L__0+bofeUz7KGhN{(lnm z%t5Y;sK1v+m;muc^vN;0?fC)KCxFGjQ-~^*$N+szvwa_4H1at)HRE(pV0ZnxrmmeUFSbFp$VXqrPkQi Gq<$MAO^6o& delta 1296 zcmV+r1@HQ!3Zn{;B!6{KyJIstSD^v|2mml01_@w>NC9O71OX}p5X~&9Q~qhDv}3SP z`)gUbP6UDk(8-6U*Xmiv!su`_AJz}H>W;ygLdqgQNy;P`I#4XCh>zD391D2$r^)^( zG$Gzdir`4ZEc$mGiMxW&elY$z*jD3DAx5<%iLpXib;-Kgz<+D0J$0V%Kn)!vTg@~SX6tea-p-b37+D%N z>(riGpyrfn^u^Z+F4*CL=^$rz9r*GP`hG*i)YuSKiEOQ@M3E}pRe_imwr@sY@Gz)b zmv-h+U&H3pbAR93@U#p~QjP-b64)P@fP=zE+6Y579?9}+XLlg8Fq$5S{~bKJ9{FQ) z5)7*p4T=SYR{|OtTD4ie&rglFhr3)hW)-myPZo*JTo3B?|EGGyh=E^_ftjb-t#GpE z7G1_zD^Kr61^DL5?vye_ccbf+wv;|b?h?_utF1>3M}N3TdUBY%>RxHu7vboPp`hC^ z!W=}g)-*2n@Ruy;V%rUpR31z_12qr7sn2xabD%Fji0?_nK0q&QQBFoGLpE}z-=s4| z{$uFFzN3}VHIJW$pvBBsCh_Cn>HEG!V$~<{tKLg2^PDY!*ah+Z??MM;Bj)YoL}kJT z+=!%afqxtg{R(H$0qp0-taO1Ex6uv=c+ra-F=tr{>N>oAcxjyZbM8ybVnI-lcc6=w z=y)3~3bgXhBrAwQR{#FV=QDc^?SU>sB0Yx69q;7Jhe)K0Pg27%u{c}*BMgHu82a6k zIwKVthBMkBaUxul{b)s18+F>0h`%90{u${Kq!Z?4PNtvy}V&VTN` z;(yIg%%kDxoH{8{-&3?e{~DW@)_%@5uBAnn`{X-lyk?%(`BY!YXmlrEEAp72r>SWw zZ+o==f2P}Zg^Y1JOxXK{R^KY8SXR#G0x~NQmuOnMNgAxsG&X5+2Ls@Tu0N2P1?!ID z5#=RfF_8-Rmu_cov5Iec%E6e*nIZjI<9~L<^ysyoJpZ}SzN5)0x=(SNW~Q6t@su?f za;=bL9RP_nb@s72l_^HEOd-V(647F{ZCU(6?cV#1QPCx0==z_|6I_03e?;x|iO#^a z$Al616+6Jng1#)Hlrht}MU!Izk>s}dD}h=h(L9GpflPL3Ju|s|5!fzs=h}^P3xDAI z{{?q@&;Zr4rA|D;N#fm}tUNe;#Nn{9>vU{IeEb(`iiqIwhBW50jI=QV5+%VRGKio< zS&WyC6H2uPDsn1A*S)H)JGHO(*ZJn&?Ml1^qJm~a_|I`q`j`#=ukafgHMLcW`dX&y z$+V!Z4zaRZDT&9!Yq6#x*D?Xdet#%&<`I2i6}u1CIPuTAGPgxj@I+v@okF!k*h@ILy?D3{+je zWxf3l(IhG^p}A1mU;)C`#c1W`Oc43h z##Z|t)g0NPFc}_Z+kw|ACuU@`=sQqxWlky|1FxCC{RNgRyiLDMo02nB{aOo6xpc))0?RH~4-?wBSH%sdW6fd~Q>2 zs>>$e4f;@;sp|91qjpI>7;3_sT_xuL>Th>uw+4%Q@C4P&(x|W(KAgX<6;;Lmp8x26 UXKvrN1a7^BM)ns80s{d60jgGjuK)l5 literal 270 zcmV+p0rCDYf&mHwf&l>l;J{;2x?Y?HP5cBJ(r6D%vG?N|+i4A#oUbo=1u9WviqXRx zk#A<;xq^=uguF{dOUld}D<5V&rZ&Yo`9$X?@I%BAk%OL0S{WXa59(IU`m+{JRO-k> zbZ}bOsEKTb>7_&!3YxqX2oEXW#(hfYgZRzSOa;BtK-0Z^*{n0nVB-p7T5!zAL%`j@p z$qz#_CJ=*RG!wV`9>0368zh&8x|$48g<9}FtAkN|*1pt^S9r5~iyP!{@kt^xRcr=l UK$Z($a_8OVW^Dgg0s{d60fq8^5C8xG diff --git a/example/example_data/user.private b/example/example_data/user.private deleted file mode 100644 index eaf856fa2ce5845c86061f384bdf19cd4d8b5fd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1217 zcmV;y1U~yPf&{$+0RS)!1_>&LNQUrr!ay9qXGc{0)hbn0PR~&BnkzI z%IuSVcB}6p`2s<(a+U7h9+v3dti5`MrX;4lWxs3OgHGpW=*3$3oZ^HaPo&wmL1L0O}H#1jwvNbr* z5-I7@J>g+?Nm^5W%ZN|wypg(GFlv(8GvP$l{@!is7m*hnD3(Vw3#myeECO(X&I0`6 z9!HD0XhwoY^w_f^wq;9&nbds3El9PixZbBJ_+%x6qj|jt?froj*+sOOmVSEv#L<%e zOb|74k^rac9Mkx{umQf41a^S$qM=BHM(-okaL@N0P;G@CLW#D}*#ZLr009Dm0RR}; zKQsKSnZa4b6E8nbxT3}Ww1+x%7-8XXou^vkR}ixY1?8TL!h>mMW>(I9cXL6#RK->^I1e4&_E35OK0hjmFY4D;Oy>O}s68SF z(22sop2qWC5xchyL&=Cy#`)csbvEYbX2&3@qmKQ(J@J ziJvbm@)mdraO8lZFIo?(}`_+<%S-5Pci{w$2(Jp0ERYq2gr6at1}OuR{7?#%Co&y@Eyw6&b1cGm;W}*sS?wq zd(7I*!M1H=H)mhp#HEc|CkZr)i&vG={!GF^3%7x&#EjfR9{NjbuSA*UBv>z`uIYi5 zvx&7vAR9Nadi9G!hSEJ@j=srncbw*?Lfy@2&^KO^ZwLoF`6x36XjOZU+64?(&_H!R zy#j%NK>oFlJA_)ZTglvX%R{6I{Yw%DM2?(tp~KT`i>a6$vY9H^J-6txb>CBMd7yN+ zZB-Icaguk^|3x+z`dpieW&5bl+=oj8N{gC3{U{}aMXIc23|Nxw4)l!IyD~ot`@|q= fSMspYzX(ZQ>;J)6S3FG*l-u1HBu~$<-q;G8*|T8P diff --git a/example/example_data/user.public b/example/example_data/user.public deleted file mode 100644 index 0be4b27a2a681d2bfa103dd4e79e96cea85cee25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 270 zcmV+p0rCDYf&mHwf&l>l?ORSH3I&PE?2~?WtM4KC0zt5HmG0gimgwHC(lpgST&td? zaEUwbE-#v_!U%9j)|!++IsNw`>-_|J2o>|?qpp48e;y!-9)e(LP)-=RjQ5r`fN0j% z((mnW!u&s;0@#}a;Z|)oGgo-BH8{@_De2NZ;bC@3T2p?@h)?Uhk-A(kYLePB;Y8K` z-fij^krx~&mPa%TsYxm<0&s%P0{r40M~k^=MuJB4*s~(GWlM#b)O^A%NVTiD-lr+} zWF>>6dA$ei{eczPMYNfgetQ1I(USg55H)g=0H^C5)A+rx0lt$2c7X4qp-6;A?<3T3 U&-WcrZG|2}iMG($0s{d60o<5{TL1t6 diff --git a/example/provisioning_example.cc b/example/provisioning_example.cc index 2539807..8559989 100644 --- a/example/provisioning_example.cc +++ b/example/provisioning_example.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -12,15 +12,16 @@ #include #include -#include "provisioning_sdk/public/certificate_type.h" +#include "common/certificate_type.h" #include "provisioning_sdk/public/provisioning_engine.h" #include "provisioning_sdk/public/provisioning_session.h" #include "provisioning_sdk/public/provisioning_status.h" +using widevine::kCertificateTypeTesting; using widevine::OK; using widevine::ProvisioningEngine; using widevine::ProvisioningSession; -using widevine::kCertTesting; +using widevine::SignedProvisioningMessage; std::string GetContents(const std::string& file_name) { if (file_name.empty()) { @@ -42,7 +43,7 @@ std::string GetContents(const std::string& file_name) { } if (!feof(file)) std::cout << "Failed to read all file contents."; fclose(file); - return contents;; + return contents; } int main(int argc, char** argv) { @@ -50,7 +51,7 @@ int main(int argc, char** argv) { // Call Initialize to setup the engine. if (engine.Initialize( - kCertTesting, GetContents("example_data/service.cert"), + kCertificateTypeTesting, GetContents("example_data/service.cert"), GetContents("example_data/service.encrypted.private"), GetContents("example_data/service.passphrase"), GetContents("example_data/provisioner.cert"), @@ -95,8 +96,9 @@ int main(int argc, char** argv) { // message is processed successfully; if ProcessMessage fails, they can be // reused on another session. std::unique_ptr session; - if (engine.NewProvisioningSession(GetContents("example_data/user.public"), - GetContents("example_data/user.private"), + if (engine.NewProvisioningSession(SignedProvisioningMessage::PROVISIONING_30, + GetContents("example_data/device.public"), + GetContents("example_data/device.private"), &session) != OK) { std::cout << "Failed to create session." << std::endl; return 1; diff --git a/example/provisioning_message_generator.cc b/example/provisioning_message_generator.cc index d186668..bf84f51 100644 --- a/example/provisioning_message_generator.cc +++ b/example/provisioning_message_generator.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -21,12 +21,13 @@ DEFINE_string(service_public_key_path, "example_data/service.public", "Indicates the file path to service public key. If omitted, the " "token is not encrypted."); -DEFINE_string( - certificate_path, "", - "Indicates the file path to the certificate chain. Should not be empty."); -DEFINE_string(certificate_private_key_path, "", - "Indicaets the file path to the certificate private key. Should " +DEFINE_string(certificate_path, "", + "Indicates the file path to the OEM certificate chain. Should " "not be empty."); +DEFINE_string( + certificate_private_key_path, "", + "Indicates the file path to the OEM certificate private key. Should " + "not be empty."); DEFINE_string( output_path, "", "Specifies where to write the output message. Should not be empty."); @@ -71,9 +72,9 @@ bool GenerateProvisioningMessage(const std::string& service_public_key, } // namespace widevine +using widevine::GenerateProvisioningMessage; using widevine::GetContents; using widevine::SetContents; -using widevine::GenerateProvisioningMessage; int main(int argc, char** argv) { gflags::ParseCommandLineFlags(&argc, &argv, true); diff --git a/glog.BUILD b/glog.BUILD index dc6dd25..1ddd6f7 100644 --- a/glog.BUILD +++ b/glog.BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 diff --git a/gtest.BUILD b/gtest.BUILD deleted file mode 100644 index 3131608..0000000 --- a/gtest.BUILD +++ /dev/null @@ -1,37 +0,0 @@ -################################################################################ -# Copyright 2016 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. -################################################################################ - -cc_library( - name = "gtest", - srcs = [ - "googlemock/src/gmock-all.cc", - "googletest/src/gtest-all.cc", - ], - hdrs = glob([ - "googlemock/**/*.h", - "googlemock/src/*.cc", - "googletest/**/*.h", - "googletest/src/*.cc", - ]), - includes = [ - "googlemock", - "googlemock/include", - "googletest", - "googletest/include", - ], - linkopts = ["-pthread"], - visibility = ["//visibility:public"], -) - -cc_library( - name = "gtest_main", - srcs = ["googlemock/src/gmock_main.cc"], - linkopts = ["-pthread"], - visibility = ["//visibility:public"], - deps = [":gtest"], -) diff --git a/oem_certificate_generator/BUILD b/oem_certificate_generator/BUILD index f261299..b2b322b 100644 --- a/oem_certificate_generator/BUILD +++ b/oem_certificate_generator/BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -16,8 +16,8 @@ py_binary( ) py_library( - name = "oem_certificate_generator_helper", - srcs = ["oem_certificate_generator_helper.py"], + name = "oem_certificate_test_helper", + srcs = ["oem_certificate_test_helper.py"], deps = [ ":oem_certificate", ], @@ -28,6 +28,6 @@ py_test( srcs = ["oem_certificate_test.py"], deps = [ ":oem_certificate", - ":oem_certificate_generator_helper", + ":oem_certificate_test_helper", ], ) diff --git a/oem_certificate_generator/oem_certificate.py b/oem_certificate_generator/oem_certificate.py index 52ef308..1849485 100644 --- a/oem_certificate_generator/oem_certificate.py +++ b/oem_certificate_generator/oem_certificate.py @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -130,9 +130,9 @@ class X509CertificateChain(object): for certificate in self._certificates: backend._lib.sk_X509_push(x509_stack, certificate._x509) - pkcs7_partial = 0x4000 p7 = backend._lib.PKCS7_sign(backend._ffi.NULL, backend._ffi.NULL, - x509_stack, backend._ffi.NULL, pkcs7_partial) + x509_stack, backend._ffi.NULL, + backend._lib.PKCS7_DETACHED) p7 = backend._ffi.gc(p7, backend._lib.PKCS7_free) bio = backend._create_mem_bio_gc() @@ -174,20 +174,25 @@ def generate_csr(args): format=serialization.PrivateFormat.PKCS8, encryption_algorithm=_get_encryption_algorithm(args.passphrase))) + x509_name = [ + # Provide various details about who we are. + x509.NameAttribute(oid.NameOID.COUNTRY_NAME, + six.text_type(args.country_name)), + x509.NameAttribute(oid.NameOID.STATE_OR_PROVINCE_NAME, + six.text_type(args.state_or_province_name)), + x509.NameAttribute(oid.NameOID.LOCALITY_NAME, + six.text_type(args.locality_name)), + x509.NameAttribute(oid.NameOID.ORGANIZATION_NAME, + six.text_type(args.organization_name)), + x509.NameAttribute(oid.NameOID.ORGANIZATIONAL_UNIT_NAME, + six.text_type(args.organizational_unit_name)), + ] + if args.common_name: + x509_name.append(x509.NameAttribute(oid.NameOID.COMMON_NAME, + six.text_type(args.common_name))) csr = x509.CertificateSigningRequestBuilder().subject_name( - x509.Name([ - # Provide various details about who we are. - x509.NameAttribute(oid.NameOID.COUNTRY_NAME, - six.text_type(args.country_name)), - x509.NameAttribute(oid.NameOID.STATE_OR_PROVINCE_NAME, - six.text_type(args.state_or_province_name)), - x509.NameAttribute(oid.NameOID.LOCALITY_NAME, - six.text_type(args.locality_name)), - x509.NameAttribute(oid.NameOID.ORGANIZATION_NAME, - six.text_type(args.organization_name)), - x509.NameAttribute(oid.NameOID.ORGANIZATIONAL_UNIT_NAME, - six.text_type(args.organizational_unit_name)), - ])).sign(key, hashes.SHA256(), backends.default_backend()) + x509.Name(x509_name)).sign(key, hashes.SHA256(), + backends.default_backend()) args.output_csr_file.write(csr.public_bytes(serialization.Encoding.PEM)) @@ -248,8 +253,14 @@ def generate_intermediate_certificate(args): def generate_leaf_certificate(args): """Subparser handler for generating leaf certificate.""" intermediate_cert_bytes = args.intermediate_certificate_file.read() - intermediate_cert = x509.load_der_x509_certificate(intermediate_cert_bytes, - backends.default_backend()) + + try: + intermediate_cert = x509.load_pem_x509_certificate( + intermediate_cert_bytes, backends.default_backend()) + except ValueError: + intermediate_cert = x509.load_der_x509_certificate( + intermediate_cert_bytes, backends.default_backend()) + intermediate_private_key = serialization.load_der_private_key( args.intermediate_private_key_file.read(), password=args.intermediate_private_key_passphrase, @@ -335,8 +346,14 @@ def _handle_csr(data): x509.load_pem_x509_csr(data, backends.default_backend())) -def _handle_certificate(data): - """Utility function for get_info to parse certificate.""" +def _handle_pem_certificate(data): + """Utility function for get_info to parse pem certificate.""" + return _certificate_as_string( + x509.load_pem_x509_certificate(data, backends.default_backend())) + + +def _handle_der_certificate(data): + """Utility function for get_info to parse der certificate.""" return _certificate_as_string( x509.load_der_x509_certificate(data, backends.default_backend())) @@ -354,7 +371,10 @@ def get_info(args, out=sys.stdout): # The input is either a CSR or a certificate, or a certificate chain. # Loop through the corresponding handlers one by one. data = args.file.read() - for handler in [_handle_csr, _handle_certificate, _handle_certificate_chain]: + for handler in [ + _handle_csr, _handle_der_certificate, _handle_pem_certificate, + _handle_certificate_chain + ]: try: out.write(handler(data)) return @@ -375,13 +395,14 @@ def create_parser(): parser_csr.add_argument( '--key_size', type=_multiple_of_1024, - default=3072, + default=2048, help='specify RSA key size.') parser_csr.add_argument('-C', '--country_name', required=True) parser_csr.add_argument('-ST', '--state_or_province_name', required=True) parser_csr.add_argument('-L', '--locality_name', required=True) parser_csr.add_argument('-O', '--organization_name', required=True) parser_csr.add_argument('-OU', '--organizational_unit_name', required=True) + parser_csr.add_argument('-CN', '--common_name', required=False) parser_csr.add_argument( '--output_csr_file', type=argparse.FileType('wb'), required=True) parser_csr.add_argument( @@ -425,7 +446,7 @@ def create_parser(): parser_leaf_cert.add_argument( '--key_size', type=_multiple_of_1024, - default=3072, + default=2048, help='specify RSA key size.') parser_leaf_cert.add_argument( '--not_valid_before', diff --git a/oem_certificate_generator/oem_certificate_test.py b/oem_certificate_generator/oem_certificate_test.py index 87f48c8..85de74b 100644 --- a/oem_certificate_generator/oem_certificate_test.py +++ b/oem_certificate_generator/oem_certificate_test.py @@ -1,11 +1,12 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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. ################################################################################ +import base64 import datetime import os import shutil @@ -21,7 +22,7 @@ from cryptography.hazmat.primitives.asymmetric import padding from cryptography.x509 import oid import oem_certificate -import oem_certificate_generator_helper as oem_cert_gen_helper +import oem_certificate_test_helper as oem_cert_test_helper class ArgParseObject(object): @@ -38,7 +39,7 @@ class OemCertificateTest(unittest.TestCase): system_id) def test_generate_csr(self): - args = oem_cert_gen_helper.setup_csr_rgs() + args = oem_cert_test_helper.setup_csr_args() oem_certificate.generate_csr(args) # Verify CSR. csr = x509.load_pem_x509_csr(args.output_csr_file.getvalue(), @@ -59,6 +60,44 @@ class OemCertificateTest(unittest.TestCase): self.assertEqual( subject.get_attributes_for_oid(oid.NameOID.ORGANIZATIONAL_UNIT_NAME)[0] .value, args.organizational_unit_name) + self.assertEqual( + len(subject.get_attributes_for_oid(oid.NameOID.COMMON_NAME)), 0) + + private_key = serialization.load_der_private_key( + args.output_private_key_file.getvalue(), + args.passphrase, + backend=backends.default_backend()) + self.assertEqual(private_key.key_size, args.key_size) + self.assertEqual(csr.public_key().key_size, args.key_size) + # Verify csr and private key match. + self.assertEqual(csr.public_key().public_numbers(), + private_key.public_key().public_numbers()) + + def test_generate_csr_(self): + args = oem_cert_test_helper.setup_csr_args(common_name='MyCommonName') + oem_certificate.generate_csr(args) + # Verify CSR. + csr = x509.load_pem_x509_csr(args.output_csr_file.getvalue(), + backends.default_backend()) + subject = csr.subject + self.assertEqual( + subject.get_attributes_for_oid(oid.NameOID.COUNTRY_NAME)[0].value, + args.country_name) + self.assertEqual( + subject.get_attributes_for_oid(oid.NameOID.STATE_OR_PROVINCE_NAME)[0] + .value, args.state_or_province_name) + self.assertEqual( + subject.get_attributes_for_oid(oid.NameOID.LOCALITY_NAME)[0].value, + args.locality_name) + self.assertEqual( + subject.get_attributes_for_oid(oid.NameOID.ORGANIZATION_NAME)[0].value, + args.organization_name) + self.assertEqual( + subject.get_attributes_for_oid(oid.NameOID.ORGANIZATIONAL_UNIT_NAME)[0] + .value, args.organizational_unit_name) + self.assertEqual( + subject.get_attributes_for_oid(oid.NameOID.COMMON_NAME)[0] + .value, args.common_name) private_key = serialization.load_der_private_key( args.output_private_key_file.getvalue(), @@ -71,7 +110,7 @@ class OemCertificateTest(unittest.TestCase): private_key.public_key().public_numbers()) def test_generate_csr_with_keysize4096_and_passphrase(self): - args = oem_cert_gen_helper.setup_csr_rgs( + args = oem_cert_test_helper.setup_csr_args( key_size=4096, passphrase='passphrase_4096') oem_certificate.generate_csr(args) private_key = serialization.load_der_private_key( @@ -87,14 +126,14 @@ class OemCertificateTest(unittest.TestCase): private_key.public_key().public_numbers()) def test_generate_intermediate_certificate(self): - csr_args = oem_cert_gen_helper.setup_csr_rgs() + csr_args = oem_cert_test_helper.setup_csr_args() oem_certificate.generate_csr(csr_args) csr_bytes = csr_args.output_csr_file.getvalue() csr = x509.load_pem_x509_csr(csr_bytes, backends.default_backend()) root_key, root_certificate = ( - oem_cert_gen_helper.create_root_certificate_and_key()) - args = oem_cert_gen_helper.setup_intermediate_cert_args( + oem_cert_test_helper.create_root_certificate_and_key()) + args = oem_cert_test_helper.setup_intermediate_cert_args( csr_bytes, root_key, root_certificate) oem_certificate.generate_intermediate_certificate(args) @@ -118,18 +157,20 @@ class OemCertificateTest(unittest.TestCase): def test_generate_intermediate_with_cert_mismatch_root_cert_and_key(self): root_key1, _ = ( - oem_cert_gen_helper.create_root_certificate_and_key()) - _, root_certificate2 = oem_cert_gen_helper.create_root_certificate_and_key() - args = oem_cert_gen_helper.setup_intermediate_cert_args( + oem_cert_test_helper.create_root_certificate_and_key()) + _, root_certificate2 = oem_cert_test_helper.create_root_certificate_and_key( + ) + args = oem_cert_test_helper.setup_intermediate_cert_args( 'some csr data', root_key1, root_certificate2) with self.assertRaises(ValueError) as context: oem_certificate.generate_intermediate_certificate(args) self.assertTrue('certificate does not match' in str(context.exception)) - def test_generate_leaf_certificate(self): + def test_generate_leaf_certificate_from_pem_intermediate_cert(self): intermediate_key_bytes, intermediate_certificate_bytes = ( - oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes()) - args = oem_cert_gen_helper.setup_leaf_cert_args( + oem_cert_test_helper.create_intermediate_certificate_and_key_bytes( + pem_format=True)) + args = oem_cert_test_helper.setup_leaf_cert_args( intermediate_key_bytes, intermediate_certificate_bytes) oem_certificate.generate_leaf_certificate(args) @@ -141,7 +182,7 @@ class OemCertificateTest(unittest.TestCase): intermediate_cert = certificates[1] leaf_cert = certificates[0] self.assertEqual( - intermediate_cert.public_bytes(serialization.Encoding.DER), + intermediate_cert.public_bytes(serialization.Encoding.PEM), intermediate_certificate_bytes) intermediate_cert.public_key().verify(leaf_cert.signature, leaf_cert.tbs_certificate_bytes, @@ -167,23 +208,46 @@ class OemCertificateTest(unittest.TestCase): self.assertEqual(leaf_cert.public_key().public_numbers(), leaf_key.public_key().public_numbers()) + def test_generate_leaf_certificate_from_der_intermediate_cert(self): + intermediate_key_bytes, intermediate_certificate_bytes = ( + oem_cert_test_helper.create_intermediate_certificate_and_key_bytes( + pem_format=False)) + args = oem_cert_test_helper.setup_leaf_cert_args( + intermediate_key_bytes, intermediate_certificate_bytes) + oem_certificate.generate_leaf_certificate(args) + + certificate_chain = oem_certificate.X509CertificateChain.load_der( + args.output_certificate_file.getvalue()) + + certificates = list(certificate_chain) + self.assertEqual(len(certificates), 2) + intermediate_cert = certificates[1] + leaf_cert = certificates[0] + self.assertEqual( + intermediate_cert.public_bytes(serialization.Encoding.DER), + intermediate_certificate_bytes) + intermediate_cert.public_key().verify(leaf_cert.signature, + leaf_cert.tbs_certificate_bytes, + padding.PKCS1v15(), + leaf_cert.signature_hash_algorithm) + def test_generate_leaf_certificate_with_keysize4096_and_passphrase(self): intermediate_key_bytes, intermediate_certificate_bytes = ( - oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes()) - args = oem_cert_gen_helper.setup_leaf_cert_args( + oem_cert_test_helper.create_intermediate_certificate_and_key_bytes()) + args = oem_cert_test_helper.setup_leaf_cert_args( intermediate_key_bytes, intermediate_certificate_bytes, key_size=4096, passphrase='leaf passphrase') oem_certificate.generate_leaf_certificate(args) - serialization.load_der_private_key( + leaf_key = serialization.load_der_private_key( args.output_private_key_file.getvalue(), 'leaf passphrase', backend=backends.default_backend()) - self.assertEqual(4096, args.key_size) + self.assertEqual(4096, leaf_key.key_size) def test_get_csr_info(self): - args = oem_cert_gen_helper.setup_csr_rgs() + args = oem_cert_test_helper.setup_csr_args() oem_certificate.generate_csr(args) args.file = StringIO.StringIO(args.output_csr_file.getvalue()) output = StringIO.StringIO() @@ -198,9 +262,33 @@ class OemCertificateTest(unittest.TestCase): Key Size: 4096""" self.assertEqual(output.getvalue(), textwrap.dedent(expected_info)) - def test_get_certificate_info(self): + def test_get_pem_certificate_info(self): _, intermediate_certificate_bytes = ( - oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes()) + oem_cert_test_helper.create_intermediate_certificate_and_key_bytes( + pem_format=True)) + args = ArgParseObject() + args.file = StringIO.StringIO(intermediate_certificate_bytes) + output = StringIO.StringIO() + oem_certificate.get_info(args, output) + expected_info = """\ + Certificate Subject Name: + , value=u'US')> + , value=u'WA')> + , value=u'Kirkland')> + , value=u'CompanyXYZ')> + , value=u'ContentProtection')> + Issuer Name: + , value=u'root_cert')> + Key Size: 4096 + Widevine System Id: 2001 + Not valid before: 2001-08-09 00:00:00 + Not valid after: 2001-11-17 00:00:00""" + self.assertEqual(output.getvalue(), textwrap.dedent(expected_info)) + + def test_get_der_certificate_info(self): + _, intermediate_certificate_bytes = ( + oem_cert_test_helper.create_intermediate_certificate_and_key_bytes( + pem_format=False)) args = ArgParseObject() args.file = StringIO.StringIO(intermediate_certificate_bytes) output = StringIO.StringIO() @@ -222,8 +310,8 @@ class OemCertificateTest(unittest.TestCase): def test_get_certificate_chain_info(self): intermediate_key_bytes, intermediate_certificate_bytes = ( - oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes()) - args = oem_cert_gen_helper.setup_leaf_cert_args( + oem_cert_test_helper.create_intermediate_certificate_and_key_bytes()) + args = oem_cert_test_helper.setup_leaf_cert_args( intermediate_key_bytes, intermediate_certificate_bytes) oem_certificate.generate_leaf_certificate(args) args.file = StringIO.StringIO(args.output_certificate_file.getvalue()) @@ -262,6 +350,92 @@ class OemCertificateTest(unittest.TestCase): Not valid after: 2001-11-17 00:00:00""" self.assertEqual(output.getvalue(), textwrap.dedent(expected_info)) + def test_get_certificate_chain_info_fixed_input(self): + # This was generated from args.output_certificate_file in the test above. + data_b64 = ( + 'MIIJCQYJKoZIhvcNAQcCoIII+jCCCPYCAQExADAPBgkqhkiG9w0BBwGgAgQAoIII2jCC' + 'A+wwggHUoAMCAQICEF8YrBKJsoFPrBNHO2LSenUwDQYJKoZIhvcNAQELBQAwXjELMAkG' + 'A1UEBhMCVVMxCzAJBgNVBAgMAldBMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwK' + 'Q29tcGFueVhZWjEaMBgGA1UECwwRQ29udGVudFByb3RlY3Rpb24wHhcNMDEwODA5MDAw' + 'MDAwWhcNMjMwNzA1MDAwMDAwWjByMRIwEAYDVQQDDAkyMDAxLWxlYWYxCzAJBgNVBAYT' + 'AlVTMQswCQYDVQQIDAJXQTERMA8GA1UEBwwIS2lya2xhbmQxEzARBgNVBAoMCkNvbXBh' + 'bnlYWVoxGjAYBgNVBAsMEUNvbnRlbnRQcm90ZWN0aW9uMIGfMA0GCSqGSIb3DQEBAQUA' + 'A4GNADCBiQKBgQCvY7KZbrMNw/ltcDqTlB+Bu3E5Cbv/JV5Adhnwuk9OiPPJhjx+fx4r' + 'Jo05hM1HImHZSB7NtSjUP2Z9tbL8Fa3DgtI6nAJdQZRGrMxfY3EKe2FoQFbbJFMMXqw9' + 'yNWLjQzBBB7AkQekdXuJHsiZYAoARa2sSbYYLgQhaLLLj5VeVwIDAQABoxYwFDASBgor' + 'BgEEAdZ5BAEBBAQCAgfRMA0GCSqGSIb3DQEBCwUAA4ICAQCO0eQY2brYOiRQLKsoCHhI' + '4/Mi3FOM+rfbqQzM+vesrwahDPLE389igMcNkYTX7QFwdeYMnoqtAeyiPGL42ussqY0h' + 'xTdEOAdXJ2cz99ce8d9EnLWTWU4k1Bk/DAZRbIEPmEi2yigr/0pL1oU6J+uGWx3vf3Eh' + '2vVwDtU4ptSwOR9pcT3UgIfVnxVB49i94PqeQQv4JAQ3jezEzt91NkvGMAHTR602hWUU' + 'nVlIfnzu9KZWVfr4iyh5vCMcT9YIuBzY5EaoCAcfx8hO7xXLRKfQ9MKfzLjbmoCvOp1o' + 'kSLATonsY44JO1e6Uf+RfhtPslk7PoyTlkELBwyRJ+wIwJK9VYxvn5Wm72fjXgReNdDR' + 'xL1hB9U6ccQKwdof+C1TVAANCYejPjGQNN9PzgpxFPMbmmphQWqU/K0c6NzP6WoGJFoS' + 'HJBa2Mlvi41g1GsGHaJCqpI9bXOxuHhQr5jzEh650S1VuhGfzsO1ycfxIxXWqj1SNm/w' + 'mIA504LbQ4o400Ym/QXL4pkeI3XNrIDCYm6Zp8lHuOeqsC7JUulg9O4X9BaQfUHWzbxP' + 'UOSzkPaSMMZ6XP4f8ziUbi0OoDU9e2EnHqyc/csXqZJFdKqyySBYwMqDJi2U9nIc4fmT' + 'rLKRLG3YcPspgo4fuNtiQMPVwYHypr0siAAuoHEo68Fle9lyijCCBOYwggNOoAMCAQIC' + 'EF77T7iz1YiiXO156wo7yZYwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJcm9vdF9j' + 'ZXJ0MB4XDTAxMDgwOTAwMDAwMFoXDTAxMTExNzAwMDAwMFowXjELMAkGA1UEBhMCVVMx' + 'CzAJBgNVBAgMAldBMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKQ29tcGFueVhZ' + 'WjEaMBgGA1UECwwRQ29udGVudFByb3RlY3Rpb24wggIiMA0GCSqGSIb3DQEBAQUAA4IC' + 'DwAwggIKAoICAQC4rLWKHrw+TLvwZW3iZiDGbqVzXLTy7d78PmuAUfJEWW23hZUhG6vN' + 'YzFpaoYgzDjaQ4O5wivdKKsL+oN0/ZY4cK5N8fcPhpY/iHJTC8/AdbC1qEnrBWsE2ptx' + 'M1DKeA2a0Kk5sYQn55WyJaTuGjDf2C0AxveQuua7L4rzC+JadP5HczJngQlsqikXexxO' + 'hHenvdwvovnPks93sdAz8OFopIpT/pXag8bPL224RpVoN5LGPDT9yZ8fCm2w5w2USQA6' + 'Ir4HnCHpgekcY/lPU3CXhMecjXcBZ1k3fOm9j8U4hq9WM6lSSOUYdVpsZ+ZuIhDdhDuP' + 'sxnjo5KSIU9gvDp4m8fgKuxfskKXv7CxkWVRM2AuX80eOBiIYK7UZOGasGmR9QeYQOHu' + 'Nrak+JuhK1iQEcbOsX6TEYhLX5ihGxNo03V5XoURjiSo3y7g6NQ1stBiiAqIV7f9Iu9t' + 'oTDvwsl7pBUKxO8FeW8W68Cp5M+RdUT853X/9DUWlpJvIecS7oe6/MVfyEKPfGUj1cOr' + 'WFHTOKzNtbM+d4YEKjpIrD47RwtffJeZGfKfeWcvHq3gL3hR3O9VUVTl/m3rGz8/JYVW' + '9uJcAoR/ZoCzS71/fclzVOmZu/OHpsIYAicdkhUJLjMZgtdjfOR9VCFe9Aop7+yrrjiA' + '9iQZ38FHf33EZRUrUwIDAQABo2owaDAdBgNVHQ4EFgQUxyronsrwZ7qDmZgUz/xqFMnE' + 'riswHwYDVR0jBBgwFoAUV1kgQN4+tYWvzhkBQvOuPtOlo0gwEgYDVR0TAQH/BAgwBgEB' + '/wIBADASBgorBgEEAdZ5BAEBBAQCAgfRMA0GCSqGSIb3DQEBCwUAA4IBgQAhXwS6/bTY' + '9ViWOfWGPYiGqpdvJ7B8ta/rdD3OwTgmTMOHTNNUt2YzsUYTTeW+yS5FKc4EC/51FRGT' + 'sE658qNi/V5B87o30aA6z2C8YtJJgBBw1T2uHBJVTul9YyXprJTuBO0nHjx+gbGSoiDr' + 'WG9SPG80ZwqTnG0EiHJeCiXfRfAWyYMqjMy0lJnQNNTKPeOh/U1iqMKQkGi0v5dHczXQ' + 'bAtcnTowJeNn5zmhbAZTsVRds0Rp3QhlTshZRkYIjs38bPaKv87NG1wyhQvXwIwiLj0b' + 'mWhkLpp2+Ug2DZhEOuGIYRdfcgR0bUh54FBHM+PttUc49OaNOMTZNFi2KZmYO30vT256' + 'OSGis06hC6pggIzGA7tKP4ATSOBA1fe27ef0YD6pD2dAdQnhaXguDo3/eHbpUXXpqLr6' + 'nm0mTbNTgcC673L5YA8qpQkAzk9vLg4UaslMbPfeKM8rqduJFcjTyVY3C4jBC0qxf6z6' + 'vpWbEO7UpHHdfvWe9DEBODFbyXMxAA==') + args = oem_cert_test_helper.ArgParseObject() + args.file = StringIO.StringIO(base64.b64decode(data_b64)) + output = StringIO.StringIO() + oem_certificate.get_info(args, output) + expected_info = """\ + Certificate Subject Name: + , value=u'2001-leaf')> + , value=u'US')> + , value=u'WA')> + , value=u'Kirkland')> + , value=u'CompanyXYZ')> + , value=u'ContentProtection')> + Issuer Name: + , value=u'US')> + , value=u'WA')> + , value=u'Kirkland')> + , value=u'CompanyXYZ')> + , value=u'ContentProtection')> + Key Size: 1024 + Widevine System Id: 2001 + Not valid before: 2001-08-09 00:00:00 + Not valid after: 2023-07-05 00:00:00 + + Certificate Subject Name: + , value=u'US')> + , value=u'WA')> + , value=u'Kirkland')> + , value=u'CompanyXYZ')> + , value=u'ContentProtection')> + Issuer Name: + , value=u'root_cert')> + Key Size: 4096 + Widevine System Id: 2001 + Not valid before: 2001-08-09 00:00:00 + Not valid after: 2001-11-17 00:00:00""" + self.assertEqual(output.getvalue(), textwrap.dedent(expected_info)) + def test_secure_erase(self): args = ArgParseObject() args.file = tempfile.NamedTemporaryFile(delete=False) @@ -303,6 +477,32 @@ class OemCertificateArgParseTest(unittest.TestCase): self.assertEqual(args.output_private_key_file.mode, 'wb') self.assertEqual(args.passphrase, 'pass') self.assertEqual(args.func, oem_certificate.generate_csr) + self.assertIsNone(args.common_name) + + def test_generate_csr_with_cn(self): + cmds = ('generate_csr --key_size 4096 -C USA -ST WA ' + '-L Kirkland -O Company -OU Widevine -CN MyCommonName').split() + output_private_key_file = os.path.join(self.test_dir, 'private_key') + output_csr_file = os.path.join(self.test_dir, 'csr') + cmds.extend([ + '--output_csr_file', output_csr_file, '--output_private_key_file', + output_private_key_file, '--passphrase', 'pass' + ]) + + args = self.parser.parse_args(cmds) + self.assertEqual(args.key_size, 4096) + self.assertEqual(args.country_name, 'USA') + self.assertEqual(args.state_or_province_name, 'WA') + self.assertEqual(args.locality_name, 'Kirkland') + self.assertEqual(args.organization_name, 'Company') + self.assertEqual(args.organizational_unit_name, 'Widevine') + self.assertEqual(args.output_csr_file.name, output_csr_file) + self.assertEqual(args.output_csr_file.mode, 'wb') + self.assertEqual(args.output_private_key_file.name, output_private_key_file) + self.assertEqual(args.output_private_key_file.mode, 'wb') + self.assertEqual(args.passphrase, 'pass') + self.assertEqual(args.common_name, 'MyCommonName') + self.assertEqual(args.func, oem_certificate.generate_csr) def _fill_file_with_dummy_contents(self, file_name): with open(file_name, 'wb') as f: diff --git a/oem_certificate_generator/oem_certificate_generator_helper.py b/oem_certificate_generator/oem_certificate_test_helper.py similarity index 82% rename from oem_certificate_generator/oem_certificate_generator_helper.py rename to oem_certificate_generator/oem_certificate_test_helper.py index f5780be..0a7a379 100644 --- a/oem_certificate_generator/oem_certificate_generator_helper.py +++ b/oem_certificate_generator/oem_certificate_test_helper.py @@ -1,12 +1,12 @@ ################################################################################ -# Copyright 2017 Google Inc. +# 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 utility functions for OEM certificate generation.""" +"""Common test utility functions for OEM certificate generation.""" import datetime import StringIO @@ -51,15 +51,16 @@ def create_root_certificate_and_key(): return (key, certificate) -def setup_csr_rgs(country_name=_COUNTRY_NAME, - state_or_province_name=_STATE_OR_PROVINCE_NAME, - locality_name=_LOCALITY_NAME, - organization_name=_ORGANIZATION_NAME, - organizational_unit_name=_ORGANIZATIONAL_UNIT_NAME, - key_size=4096, - output_csr_file=None, - output_private_key_file=None, - passphrase=None): +def setup_csr_args(country_name=_COUNTRY_NAME, + state_or_province_name=_STATE_OR_PROVINCE_NAME, + locality_name=_LOCALITY_NAME, + organization_name=_ORGANIZATION_NAME, + organizational_unit_name=_ORGANIZATIONAL_UNIT_NAME, + key_size=4096, + output_csr_file=None, + output_private_key_file=None, + passphrase=None, + common_name=None): """Sets up arguments to OEM Certificate generator for generating csr.""" args = ArgParseObject() args.key_size = key_size @@ -68,6 +69,7 @@ def setup_csr_rgs(country_name=_COUNTRY_NAME, args.locality_name = locality_name args.organization_name = organization_name args.organizational_unit_name = organizational_unit_name + args.common_name = common_name if output_csr_file: args.output_csr_file = output_csr_file else: @@ -141,14 +143,22 @@ def setup_leaf_cert_args(intermediate_key_bytes, def create_intermediate_certificate_and_key_bytes(key_size=4096, - passphrase=None): + passphrase=None, + pem_format=True): """Creates an intermediate certificate and key.""" - csr_args = setup_csr_rgs(key_size=key_size, passphrase=passphrase) + csr_args = setup_csr_args(key_size=key_size, passphrase=passphrase) oem_certificate.generate_csr(csr_args) csr_bytes = csr_args.output_csr_file.getvalue() root_key, root_certificate = create_root_certificate_and_key() args = setup_intermediate_cert_args(csr_bytes, root_key, root_certificate) + oem_certificate.generate_intermediate_certificate(args) - return (csr_args.output_private_key_file.getvalue(), - args.output_certificate_file.getvalue()) + + cert_bytes = args.output_certificate_file.getvalue() + if pem_format: + cert = x509.load_der_x509_certificate(cert_bytes, + backends.default_backend()) + cert_bytes = cert.public_bytes(serialization.Encoding.PEM) + + return (csr_args.output_private_key_file.getvalue(), cert_bytes) diff --git a/protos/public/BUILD b/protos/public/BUILD index 1e636a9..1f5de4e 100644 --- a/protos/public/BUILD +++ b/protos/public/BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -19,7 +19,8 @@ filegroup( "certificate_provisioning.proto", "client_identification.proto", "provisioned_device_info.proto", - "signed_device_certificate.proto", + "remote_attestation.proto", + "signed_drm_certificate.proto", ] ) @@ -28,7 +29,10 @@ cc_proto_library( srcs = ["certificate_provisioning.proto"], default_runtime = "@protobuf_repo//:protobuf", protoc = "@protobuf_repo//:protoc", - deps = [":client_identification_proto"], + deps = [ + ":client_identification_proto", + ":remote_attestation_proto", + ], ) py_proto_library( @@ -36,7 +40,10 @@ py_proto_library( srcs = ["certificate_provisioning.proto"], default_runtime = "@protobuf_repo//:protobuf_python", protoc = "@protobuf_repo//:protoc", - deps = [":client_identification_py_pb2"], + deps = [ + ":client_identification_py_pb2", + ":remote_attestation_py_pb2", + ], ) cc_proto_library( @@ -54,31 +61,70 @@ py_proto_library( ) cc_proto_library( - name = "device_certificate_proto", - srcs = ["device_certificate.proto"], + name = "device_certificate_status_proto", + srcs = ["device_certificate_status.proto"], default_runtime = "@protobuf_repo//:protobuf", protoc = "@protobuf_repo//:protoc", deps = [":provisioned_device_info_proto"], ) py_proto_library( - name = "device_certificate_py_pb2", - srcs = ["device_certificate.proto"], + name = "device_certificate_status_py_pb2", + srcs = ["device_certificate_status.proto"], default_runtime = "@protobuf_repo//:protobuf_python", protoc = "@protobuf_repo//:protoc", deps = [":provisioned_device_info_py_pb2"], ) cc_proto_library( - name = "signed_device_certificate_proto", - srcs = ["signed_device_certificate.proto"], + name = "drm_certificate_proto", + srcs = ["drm_certificate.proto"], + default_runtime = "@protobuf_repo//:protobuf", + protoc = "@protobuf_repo//:protoc", + deps = [":provisioned_device_info_proto"], +) + +py_proto_library( + name = "drm_certificate_py_pb2", + srcs = ["drm_certificate.proto"], + default_runtime = "@protobuf_repo//:protobuf_python", + protoc = "@protobuf_repo//:protoc", + deps = [":provisioned_device_info_py_pb2"], +) + +cc_proto_library( + name = "errors_proto", + srcs = ["errors.proto"], + default_runtime = "@protobuf_repo//:protobuf", + protoc = "@protobuf_repo//:protoc", +) + +cc_proto_library( + name = "remote_attestation_proto", + srcs = ["remote_attestation.proto"], + default_runtime = "@protobuf_repo//:protobuf", + protoc = "@protobuf_repo//:protoc", + deps = [":client_identification_proto"], +) + +py_proto_library( + name = "remote_attestation_py_pb2", + srcs = ["remote_attestation.proto"], + default_runtime = "@protobuf_repo//:protobuf_python", + protoc = "@protobuf_repo//:protoc", + deps = [":client_identification_py_pb2"], +) + +cc_proto_library( + name = "signed_drm_certificate_proto", + srcs = ["signed_drm_certificate.proto"], default_runtime = "@protobuf_repo//:protobuf", protoc = "@protobuf_repo//:protoc", ) py_proto_library( - name = "signed_device_certificate_py_pb2", - srcs = ["signed_device_certificate.proto"], + name = "signed_drm_certificate_py_pb2", + srcs = ["signed_drm_certificate.proto"], default_runtime = "@protobuf_repo//:protobuf_python", protoc = "@protobuf_repo//:protoc", ) diff --git a/protos/public/certificate_provisioning.proto b/protos/public/certificate_provisioning.proto index 06e7b94..d6b3b60 100644 --- a/protos/public/certificate_provisioning.proto +++ b/protos/public/certificate_provisioning.proto @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -17,13 +17,15 @@ package widevine; option java_package = "com.google.video.widevine.protos"; import "protos/public/client_identification.proto"; +import "protos/public/remote_attestation.proto"; // ProvisioningOptions specifies the type of certificate to specify and // in the case of X509 certificates, the certificate authority to use. message ProvisioningOptions { enum CertificateType { - WIDEVINE_DRM = 0; // Default. The original certificate type. - X509 = 1; // X.509 certificate. + WIDEVINE_DRM = 0; // Default. The original certificate type. + X509 = 1; // X.509 certificate. + WIDEVINE_KEYBOX = 2; } optional CertificateType certificate_type = 1 [default = WIDEVINE_DRM]; @@ -32,10 +34,30 @@ message ProvisioningOptions { // authority for signing the generated certificate. This is required iff the // certificate type is X509. optional string certificate_authority = 2; + // System ID for OTA keybox provisioning. Requires device secure boot. + optional uint32 system_id = 3; } // Provisioning request sent by client devices to provisioning service. message ProvisioningRequest { + message EncryptedSessionKeys { + message SessionKeys { + // 16 bytes encryption key generated by client, used by the server to: + // (1) AES-128-CBC decrypt encrypted_client_id in + // EncryptedClientIdentification which is in RemoteAttestation + // (2) AES-128-CBC encrypt device_key to be returned in + // ProvisioningResponse. + optional bytes encryption_key = 1; + // 32 bytes mac key generated by client, used by server to sign + // the ProvisioningResponse. + optional bytes mac_key = 2; + } + // Serial number of certificate which was used to encrypt the session keys. + // Required. + optional bytes certificate_serial_number = 1; + // Serialized, encrypted session keys. Required. + optional bytes encrypted_session_keys = 2; + } oneof clear_or_encrypted_client_id { // Device root of trust and other client identification. Required. ClientIdentification client_id = 1; @@ -56,12 +78,25 @@ message ProvisioningRequest { // to the client certificate serial number. bytes spoid = 7; } + // SessionKeys encrypted using a service cert public key. + // Required for keybox provisioning. + optional EncryptedSessionKeys encrypted_session_keys = 8; } // Provisioning response sent by the provisioning server to client devices. // This message is used for both regular Widevine DRM certificates and for // application-specific X.509 certificates. message ProvisioningResponse { + message OtaKeybox { + // Iv used along with SessionKeys.encryption_key for encrypting device key. + optional bytes device_key_encryption_iv = 1; + // Device key component of the keybox, encrypted using the + // SessionKeys.encryption_key in the request and |device_key_encryption_iv| + // above. + optional bytes encrypted_device_key = 2; + // Device CA token component of the keybox. + optional bytes device_ca_token = 3; + } // AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded. // Required. For X.509 certificates, the private RSA key may also include // a prefix as specified by private_key_prefix in the X509CertificateMetadata @@ -70,7 +105,7 @@ message ProvisioningResponse { // Initialization vector used to encrypt device_rsa_key. Required. optional bytes device_rsa_key_iv = 2; // For Widevine DRM certificates, this contains the serialized - // SignedDrmDeviceCertificate. For X.509 certificates, this contains the PEM + // SignedDrmCertificate. For X.509 certificates, this contains the PEM // encoded X.509 certificate. Required. optional bytes device_certificate = 3; // Nonce value matching nonce in ProvisioningRequest. Required. @@ -79,21 +114,60 @@ message ProvisioningResponse { // provisioned device. Encrypted with the device OEM public key using // RSA-OAEP. optional bytes wrapping_key = 5; + // Only populated in OTA keybox provisioning response. + optional OtaKeybox ota_keybox = 6; +} + +// Protocol-specific context data used to hold the state of the server in +// stateful provisioning protocols. For more information, please refer to +// mE_ZP4WmSX-JNldg +message ProvisioningContext { + // Serialized ProvisioningContextKeyData. Required. + optional bytes key_data = 1; + // Protocol-dependent context data, encrypted with key and IV in key_data. + // Required. + optional bytes context_data = 2; + // HMAC-SHA256 MAC of |context_data|, generated using MAC key in key_data. + optional bytes mac = 3; +} + +// Cryptographic tokens to be used for ProvisioningContext. +message ProvisioningContextKeyData { + // Encryption key, usually 32 bytes used for AES-256-CBC. Required. + optional bytes encryption_key = 1; + // Encryption IV, 16 bytes. Required. + optional bytes encryption_iv = 2; + // MAC key, usually 32 bytes used with HMAC-SHA256. Required. + optional bytes mac_key = 3; } // Serialized ProvisioningRequest or ProvisioningResponse signed with // The message authentication key. message SignedProvisioningMessage { enum ProtocolVersion { - PROVISIONING_20 = 2; // Keybox factory-provisioned devices. - PROVISIONING_30 = 3; // OEM certificate factory-provisioned devices. - INTEL_SIGMA_101 = 101; // Intel Sigma 1.0.1 protocol. + SERVICE_CERTIFICATE_REQUEST = 1; // Service certificate request. + PROVISIONING_20 = 2; // Keybox factory-provisioned devices. + PROVISIONING_30 = 3; // OEM certificate factory-provisioned devices. + ARCPP_PROVISIONING = 4; // ChromeOS/Arc++ devices. + INTEL_SIGMA_101 = 101; // Intel Sigma 1.0.1 protocol. } - // Serialized ProvisioningRequest or ProvisioningResponse. Required. + // Serialized protobuf message for the corresponding protocol and stage of + // the provisioning exchange. ProvisioningRequest or ProvisioningResponse + // in the case of Provisioning 2.0, 3.0 and ARCPP_PROVISIONING. Required. optional bytes message = 1; - // HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required. + // HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required + // for provisioning 2.0 and 3.0. For ARCPP_PROVISIONING, only used in + // response. optional bytes signature = 2; // Version number of provisioning protocol. optional ProtocolVersion protocol_version = 3 [default = PROVISIONING_20]; + // Protocol-specific context / state information for multiple-exchange, + // stateful provisioing protocols. Optional. + optional ProvisioningContext provisioning_context = 4; + // Remote attestation data to authenticate that the ChromeOS client device + // is operating in verified mode. Remote attestation challenge data is + // |message| field above. Required for ARCPP_PROVISIONING request. + // It contains signature of |message|. + optional RemoteAttestation remote_attestation = 5; } diff --git a/protos/public/client_identification.proto b/protos/public/client_identification.proto index f2281ff..79c83aa 100644 --- a/protos/public/client_identification.proto +++ b/protos/public/client_identification.proto @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -40,6 +40,7 @@ message ClientIdentification { HDCP_V2 = 2; HDCP_V2_1 = 3; HDCP_V2_2 = 4; + HDCP_V2_3 = 5; HDCP_NO_DIGITAL_OUTPUT = 0xff; } @@ -48,6 +49,13 @@ message ClientIdentification { 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]; @@ -63,6 +71,17 @@ message ClientIdentification { // 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]; + // Clients can indicate a performance level supported by OEMCrypto. + // This will allow applications and providers to choose an appropriate + // quality of content to serve. Currently defined tiers are + // 1 (low), 2 (medium) and 3 (high). Any other value indicate that + // the resource rating is unavailable or reporting erroneous values + // for that device. For details see, + // https://docs.google.com/document/d/1wodSYK-Unj3AgTSXqujWuBCAFC00qF85G1AhfLtqdko + optional uint32 resource_rating_tier = 12 [default = 0]; } // Type of factory-provisioned device root of trust. Optional. diff --git a/protos/public/device_certificate.proto b/protos/public/device_certificate.proto deleted file mode 100644 index 041825e..0000000 --- a/protos/public/device_certificate.proto +++ /dev/null @@ -1,91 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 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. -//////////////////////////////////////////////////////////////////////////////// - -// -// Description: -// Device certificate and certificate status list format definitions. - -syntax = "proto2"; - -package widevine; - -option java_outer_classname = "DeviceCertificateProtos"; -option java_package = "com.google.video.widevine.protos"; - -import "protos/public/provisioned_device_info.proto"; - -// DRM certificate definition for user devices, intermediate, service, and root -// certificates. -message DrmDeviceCertificate { - enum CertificateType { - ROOT = 0; - DRM_INTERMEDIATE = 1; - DRM_USER_DEVICE = 2; - SERVICE = 3; - PROVISIONER = 4; - } - - // Type of certificate. Required. - optional CertificateType 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; -} - -// Contains DRM and OEM certificate status and device information for a -// specific system ID. -message DeviceCertificateStatus { - enum Status { - VALID = 0; - REVOKED = 1; - }; - - // Serial number of the intermediate DrmDeviceCertificate to which this - // message refers. Required. - optional bytes drm_serial_number = 1; - // Status of the certificate. Optional. - optional Status status = 2 [default = 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; -} - -// 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; -} - -// Signed CertificateStatusList -message SignedCertificateStatusList { - // 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; -} 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..e03a74b --- /dev/null +++ b/protos/public/drm_certificate.proto @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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; + } + enum ServiceType { + UNKNOWN = 0; LICENSE_SERVER_SDK = 1; LICENSE_SERVER_PROXY_SDK = 2; + } + // 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; + // This field is used only when type = SERVICE to specify which SDK uses + // service certificate. + optional ServiceType service_type = 8 [default = UNKNOWN]; +} diff --git a/protos/public/errors.proto b/protos/public/errors.proto new file mode 100644 index 0000000..cb3c10b --- /dev/null +++ b/protos/public/errors.proto @@ -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. +//////////////////////////////////////////////////////////////////////////////// +// +// 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; + + // Invalid method parameter. + INVALID_PARAMETER = 170; + +} diff --git a/protos/public/provisioned_device_info.proto b/protos/public/provisioned_device_info.proto index c5c5b97..f6d4104 100644 --- a/protos/public/provisioned_device_info.proto +++ b/protos/public/provisioned_device_info.proto @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -26,6 +26,30 @@ message ProvisionedDeviceInfo { 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; @@ -44,4 +68,6 @@ message ProvisionedDeviceInfo { // 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/signed_device_certificate.proto b/protos/public/signed_drm_certificate.proto similarity index 69% rename from protos/public/signed_device_certificate.proto rename to protos/public/signed_drm_certificate.proto index 23a9fbe..bbd0835 100644 --- a/protos/public/signed_device_certificate.proto +++ b/protos/public/signed_drm_certificate.proto @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -12,16 +12,16 @@ syntax = "proto2"; package widevine; -option java_outer_classname = "SignedDeviceCertificateProtos"; +option java_outer_classname = "SignedDrmCertificateProtos"; option java_package = "com.google.video.widevine.protos"; -// DrmDeviceCertificate signed by a higher (CA) DRM certificate. -message SignedDrmDeviceCertificate { +// 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; - // SignedDrmDeviceCertificate used to sign this certificate. - optional SignedDrmDeviceCertificate signer = 3; + // SignedDrmCertificate used to sign this certificate. + optional SignedDrmCertificate signer = 3; } diff --git a/provisioning_sdk/internal/BUILD b/provisioning_sdk/internal/BUILD index 4f04960..4dbdf93 100644 --- a/provisioning_sdk/internal/BUILD +++ b/provisioning_sdk/internal/BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -12,7 +12,9 @@ package_group( name = "internal", packages = [ + "//arcpp_provisioning/...", "//provisioning_sdk/...", + "//sigma101_provisioning/...", ], ) @@ -27,14 +29,21 @@ cc_library( deps = [ ":oem_device_cert", "//base", + "@abseil_repo//absl/synchronization", + "//common:aes_cbc_util", + "//common:certificate_type", + "//common:crypto_util", + "//common:drm_root_certificate", + "//common:drm_service_certificate", "//common:random_util", "//common:rsa_key", - "//provisioning_sdk/internal/certificates:root_certificates", - "//provisioning_sdk/public:certificate_type", + "//provisioning_sdk/internal/certificates:root_oem_certificates", "//provisioning_sdk/public:provisioning_status", - "//protos/public:device_certificate_proto", + "//protos/public:certificate_provisioning_proto", + "//protos/public:device_certificate_status_proto", + "//protos/public:drm_certificate_proto", "//protos/public:provisioned_device_info_proto", - "//protos/public:signed_device_certificate_proto", + "//protos/public:signed_drm_certificate_proto", ], ) @@ -45,9 +54,15 @@ cc_test( deps = [ ":provisioning_engine_impl", "//base", - "//external:gtest_main", + "//testing:gunit_main", + "//common:certificate_type", + "//common:drm_service_certificate", "//common:mock_rsa_key", - "//provisioning_sdk/public:certificate_type", + "//common:rsa_test_keys", + "//common:rsa_util", + "//common:status", + "//common:test_drm_certificates", + "//protos/public:certificate_provisioning_proto", ], ) @@ -56,18 +71,9 @@ cc_library( srcs = ["provisioning_session_impl.cc"], hdrs = ["provisioning_session_impl.h"], deps = [ - ":oem_device_cert", - ":provisioning_engine_impl", "//base", - "//common:aes_cbc_util", - "//common:random_util", "//common:rsa_key", - "//common:sha_util", "//provisioning_sdk/public:provisioning_status", - "//protos/public:certificate_provisioning_proto", - "//protos/public:client_identification_proto", - "//protos/public:device_certificate_proto", - "//protos/public:provisioned_device_info_proto", ], ) @@ -75,11 +81,46 @@ cc_test( name = "provisioning_session_impl_test", size = "small", srcs = ["provisioning_session_impl_test.cc"], + deps = [ + ":provisioning_engine_impl", + ":provisioning_session_impl", + "//testing:gunit_main", + "//common:mock_rsa_key", + ], +) + +cc_library( + name = "provisioning30_session_impl", + srcs = ["provisioning30_session_impl.cc"], + hdrs = ["provisioning30_session_impl.h"], deps = [ ":oem_device_cert", ":provisioning_engine_impl", ":provisioning_session_impl", - "//external:gtest_main", + "//base", + "//common:aes_cbc_util", + "//common:drm_service_certificate", + "//common:random_util", + "//common:rsa_key", + "//common:sha_util", + "//provisioning_sdk/public:provisioning_status", + "//protos/public:certificate_provisioning_proto", + "//protos/public:client_identification_proto", + "//protos/public:drm_certificate_proto", + "//protos/public:provisioned_device_info_proto", + ], +) + +cc_test( + name = "provisioning30_session_impl_test", + size = "small", + srcs = ["provisioning30_session_impl_test.cc"], + deps = [ + ":oem_device_cert", + ":provisioning30_session_impl", + ":provisioning_engine_impl", + ":provisioning_session_impl", + "//testing:gunit_main", "//common:aes_cbc_util", "//common:mock_rsa_key", "//common:sha_util", @@ -92,11 +133,12 @@ cc_library( hdrs = ["oem_device_cert.h"], deps = [ "//base", + "@abseil_repo//absl/memory", "//external:openssl", + "//common:certificate_type", "//common:openssl_util", "//common:rsa_key", - "//provisioning_sdk/internal/certificates:root_certificates", - "//provisioning_sdk/public:certificate_type", + "//provisioning_sdk/internal/certificates:root_oem_certificates", ], ) @@ -106,7 +148,8 @@ cc_test( srcs = ["oem_device_cert_test.cc"], deps = [ ":oem_device_cert", - "//external:gtest_main", - "//provisioning_sdk/internal/certificates:test_certificates", + "//testing:gunit_main", + "//common:rsa_key", + "//provisioning_sdk/internal/certificates:test_oem_certificates", ], ) diff --git a/provisioning_sdk/internal/certificates/BUILD b/provisioning_sdk/internal/certificates/BUILD index 3f8d044..dfd3f53 100644 --- a/provisioning_sdk/internal/certificates/BUILD +++ b/provisioning_sdk/internal/certificates/BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -13,37 +13,13 @@ package( ) cc_library( - name = "root_certificates", + name = "root_oem_certificates", srcs = [ - "root_certificates.cc", - ":drm_ca_root_dev_cert", - ":drm_ca_root_prod_cert", - ":drm_ca_root_test_cert", + "root_oem_certificates.cc", ":oem_ca_root_dev_der", ":oem_ca_root_prod_der", ], - hdrs = ["root_certificates.h"], -) - -genrule( - name = "drm_ca_root_test_cert", - srcs = ["drm_ca_root_test.cert"], - outs = ["drm_ca_root_test_cert.h"], - cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", -) - -genrule( - name = "drm_ca_root_dev_cert", - srcs = ["drm_ca_root_dev.cert"], - outs = ["drm_ca_root_dev_cert.h"], - cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", -) - -genrule( - name = "drm_ca_root_prod_cert", - srcs = ["drm_ca_root_prod.cert"], - outs = ["drm_ca_root_prod_cert.h"], - cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", + hdrs = ["root_oem_certificates.h"], ) genrule( @@ -61,9 +37,9 @@ genrule( ) cc_library( - name = "test_certificates", + name = "test_oem_certificates", srcs = [ - "test_certificates.cc", + "test_oem_certificates.cc", ":backwards_chain_der", ":expired_2000_chain_der", ":invalid_chain_der", @@ -71,7 +47,7 @@ cc_library( ":sysid_2001_chain_der", ":sysid_2001_public_key_der", ], - hdrs = ["test_certificates.h"], + hdrs = ["test_oem_certificates.h"], ) genrule( diff --git a/provisioning_sdk/internal/certificates/drm_ca_root_dev.cert b/provisioning_sdk/internal/certificates/drm_ca_root_dev.cert deleted file mode 100644 index bbd5a823798b81104f80951e0981ed292dc7e935..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 802 zcmV+-1Ks=zoC62|5&-}h!<2}N1tN|EFoFS!0)hd70Kfn?Z;t8DhTF~EPW{bAzipNU z0Jw7hoU}Qp*uV*Mz$9upO*TK8Uz68A5(rQZRrB!~?ie9%>b43>3!HK{>kDZ7QbuCXqM2smD|FplubKnI zU6cFGMVjvWdl;0#jDj?O0umwoK*3PS^nBHpzEyub9dI0)M$G#j3JQG}BsL`Ump4v%$KnzKNORZJVjnuPO+3^A5@2v%gUc#q{$Pdk7Ja zVIN^++ZBXciqR-Q-o-Mtt*vED_4m<`6kWoFn9S+sl|tb@{C~tr?VRLEKLZ;Lp!@YW zNz&h=h_ZH*2p|~#+;VJgzhO56+vRd-;F&ME;4ZtLmNsFkEu@p^P*~<+;S|rThM0dm zXBN)5aF9A$)ZyYan7@-j5wb+GWWwhOMYnITy>94d`i<23OYf+iiVE2jcL@=m!%!4# zMFIl>009z!0~Y~cB;oyaU>96oZ=ZkP?(p}0vQT9{tAyvHuwtb!|r zR(r1U*xIJo`fYG3F3)N2SCOZxKov?!8@x8V9F#LQVIrL(X7Gq!d_1N9fzKL5p)23X zJ=Mp@I(4im`u$tl^^e)#2&9b}N~C#iNA?Hcy}vLz`Srk%ZEz<}b(n1W=t5YTsznSz3~nb_SJGR8J#{ah^W*@}kw_)VnDUWpL8a); zWT!58$9950_*aUZcqOg#8jJjcnwW>=RX!Z2fREO8s{_XcX2YONGaos08!Er%u%Bze z*3t|+<%*Jx+5i9m diff --git a/provisioning_sdk/internal/certificates/drm_ca_root_prod.cert b/provisioning_sdk/internal/certificates/drm_ca_root_prod.cert deleted file mode 100644 index 01987e395379bfe189cffce274205df1002d4626..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 802 zcmV+-1Ks=zoC62|5&-}h-IR!n1tN|EFoFS!0)hd70JQ!&!)1^I+dL7M3Fv6e^21)6 z^5;z>u@KvbW#8QUpEaOY15-@)Z8d$Sc-yl!(m$L)sg)*jf4IJ8-^C%Acoi0CT`xus zL(7VUIjj4yHGm?%G$V_vQ(JQEOSMR8Q$9yu{R#c2b`y^elGaS$u6C)q*&-0BopD9h z*Wno@g`88Gm+OSr%&3ThAaEui{eD1DC*mcOZ@fNop!&hRDYQ+3T*0)53OV z8DsqYf=Da3vNO4T>`S_dA1An*h)9I07?KWU^~h`y|7$knbTrD0GqL%&jXrU0Q=InsxbObUPyOa7uq#2>lr6 z=PTq#ClV33BkWj{JggGZh;2bV#r_=Jx%kvkJ_qFkZ>JLI68E*vrj}Jdc(i1KP|`DB zkpcq&009z!16c9aO#}&h-`DS5I|4Zu`V=;GN;qeQR}!Zb?ll(GJ-Yv z2+d_dSm=Ez+5}jF^*3p}^IBJg7ke^?aI&y=oU+3e(GeypBXyZRxpyKm4ZIo|!PRAu zc2#r_4rxtaO^+F`-_{l}l&F$lPrQ0D_^ytvGq>q5(2NR-Y=brUih;8J9EhwcX2t)# z<`>Xn3*3rD_pm-%$R0{0FKngb9EcQy)y3~#pFM>|bK+}IQddm-l5A#miq;JquJi1# zQ6d9f3NB-loC^UUsHuc57s{@4>>?X^uJ)jEEZ+x5+lYoR6nG&KB8aTlRCM|CCc>pw z$W~7PDIExVdi#}$!|s?=o;+)m1rrkXak;A#!wj272zxy;YG}$HoRDJ0?GU*i7bVV+ gxoyqFM)jj&6LbQnVx;<2+1}5h=74ln21(Td3~h0F(f|Me diff --git a/provisioning_sdk/internal/certificates/drm_ca_root_test.cert b/provisioning_sdk/internal/certificates/drm_ca_root_test.cert deleted file mode 100644 index 175ada4f04000afbf8ccfd074630fd99c42ddf49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 799 zcmV+)1K|7$nF9y_5&-}hxnLrW12BRCiUNWGfdHjq2j9qqbm1zuyTEvMz7spM2ITWtXPROQZBQdhSrOw~0-q$gu)8s!?rJQwF#q82lkPgMvMz+6M?z7%`n!;qEO$ljEc@(T z*vd(iQ(ZT&hDX}sz3^WLQ;HDKn$Umg@Alg29xhbuMBX~#$lDj_$vS}%O}8xxgR3yN z^&10*A*uDs6(+Aa^IZXb;~7-ZE)W)+?!&#z+X8~nU<*QWh3woxeDO5cCn5Q!fN{1T`7K78lla;H%z`;lW9049L!H zhfoBy*Cg~V3=uW5XN4O$|1WX%Fc=v1?H*C4V71kK1^`!ghkzmLrcM)QnNat+0^-H2n&NXb@3{v z&h|e_gwPE7@()5N8XA(PeQ&K-f - -namespace widevine { - -// This class contains DRM and OEM root certificates. -class RootCertificates { - public: - RootCertificates(); - ~RootCertificates(); - - const std::string& drm_root_test_certificate() const { - return drm_root_test_certificate_; - } - - const std::string& drm_root_dev_certificate() const { - return drm_root_dev_certificate_; - } - - const std::string& drm_root_prod_certificate() const { - return drm_root_prod_certificate_; - } - - const std::string& oem_root_dev_certificate() const { - return oem_root_dev_certificate_; - } - - const std::string& oem_root_prod_certificate() const { - return oem_root_prod_certificate_; - } - - private: - RootCertificates(const RootCertificates&) = delete; - RootCertificates& operator=(const RootCertificates&) = delete; - - std::string drm_root_test_certificate_; - std::string drm_root_dev_certificate_; - std::string drm_root_prod_certificate_; - std::string oem_root_dev_certificate_; - std::string oem_root_prod_certificate_; -}; - -} // namespace widevine - -#endif // PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_CERTIFICATES_H_ - diff --git a/provisioning_sdk/internal/certificates/root_oem_certificates.cc b/provisioning_sdk/internal/certificates/root_oem_certificates.cc new file mode 100644 index 0000000..7f20446 --- /dev/null +++ b/provisioning_sdk/internal/certificates/root_oem_certificates.cc @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/certificates/root_oem_certificates.h" + +#include "provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.h" +#include "provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.h" + +namespace widevine { + +RootOemCertificates::RootOemCertificates() + : oem_root_dev_certificate_( + oem_ca_root_dev_cert_der, + oem_ca_root_dev_cert_der + oem_ca_root_dev_cert_der_len), + oem_root_prod_certificate_( + oem_ca_root_prod_cert_der, + oem_ca_root_prod_cert_der + oem_ca_root_prod_cert_der_len) {} + +RootOemCertificates::~RootOemCertificates() {} + +} // namespace widevine diff --git a/provisioning_sdk/internal/certificates/root_oem_certificates.h b/provisioning_sdk/internal/certificates/root_oem_certificates.h new file mode 100644 index 0000000..e13c7c7 --- /dev/null +++ b/provisioning_sdk/internal/certificates/root_oem_certificates.h @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_OEM_CERTIFICATES_H_ +#define PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_OEM_CERTIFICATES_H_ + +#include + +namespace widevine { + +// This class contains development and production OEM root certificates. +class RootOemCertificates { + public: + RootOemCertificates(); + ~RootOemCertificates(); + + const std::string& oem_root_dev_certificate() const { + return oem_root_dev_certificate_; + } + + const std::string& oem_root_prod_certificate() const { + return oem_root_prod_certificate_; + } + + private: + RootOemCertificates(const RootOemCertificates&) = delete; + RootOemCertificates& operator=(const RootOemCertificates&) = delete; + + std::string oem_root_dev_certificate_; + std::string oem_root_prod_certificate_; +}; + +} // namespace widevine + +#endif // PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_OEM_CERTIFICATES_H_ diff --git a/provisioning_sdk/internal/certificates/sysid-2001-chain.der b/provisioning_sdk/internal/certificates/sysid-2001-chain.der index 2b935beb80996d6e803b7f5cf3b0bd4597e4dd2c..db1c6502a77f8df1c011e6d8d8c7a57368c5edbc 100644 GIT binary patch delta 1670 zcmZwFc|6m990zba95WlsHaccRB|5${IdUYw{gfiaj^sy<$)@GVkze&#TZ9PN_8 z?moMK0I7qExfI@`}KNspHn#ptw~)UeC$ z1>BcnN^v8Y@YP70+m=l^yZBC#H}moE3<}T6oCO`vS!MkkOU<8wgkcGl!usr3wzu-V zA(^!J^!#y*cvaodkqOrys;}*fI4NVgJsVHYg#WM|)+Zp{7}>V}WN6Y=C#m7IiK<$E zqL=Rzn`bzAq?yqecloNz8jk}n^z3F=ADQb+{<8rAs`h{DFyHWaG(DOhlFCG=!^u?hlgFYZ|hB~ zT64skW>6IDbpZdRHiqg0*wg6w@?oREQGL+8SYcqZg{@h1Z-emODNema;7RVR?#{R-H3D<{!T< zcP^9(+-w(i2Oet2T##=!5H~z~gRUh+I%sbY&n5=irR|@GHE)u|R(bjzYM4k0K+jFK zG7<-OZ6?AaMan`?I+pFJK4xdQv#a)Yo$k593+G?HkH2E~V~ulH=^(3&EPTtYHM}a} zBv|RjZqy)2a%_)34=kCaTqm8tcR+K`mC;%^QX~6%sz`IkG;J1xetaj|cd=W*lT_G_ zhIv&_dAYrE+~nT-e4C3ns`-_rR)hPMH_g9XVkyVBK-uTd*taxjuAc+AQ1NHi)w}}8 zPq@Em5-ov$)BF;@kw*Q8^jDTOEDUjC>&ilHH5X#JaWlv(KL9%H`39a&aOApP9+nm0h33) zC9tu!r<&W7h*@_i(WB|JsULDC4QtJ*`O_I2>Y5Z{vDFQMfdGmc7UT~f05KTp9f&=Z zTk%MlM@jq>sbZb1-91mwOeID)kw6jkW~O6t>y{{oXtRgQJbXmr{fi&tLXy~>Cco|( z4^jFxHj@u7HKNQ`@BPLy1PJ;U%l}okph{)Wq!5~PginTree&{8OXQRiuu7C`f8BSE zq2#rO?t0n|OZ_c{9cj__-dNDONh8Ihj^Q=OPFFj0cnp_i=YCl3Gnv1r9iyYpO+AV@ z)#=U%f9C3?kvFkpiq3(NrW$w7&Ic5qIS})!M6~Nu2`QC>z0b7f>^XhdO{=}^W%SSN z;mOKHf1fC~?BtW@z`kR1TMnttOzH2LHZsC~)a_C0jcdDTZ@#U;li~$8FCEV%L9z?7 zoa=>p@=*V(IFInIQwQ7NH*X0gO@4Rj{K}RFR9IHTDHF{En8Ul$9z{1}`t?d;L! zCVelvxy(T@|Wx5D@dEB&pVU$ODl5}SLS z&o@ZC)A_oJ+mBy0Iq?mZd5Eap(P48bjKg`bfV&LVTH3PZEVm^&1DRehu0xmwwaeX= z7IafL>0`&5ArqQ{W1IW4D()VaFGKh{JlOeSgyn>lg)doTy&$7cxb@Uajxw9qkIa+9 c)p@=Ko_2-<#n~LW$C$UqTgB>y5*32VAL*d=$p8QV delta 1833 zcmbW0e>~Is9>@3H{FpP7U!%y6jZ_o+F2;dWMJ-6GAhs$E_ek{eh1+4(r4&lvGg5g?+CL05;Va* zJN%8jvU3Q7K@ZnG{zG6u1d|>f5^xc(2rBHD2zgmM|9}WOGZxfVN8-&uB4~jp5b&ml zz0{G1chLMRg0%k`FJtq^cr8%#OT6mWcqjkx$e``8NM=ZQFkTH*{qh0if9V;X0Gd6` zSjG{uN56#?)m!()7VKI~#3)d>h7Jx(5XTJ`EQuo>? zIQQy_X*a!N7t%fY`KE>Y4tJfNvw>Y?o4xCRoo}rb4G%W_O7uer_bPTnJzQGVQ~HhQ z6eOa!&BQ$}Z3w>nVe!J_YpOS|)w4&@vJ=jw&)PA=d23RzqD&HbKSMu3gH#%Z(mzr+ zmpyj(Rp?5e4>8NUg?6l^u}?0}&+~&eM%Uo##Rc;NeS-t#zvH{sCFkd-m~_t=ojZz` zG4^c*)r6C#J~_h&XhbuWUo?OUO3oF2rkSpCfnzLT zYIIm{t;e+ip>4gCMeW%YWA;gx=KRjMsfIaQS4t-BSN?^3ZPWEr_B9(Lqj0_7CJUE# zOWHn9*{&ne~h|66lA9Lazz!K;0u`SUCy=vRBA}T+-a~P!u8PP72(gr z#;f^CpyT;ooqVw zp_rYEY?F5*7=s+CrtP^DWh}w+*k6lkN;CogP}G0opV-Mx@&4bLZ~zR5%*3m!7Y502P8vQ-)B&@mlFU%>{K zcUCb+H^YovJtGI7P1NhoX=Ss`*WrY7;!LOXT@PybCcfYFi-ITl-nVcrZT3ns3x{Q))kOS18?HpGKP7^$cs4B#9O3UH9}tlE|y9 zpK>)OTc4Nf(MM6}tjQIVJTCv8XNE&}zDN0hHHymN>}^l{9FrsP$Ckmqw3G&Z4{B|n zR7*ihO-glw5p+3TK+fisbgGbEJ~}26jPYrvvan!2cz)q;~^v)5Z*|TXac6F2u?tAO?C!5B1@G(3f zEGZ$*W6#r{d8ML^JWTGD|Pkyoz)mvaCppDuqQi9P2WqS>z>2dj4R1gf|Pv zOu*U3;_0Xnc&=jp2uD1SavqpfezA%1<5zWHB+i(X)4j_4GT4YXpk2~1x4FwR3u`ly z;}sK49kg1F_7p#S+DAFQUn4!~t`g#eJ^Rj#VK#eps>^^!wX6$pErT)5eS>hfJ6r^m zb%H+ma$2PQN5#JNOK-M%4+#`bYWH+aFk%7A;@yJ&*j_b&97ThhTeUf|>Kz*>+Z(Ol{C*8~EPCpqEYq@eaZs49P^sp55IXU%zbVYPD4 zPd@$*gzGM2mBE=qVx3P1Th07c5b(c~BJSoe^OVdlrO?+t4lVmbQJLyM9xaLFqCWglJr0AX;H5zX9u&o=Twyju zvj_!S6y5<0GCZ1*08pqM%%6`nx59M@GK&y8hWXccqn&gR#w4+SrN3Xo*A=53;1z2d z4mi3ryp>0kKya*?9}A9FlQt`D1m~3+H`*3gneGE<7BQ;Bw~}>J{PrMKMEKN=cooMQ zw%%;$NnNj~PH%kTYE0GVjE@UlItkXZM62L6Qha8Gq(Ijo>?8#S*@RJA(m2%}>ToI{ z=7RmCMjTIBZYe#kxQ_039~CptlpTjWH^qtQeTB2U=wfPwfBu?lw1E91&$$6a*@PQ%@M~Ev)N%8Pg$c8vj4XmV9u+cA7tK8&MBSdkLSIn3+pCvNHQRqSgbALAK!UJu;u*%ZT zwbwXc3%nc<#R6AQzZZwhEpJB+3!%U>ReX=o2=nalanFeqtckSNAqU(}q}9P9(wp}L z1E7^UJ|}P_ei}NYYZW9tvFMlDv)*d-$y_*=H@;*&sUD9O&v zrvEX-`1x(=;&N|TcptJ5fFel1ovSrWG+dd|C}cP`6ObAKryFFqs68Nxhu*B(chd76 UO3Ija2M9Z%XlVYE0s{d60f_~C5C8xG diff --git a/provisioning_sdk/internal/certificates/test_certificates.cc b/provisioning_sdk/internal/certificates/test_oem_certificates.cc similarity index 89% rename from provisioning_sdk/internal/certificates/test_certificates.cc rename to provisioning_sdk/internal/certificates/test_oem_certificates.cc index a03172a..0e33f56 100644 --- a/provisioning_sdk/internal/certificates/test_certificates.cc +++ b/provisioning_sdk/internal/certificates/test_oem_certificates.cc @@ -1,12 +1,12 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 "provisioning_sdk/internal/certificates/test_certificates.h" +#include "provisioning_sdk/internal/certificates/test_oem_certificates.h" #include "provisioning_sdk/internal/certificates/backwards_chain.h" #include "provisioning_sdk/internal/certificates/expired_2000_chain.h" @@ -17,7 +17,7 @@ namespace widevine { -TestCertificates::TestCertificates() +TestOemCertificates::TestOemCertificates() : single_certificate_chain_der_( single_cert_chain_der, single_cert_chain_der + single_cert_chain_der_len), @@ -35,6 +35,6 @@ TestCertificates::TestCertificates() invalid_certificate_chain_der_( invalid_chain_der, invalid_chain_der + invalid_chain_der_len) {} -TestCertificates::~TestCertificates() {} +TestOemCertificates::~TestOemCertificates() {} } // namespace widevine diff --git a/provisioning_sdk/internal/certificates/test_certificates.h b/provisioning_sdk/internal/certificates/test_oem_certificates.h similarity index 73% rename from provisioning_sdk/internal/certificates/test_certificates.h rename to provisioning_sdk/internal/certificates/test_oem_certificates.h index 543257d..d612cf8 100644 --- a/provisioning_sdk/internal/certificates/test_certificates.h +++ b/provisioning_sdk/internal/certificates/test_oem_certificates.h @@ -1,24 +1,24 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_ -#define PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_ +#ifndef PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_OEM_CERTIFICATES_H_ +#define PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_OEM_CERTIFICATES_H_ #include namespace widevine { -// This class contains DER-encoded test certificates. The keys for these +// This class contains DER-encoded test OEM certificates. The keys for these // certificates came from common/rsa_test_keys.h. -class TestCertificates { +class TestOemCertificates { public: - TestCertificates(); - ~TestCertificates(); + TestOemCertificates(); + ~TestOemCertificates(); const std::string& single_certificate_chain_der() const { return single_certificate_chain_der_; @@ -45,8 +45,8 @@ class TestCertificates { } private: - TestCertificates(const TestCertificates&) = delete; - TestCertificates& operator=(const TestCertificates&) = delete; + TestOemCertificates(const TestOemCertificates&) = delete; + TestOemCertificates& operator=(const TestOemCertificates&) = delete; std::string single_certificate_chain_der_; // leaf + intermediate certificates. @@ -60,4 +60,4 @@ class TestCertificates { } // namespace widevine -#endif // PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_ +#endif // PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_OEM_CERTIFICATES_H_ diff --git a/provisioning_sdk/internal/oem_device_cert.cc b/provisioning_sdk/internal/oem_device_cert.cc index edc99f2..47cdfe0 100644 --- a/provisioning_sdk/internal/oem_device_cert.cc +++ b/provisioning_sdk/internal/oem_device_cert.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -11,10 +11,12 @@ #include #include "glog/logging.h" +#include "absl/memory/memory.h" #include "openssl/asn1.h" +#include "openssl/pkcs7.h" #include "openssl/x509.h" #include "openssl/x509v3.h" -#include "provisioning_sdk/internal/certificates/root_certificates.h" +#include "provisioning_sdk/internal/certificates/root_oem_certificates.h" namespace widevine { namespace { @@ -29,7 +31,7 @@ bool ExtractPublicKey(X509* x509, std::unique_ptr* public_key) { LOG(WARNING) << "X509_get_pubkey failed."; return false; } - public_key->reset(new RsaPublicKey(EVP_PKEY_get1_RSA(pkey.get()))); + *public_key = absl::make_unique(EVP_PKEY_get1_RSA(pkey.get())); return true; } @@ -105,12 +107,12 @@ OemDeviceCert::OemDeviceCert() {} OemDeviceCert::~OemDeviceCert() {} bool OemDeviceCert::Initialize(CertificateType certificate_type) { - RootCertificates root_certificates; + RootOemCertificates root_certificates; switch (certificate_type) { - case kCertTesting: - case kCertDevelopment: + case kCertificateTypeTesting: + case kCertificateTypeDevelopment: return Initialize(root_certificates.oem_root_dev_certificate()); - case kCertProduction: + case kCertificateTypeProduction: return Initialize(root_certificates.oem_root_prod_certificate()); default: LOG(WARNING) << "Invalid certificate type " << certificate_type; diff --git a/provisioning_sdk/internal/oem_device_cert.h b/provisioning_sdk/internal/oem_device_cert.h index 17d5cf3..0755723 100644 --- a/provisioning_sdk/internal/oem_device_cert.h +++ b/provisioning_sdk/internal/oem_device_cert.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -14,9 +14,9 @@ #include #include +#include "common/certificate_type.h" #include "common/openssl_util.h" #include "common/rsa_key.h" -#include "provisioning_sdk/public/certificate_type.h" namespace widevine { diff --git a/provisioning_sdk/internal/oem_device_cert_test.cc b/provisioning_sdk/internal/oem_device_cert_test.cc index 69405e8..549fbb0 100644 --- a/provisioning_sdk/internal/oem_device_cert_test.cc +++ b/provisioning_sdk/internal/oem_device_cert_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -8,19 +8,20 @@ #include "provisioning_sdk/internal/oem_device_cert.h" -#include "gtest/gtest.h" -#include "provisioning_sdk/internal/certificates/test_certificates.h" +#include "testing/gunit.h" +#include "common/rsa_key.h" +#include "provisioning_sdk/internal/certificates/test_oem_certificates.h" namespace widevine { class OemDeviceCertTest : public ::testing::Test { protected: void SetUp() override { - ASSERT_TRUE(oem_device_cert_.Initialize(kCertTesting)); + ASSERT_TRUE(oem_device_cert_.Initialize(kCertificateTypeTesting)); } OemDeviceCert oem_device_cert_; - TestCertificates test_certificates_; + TestOemCertificates test_oem_certificates_; }; TEST_F(OemDeviceCertTest, EmptyCertificateChain) { @@ -45,7 +46,7 @@ TEST_F(OemDeviceCertTest, OnlyOneCertificateInCertificateChain) { uint32_t system_id; std::string oem_ca_serial_number; EXPECT_FALSE(oem_device_cert_.VerifyCertificateChain( - test_certificates_.single_certificate_chain_der(), &leaf_public_key, + test_oem_certificates_.single_certificate_chain_der(), &leaf_public_key, &system_id, &oem_ca_serial_number)); } @@ -54,11 +55,11 @@ TEST_F(OemDeviceCertTest, ValidCertificateChain) { uint32_t system_id; std::string oem_ca_serial_number; ASSERT_TRUE(oem_device_cert_.VerifyCertificateChain( - test_certificates_.valid_certificate_chain_der(), &leaf_public_key, + test_oem_certificates_.valid_certificate_chain_der(), &leaf_public_key, &system_id, &oem_ca_serial_number)); std::unique_ptr public_key(RsaPublicKey::Create( - test_certificates_.valid_certificate_public_key_der())); + test_oem_certificates_.valid_certificate_public_key_der())); ASSERT_TRUE(public_key); EXPECT_TRUE(leaf_public_key->MatchesPublicKey(*public_key)); EXPECT_EQ(2001u, system_id); @@ -70,7 +71,7 @@ TEST_F(OemDeviceCertTest, ExpiredCertificateChain) { uint32_t system_id; std::string oem_ca_serial_number; ASSERT_FALSE(oem_device_cert_.VerifyCertificateChain( - test_certificates_.expired_certificate_chain_der(), &leaf_public_key, + test_oem_certificates_.expired_certificate_chain_der(), &leaf_public_key, &system_id, &oem_ca_serial_number)); } @@ -79,8 +80,8 @@ TEST_F(OemDeviceCertTest, OutOfOrderCertificateChain) { uint32_t system_id; std::string oem_ca_serial_number; ASSERT_FALSE(oem_device_cert_.VerifyCertificateChain( - test_certificates_.backwards_certificate_chain_der(), &leaf_public_key, - &system_id, &oem_ca_serial_number)); + test_oem_certificates_.backwards_certificate_chain_der(), + &leaf_public_key, &system_id, &oem_ca_serial_number)); } TEST_F(OemDeviceCertTest, CertificateChainNotSignedByRoot) { @@ -88,7 +89,7 @@ TEST_F(OemDeviceCertTest, CertificateChainNotSignedByRoot) { uint32_t system_id; std::string oem_ca_serial_number; ASSERT_FALSE(oem_device_cert_.VerifyCertificateChain( - test_certificates_.invalid_certificate_chain_der(), &leaf_public_key, + test_oem_certificates_.invalid_certificate_chain_der(), &leaf_public_key, &system_id, &oem_ca_serial_number)); } diff --git a/provisioning_sdk/internal/provisioning30_session_impl.cc b/provisioning_sdk/internal/provisioning30_session_impl.cc new file mode 100644 index 0000000..fed7f87 --- /dev/null +++ b/provisioning_sdk/internal/provisioning30_session_impl.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 "provisioning_sdk/internal/provisioning30_session_impl.h" + +#include + +#include "glog/logging.h" +#include "common/aes_cbc_util.h" +#include "common/random_util.h" +#include "common/rsa_key.h" +#include "common/sha_util.h" +#include "provisioning_sdk/internal/provisioning_engine_impl.h" +#include "provisioning_sdk/public/provisioning_status.h" + +#define LOG_EVERY_N_WITH_PROTO(message, proto) \ + LOG_EVERY_N(WARNING, FLAGS_prov_sdk_log_every_n) \ + << (message) << " [proto: " << (proto).ShortDebugString() << "]" + +namespace widevine { + +Provisioning30SessionImpl::Provisioning30SessionImpl( + const ProvisioningEngineImpl& engine, const OemDeviceCert& oem_device_cert, + const RsaPrivateKey& service_private_key) + : ProvisioningSessionImpl(engine), + oem_device_cert_(oem_device_cert), + service_private_key_(service_private_key) {} + +ProvisioningStatus Provisioning30SessionImpl::ProcessMessage( + const std::string& message, std::string* response, bool* done) { + SignedProvisioningMessage signed_request; + ProvisioningRequest request; + + if (!ValidateAndDeserializeRequest(message, &signed_request, &request)) + return INVALID_REQUEST_MESSAGE; + + ClientIdentification client_id; + if (request.has_encrypted_client_id()) { + if (!DecryptClientIdentification(request.encrypted_client_id(), &client_id)) + return INVALID_REQUEST_MESSAGE; + } else { + DCHECK(request.has_client_id()); + client_id.Swap(request.mutable_client_id()); + } + + if (client_id.type() != ClientIdentification::OEM_DEVICE_CERTIFICATE) { + LOG_EVERY_N_WITH_PROTO("Invalid client_id type", client_id); + return INVALID_REQUEST_MESSAGE; + } + if (client_id.token().empty()) { + LOG_EVERY_N_WITH_PROTO("Missing client_id.token", client_id); + return INVALID_REQUEST_MESSAGE; + } + + std::unique_ptr cert_public_key; + uint32_t system_id; + std::string oem_ca_serial_number; + if (!oem_device_cert_.VerifyCertificateChain(client_id.token(), + &cert_public_key, &system_id, + &oem_ca_serial_number)) { + LOG_EVERY_N_WITH_PROTO("Invalid token", client_id); + return INVALID_REQUEST_MESSAGE; + } + if (!cert_public_key->VerifySignature(signed_request.message(), + signed_request.signature())) { + LOG_EVERY_N_WITH_PROTO("Signature verification failed", client_id); + return INVALID_REQUEST_MESSAGE; + } + + // Save device_info for query later. + device_info_ = engine_.GetDeviceInfo(system_id); + + std::string certificate_serial_number; + if (request.has_spoid()) { + certificate_serial_number = request.spoid(); + } else { + // Generate stable serial number. + const std::string stable_data(client_id.token() + request.stable_id() + + request.provider_id() + + engine_.secret_spoid_sauce()); + const std::string hash = Sha256_Hash(stable_data); + const size_t RootCertificateSerialNumberSize = 16; + certificate_serial_number = hash.substr(0, RootCertificateSerialNumberSize); + } + + ProvisioningResponse provisioning_response; + ProvisioningStatus status = GenerateProvisioningResponse( + system_id, oem_ca_serial_number, request.provider_id(), + certificate_serial_number, *cert_public_key, &provisioning_response); + if (status != OK) return status; + provisioning_response.set_nonce(request.nonce()); + + // Sign the response. + SignedProvisioningMessage signed_message; + if (!provisioning_response.SerializeToString( + signed_message.mutable_message())) { + LOG(WARNING) << "Error serializing ProvisioningResponse."; + return INTERNAL_ERROR; + } + if (!service_private_key_.GenerateSignature( + signed_message.message(), signed_message.mutable_signature())) { + LOG(WARNING) << "Failed to sign ProvisioningResponse."; + return INTERNAL_ERROR; + } + if (!signed_message.SerializeToString(response)) { + LOG(WARNING) << "Error serializing SignedProvisioningMessage."; + return INTERNAL_ERROR; + } + + *done = true; + return OK; +} + +bool Provisioning30SessionImpl::ValidateAndDeserializeRequest( + const std::string& message, SignedProvisioningMessage* signed_request, + ProvisioningRequest* request) const { + if (!signed_request->ParseFromString(message)) { + LOG_EVERY_N(WARNING, FLAGS_prov_sdk_log_every_n) + << "Failed to parse SignedProvisioningMessage."; + return false; + } + VLOG(1) << "signed_request: " << signed_request->ShortDebugString(); + + if (signed_request->message().empty()) { + LOG_EVERY_N_WITH_PROTO("Missing message", *signed_request); + return false; + } + if (signed_request->signature().empty()) { + LOG_EVERY_N_WITH_PROTO("Missing signature", *signed_request); + return false; + } + if (!request->ParseFromString(signed_request->message())) { + LOG_EVERY_N_WITH_PROTO("Failed to parse ProvisioningRequest", + *signed_request); + return false; + } + + if (request->has_encrypted_client_id()) { + const EncryptedClientIdentification& encrypted_client_id = + request->encrypted_client_id(); + if (encrypted_client_id.encrypted_client_id().empty()) { + LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id", + encrypted_client_id); + return false; + } + if (encrypted_client_id.encrypted_client_id_iv().empty()) { + LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id_iv", + encrypted_client_id); + return false; + } + if (encrypted_client_id.encrypted_privacy_key().empty()) { + LOG_EVERY_N_WITH_PROTO("Missing encrypted_privacy_key", + encrypted_client_id); + return false; + } + } else if (!request->has_client_id()) { + LOG_EVERY_N_WITH_PROTO("Missing clear_or_encrypted_client_id", *request); + return false; + } + + const size_t kMinimumRequiredNonceLength = 4; + if (request->nonce().size() < kMinimumRequiredNonceLength) { + LOG_EVERY_N_WITH_PROTO("Missing or invalid nonce", *request); + return false; + } + return true; +} + +bool Provisioning30SessionImpl::DecryptClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id) { + std::string privacy_key; + if (!service_private_key_.Decrypt(encrypted_client_id.encrypted_privacy_key(), + &privacy_key)) { + LOG_EVERY_N_WITH_PROTO("Failed to decrypt encrypted_privacy_key", + encrypted_client_id); + return false; + } + 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()) { + LOG_EVERY_N_WITH_PROTO("Failed to decrypt client_id", encrypted_client_id); + return false; + } + if (!client_id->ParseFromString(serialized_client_id)) { + LOG_EVERY_N_WITH_PROTO("Failed to parse client_id", encrypted_client_id); + return false; + } + return true; +} + +ProvisioningStatus Provisioning30SessionImpl::GenerateProvisioningResponse( + uint32_t system_id, const std::string& oem_ca_serial_number, + const std::string& provider_id, const std::string& certificate_serial_number, + const RsaPublicKey& cert_public_key, ProvisioningResponse* response) { + ProvisioningStatus status = engine_.GenerateProviderDeviceDrmCertificate( + system_id, oem_ca_serial_number, provider_id, device_drm_public_key_, + certificate_serial_number, response->mutable_device_certificate()); + if (status != OK) return status; + + const size_t kAesKeySize = 16; + const size_t kIvSize = 16; + + // Encrypt private key. + std::string message_key; + if (!RandomBytes(kAesKeySize, &message_key)) { + LOG(WARNING) << "Failed to generate message_key."; + return INTERNAL_ERROR; + } + std::string iv; + if (!RandomBytes(kIvSize, &iv)) { + LOG(WARNING) << "Failed to generate iv."; + return INTERNAL_ERROR; + } + response->set_device_rsa_key_iv(iv); + response->set_device_rsa_key( + crypto_util::EncryptAesCbc(message_key, iv, device_drm_private_key_)); + if (response->device_rsa_key().empty()) { + LOG(WARNING) << "Failed to encrypt device_rsa_key"; + return INTERNAL_ERROR; + } + if (!cert_public_key.Encrypt(message_key, response->mutable_wrapping_key())) { + LOG(WARNING) << "Failed to encrypt wrapping_key"; + return INTERNAL_ERROR; + } + return OK; +} + +} // namespace widevine diff --git a/provisioning_sdk/internal/provisioning30_session_impl.h b/provisioning_sdk/internal/provisioning30_session_impl.h new file mode 100644 index 0000000..8cc5e34 --- /dev/null +++ b/provisioning_sdk/internal/provisioning30_session_impl.h @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +// ProvisioningSession internal implementation. + +#ifndef PROVISIONING_SDK_INTERNAL_PROVISIONING30_SESSION_IMPL_H_ +#define PROVISIONING_SDK_INTERNAL_PROVISIONING30_SESSION_IMPL_H_ + +#include +#include +#include +#include + +#include "common/rsa_key.h" +#include "provisioning_sdk/internal/oem_device_cert.h" +#include "provisioning_sdk/internal/provisioning_session_impl.h" +#include "provisioning_sdk/public/provisioning_status.h" +#include "protos/public/certificate_provisioning.pb.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/provisioned_device_info.pb.h" + +namespace widevine { + +class ProvisioningEngineImpl; + +class Provisioning30SessionImpl : public ProvisioningSessionImpl { + public: + Provisioning30SessionImpl(const ProvisioningEngineImpl& engine, + const OemDeviceCert& oem_device_cert, + const RsaPrivateKey& service_private_key); + + // Process a message from the client device. + // * |message| is the message received from the client device. + // * |response| will contain, upon successful return, a message to be sent + // back to the client device as a response to |message|. + // * |done| will indicate, upon successful return, whether the provisioning + // exchange is complete. + // Returns OK if successful, or an appropriate error status code otherwise. + ProvisioningStatus ProcessMessage(const std::string& message, + std::string* response, + bool* done) override; + + // * Returns a ProvisioneddeviceInfo message containing information about the + // type of device being provisioned. May return nullptr. + const ProvisionedDeviceInfo* GetDeviceInfo() const override { + return device_info_.get(); + } + + private: + Provisioning30SessionImpl(const Provisioning30SessionImpl&) = delete; + Provisioning30SessionImpl& operator=(const Provisioning30SessionImpl&) = + delete; + + bool ValidateAndDeserializeRequest(const std::string& message, + SignedProvisioningMessage* signed_request, + ProvisioningRequest* request) const; + bool DecryptClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id); + ProvisioningStatus GenerateProvisioningResponse( + uint32_t system_id, const std::string& oem_ca_serial_number, + const std::string& provider_id, const std::string& certificate_serial_number, + const RsaPublicKey& cert_public_key, ProvisioningResponse* response); + + const OemDeviceCert& oem_device_cert_; + const RsaPrivateKey& service_private_key_; + std::shared_ptr device_info_; +}; + +} // namespace widevine + +#endif // PROVISIONING_SDK_INTERNAL_PROVISIONING30_SESSION_IMPL_H_ diff --git a/provisioning_sdk/internal/provisioning30_session_impl_test.cc b/provisioning_sdk/internal/provisioning30_session_impl_test.cc new file mode 100644 index 0000000..1bbc771 --- /dev/null +++ b/provisioning_sdk/internal/provisioning30_session_impl_test.cc @@ -0,0 +1,406 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/provisioning30_session_impl.h" + +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "common/aes_cbc_util.h" +#include "common/mock_rsa_key.h" +#include "common/sha_util.h" +#include "provisioning_sdk/internal/oem_device_cert.h" +#include "provisioning_sdk/internal/provisioning_engine_impl.h" + +using ::testing::_; +using ::testing::ByMove; +using ::testing::DoAll; +using ::testing::IsEmpty; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::SetArgPointee; + +namespace { +const char kEncryptedClientIdIv[] = "sixteen_bytes_iv"; +const char kPrivacyKey[] = "privacy_key_16B_"; +const char kProviderId[] = "testing_provider"; +const char kClientToken[] = "client_id_token"; +const char kDevicePublicKey[] = "device_public_key"; +const char kEncryptedPrivacyKey[] = "encrypted_privacy_key"; +const char kDevicePrivateKey[] = "device_private_key"; +const char kWrappingKey[] = "wrapping_key"; +const char kDeviceCertificate[] = "device_certificate"; +const char kNonce[] = "testing_nonce"; +const char kSignature[] = "generated_signature"; + +// Derives Stable Per-Origin IDentifiers. +std::string DeriveSpoid(const std::string& client_token, const std::string& provider_id, + const std::string& secret_sauce) { + return widevine::Sha256_Hash(client_token + provider_id + secret_sauce) + .substr(0, 16); +} + +} // namespace + +namespace widevine { + +class MockProvisioningEngineImpl : public ProvisioningEngineImpl { + public: + MOCK_CONST_METHOD6(GenerateProviderDeviceDrmCertificate, + ProvisioningStatus(uint32_t system_id, + const std::string& oem_ca_serial_number, + const std::string& provider_id, + const std::string& public_key, + const std::string& certificate_serial_number, + std::string* certificate)); +}; + +class MockOemDeviceCert : public OemDeviceCert { + public: + // gmock does not support SetArgPointee on std::unique_ptr, so we have to + // workaround it with a trick. + MOCK_CONST_METHOD4(DoVerifyCertificateChain, + bool(const std::string& certificate_chain, + RsaPublicKey** leaf_public_key, uint32_t* system_id, + std::string* oem_ca_serial_number)); + bool VerifyCertificateChain(const std::string& certificate_chain, + std::unique_ptr* leaf_public_key, + uint32_t* system_id, + std::string* oem_ca_serial_number) const override { + RsaPublicKey* raw_leaf_public_key = nullptr; + if (!DoVerifyCertificateChain(certificate_chain, &raw_leaf_public_key, + system_id, oem_ca_serial_number)) { + return false; + } + *leaf_public_key = std::unique_ptr(raw_leaf_public_key); + return true; + } +}; + +class Provisioning30SessionImplTest : public ::testing::Test { + protected: + Provisioning30SessionImplTest() + : session_impl_(mock_engine_impl_, mock_oem_device_cert_, + mock_service_private_key_) { + mock_rsa_key_factory_ = new MockRsaKeyFactory; + session_impl_.set_rsa_key_factory( + std::unique_ptr(mock_rsa_key_factory_)); + } + + Provisioning30SessionImpl session_impl_; + MockRsaKeyFactory* mock_rsa_key_factory_ = nullptr; + MockProvisioningEngineImpl mock_engine_impl_; + MockOemDeviceCert mock_oem_device_cert_; + MockRsaPrivateKey mock_service_private_key_; +}; + +class Provisioning30SessionImplProcessTest + : public Provisioning30SessionImplTest { + public: + void SetUp() override { + MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey; + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kDevicePublicKey)) + .WillOnce( + Return(ByMove(std::unique_ptr(mock_rsa_public_key)))); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs8PrivateKey(kDevicePrivateKey, IsEmpty())) + .WillOnce(Return( + ByMove(std::unique_ptr(new MockRsaPrivateKey)))); + EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_)) + .WillOnce(Return(true)); + ASSERT_EQ(OK, + session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey)); + + // Setup Provisioning Message. + client_id_.set_type(ClientIdentification::OEM_DEVICE_CERTIFICATE); + client_id_.set_token(kClientToken); + + EncryptedClientIdentification* encrypted_client_id = + prov_request_.mutable_encrypted_client_id(); + encrypted_client_id->set_encrypted_client_id(crypto_util::EncryptAesCbc( + kPrivacyKey, kEncryptedClientIdIv, client_id_.SerializeAsString())); + encrypted_client_id->set_encrypted_client_id_iv(kEncryptedClientIdIv); + encrypted_client_id->set_encrypted_privacy_key(kEncryptedPrivacyKey); + prov_request_.set_provider_id(kProviderId); + prov_request_.set_nonce(kNonce); + + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + signed_prov_message_.set_signature("testing_signature"); + } + + ClientIdentification client_id_; + ProvisioningRequest prov_request_; + SignedProvisioningMessage signed_prov_message_; +}; + +TEST_F(Provisioning30SessionImplProcessTest, InvalidMessage) { + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage("invalid_message", &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, EmptyMessage) { + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage("", &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, MissingMessage) { + signed_prov_message_.clear_message(); + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, MissingSignature) { + signed_prov_message_.clear_signature(); + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, MissingClientId) { + prov_request_.clear_encrypted_client_id(); + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, MissingEncryptedClientId) { + prov_request_.mutable_encrypted_client_id()->clear_encrypted_client_id(); + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, MissingEncryptedClientIdIv) { + prov_request_.mutable_encrypted_client_id()->clear_encrypted_client_id_iv(); + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, MissingEncryptedPrivacyKey) { + prov_request_.mutable_encrypted_client_id()->clear_encrypted_privacy_key(); + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, InvalidNonce) { + // Nonce should be at least 4 buytes. + const char kNonceWithLessThanFourBytes[] = "xx"; + prov_request_.set_nonce(kNonceWithLessThanFourBytes); + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, PrivacyKeyDecryptionFailed) { + EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) + .WillOnce(Return(false)); + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, InvalidEncryptedClientId) { + prov_request_.mutable_encrypted_client_id()->set_encrypted_client_id( + "invalid_encrypted_client_id"); + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) + .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); + + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, VerifyCertificateChainFailed) { + EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) + .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); + EXPECT_CALL(mock_oem_device_cert_, + DoVerifyCertificateChain(kClientToken, _, _, _)) + .WillOnce(Return(false)); + + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, + ClearClientIdVerifyCertificateChainFailed) { + *prov_request_.mutable_client_id() = client_id_; + prov_request_.clear_encrypted_client_id(); + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + + EXPECT_CALL(mock_oem_device_cert_, + DoVerifyCertificateChain(kClientToken, _, _, _)) + .WillOnce(Return(false)); + + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, ClearClientIdInvalidClientIdType) { + client_id_.set_type(ClientIdentification::KEYBOX); + *prov_request_.mutable_client_id() = client_id_; + prov_request_.clear_encrypted_client_id(); + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, ClearClientIdMissingToken) { + client_id_.clear_token(); + *prov_request_.mutable_client_id() = client_id_; + prov_request_.clear_encrypted_client_id(); + signed_prov_message_.set_message(prov_request_.SerializeAsString()); + + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, VerifySignatureFailed) { + EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) + .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); + MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey; + EXPECT_CALL(mock_oem_device_cert_, + DoVerifyCertificateChain(kClientToken, _, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(mock_cert_public_key), Return(true))); + EXPECT_CALL(*mock_cert_public_key, + VerifySignature(signed_prov_message_.message(), + signed_prov_message_.signature())) + .WillOnce(Return(false)); + + std::string response; + bool done; + EXPECT_EQ(INVALID_REQUEST_MESSAGE, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, GenerateDeviceCertificateFailed) { + const uint32_t kSystemId = 1234; + const char kExpectedOemSerialNumber[] = "test_oem_serial_number"; + + EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) + .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); + MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey; + EXPECT_CALL(mock_oem_device_cert_, + DoVerifyCertificateChain(kClientToken, _, _, _)) + .WillOnce(DoAll( + SetArgPointee<1>(mock_cert_public_key), SetArgPointee<2>(kSystemId), + SetArgPointee<3>(kExpectedOemSerialNumber), Return(true))); + EXPECT_CALL(*mock_cert_public_key, + VerifySignature(signed_prov_message_.message(), + signed_prov_message_.signature())) + .WillOnce(Return(true)); + + EXPECT_CALL( + mock_engine_impl_, + GenerateProviderDeviceDrmCertificate( + kSystemId, kExpectedOemSerialNumber, kProviderId, kDevicePublicKey, + DeriveSpoid(kClientToken, kProviderId, ""), _)) + .WillOnce(Return(INTERNAL_ERROR)); + + std::string response; + bool done; + EXPECT_EQ(INTERNAL_ERROR, + session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); +} + +TEST_F(Provisioning30SessionImplProcessTest, Success) { + const uint32_t kSystemId = 1234; + EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) + .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); + MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey; + EXPECT_CALL(mock_oem_device_cert_, + DoVerifyCertificateChain(kClientToken, _, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(mock_cert_public_key), + SetArgPointee<2>(kSystemId), Return(true))); + EXPECT_CALL(*mock_cert_public_key, + VerifySignature(signed_prov_message_.message(), + signed_prov_message_.signature())) + .WillOnce(Return(true)); + + EXPECT_CALL(mock_engine_impl_, GenerateProviderDeviceDrmCertificate( + kSystemId, _, _, kDevicePublicKey, _, _)) + .WillOnce(DoAll(SetArgPointee<5>(kDeviceCertificate), Return(OK))); + + std::string message_key; + EXPECT_CALL(*mock_cert_public_key, Encrypt(_, _)) + .WillOnce(DoAll(SaveArg<0>(&message_key), SetArgPointee<1>(kWrappingKey), + Return(true))); + std::string message; + EXPECT_CALL(mock_service_private_key_, GenerateSignature(_, _)) + .WillOnce(DoAll(SaveArg<0>(&message), SetArgPointee<1>(kSignature), + Return(true))); + + std::string response; + bool done; + ASSERT_EQ(OK, session_impl_.ProcessMessage( + signed_prov_message_.SerializeAsString(), &response, &done)); + + // Verify the response. + EXPECT_TRUE(done); + SignedProvisioningMessage signed_prov_message; + ASSERT_TRUE(signed_prov_message.ParseFromString(response)); + EXPECT_EQ(message, signed_prov_message.message()); + EXPECT_EQ(kSignature, signed_prov_message.signature()); + + ProvisioningResponse prov_response; + ASSERT_TRUE(prov_response.ParseFromString(message)); + EXPECT_EQ( + kDevicePrivateKey, + crypto_util::DecryptAesCbc(message_key, prov_response.device_rsa_key_iv(), + prov_response.device_rsa_key())); + EXPECT_EQ(kDeviceCertificate, prov_response.device_certificate()); + EXPECT_EQ(kNonce, prov_response.nonce()); + EXPECT_EQ(kWrappingKey, prov_response.wrapping_key()); +} + +} // namespace widevine diff --git a/provisioning_sdk/internal/provisioning_engine_impl.cc b/provisioning_sdk/internal/provisioning_engine_impl.cc index 838df9e..07dfccf 100644 --- a/provisioning_sdk/internal/provisioning_engine_impl.cc +++ b/provisioning_sdk/internal/provisioning_engine_impl.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -10,16 +10,21 @@ #include #include -#include #include #include #include #include #include "glog/logging.h" +#include "absl/synchronization/mutex.h" +#include "common/aes_cbc_util.h" +#include "common/certificate_type.h" +#include "common/crypto_util.h" +#include "common/drm_root_certificate.h" +#include "common/drm_service_certificate.h" #include "common/random_util.h" #include "common/rsa_key.h" -#include "provisioning_sdk/internal/certificates/root_certificates.h" +#include "provisioning_sdk/internal/certificates/root_oem_certificates.h" #include "provisioning_sdk/public/provisioning_status.h" #define LOG_WITH_PROTO(message, proto) \ @@ -28,68 +33,24 @@ namespace widevine { namespace { -// Verify that |certificate| is signed by |public_key|. If |public_key| is null, -// |certificate| should be self signed. -bool VerifyAndExtractCertificate(const RsaPublicKey* public_key, - const std::string& certificate, - SignedDrmDeviceCertificate* signed_drm_cert, - DrmDeviceCertificate* drm_cert) { - DCHECK(signed_drm_cert); - DCHECK(drm_cert); - if (!signed_drm_cert->ParseFromString(certificate)) { - LOG(WARNING) << "Failed to parse SignedDrmDeviceCertificate."; - return false; - } - if (signed_drm_cert->drm_certificate().empty()) { - LOG_WITH_PROTO("Missing drm_certificate", *signed_drm_cert); - return false; - } - if (signed_drm_cert->signature().empty()) { - LOG_WITH_PROTO("Missing signature", *signed_drm_cert); - return false; - } +const size_t kContextEncryptionKeySize(32); +const size_t kContextEncryptionIvSize(16); +const size_t kContextMacKeySize(32); - if (!drm_cert->ParseFromString(signed_drm_cert->drm_certificate())) { - LOG_WITH_PROTO("Failed to parse DrmDeviceCertificate", *signed_drm_cert); - return false; - } - if (drm_cert->public_key().empty()) { - LOG_WITH_PROTO("Missing public_key", *drm_cert); - return false; - } - - std::unique_ptr local_public_key; - if (!public_key) { - local_public_key.reset(RsaPublicKey::Create(drm_cert->public_key())); - if (!local_public_key) { - LOG_WITH_PROTO("Invalid root public key", *drm_cert); - return false; - } - public_key = local_public_key.get(); - } - - if (!public_key->VerifySignature(signed_drm_cert->drm_certificate(), - signed_drm_cert->signature())) { - LOG_WITH_PROTO("Signature verification failed", *signed_drm_cert); - return false; - } - return true; -} - -bool GenerateCertificate(DrmDeviceCertificate::CertificateType type, - uint32_t system_id, const std::string& provider_id, - const std::string& serial_number, const std::string& public_key, +bool GenerateCertificate(DrmCertificate::Type type, uint32_t system_id, + const std::string& provider_id, const std::string& serial_number, + const std::string& public_key, const RsaPrivateKey& signing_key, - const SignedDrmDeviceCertificate& signer, + const SignedDrmCertificate& signer, std::string* certificate) { - DCHECK(type == DrmDeviceCertificate::DRM_INTERMEDIATE || - type == DrmDeviceCertificate::DRM_USER_DEVICE); + DCHECK(type == DrmCertificate::DEVICE_MODEL || + type == DrmCertificate::DEVICE); if (serial_number.empty()) { LOG(WARNING) << "Require an non-empty serial number."; return false; } - DrmDeviceCertificate drm_cert; + DrmCertificate drm_cert; drm_cert.set_type(type); drm_cert.set_system_id(system_id); if (!provider_id.empty()) drm_cert.set_provider_id(provider_id); @@ -97,20 +58,19 @@ bool GenerateCertificate(DrmDeviceCertificate::CertificateType type, drm_cert.set_creation_time_seconds(time(nullptr)); drm_cert.set_public_key(public_key); - SignedDrmDeviceCertificate signed_cert; - if (!drm_cert.SerializeToString( - signed_cert.mutable_drm_certificate())) { - LOG(WARNING) << "Error serializing DrmDeviceCertificate."; + SignedDrmCertificate signed_cert; + if (!drm_cert.SerializeToString(signed_cert.mutable_drm_certificate())) { + LOG(WARNING) << "Error serializing DrmCertificate."; return false; } if (!signing_key.GenerateSignature(signed_cert.drm_certificate(), signed_cert.mutable_signature())) { - LOG(WARNING) << "Failed to generate signature for DrmDeviceCertificate."; + LOG(WARNING) << "Failed to generate signature for DrmCertificate."; return false; } *signed_cert.mutable_signer() = signer; if (!signed_cert.SerializeToString(certificate)) { - LOG(WARNING) << "Failed to serialize SignedDrmDeviceCertificate to string."; + LOG(WARNING) << "Failed to serialize SignedDrmCertificate to string."; return false; } return true; @@ -135,8 +95,12 @@ bool IsSerialNumberEq(const std::string& a, const std::string& b) { } // namespace +// NOTE: certificate_expiration_seconds_utc_ not initialized in initialization +// list due to bug in clang and/or CLIF which causes the initialization to be +// skipped. ProvisioningEngineImpl::ProvisioningEngineImpl() - : rsa_key_factory_(new RsaKeyFactory) {} + : rsa_key_factory_(new RsaKeyFactory), + certificate_expiration_seconds_utc_(0) {} ProvisioningEngineImpl::~ProvisioningEngineImpl() {} @@ -148,38 +112,41 @@ ProvisioningStatus ProvisioningEngineImpl::Initialize( const std::string& provisioning_private_key, const std::string& provisioning_private_key_passphrase, const std::string& secret_spoid_sauce) { - if (!LoadDrmRootPublicKey(certificate_type)) return INVALID_CERTIFICATE_TYPE; + Status status; - SignedDrmDeviceCertificate signed_drm_cert; - DrmDeviceCertificate drm_cert; - if (!VerifyAndExtractCertificate(root_public_key_.get(), - drm_service_certificate, &signed_drm_cert, - &drm_cert)) { - return INVALID_SERVICE_DRM_CERTIFICATE; + if (!drm_root_certificate_) { + status = DrmRootCertificate::CreateByType(certificate_type, + &drm_root_certificate_); + if (!status.ok()) { + LOG(ERROR) << status; + return INVALID_CERTIFICATE_TYPE; + } } - if (drm_cert.type() != DrmDeviceCertificate::SERVICE) { - LOG(WARNING) << "Expecting SERVICE certificate."; - return INVALID_SERVICE_DRM_CERTIFICATE; - } - service_public_key_ = - rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key()); - if (!service_public_key_) return INVALID_SERVICE_DRM_CERTIFICATE; - service_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey( - service_private_key, service_private_key_passphrase); - if (!service_private_key_) return INVALID_SERVICE_PRIVATE_KEY; - if (!service_public_key_->MatchesPrivateKey(*service_private_key_)) { - LOG(WARNING) << "Services public key and private key do not match."; - return INVALID_SERVICE_PRIVATE_KEY; + drm_root_public_key_ = rsa_key_factory_->CreateFromPkcs1PublicKey( + drm_root_certificate_->public_key()); + if (!drm_root_public_key_) { + LOG(ERROR) << "Failed to instiate DRM root public key."; + return INTERNAL_ERROR; } - if (!VerifyAndExtractCertificate(root_public_key_.get(), - provisioning_drm_certificate, - &signed_provisioning_cert_, &drm_cert)) { + status = DrmServiceCertificate::AddDrmServiceCertificate( + drm_root_certificate_.get(), drm_service_certificate, service_private_key, + service_private_key_passphrase); + if (!status.ok()) { + LOG(ERROR) << status; + return INVALID_SERVICE_DRM_CERTIFICATE; + } + + DrmCertificate drm_cert; + status = drm_root_certificate_->VerifyCertificate( + provisioning_drm_certificate, &signed_provisioning_cert_, &drm_cert); + if (!status.ok()) { + LOG(ERROR) << status; return INVALID_PROVISIONER_DRM_CERTIFICATE; } - if (drm_cert.type() != DrmDeviceCertificate::ROOT && - drm_cert.type() != DrmDeviceCertificate::PROVISIONER) { - LOG(WARNING) << "Expecting ROOT or PROVISIONER certificate."; + if (drm_cert.type() != DrmCertificate::ROOT && + drm_cert.type() != DrmCertificate::PROVISIONER) { + LOG(ERROR) << "Expecting ROOT or PROVISIONER certificate."; return INVALID_PROVISIONER_DRM_CERTIFICATE; } @@ -191,12 +158,12 @@ ProvisioningStatus ProvisioningEngineImpl::Initialize( if (!provisioning_private_key_) return INVALID_PROVISIONER_PRIVATE_KEY; if (!provisioning_public_key_->MatchesPrivateKey( *provisioning_private_key_)) { - LOG(WARNING) << "Provisioning public key and private key do not match."; + LOG(ERROR) << "Provisioning public key and private key do not match."; return INVALID_PROVISIONER_PRIVATE_KEY; } if (secret_spoid_sauce.empty()) { - LOG(WARNING) << "SPOID secret sauce is empty!"; + LOG(ERROR) << "SPOID secret sauce is empty!"; return INVALID_SPOID_SAUCE; } secret_spoid_sauce_ = secret_spoid_sauce; @@ -212,12 +179,13 @@ ProvisioningStatus ProvisioningEngineImpl::SetCertificateStatusList( return INVALID_STATUS_LIST; } - SignedCertificateStatusList signed_cert_status_list; + SignedDeviceCertificateStatusList signed_cert_status_list; if (!signed_cert_status_list.ParseFromString(certificate_status_list)) { - LOG(WARNING) << "Error parsing SignedCertificateStatusList."; + LOG(WARNING) << "Error parsing SignedDeviceCertificateStatusList."; return INVALID_STATUS_LIST; } - if (!root_public_key_->VerifySignature( + + if (!drm_root_public_key_->VerifySignature( signed_cert_status_list.certificate_status_list(), signed_cert_status_list.signature())) { LOG_WITH_PROTO("Signature verification failed", signed_cert_status_list); @@ -232,7 +200,7 @@ ProvisioningStatus ProvisioningEngineImpl::SetCertificateStatusList( return INVALID_STATUS_LIST; } - WriterMutexLock writer_lock(&mutex_); + absl::WriterMutexLock writer_lock(&cert_status_mutex_); if (expiration_period_seconds == 0) { certificate_expiration_seconds_utc_ = std::numeric_limits::max(); @@ -273,14 +241,14 @@ ProvisioningStatus ProvisioningEngineImpl::GenerateDrmIntermediateCertificate( rsa_key_factory_->CreateFromPkcs1PublicKey(public_key); if (!intermediate_public_key) return INVALID_INTERMEDIATE_PUBLIC_KEY; - const size_t kCertificateSerialNumberSize = 16; + const size_t RootCertificateSerialNumberSize = 16; std::string serial_number; - if (!RandomBytes(kCertificateSerialNumberSize, &serial_number)) { + if (!RandomBytes(RootCertificateSerialNumberSize, &serial_number)) { LOG(WARNING) << "Failed to generate serial_number."; return INTERNAL_ERROR; } - if (!GenerateCertificate(DrmDeviceCertificate::DRM_INTERMEDIATE, - system_id, std::string(), serial_number, public_key, + if (!GenerateCertificate(DrmCertificate::DEVICE_MODEL, system_id, std::string(), + serial_number, public_key, *provisioning_private_key_, signed_provisioning_cert_, certificate)) { return INTERNAL_ERROR; @@ -291,15 +259,16 @@ ProvisioningStatus ProvisioningEngineImpl::GenerateDrmIntermediateCertificate( ProvisioningStatus ProvisioningEngineImpl::AddDrmIntermediateCertificate( const std::string& intermediate_cert, const std::string& cert_private_key, const std::string& cert_private_key_passphrase) { - SignedDrmDeviceCertificate intermediate_signed_cert; - DrmDeviceCertificate intermediate_drm_cert; - if (!VerifyAndExtractCertificate(provisioning_public_key_.get(), - intermediate_cert, &intermediate_signed_cert, - &intermediate_drm_cert)) { + SignedDrmCertificate intermediate_signed_cert; + DrmCertificate intermediate_drm_cert; + Status status = drm_root_certificate_->VerifyCertificate( + intermediate_cert, &intermediate_signed_cert, &intermediate_drm_cert); + if (!status.ok()) { + LOG(ERROR) << status; return INVALID_INTERMEDIATE_DRM_CERTIFICATE; } - if (intermediate_drm_cert.type() != - DrmDeviceCertificate::DRM_INTERMEDIATE) { + + if (intermediate_drm_cert.type() != DrmCertificate::DEVICE_MODEL) { LOG_WITH_PROTO("Invalid device certificate type", intermediate_drm_cert); return INVALID_INTERMEDIATE_DRM_CERTIFICATE; } @@ -309,9 +278,9 @@ ProvisioningStatus ProvisioningEngineImpl::AddDrmIntermediateCertificate( } const std::string empty_oem_ca_serial_number; - ProvisioningStatus status = CheckDeviceStatus( + ProvisioningStatus provisioning_status = CheckDeviceStatus( intermediate_drm_cert.system_id(), empty_oem_ca_serial_number); - if (status != OK) return status; + if (provisioning_status != OK) return provisioning_status; auto intermediate_public_key = rsa_key_factory_->CreateFromPkcs1PublicKey( intermediate_drm_cert.public_key()); @@ -325,7 +294,7 @@ ProvisioningStatus ProvisioningEngineImpl::AddDrmIntermediateCertificate( return INVALID_INTERMEDIATE_PRIVATE_KEY; } - WriterMutexLock writer_lock(&mutex_); + absl::WriterMutexLock writer_lock(&cert_status_mutex_); auto& certificate_info = intermediate_certs_info_[intermediate_drm_cert.system_id()]; certificate_info.signed_drm_certificate.Swap(&intermediate_signed_cert); @@ -359,25 +328,25 @@ ProvisioningStatus ProvisioningEngineImpl::GenerateProviderDeviceDrmCertificate( if (status != OK) return status; std::shared_ptr intermediate_private_key; - const SignedDrmDeviceCertificate* intermediate_cert = nullptr; + const SignedDrmCertificate* intermediate_cert = nullptr; { - ReaderMutexLock reader_lock(&mutex_); + absl::ReaderMutexLock reader_lock(&cert_status_mutex_); auto info_it = intermediate_certs_info_.find(system_id); if (info_it == intermediate_certs_info_.end() || !info_it->second.private_key) { LOG(WARNING) << "Cannot find the intermediate certificate for system: " << system_id; - return MISSING_DRM_INTERMEDIATE_CERT; + return MISSING_DEVICE_MODEL_CERT; } intermediate_private_key = info_it->second.private_key; intermediate_cert = &info_it->second.signed_drm_certificate; } - if (!GenerateCertificate(DrmDeviceCertificate::DRM_USER_DEVICE, - system_id, provider_id, certificate_serial_number, - public_key, *intermediate_private_key, - *intermediate_cert, certificate)) { + if (!GenerateCertificate(DrmCertificate::DEVICE, system_id, provider_id, + certificate_serial_number, public_key, + *intermediate_private_key, *intermediate_cert, + certificate)) { return INTERNAL_ERROR; } return OK; @@ -385,7 +354,7 @@ ProvisioningStatus ProvisioningEngineImpl::GenerateProviderDeviceDrmCertificate( std::shared_ptr ProvisioningEngineImpl::GetDeviceInfo( uint32_t system_id) const { - ReaderMutexLock reader_lock(&mutex_); + absl::ReaderMutexLock reader_lock(&cert_status_mutex_); auto info_it = intermediate_certs_info_.find(system_id); if (info_it == intermediate_certs_info_.end()) { LOG(WARNING) << "Cannot find the system id in device certificate list: " @@ -395,45 +364,71 @@ std::shared_ptr ProvisioningEngineImpl::GetDeviceInfo( return info_it->second.device_info; } -bool ProvisioningEngineImpl::LoadDrmRootPublicKey( - CertificateType certificate_type) { - const std::string* root_cert_string = nullptr; - RootCertificates root_certificates; - switch (certificate_type) { - case kCertTesting: - root_cert_string = &root_certificates.drm_root_test_certificate(); - break; - case kCertDevelopment: - root_cert_string = &root_certificates.drm_root_dev_certificate(); - break; - case kCertProduction: - root_cert_string = &root_certificates.drm_root_prod_certificate(); - break; - default: - LOG(WARNING) << "Invalid certificate type " << certificate_type; - return false; - } +ProvisioningStatus ProvisioningEngineImpl::StoreContext( + const std::string& context_data, ProvisioningContext* context) const { + DCHECK(context); - SignedDrmDeviceCertificate signed_root_cert; - DrmDeviceCertificate root_cert; - if (!VerifyAndExtractCertificate(nullptr /* self signed */, *root_cert_string, - &signed_root_cert, &root_cert)) { - LOG(WARNING) << "Failed to extract root certificate."; - return false; + ProvisioningContextKeyData key_data; + if (!RandomBytes(kContextEncryptionKeySize, + key_data.mutable_encryption_key()) || + !RandomBytes(kContextEncryptionIvSize, + key_data.mutable_encryption_iv()) || + !RandomBytes(kContextMacKeySize, key_data.mutable_mac_key())) { + LOG(ERROR) << "Failed to generate random context key data."; + return INTERNAL_ERROR; } - if (root_cert.type() != DrmDeviceCertificate::ROOT) { - LOG(WARNING) << "Expecting ROOT certificate."; - return false; + context->set_context_data(crypto_util::EncryptAesCbc( + key_data.encryption_key(), key_data.encryption_iv(), context_data)); + context->set_mac(crypto_util::CreateSignatureHmacSha256( + key_data.mac_key(), context->context_data())); + if (!DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() + ->public_key() + ->Encrypt(key_data.SerializeAsString(), + context->mutable_key_data())) { + LOG(WARNING) << "Context key data encryption failed"; + return INTERNAL_ERROR; } - root_public_key_ = - rsa_key_factory_->CreateFromPkcs1PublicKey(root_cert.public_key()); - CHECK(root_public_key_); - return true; + return OK; +} + +ProvisioningStatus ProvisioningEngineImpl::RetrieveContext( + const ProvisioningContext& context, std::string* context_data) const { + DCHECK(context_data); + + std::string serialized_key_data; + if (!DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() + ->private_key() + ->Decrypt(context.key_data(), &serialized_key_data)) { + LOG(WARNING) << "Could not decrypt context key data"; + return INVALID_CONTEXT_KEY_DATA; + } + if (serialized_key_data.empty()) { + LOG(WARNING) << "Context key data is missing."; + return INVALID_CONTEXT_KEY_DATA; + } + ProvisioningContextKeyData key_data; + if (!key_data.ParseFromString(serialized_key_data)) { + LOG(WARNING) << "Invalid context key data."; + return INVALID_CONTEXT_KEY_DATA; + } + if (!crypto_util::VerifySignatureHmacSha256(key_data.mac_key(), context.mac(), + context.context_data())) { + LOG(WARNING) << "Provisioning context MAC verification failed."; + return INVALID_CONTEXT; + } + *context_data = crypto_util::DecryptAesCbc(key_data.encryption_key(), + key_data.encryption_iv(), + context.context_data()); + if (context_data->empty()) { + LOG(WARNING) << "Provisioning context decryption failed."; + return INVALID_CONTEXT; + } + return OK; } ProvisioningStatus ProvisioningEngineImpl::CheckDeviceStatus( uint32_t system_id, const std::string& oem_ca_serial_number) const { - ReaderMutexLock reader_lock(&mutex_); + absl::ReaderMutexLock reader_lock(&cert_status_mutex_); if (certificate_expiration_seconds_utc_ < time(nullptr)) return STATUS_LIST_EXPIRED; @@ -451,8 +446,8 @@ ProvisioningStatus ProvisioningEngineImpl::CheckDeviceStatus( << system_id; return DEVICE_REVOKED; } - if (certificate_status_it->second.status() != - DeviceCertificateStatus::VALID) { + if (certificate_status_it->second.status() == + DeviceCertificateStatus::STATUS_REVOKED) { LOG(WARNING) << "Device revoked " << system_id; return DEVICE_REVOKED; } diff --git a/provisioning_sdk/internal/provisioning_engine_impl.h b/provisioning_sdk/internal/provisioning_engine_impl.h index 77d9c99..c3f30bb 100644 --- a/provisioning_sdk/internal/provisioning_engine_impl.h +++ b/provisioning_sdk/internal/provisioning_engine_impl.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -17,19 +17,25 @@ #include #include -#include "base/mutex.h" +#include #include "base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "common/certificate_type.h" +#include "common/drm_root_certificate.h" #include "common/rsa_key.h" #include "provisioning_sdk/internal/oem_device_cert.h" -#include "provisioning_sdk/public/certificate_type.h" #include "provisioning_sdk/public/provisioning_status.h" -#include "protos/public/device_certificate.pb.h" +#include "protos/public/certificate_provisioning.pb.h" +#include "protos/public/device_certificate_status.pb.h" +#include "protos/public/drm_certificate.pb.h" #include "protos/public/provisioned_device_info.pb.h" -#include "protos/public/signed_device_certificate.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" namespace widevine { +class DrmRootCertificate; class ProvisioningSession; +class RsaPublicKey; class ProvisioningEngineImpl { public: @@ -55,8 +61,7 @@ class ProvisioningEngineImpl { // derivation of Stable Per-Origin IDentifiers. // * Returns OK on success, or an appropriate error status code otherwise. ProvisioningStatus Initialize( - CertificateType certificate_type, - const std::string& drm_service_certificate, + CertificateType certificate_type, const std::string& drm_service_certificate, const std::string& service_private_key, const std::string& service_private_key_passphrase, const std::string& provisioning_drm_certificate, @@ -128,24 +133,31 @@ class ProvisioningEngineImpl { const std::string& certificate_serial_number, std::string* certificate) const; // Get the device info for the given |system_id|. - std::shared_ptr GetDeviceInfo(uint32_t system_id) const; + virtual std::shared_ptr GetDeviceInfo( + uint32_t system_id) const; - // Returns the service private key. - const RsaPrivateKey* service_private_key() const { - return service_private_key_.get(); + // Encrypt, store, and sign context/state data. + virtual ProvisioningStatus StoreContext(const std::string& context_data, + ProvisioningContext* context) const; + + // Verify, decrypt, and retrieve context/state data. + virtual ProvisioningStatus RetrieveContext(const ProvisioningContext& context, + std::string* context_data) const; + + const DrmRootCertificate* drm_root_certificate() const { + return drm_root_certificate_.get(); } const OemDeviceCert& oem_device_cert() const { return oem_device_cert_; } const std::string& secret_spoid_sauce() const { return secret_spoid_sauce_; } private: friend class ProvisioningEngineImplTest; + friend class ProvisioningEngineImplProvTest; + friend class Sigma101ProvisioningSessionImplTest; ProvisioningEngineImpl(const ProvisioningEngineImpl&) = delete; ProvisioningEngineImpl& operator=(const ProvisioningEngineImpl&) = delete; - // Load DRM root public key with type |certificate_type|. - bool LoadDrmRootPublicKey(CertificateType certificate_type); - // Check device status. // If |oem_ca_serial_number| is empty, we do not care whether serial number // matches or not. @@ -153,29 +165,36 @@ class ProvisioningEngineImpl { ProvisioningStatus CheckDeviceStatus( uint32_t system_id, const std::string& oem_ca_serial_number) const; + // Inject DRM root certificate for testing. + void set_drm_root_certificate( + std::unique_ptr drm_root_certificate) { + drm_root_certificate_ = std::move(drm_root_certificate); + } + // Inject rsa_key_factory for testing. void set_rsa_key_factory(std::unique_ptr rsa_key_factory) { rsa_key_factory_ = std::move(rsa_key_factory); } std::unique_ptr rsa_key_factory_; - std::unique_ptr root_public_key_; - std::unique_ptr service_public_key_; - std::unique_ptr service_private_key_; - SignedDrmDeviceCertificate signed_provisioning_cert_; + std::unique_ptr drm_root_certificate_; + std::unique_ptr drm_root_public_key_; + SignedDrmCertificate signed_provisioning_cert_; std::unique_ptr provisioning_public_key_; std::unique_ptr provisioning_private_key_; std::string secret_spoid_sauce_; + std::string context_encryption_key_; + std::string context_mac_key_; OemDeviceCert oem_device_cert_; - mutable Mutex mutex_; + mutable absl::Mutex cert_status_mutex_; // POSIX time, in seconds, when the list would be expired. - uint32_t certificate_expiration_seconds_utc_ GUARDED_BY(mutex_) = 0; + uint32_t certificate_expiration_seconds_utc_ GUARDED_BY(cert_status_mutex_); // Maps with system_id as the key. std::map certificate_status_map_ - GUARDED_BY(mutex_); + GUARDED_BY(cert_status_mutex_); struct IntermediateCertificateInfo { - SignedDrmDeviceCertificate signed_drm_certificate; + SignedDrmCertificate signed_drm_certificate; std::shared_ptr device_info; std::shared_ptr private_key; }; diff --git a/provisioning_sdk/internal/provisioning_engine_impl_test.cc b/provisioning_sdk/internal/provisioning_engine_impl_test.cc index c3856cb..00eb510 100644 --- a/provisioning_sdk/internal/provisioning_engine_impl_test.cc +++ b/provisioning_sdk/internal/provisioning_engine_impl_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -12,14 +12,18 @@ #include #include "glog/logging.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "common/certificate_type.h" #include "common/mock_rsa_key.h" -#include "provisioning_sdk/public/certificate_type.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" +#include "common/test_drm_certificates.h" using ::testing::_; using ::testing::ByMove; using ::testing::DoAll; +using ::testing::Invoke; using ::testing::Return; using ::testing::SaveArg; using ::testing::SetArgPointee; @@ -31,7 +35,8 @@ const int kExpirationPeriodSeconds = 3600; const int kInfiniteExpirationPeriodSeconds = 0; const char kEmptyOemSerialNumber[] = ""; const char kSecretSauce[] = "Twas bryllyg, and ye slythy toves"; -const char kCertificateSerialNumber[] = "certificate_serial_number"; +const char RootCertificateSerialNumber[] = "certificate_serial_number"; +const char kDrmRootPublicKey[] = "drm_root_public_key"; const char kOemSerialNumber0[] = "oem_serial_number_0"; const char kOemSerialNumber1[] = "oem_serial_number_1"; const char kDevicePublicKey[] = "device_public_key"; @@ -40,13 +45,11 @@ const char kIntermediatePrivateKey[] = "intermediate_private_key"; const char kIntermediatePrivateKeyPassphrase[] = "intermediate_private_key_passphrase"; const char kIntermediatePublicKey[] = "intermediate_public_key"; -const char kServicePrivateKey[] = "service_private_key"; -const char kServicePrivateKeyPassphrase[] = "service_private_key_phassphrase"; -const char kServiceDrmCertificate[] = "service_drm_certificate"; +const char kServicePrivateKeyPassphrase[] = "service_private_key_passphrase"; const char kProvisioningDrmCertificate[] = "provisioning_drm_certificate"; const char kProvisioningPrivateKey[] = "provisioning_private_key"; const char kProvisioningPrivateKeyPassphrase[] = - "provisioning_private_key_phassphrase"; + "provisioning_private_key_passphrase"; const char kProvisioningPublicKey[] = "provisioning_public_key"; const char kProvisioningSignature[] = "provisioning_signature"; @@ -63,12 +66,28 @@ std::string StrCat(const std::string& in, int i) { namespace widevine { +class MockDrmRootCertificate : public DrmRootCertificate { + public: + MockDrmRootCertificate() + : DrmRootCertificate(kCertificateTypeTesting, std::string(), std::string(), + kDrmRootPublicKey, + std::unique_ptr()) {} + + MOCK_CONST_METHOD3(VerifyCertificate, + Status(const std::string& serialized_cert, + SignedDrmCertificate* signed_drm_cert, + DrmCertificate* drm_cert)); +}; + class ProvisioningEngineImplTest : public ::testing::Test { protected: ProvisioningEngineImplTest() { - mock_rsa_key_factory_ = new MockRsaKeyFactory; - engine_impl_.set_rsa_key_factory( - std::unique_ptr(mock_rsa_key_factory_)); + RsaTestKeys test_keys; + rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys.private_test_key_2_2048_bits(), kServicePrivateKeyPassphrase, + &service_private_key_); + TestDrmCertificates test_certificates; + service_certificate_ = test_certificates.test_service_certificate(); } ProvisioningStatus CheckDeviceStatus(uint32_t system_id, @@ -77,211 +96,111 @@ class ProvisioningEngineImplTest : public ::testing::Test { } ProvisioningEngineImpl engine_impl_; - MockRsaKeyFactory* mock_rsa_key_factory_ = nullptr; + std::string service_certificate_; + std::string service_private_key_; }; -TEST_F(ProvisioningEngineImplTest, InvalidCertType) { - CertificateType invalid_certificate = static_cast(100); - EXPECT_EQ( - INVALID_CERTIFICATE_TYPE, - engine_impl_.Initialize( - invalid_certificate, kServiceDrmCertificate, kServicePrivateKey, - kServicePrivateKeyPassphrase, kProvisioningDrmCertificate, - kProvisioningPrivateKey, kProvisioningPrivateKeyPassphrase, - kSecretSauce)); +TEST_F(ProvisioningEngineImplTest, InvalidDrmServiceCertificate) { + EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, + engine_impl_.Initialize( + kCertificateTypeTesting, "bad-certificate", + service_private_key_, kServicePrivateKeyPassphrase, + kProvisioningDrmCertificate, kProvisioningPrivateKey, + kProvisioningPrivateKeyPassphrase, kSecretSauce)); } -class ProvisioningEngineImplServiceTest : public ProvisioningEngineImplTest { +TEST_F(ProvisioningEngineImplTest, InvalidServiceKey) { + EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, + engine_impl_.Initialize( + kCertificateTypeTesting, service_certificate_, "bad_key", + kServicePrivateKeyPassphrase, kProvisioningDrmCertificate, + kProvisioningPrivateKey, kProvisioningPrivateKeyPassphrase, + kSecretSauce)); +} + +TEST_F(ProvisioningEngineImplTest, InvalidServiceKeyPassprase) { + EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, + engine_impl_.Initialize( + kCertificateTypeTesting, service_certificate_, + service_private_key_, "bad-passphrase", + kProvisioningDrmCertificate, kProvisioningPrivateKey, + kProvisioningPrivateKeyPassphrase, kSecretSauce)); +} + +class ProvisioningEngineImplProvTest : public ProvisioningEngineImplTest { protected: void SetUp() override { - mock_root_public_key_ = new MockRsaPublicKey(); - EXPECT_CALL(*mock_rsa_key_factory_, CreateFromPkcs1PublicKey(_)) - .WillOnce(Return( - ByMove(std::unique_ptr(mock_root_public_key_)))); - - service_cert_.set_public_key("service_public_key"); - service_cert_.set_type(DrmDeviceCertificate::SERVICE); - signed_service_cert_.set_drm_certificate(service_cert_.SerializeAsString()); - signed_service_cert_.set_signature("service_signature"); - } - - ProvisioningStatus Initialize(const std::string& service_drm_certificate) { - return engine_impl_.Initialize( - kCertTesting, service_drm_certificate, kServicePrivateKey, - kServicePrivateKeyPassphrase, kProvisioningDrmCertificate, - kProvisioningPrivateKey, kProvisioningPrivateKeyPassphrase, - kSecretSauce); - } - - DrmDeviceCertificate service_cert_; - SignedDrmDeviceCertificate signed_service_cert_; - MockRsaPublicKey* mock_root_public_key_; -}; - -TEST_F(ProvisioningEngineImplServiceTest, Empty) { - EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, Initialize("")); -} - -TEST_F(ProvisioningEngineImplServiceTest, MissingDeviceCert) { - signed_service_cert_.clear_drm_certificate(); - EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, - Initialize(signed_service_cert_.SerializeAsString())); -} - -TEST_F(ProvisioningEngineImplServiceTest, MissingSignature) { - signed_service_cert_.clear_signature(); - EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, - Initialize(signed_service_cert_.SerializeAsString())); -} - -TEST_F(ProvisioningEngineImplServiceTest, SignatureVerificationFailure) { - EXPECT_CALL(*mock_root_public_key_, - VerifySignature(StrEq(service_cert_.SerializeAsString()), - "service_signature")) - .WillOnce(Return(false)); - EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, - Initialize(signed_service_cert_.SerializeAsString())); -} - -TEST_F(ProvisioningEngineImplServiceTest, InvalidDeviceCertType) { - service_cert_.set_type(DrmDeviceCertificate::PROVISIONER); - signed_service_cert_.set_drm_certificate(service_cert_.SerializeAsString()); - EXPECT_CALL(*mock_root_public_key_, - VerifySignature(StrEq(service_cert_.SerializeAsString()), - "service_signature")) - .WillOnce(Return(true)); - EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, - Initialize(signed_service_cert_.SerializeAsString())); -} - -TEST_F(ProvisioningEngineImplServiceTest, InvaidPublicKey) { - EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature")) - .WillOnce(Return(true)); - EXPECT_CALL(*mock_rsa_key_factory_, - CreateFromPkcs1PublicKey("service_public_key")) - .WillOnce(Return(ByMove(nullptr))); - EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, - Initialize(signed_service_cert_.SerializeAsString())); -} - -TEST_F(ProvisioningEngineImplServiceTest, InvalidPrivateKey) { - EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature")) - .WillOnce(Return(true)); - EXPECT_CALL(*mock_rsa_key_factory_, - CreateFromPkcs1PublicKey("service_public_key")) - .WillOnce( - Return(ByMove(std::unique_ptr(new MockRsaPublicKey)))); - EXPECT_CALL(*mock_rsa_key_factory_, - CreateFromPkcs8PrivateKey(kServicePrivateKey, - kServicePrivateKeyPassphrase)) - .WillOnce(Return(ByMove(nullptr))); - EXPECT_EQ(INVALID_SERVICE_PRIVATE_KEY, - Initialize(signed_service_cert_.SerializeAsString())); -} - -TEST_F(ProvisioningEngineImplServiceTest, MismatchPublicKeyPrivateKey) { - EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature")) - .WillOnce(Return(true)); - MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey; - EXPECT_CALL(*mock_rsa_key_factory_, - CreateFromPkcs1PublicKey("service_public_key")) - .WillOnce( - Return(ByMove(std::unique_ptr(mock_rsa_public_key)))); - MockRsaPrivateKey* mock_rsa_private_key = new MockRsaPrivateKey; - EXPECT_CALL(*mock_rsa_key_factory_, - CreateFromPkcs8PrivateKey(kServicePrivateKey, - kServicePrivateKeyPassphrase)) - .WillOnce( - Return(ByMove(std::unique_ptr(mock_rsa_private_key)))); - EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_)) - .WillOnce(Return(false)); - EXPECT_EQ(INVALID_SERVICE_PRIVATE_KEY, - Initialize(signed_service_cert_.SerializeAsString())); -} - -class ProvisioningEngineImplProvTest - : public ProvisioningEngineImplServiceTest { - protected: - void SetUp() override { - ProvisioningEngineImplServiceTest::SetUp(); - - // Service certificate expectations. - EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature")) - .WillOnce(Return(true)); - mock_service_public_key_ = new MockRsaPublicKey; - EXPECT_CALL(*mock_rsa_key_factory_, - CreateFromPkcs1PublicKey("service_public_key")) - .WillOnce(Return( - ByMove(std::unique_ptr(mock_service_public_key_)))); - mock_service_private_key_ = new MockRsaPrivateKey; - EXPECT_CALL(*mock_rsa_key_factory_, - CreateFromPkcs8PrivateKey(kServicePrivateKey, - kServicePrivateKeyPassphrase)) - .WillOnce(Return( - ByMove(std::unique_ptr(mock_service_private_key_)))); - EXPECT_CALL(*mock_service_public_key_, MatchesPrivateKey(_)) - .WillOnce(Return(true)); - prov_cert_.set_public_key(kProvisioningPublicKey); - prov_cert_.set_type(DrmDeviceCertificate::PROVISIONER); + prov_cert_.set_type(DrmCertificate::PROVISIONER); signed_prov_cert_.set_drm_certificate(prov_cert_.SerializeAsString()); signed_prov_cert_.set_signature(kProvisioningSignature); + + SetUpMockDrmRootCertificateWithExpectations(); + SetUpMockRsa(); + SetUpMockRootPublicKey(); + } + + void SetUpMockDrmRootCertificateWithExpectations() { + mock_drm_root_certificate_ = new MockDrmRootCertificate; + engine_impl_.set_drm_root_certificate( + std::unique_ptr(mock_drm_root_certificate_)); + + EXPECT_CALL(*mock_drm_root_certificate_, VerifyCertificate(_, _, _)) + .WillRepeatedly(Invoke([](const std::string& serialized_cert, + SignedDrmCertificate* signed_drm_cert, + DrmCertificate* drm_cert) { + SignedDrmCertificate signed_cert; + if (!signed_cert.ParseFromString(serialized_cert)) { + return Status(error::INVALID_ARGUMENT, + "Invalid SignedDrmCertificate proto"); + } + if (signed_drm_cert) *signed_drm_cert = signed_cert; + if (drm_cert) { + EXPECT_TRUE( + drm_cert->ParseFromString(signed_cert.drm_certificate())); + } + return OkStatus(); + })); + } + + void SetUpMockRsa() { + mock_rsa_key_factory_ = new MockRsaKeyFactory; + engine_impl_.set_rsa_key_factory( + std::unique_ptr(mock_rsa_key_factory_)); + } + + void SetUpMockRootPublicKey() { + mock_root_public_key_ = new MockRsaPublicKey(); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kDrmRootPublicKey)) + .WillOnce(Return( + ByMove(std::unique_ptr(mock_root_public_key_)))); } ProvisioningStatus Initialize(const std::string& provisioning_drm_certificate) { return engine_impl_.Initialize( - kCertTesting, signed_service_cert_.SerializeAsString(), - kServicePrivateKey, kServicePrivateKeyPassphrase, - provisioning_drm_certificate, kProvisioningPrivateKey, - kProvisioningPrivateKeyPassphrase, "spoid_secret_sauce"); + kCertificateTypeTesting, service_certificate_, service_private_key_, + kServicePrivateKeyPassphrase, provisioning_drm_certificate, + kProvisioningPrivateKey, kProvisioningPrivateKeyPassphrase, + kSecretSauce); } - DrmDeviceCertificate prov_cert_; - SignedDrmDeviceCertificate signed_prov_cert_; - MockRsaPublicKey* mock_service_public_key_ = nullptr; - MockRsaPrivateKey* mock_service_private_key_ = nullptr; + RsaTestKeys test_keys_; + MockRsaKeyFactory* mock_rsa_key_factory_ = nullptr; + MockDrmRootCertificate* mock_drm_root_certificate_ = nullptr; + MockRsaPublicKey* mock_root_public_key_ = nullptr; + DrmCertificate prov_cert_; + SignedDrmCertificate signed_prov_cert_; }; -TEST_F(ProvisioningEngineImplProvTest, Empty) { - EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE, Initialize("")); -} - -TEST_F(ProvisioningEngineImplProvTest, MissingDeviceCert) { - signed_prov_cert_.clear_drm_certificate(); - EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE, - Initialize(signed_prov_cert_.SerializeAsString())); -} - -TEST_F(ProvisioningEngineImplProvTest, MissingSignature) { - signed_prov_cert_.clear_signature(); - EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE, - Initialize(signed_prov_cert_.SerializeAsString())); -} - -TEST_F(ProvisioningEngineImplProvTest, SignatureVerificationFailure) { - EXPECT_CALL(*mock_root_public_key_, - VerifySignature(StrEq(prov_cert_.SerializeAsString()), - kProvisioningSignature)) - .WillOnce(Return(false)); - EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE, - Initialize(signed_prov_cert_.SerializeAsString())); -} - TEST_F(ProvisioningEngineImplProvTest, InvalidDeviceCertType) { - prov_cert_.set_type(DrmDeviceCertificate::SERVICE); + prov_cert_.set_type(DrmCertificate::SERVICE); signed_prov_cert_.set_drm_certificate(prov_cert_.SerializeAsString()); - EXPECT_CALL(*mock_root_public_key_, - VerifySignature(StrEq(prov_cert_.SerializeAsString()), - kProvisioningSignature)) - .WillOnce(Return(true)); EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE, Initialize(signed_prov_cert_.SerializeAsString())); } -TEST_F(ProvisioningEngineImplProvTest, InvaidPublicKey) { - EXPECT_CALL(*mock_root_public_key_, - VerifySignature(_, kProvisioningSignature)) - .WillOnce(Return(true)); +TEST_F(ProvisioningEngineImplProvTest, InvalidPublicKey) { EXPECT_CALL(*mock_rsa_key_factory_, CreateFromPkcs1PublicKey(kProvisioningPublicKey)) .WillOnce(Return(ByMove(nullptr))); @@ -290,9 +209,6 @@ TEST_F(ProvisioningEngineImplProvTest, InvaidPublicKey) { } TEST_F(ProvisioningEngineImplProvTest, InvalidPrivateKey) { - EXPECT_CALL(*mock_root_public_key_, - VerifySignature(_, kProvisioningSignature)) - .WillOnce(Return(true)); EXPECT_CALL(*mock_rsa_key_factory_, CreateFromPkcs1PublicKey(kProvisioningPublicKey)) .WillOnce( @@ -306,9 +222,6 @@ TEST_F(ProvisioningEngineImplProvTest, InvalidPrivateKey) { } TEST_F(ProvisioningEngineImplProvTest, MismatchPublicKeyPrivateKey) { - EXPECT_CALL(*mock_root_public_key_, - VerifySignature(_, kProvisioningSignature)) - .WillOnce(Return(true)); MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey; EXPECT_CALL(*mock_rsa_key_factory_, CreateFromPkcs1PublicKey(kProvisioningPublicKey)) @@ -326,35 +239,113 @@ TEST_F(ProvisioningEngineImplProvTest, MismatchPublicKeyPrivateKey) { Initialize(signed_prov_cert_.SerializeAsString())); } -class ProvisioningEngineImplGeneralTest +class ProvisioningEngineImplContextTest : public ProvisioningEngineImplProvTest { protected: void SetUp() override { ProvisioningEngineImplProvTest::SetUp(); - // Provisioning certificate expectations. - EXPECT_CALL(*mock_root_public_key_, - VerifySignature(_, kProvisioningSignature)) + SetUpMockRsaExpectations(); + ASSERT_EQ(OK, ProvisioningEngineImplProvTest::Initialize( + signed_prov_cert_.SerializeAsString())); + } + + void SetUpMockRsaExpectations() { + MockRsaPublicKey* mock_prov_public_key(new MockRsaPublicKey); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kProvisioningPublicKey)) + .WillOnce(Return( + ByMove(std::unique_ptr(mock_prov_public_key)))); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs8PrivateKey(kProvisioningPrivateKey, + kProvisioningPrivateKeyPassphrase)) + .WillOnce(Return( + ByMove(std::unique_ptr(new MockRsaPrivateKey)))); + EXPECT_CALL(*mock_prov_public_key, MatchesPrivateKey(_)) .WillOnce(Return(true)); - mock_prov_public_key_ = new MockRsaPublicKey; + } +}; + +TEST_F(ProvisioningEngineImplContextTest, ContextStoreAndRetrieveSuccess) { + const char kContextData[] = "I dislike tacky orange things"; + ProvisioningContext context; + ASSERT_EQ(OK, engine_impl_.StoreContext(kContextData, &context)); + EXPECT_NE(kContextData, context.context_data()); + EXPECT_FALSE(context.mac().empty()); + std::string context_data; + ASSERT_EQ(OK, engine_impl_.RetrieveContext(context, &context_data)); + EXPECT_EQ(kContextData, context_data); +} + +TEST_F(ProvisioningEngineImplContextTest, ContextStoreAndRetrieveFailBadData) { + const char kContextData[] = "Climate change is not a hoax"; + ProvisioningContext context; + ASSERT_EQ(OK, engine_impl_.StoreContext(kContextData, &context)); + ++(*context.mutable_context_data())[5]; + std::string context_data; + ASSERT_EQ(INVALID_CONTEXT, + engine_impl_.RetrieveContext(context, &context_data)); +} + +TEST_F(ProvisioningEngineImplContextTest, ContextStoreAndRetrieveFailBadMac) { + const char kContextData[] = "No one wants coal anymore"; + ProvisioningContext context; + ASSERT_EQ(OK, engine_impl_.StoreContext(kContextData, &context)); + ++(*context.mutable_mac())[5]; + std::string context_data; + ASSERT_EQ(INVALID_CONTEXT, + engine_impl_.RetrieveContext(context, &context_data)); +} + +TEST_F(ProvisioningEngineImplContextTest, + ContextStoreAndRetrieveFailBadKeyData) { + const char kContextData[] = "No one wants coal anymore"; + ProvisioningContext context; + ASSERT_EQ(OK, engine_impl_.StoreContext(kContextData, &context)); + ++(*context.mutable_key_data())[5]; + std::string context_data; + ASSERT_EQ(INVALID_CONTEXT_KEY_DATA, + engine_impl_.RetrieveContext(context, &context_data)); +} + +class ProvisioningEngineImplGeneralTest + : public ProvisioningEngineImplProvTest { + protected: + void SetUp() override { + ProvisioningEngineImplProvTest::SetUp(); + SetUpMockProvisionerKey(); + + // Set up a DrmCertificate to be used later. + intermediate_cert_.set_type(DrmCertificate::DEVICE_MODEL); + intermediate_cert_.set_system_id(kSystemId); + intermediate_cert_.set_public_key(kIntermediatePublicKey); + signed_intermediate_cert_.set_drm_certificate( + intermediate_cert_.SerializeAsString()); + signed_intermediate_cert_.set_signature(kSignature); + + ASSERT_EQ(OK, ProvisioningEngineImplProvTest::Initialize( + signed_prov_cert_.SerializeAsString())); + } + + void SetUpMockProvisionerKey() { + mock_prov_public_key_ = new MockRsaPublicKey(); EXPECT_CALL(*mock_rsa_key_factory_, CreateFromPkcs1PublicKey(kProvisioningPublicKey)) .WillOnce(Return( ByMove(std::unique_ptr(mock_prov_public_key_)))); - mock_prov_private_key_ = new MockRsaPrivateKey; - EXPECT_CALL( - *mock_rsa_key_factory_, - CreateFromPkcs8PrivateKey(kProvisioningPrivateKey, - kProvisioningPrivateKeyPassphrase)) - .WillOnce(Return( + + mock_prov_private_key_ = new MockRsaPrivateKey(); + ON_CALL(*mock_rsa_key_factory_, + CreateFromPkcs8PrivateKey(kProvisioningPrivateKey, + kProvisioningPrivateKeyPassphrase)) + .WillByDefault(Return( ByMove(std::unique_ptr(mock_prov_private_key_)))); EXPECT_CALL(*mock_prov_public_key_, MatchesPrivateKey(_)) .WillOnce(Return(true)); + } - ASSERT_EQ(OK, ProvisioningEngineImplProvTest::Initialize( - signed_prov_cert_.SerializeAsString())); - - // Setup certificate status list. + void SetUpDcsl() { + // SetUp certificate status list. cert_status_list_.set_creation_time_seconds(time(nullptr)); for (int i = 0; i < 2; ++i) { DeviceCertificateStatus* cert_status = @@ -365,11 +356,11 @@ class ProvisioningEngineImplGeneralTest device_info->set_model(StrCat("model_", i)); } cert_status_list_.mutable_certificate_status(0)->set_status( - DeviceCertificateStatus::VALID); + DeviceCertificateStatus::STATUS_RELEASED); cert_status_list_.mutable_certificate_status(1)->set_status( - DeviceCertificateStatus::REVOKED); + DeviceCertificateStatus::STATUS_REVOKED); - SignedCertificateStatusList signed_cert_status_list; + SignedDeviceCertificateStatusList signed_cert_status_list; signed_cert_status_list.set_certificate_status_list( cert_status_list_.SerializeAsString()); EXPECT_CALL(*mock_root_public_key_, @@ -380,22 +371,14 @@ class ProvisioningEngineImplGeneralTest signed_cert_status_list.set_signature("cert_status_list_signature"); ASSERT_EQ(OK, engine_impl_.SetCertificateStatusList( signed_cert_status_list.SerializeAsString(), - kExpirationPeriodSeconds)); - - // Setup a DrmDeviceCertificate to be used later. - intermediate_cert_.set_type(DrmDeviceCertificate::DRM_INTERMEDIATE); - intermediate_cert_.set_system_id(kSystemId); - intermediate_cert_.set_public_key(kIntermediatePublicKey); - signed_intermediate_cert_.set_drm_certificate( - intermediate_cert_.SerializeAsString()); - signed_intermediate_cert_.set_signature(kSignature); + kInfiniteExpirationPeriodSeconds)); } MockRsaPublicKey* mock_prov_public_key_ = nullptr; MockRsaPrivateKey* mock_prov_private_key_ = nullptr; DeviceCertificateStatusList cert_status_list_; - DrmDeviceCertificate intermediate_cert_; - SignedDrmDeviceCertificate signed_intermediate_cert_; + DrmCertificate intermediate_cert_; + SignedDrmCertificate signed_intermediate_cert_; }; TEST_F(ProvisioningEngineImplGeneralTest, InvalidCertificateStatusList) { @@ -408,7 +391,7 @@ TEST_F(ProvisioningEngineImplGeneralTest, InvalidCertificateStatusList) { TEST_F(ProvisioningEngineImplGeneralTest, CertificateStatusListIncorrectSignature) { - SignedCertificateStatusList signed_cert_status_list; + SignedDeviceCertificateStatusList signed_cert_status_list; signed_cert_status_list.set_certificate_status_list( cert_status_list_.SerializeAsString()); EXPECT_CALL(*mock_root_public_key_, @@ -422,6 +405,8 @@ TEST_F(ProvisioningEngineImplGeneralTest, } TEST_F(ProvisioningEngineImplGeneralTest, GetDeviceInfoAndCheckDeviceStatus) { + SetUpDcsl(); + EXPECT_EQ(OK, CheckDeviceStatus(kSystemId, kEmptyOemSerialNumber)); auto device_info = engine_impl_.GetDeviceInfo(kSystemId); ASSERT_NE(nullptr, device_info); @@ -440,8 +425,10 @@ TEST_F(ProvisioningEngineImplGeneralTest, GetDeviceInfoAndCheckDeviceStatus) { } TEST_F(ProvisioningEngineImplGeneralTest, UpdateCertificateStatusList) { + SetUpDcsl(); + cert_status_list_.mutable_certificate_status(0)->set_status( - DeviceCertificateStatus::REVOKED); + DeviceCertificateStatus::STATUS_REVOKED); DeviceCertificateStatus* cert_status = cert_status_list_.add_certificate_status(); @@ -449,7 +436,7 @@ TEST_F(ProvisioningEngineImplGeneralTest, UpdateCertificateStatusList) { device_info->set_system_id(kSystemId + 2); device_info->set_model("model_2"); - SignedCertificateStatusList signed_cert_status_list; + SignedDeviceCertificateStatusList signed_cert_status_list; signed_cert_status_list.set_certificate_status_list( cert_status_list_.SerializeAsString()); EXPECT_CALL(*mock_root_public_key_, @@ -496,16 +483,16 @@ TEST_F(ProvisioningEngineImplGeneralTest, GenerateDrmIntermediateCertificate) { ASSERT_EQ(OK, engine_impl_.GenerateDrmIntermediateCertificate( kSystemId, kIntermediatePublicKey, &certificate)); - SignedDrmDeviceCertificate signed_drm_cert_proto; + SignedDrmCertificate signed_drm_cert_proto; ASSERT_TRUE(signed_drm_cert_proto.ParseFromString(certificate)); EXPECT_EQ(drm_certificate, signed_drm_cert_proto.drm_certificate()); EXPECT_EQ(kSignature, signed_drm_cert_proto.signature()); EXPECT_EQ(signed_prov_cert_.SerializeAsString(), signed_drm_cert_proto.signer().SerializeAsString()); - DrmDeviceCertificate drm_cert_proto; + DrmCertificate drm_cert_proto; ASSERT_TRUE(drm_cert_proto.ParseFromString(drm_certificate)); - EXPECT_EQ(DrmDeviceCertificate::DRM_INTERMEDIATE, drm_cert_proto.type()); + EXPECT_EQ(DrmCertificate::DEVICE_MODEL, drm_cert_proto.type()); EXPECT_NE("", drm_cert_proto.serial_number()); EXPECT_EQ(kSystemId, drm_cert_proto.system_id()); EXPECT_EQ(kIntermediatePublicKey, drm_cert_proto.public_key()); @@ -513,10 +500,10 @@ TEST_F(ProvisioningEngineImplGeneralTest, GenerateDrmIntermediateCertificate) { TEST_F(ProvisioningEngineImplGeneralTest, AddDrmIntermediateCertificateInvalidCert) { - EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE, - engine_impl_.AddDrmIntermediateCertificate( - "", kIntermediatePrivateKey, - kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ( + INVALID_INTERMEDIATE_DRM_CERTIFICATE, + engine_impl_.AddDrmIntermediateCertificate( + "", kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE, engine_impl_.AddDrmIntermediateCertificate( "invalid_intermediate_cert", kIntermediatePrivateKey, @@ -525,20 +512,14 @@ TEST_F(ProvisioningEngineImplGeneralTest, TEST_F(ProvisioningEngineImplGeneralTest, AddDrmIntermediateCertificateIncorrectCertType) { - intermediate_cert_.set_type(DrmDeviceCertificate::DRM_USER_DEVICE); + intermediate_cert_.set_type(DrmCertificate::DEVICE); signed_intermediate_cert_.set_drm_certificate( intermediate_cert_.SerializeAsString()); - EXPECT_CALL( - *mock_prov_public_key_, - VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()), - StrEq(signed_intermediate_cert_.signature()))) - .WillOnce(Return(true)); - EXPECT_EQ( - INVALID_INTERMEDIATE_DRM_CERTIFICATE, - engine_impl_.AddDrmIntermediateCertificate( - signed_intermediate_cert_.SerializeAsString(), - kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE, + engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); } TEST_F(ProvisioningEngineImplGeneralTest, @@ -547,59 +528,42 @@ TEST_F(ProvisioningEngineImplGeneralTest, signed_intermediate_cert_.set_drm_certificate( intermediate_cert_.SerializeAsString()); - EXPECT_CALL( - *mock_prov_public_key_, - VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()), - StrEq(signed_intermediate_cert_.signature()))) - .WillOnce(Return(true)); - EXPECT_EQ( - UNKNOWN_SYSTEM_ID, - engine_impl_.AddDrmIntermediateCertificate( - signed_intermediate_cert_.SerializeAsString(), - kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(UNKNOWN_SYSTEM_ID, + engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); } TEST_F(ProvisioningEngineImplGeneralTest, AddDrmIntermediateCertificateRevokedCert) { + SetUpDcsl(); + intermediate_cert_.set_system_id(kSystemId + 1); signed_intermediate_cert_.set_drm_certificate( intermediate_cert_.SerializeAsString()); - EXPECT_CALL( - *mock_prov_public_key_, - VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()), - StrEq(signed_intermediate_cert_.signature()))) - .WillOnce(Return(true)); - EXPECT_EQ(DEVICE_REVOKED, engine_impl_.AddDrmIntermediateCertificate( - signed_intermediate_cert_.SerializeAsString(), - kIntermediatePrivateKey, - kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(DEVICE_REVOKED, + engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); } TEST_F(ProvisioningEngineImplGeneralTest, AddDrmIntermediateCertificateInvalidPublicKey) { - EXPECT_CALL( - *mock_prov_public_key_, - VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()), - StrEq(signed_intermediate_cert_.signature()))) - .WillOnce(Return(true)); + SetUpDcsl(); + EXPECT_CALL(*mock_rsa_key_factory_, CreateFromPkcs1PublicKey(kIntermediatePublicKey)) .WillOnce(Return(ByMove(nullptr))); - EXPECT_EQ( - INVALID_INTERMEDIATE_DRM_CERTIFICATE, - engine_impl_.AddDrmIntermediateCertificate( - signed_intermediate_cert_.SerializeAsString(), - kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE, + engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); } TEST_F(ProvisioningEngineImplGeneralTest, AddDrmIntermediateCertificateInvalidPrivateKey) { - EXPECT_CALL( - *mock_prov_public_key_, - VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()), - StrEq(signed_intermediate_cert_.signature()))) - .WillOnce(Return(true)); + SetUpDcsl(); EXPECT_CALL(*mock_rsa_key_factory_, CreateFromPkcs1PublicKey(kIntermediatePublicKey)) @@ -610,20 +574,15 @@ TEST_F(ProvisioningEngineImplGeneralTest, kIntermediatePrivateKeyPassphrase)) .WillOnce(Return(ByMove(nullptr))); - EXPECT_EQ( - INVALID_INTERMEDIATE_PRIVATE_KEY, - engine_impl_.AddDrmIntermediateCertificate( - signed_intermediate_cert_.SerializeAsString(), - kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(INVALID_INTERMEDIATE_PRIVATE_KEY, + engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); } TEST_F(ProvisioningEngineImplGeneralTest, AddDrmIntermediateCertificateMismatchPublicPrivateKey) { - EXPECT_CALL( - *mock_prov_public_key_, - VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()), - StrEq(signed_intermediate_cert_.signature()))) - .WillOnce(Return(true)); + SetUpDcsl(); MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey; EXPECT_CALL(*mock_rsa_key_factory_, @@ -638,20 +597,15 @@ TEST_F(ProvisioningEngineImplGeneralTest, EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_)) .WillOnce(Return(false)); - EXPECT_EQ( - INVALID_INTERMEDIATE_PRIVATE_KEY, - engine_impl_.AddDrmIntermediateCertificate( - signed_intermediate_cert_.SerializeAsString(), - kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(INVALID_INTERMEDIATE_PRIVATE_KEY, + engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); } TEST_F(ProvisioningEngineImplGeneralTest, AddDrmIntermediateCertificateSuccess) { - EXPECT_CALL( - *mock_prov_public_key_, - VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()), - StrEq(signed_intermediate_cert_.signature()))) - .WillOnce(Return(true)); + SetUpDcsl(); MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey; EXPECT_CALL(*mock_rsa_key_factory_, @@ -666,17 +620,17 @@ TEST_F(ProvisioningEngineImplGeneralTest, EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_)) .WillOnce(Return(true)); - EXPECT_EQ(OK, engine_impl_.AddDrmIntermediateCertificate( - signed_intermediate_cert_.SerializeAsString(), - kIntermediatePrivateKey, - kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(OK, + engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); } TEST_F(ProvisioningEngineImplGeneralTest, ExpiredCertificateStatusList) { cert_status_list_.set_creation_time_seconds(time(nullptr) - kExpirationPeriodSeconds - 1); - SignedCertificateStatusList signed_cert_status_list; + SignedDeviceCertificateStatusList signed_cert_status_list; signed_cert_status_list.set_certificate_status_list( cert_status_list_.SerializeAsString()); EXPECT_CALL(*mock_root_public_key_, @@ -687,54 +641,49 @@ TEST_F(ProvisioningEngineImplGeneralTest, ExpiredCertificateStatusList) { signed_cert_status_list.SerializeAsString(), kExpirationPeriodSeconds)); - EXPECT_CALL( - *mock_prov_public_key_, - VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()), - StrEq(signed_intermediate_cert_.signature()))) - .WillOnce(Return(true)); - EXPECT_EQ( - STATUS_LIST_EXPIRED, - engine_impl_.AddDrmIntermediateCertificate( - signed_intermediate_cert_.SerializeAsString(), - kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(STATUS_LIST_EXPIRED, + engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); } TEST_F(ProvisioningEngineImplGeneralTest, GenerateDeviceDrmCertificateRevokedDevice) { + SetUpDcsl(); + std::string certificate; EXPECT_EQ(DEVICE_REVOKED, engine_impl_.GenerateDeviceDrmCertificate( kSystemId + 1, kOemSerialNumber1, kDevicePublicKey, - kCertificateSerialNumber, &certificate)); + RootCertificateSerialNumber, &certificate)); } TEST_F(ProvisioningEngineImplGeneralTest, GenerateDeviceDrmCertificateWithMismatchingOemSerialNumber) { + SetUpDcsl(); + std::string certificate; // If oem serial number does not match, consider as revoked. - EXPECT_EQ(DEVICE_REVOKED, - engine_impl_.GenerateDeviceDrmCertificate( - kSystemId, kOemSerialNumber1, kDevicePublicKey, - kCertificateSerialNumber, &certificate)); + EXPECT_EQ(DEVICE_REVOKED, engine_impl_.GenerateDeviceDrmCertificate( + kSystemId, kOemSerialNumber1, kDevicePublicKey, + RootCertificateSerialNumber, &certificate)); } TEST_F(ProvisioningEngineImplGeneralTest, GenerateDeviceDrmCertificateWithoutIntermediateCert) { + SetUpDcsl(); + std::string certificate; - EXPECT_EQ(MISSING_DRM_INTERMEDIATE_CERT, + EXPECT_EQ(MISSING_DEVICE_MODEL_CERT, engine_impl_.GenerateDeviceDrmCertificate( kSystemId, kOemSerialNumber0, kDevicePublicKey, - kCertificateSerialNumber, &certificate)); + RootCertificateSerialNumber, &certificate)); } -TEST_F(ProvisioningEngineImplGeneralTest, - GenerateDeviceDrmCertificate) { +TEST_F(ProvisioningEngineImplGeneralTest, GenerateDeviceDrmCertificate) { + SetUpDcsl(); + // Add Intermediate certificate. - EXPECT_CALL( - *mock_prov_public_key_, - VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()), - StrEq(signed_intermediate_cert_.signature()))) - .WillOnce(Return(true)); MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey; EXPECT_CALL(*mock_rsa_key_factory_, @@ -750,10 +699,10 @@ TEST_F(ProvisioningEngineImplGeneralTest, EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_)) .WillOnce(Return(true)); - EXPECT_EQ(OK, engine_impl_.AddDrmIntermediateCertificate( - signed_intermediate_cert_.SerializeAsString(), - kIntermediatePrivateKey, - kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(OK, + engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase)); // Intermediate private key expectation. std::string drm_certificate; @@ -763,19 +712,19 @@ TEST_F(ProvisioningEngineImplGeneralTest, std::string certificate; EXPECT_EQ(OK, engine_impl_.GenerateDeviceDrmCertificate( kSystemId, kOemSerialNumber0, kDevicePublicKey, - kCertificateSerialNumber, &certificate)); + RootCertificateSerialNumber, &certificate)); - SignedDrmDeviceCertificate signed_drm_cert_proto; + SignedDrmCertificate signed_drm_cert_proto; ASSERT_TRUE(signed_drm_cert_proto.ParseFromString(certificate)); EXPECT_EQ(drm_certificate, signed_drm_cert_proto.drm_certificate()); EXPECT_EQ(kSignature, signed_drm_cert_proto.signature()); EXPECT_THAT(signed_intermediate_cert_.SerializeAsString(), signed_drm_cert_proto.signer().SerializeAsString()); - DrmDeviceCertificate drm_cert_proto; + DrmCertificate drm_cert_proto; ASSERT_TRUE(drm_cert_proto.ParseFromString(drm_certificate)); - EXPECT_EQ(DrmDeviceCertificate::DRM_USER_DEVICE, drm_cert_proto.type()); - EXPECT_EQ(kCertificateSerialNumber, drm_cert_proto.serial_number()); + EXPECT_EQ(DrmCertificate::DEVICE, drm_cert_proto.type()); + EXPECT_EQ(RootCertificateSerialNumber, drm_cert_proto.serial_number()); EXPECT_EQ(kSystemId, drm_cert_proto.system_id()); EXPECT_EQ(kDevicePublicKey, drm_cert_proto.public_key()); } diff --git a/provisioning_sdk/internal/provisioning_session_impl.cc b/provisioning_sdk/internal/provisioning_session_impl.cc index 8e1f477..5d870de 100644 --- a/provisioning_sdk/internal/provisioning_session_impl.cc +++ b/provisioning_sdk/internal/provisioning_session_impl.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -10,249 +10,53 @@ #include -#include "gflags/gflags.h" #include "glog/logging.h" -#include "common/aes_cbc_util.h" -#include "common/random_util.h" #include "common/rsa_key.h" -#include "common/sha_util.h" -#include "provisioning_sdk/public/provisioning_status.h" DEFINE_int32(prov_sdk_log_every_n, 1, "parameter for LOG_EVERY_N to help abate log spamming."); -#define LOG_EVERY_N_WITH_PROTO(message, proto) \ - LOG_EVERY_N(WARNING, FLAGS_prov_sdk_log_every_n) \ - << (message) << " [proto: " << (proto).ShortDebugString() << "]" - namespace widevine { ProvisioningSessionImpl::ProvisioningSessionImpl( - const ProvisioningEngineImpl& engine, const OemDeviceCert& oem_device_cert, - const RsaPrivateKey& service_private_key) + const ProvisioningEngineImpl& engine) : engine_(engine), - oem_device_cert_(oem_device_cert), - service_private_key_(service_private_key), rsa_key_factory_(new RsaKeyFactory) {} ProvisioningSessionImpl::~ProvisioningSessionImpl() {} ProvisioningStatus ProvisioningSessionImpl::Initialize( - const std::string& device_public_key, const std::string& device_private_key) { + const std::string& device_drm_public_key, const std::string& device_drm_private_key) { auto rsa_public_key = - rsa_key_factory_->CreateFromPkcs1PublicKey(device_public_key); - if (!rsa_public_key) return INVALID_DEVICE_PUBLIC_KEY; + rsa_key_factory_->CreateFromPkcs1PublicKey(device_drm_public_key); + if (!rsa_public_key) return INVALID_DRM_DEVICE_PUBLIC_KEY; // Use empty std::string to indicate the private key is not encrypted. const std::string kClearPkcs8PrivateKeyPassphrase; auto rsa_private_key = rsa_key_factory_->CreateFromPkcs8PrivateKey( - device_private_key, kClearPkcs8PrivateKeyPassphrase); - if (!rsa_private_key) return INVALID_DEVICE_PRIVATE_KEY; + device_drm_private_key, kClearPkcs8PrivateKeyPassphrase); + if (!rsa_private_key) return INVALID_DRM_DEVICE_PRIVATE_KEY; if (!rsa_public_key->MatchesPrivateKey(*rsa_private_key)) { LOG(WARNING) << "Device public key and private key do not match."; - return INVALID_DEVICE_PRIVATE_KEY; + return INVALID_DRM_DEVICE_PRIVATE_KEY; } - device_public_key_ = device_public_key; - device_private_key_ = device_private_key; + device_drm_public_key_ = device_drm_public_key; + device_drm_private_key_ = device_drm_private_key; return OK; } -ProvisioningStatus ProvisioningSessionImpl::ProcessMessage( - const std::string& message, std::string* response) { - SignedProvisioningMessage signed_request; - ProvisioningRequest request; - if (!ValidateAndDeserializeRequest(message, &signed_request, &request)) - return INVALID_REQUEST_MESSAGE; - - ClientIdentification client_id; - if (request.has_encrypted_client_id()) { - if (!DecryptClientIdentification(request.encrypted_client_id(), &client_id)) - return INVALID_REQUEST_MESSAGE; - } else { - DCHECK(request.has_client_id()); - client_id.Swap(request.mutable_client_id()); - } - - if (client_id.type() != ClientIdentification::OEM_DEVICE_CERTIFICATE) { - LOG_EVERY_N_WITH_PROTO("Invalid client_id type", client_id); - return INVALID_REQUEST_MESSAGE; - } - if (client_id.token().empty()) { - LOG_EVERY_N_WITH_PROTO("Missing client_id.token", client_id); - return INVALID_REQUEST_MESSAGE; - } - - std::unique_ptr cert_public_key; - uint32_t system_id; - std::string oem_ca_serial_number; - if (!oem_device_cert_.VerifyCertificateChain(client_id.token(), - &cert_public_key, &system_id, - &oem_ca_serial_number)) { - LOG_EVERY_N_WITH_PROTO("Invalid token", client_id); - return INVALID_REQUEST_MESSAGE; - } - if (!cert_public_key->VerifySignature(signed_request.message(), - signed_request.signature())) { - LOG_EVERY_N_WITH_PROTO("Signature verification failed", client_id); - return INVALID_REQUEST_MESSAGE; - } - - // Save device_info for query later. - device_info_ = engine_.GetDeviceInfo(system_id); - - std::string certificate_serial_number; - if (request.has_spoid()) { - certificate_serial_number = request.spoid(); - } else { - // Generate stable serial number. - const std::string stable_data(client_id.token() + request.stable_id() + - request.provider_id() + - engine_.secret_spoid_sauce()); - const std::string hash = Sha256_Hash(stable_data); - const size_t kCertificateSerialNumberSize = 16; - certificate_serial_number = hash.substr(0, kCertificateSerialNumberSize); - } - - ProvisioningResponse provisioning_response; - ProvisioningStatus status = GenerateProvisioningResponse( - system_id, oem_ca_serial_number, request.provider_id(), - certificate_serial_number, *cert_public_key, &provisioning_response); - if (status != OK) return status; - provisioning_response.set_nonce(request.nonce()); - - // Sign the response. - SignedProvisioningMessage signed_message; - if (!provisioning_response.SerializeToString( - signed_message.mutable_message())) { - LOG(WARNING) << "Error serializing ProvisioningResponse."; - return INTERNAL_ERROR; - } - if (!service_private_key_.GenerateSignature( - signed_message.message(), signed_message.mutable_signature())) { - LOG(WARNING) << "Failed to sign ProvisioningResponse."; - return INTERNAL_ERROR; - } - if (!signed_message.SerializeToString(response)) { - LOG(WARNING) << "Error serializing SignedProvisioningMessage."; - return INTERNAL_ERROR; +ProvisioningStatus ProvisioningSessionImpl::Initialize( + const std::string& keybox_device_key) { + if (keybox_device_key.empty()) { + LOG(WARNING) << "Keybox device key is empty."; + return INVALID_KEYBOX_DEVICE_KEY; } + keybox_device_key_ = keybox_device_key; return OK; } -bool ProvisioningSessionImpl::ValidateAndDeserializeRequest( - const std::string& message, SignedProvisioningMessage* signed_request, - ProvisioningRequest* request) const { - if (!signed_request->ParseFromString(message)) { - LOG_EVERY_N(WARNING, FLAGS_prov_sdk_log_every_n) - << "Failed to parse SignedProvisioningMessage."; - return false; - } - VLOG(1) << "signed_request: " << signed_request->ShortDebugString(); - - if (signed_request->message().empty()) { - LOG_EVERY_N_WITH_PROTO("Missing message", *signed_request); - return false; - } - if (signed_request->signature().empty()) { - LOG_EVERY_N_WITH_PROTO("Missing signature", *signed_request); - return false; - } - if (!request->ParseFromString(signed_request->message())) { - LOG_EVERY_N_WITH_PROTO("Failed to parse ProvisioningRequest", - *signed_request); - return false; - } - - if (request->has_encrypted_client_id()) { - const EncryptedClientIdentification& encrypted_client_id = - request->encrypted_client_id(); - if (encrypted_client_id.encrypted_client_id().empty()) { - LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id", - encrypted_client_id); - return false; - } - if (encrypted_client_id.encrypted_client_id_iv().empty()) { - LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id_iv", - encrypted_client_id); - return false; - } - if (encrypted_client_id.encrypted_privacy_key().empty()) { - LOG_EVERY_N_WITH_PROTO("Missing encrypted_privacy_key", - encrypted_client_id); - return false; - } - } else if (!request->has_client_id()) { - LOG_EVERY_N_WITH_PROTO("Missing clear_or_encrypted_client_id", *request); - return false; - } - - const size_t kMinimumRequiredNonceLength = 4; - if (request->nonce().size() < kMinimumRequiredNonceLength) { - LOG_EVERY_N_WITH_PROTO("Missing or invalid nonce", *request); - return false; - } - return true; -} - -bool ProvisioningSessionImpl::DecryptClientIdentification( - const EncryptedClientIdentification& encrypted_client_id, - ClientIdentification* client_id) { - std::string privacy_key; - if (!service_private_key_.Decrypt(encrypted_client_id.encrypted_privacy_key(), - &privacy_key)) { - LOG_EVERY_N_WITH_PROTO("Failed to decrypt encrypted_privacy_key", - encrypted_client_id); - return false; - } - 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()) { - LOG_EVERY_N_WITH_PROTO("Failed to decrypt client_id", encrypted_client_id); - return false; - } - if (!client_id->ParseFromString(serialized_client_id)) { - LOG_EVERY_N_WITH_PROTO("Failed to parse client_id", encrypted_client_id); - return false; - } - return true; -} - -ProvisioningStatus ProvisioningSessionImpl::GenerateProvisioningResponse( - uint32_t system_id, const std::string& oem_ca_serial_number, - const std::string& provider_id, const std::string& certificate_serial_number, - const RsaPublicKey& cert_public_key, ProvisioningResponse* response) { - ProvisioningStatus status = engine_.GenerateProviderDeviceDrmCertificate( - system_id, oem_ca_serial_number, provider_id, device_public_key_, - certificate_serial_number, response->mutable_device_certificate()); - if (status != OK) return status; - - const size_t kAesKeySize = 16; - const size_t kIvSize = 16; - - // Encrypt private key. - std::string message_key; - if (!RandomBytes(kAesKeySize, &message_key)) { - LOG(WARNING) << "Failed to generate message_key."; - return INTERNAL_ERROR; - } - std::string iv; - if (!RandomBytes(kIvSize, &iv)) { - LOG(WARNING) << "Failed to generate iv."; - return INTERNAL_ERROR; - } - response->set_device_rsa_key_iv(iv); - response->set_device_rsa_key( - crypto_util::EncryptAesCbc(message_key, iv, device_private_key_)); - if (response->device_rsa_key().empty()) { - LOG(WARNING) << "Failed to encrypt device_rsa_key"; - return INTERNAL_ERROR; - } - if (!cert_public_key.Encrypt(message_key, response->mutable_wrapping_key())) { - LOG(WARNING) << "Failed to encrypt wrapping_key"; - return INTERNAL_ERROR; - } - return OK; +const ProvisionedDeviceInfo* ProvisioningSessionImpl::GetDeviceInfo() const { + LOG(FATAL) << "Not implemented."; } } // namespace widevine diff --git a/provisioning_sdk/internal/provisioning_session_impl.h b/provisioning_sdk/internal/provisioning_session_impl.h index 75214ca..16b83d7 100644 --- a/provisioning_sdk/internal/provisioning_session_impl.h +++ b/provisioning_sdk/internal/provisioning_session_impl.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -14,73 +14,62 @@ #include #include #include -#include +#include "gflags/gflags.h" #include "common/rsa_key.h" -#include "provisioning_sdk/internal/oem_device_cert.h" -#include "provisioning_sdk/internal/provisioning_engine_impl.h" #include "provisioning_sdk/public/provisioning_status.h" -#include "protos/public/certificate_provisioning.pb.h" -#include "protos/public/client_identification.pb.h" -#include "protos/public/device_certificate.pb.h" -#include "protos/public/provisioned_device_info.pb.h" + +DECLARE_int32(prov_sdk_log_every_n); namespace widevine { +class ProvisioningEngineImpl; +class ProvisionedDeviceInfo; + class ProvisioningSessionImpl { public: - ProvisioningSessionImpl(const ProvisioningEngineImpl& engine, - const OemDeviceCert& oem_device_cert, - const RsaPrivateKey& service_private_key); - ~ProvisioningSessionImpl(); + explicit ProvisioningSessionImpl(const ProvisioningEngineImpl& engine); + virtual ~ProvisioningSessionImpl(); // Initialize provisioning session with given public key and private key. - ProvisioningStatus Initialize(const std::string& device_public_key, - const std::string& device_private_key); + // This is for certificate based provisioning protocol. + ProvisioningStatus Initialize(const std::string& device_drm_public_key, + const std::string& device_drm_private_key); + + // Initialize provisioning session with keybox device key. + // This is for keybox based provisioning protocol. + ProvisioningStatus Initialize(const std::string& keybox_device_key); // Process a message from the client device. // * |message| is the message received from the client device. // * |response| will contain, upon successful return, a message to be sent // back to the client device as a response to |message|. // Returns OK if successful, or an appropriate error status code otherwise. - ProvisioningStatus ProcessMessage(const std::string& message, std::string* response); + virtual ProvisioningStatus ProcessMessage(const std::string& message, + std::string* response, + bool* done) = 0; // * Returns a ProvisioneddeviceInfo message containing information about the // type of device being provisioned. May return nullptr. - const ProvisionedDeviceInfo* GetDeviceInfo() const { - return device_info_.get(); - } + virtual const ProvisionedDeviceInfo* GetDeviceInfo() const; - private: + protected: friend class ProvisioningSessionImplTest; + friend class Provisioning30SessionImplTest; ProvisioningSessionImpl(const ProvisioningSessionImpl&) = delete; ProvisioningSessionImpl& operator=(const ProvisioningSessionImpl&) = delete; - bool ValidateAndDeserializeRequest(const std::string& message, - SignedProvisioningMessage* signed_request, - ProvisioningRequest* request) const; - bool DecryptClientIdentification( - const EncryptedClientIdentification& encrypted_client_id, - ClientIdentification* client_id); - ProvisioningStatus GenerateProvisioningResponse( - uint32_t system_id, const std::string& oem_ca_serial_number, - const std::string& provider_id, const std::string& certificate_serial_number, - const RsaPublicKey& cert_public_key, ProvisioningResponse* response); - // Inject rsa_key_factory for testing. void set_rsa_key_factory(std::unique_ptr rsa_key_factory) { rsa_key_factory_ = std::move(rsa_key_factory); } const ProvisioningEngineImpl& engine_; - const OemDeviceCert& oem_device_cert_; - const RsaPrivateKey& service_private_key_; - std::unique_ptr rsa_key_factory_; - std::string device_public_key_; - std::string device_private_key_; - std::shared_ptr device_info_; + std::string device_drm_public_key_; + std::string device_drm_private_key_; + std::string keybox_device_key_; }; } // namespace widevine diff --git a/provisioning_sdk/internal/provisioning_session_impl_test.cc b/provisioning_sdk/internal/provisioning_session_impl_test.cc index 4209fd3..01f0540 100644 --- a/provisioning_sdk/internal/provisioning_session_impl_test.cc +++ b/provisioning_sdk/internal/provisioning_session_impl_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -8,12 +8,9 @@ #include "provisioning_sdk/internal/provisioning_session_impl.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "common/aes_cbc_util.h" +#include "testing/gmock.h" +#include "testing/gunit.h" #include "common/mock_rsa_key.h" -#include "common/sha_util.h" -#include "provisioning_sdk/internal/oem_device_cert.h" #include "provisioning_sdk/internal/provisioning_engine_impl.h" using ::testing::_; @@ -21,82 +18,39 @@ using ::testing::ByMove; using ::testing::DoAll; using ::testing::IsEmpty; using ::testing::Return; -using ::testing::SaveArg; -using ::testing::SetArgPointee; namespace { -const char kEncryptedClientIdIv[] = "sixteen_bytes_iv"; -const char kPrivacyKey[] = "privacy_key_16B_"; -const char kProviderId[] = "testing_provider"; -const char kClientToken[] = "client_id_token"; -const char kDevicePublicKey[] = "device_public_key"; -const char kEncryptedPrivacyKey[] = "encrypted_privacy_key"; const char kDevicePrivateKey[] = "device_private_key"; -const char kWrappingKey[] = "wrapping_key"; -const char kDeviceCertificate[] = "device_certificate"; -const char kNonce[] = "testing_nonce"; -const char kSignature[] = "generated_signature"; - -// Derives Stable Per-Origin IDentifiers. -std::string DeriveSpoid(const std::string& client_token, - const std::string& provider_id, - const std::string& secret_sauce) { - return widevine::Sha256_Hash(client_token + provider_id + secret_sauce) - .substr(0, 16); -} - +const char kDevicePublicKey[] = "device_public_key"; } // namespace namespace widevine { -class MockProvisioningEngineImpl : public ProvisioningEngineImpl { +// Fake session impl to deal with abstract methods.. +class FakeProvisioningSessionImpl : public ProvisioningSessionImpl { public: - MOCK_CONST_METHOD6(GenerateProviderDeviceDrmCertificate, - ProvisioningStatus(uint32_t system_id, - const std::string& oem_ca_serial_number, - const std::string& provider_id, - const std::string& public_key, - const std::string& certificate_serial_number, - std::string* certificate)); -}; + explicit FakeProvisioningSessionImpl(const ProvisioningEngineImpl& engine) : + ProvisioningSessionImpl(engine) {} -class MockOemDeviceCert : public OemDeviceCert { - public: - // gmock does not support SetArgPointee on std::unique_ptr, so we have to - // workaround it with a trick. - MOCK_CONST_METHOD4(DoVerifyCertificateChain, - bool(const std::string& certificate_chain, - RsaPublicKey** leaf_public_key, uint32_t* system_id, - std::string* oem_ca_serial_number)); - bool VerifyCertificateChain(const std::string& certificate_chain, - std::unique_ptr* leaf_public_key, - uint32_t* system_id, - std::string* oem_ca_serial_number) const override { - RsaPublicKey* raw_leaf_public_key = nullptr; - if (!DoVerifyCertificateChain(certificate_chain, &raw_leaf_public_key, - system_id, oem_ca_serial_number)) { - return false; - } - *leaf_public_key = std::unique_ptr(raw_leaf_public_key); - return true; + ProvisioningStatus ProcessMessage(const std::string& message, + std::string* response, + bool* done) override { + return INTERNAL_ERROR; } }; class ProvisioningSessionImplTest : public ::testing::Test { protected: ProvisioningSessionImplTest() - : session_impl_(mock_engine_impl_, mock_oem_device_cert_, - mock_service_private_key_) { + : session_impl_(engine_impl_) { mock_rsa_key_factory_ = new MockRsaKeyFactory; session_impl_.set_rsa_key_factory( std::unique_ptr(mock_rsa_key_factory_)); } - ProvisioningSessionImpl session_impl_; + ProvisioningEngineImpl engine_impl_; + FakeProvisioningSessionImpl session_impl_; MockRsaKeyFactory* mock_rsa_key_factory_ = nullptr; - MockProvisioningEngineImpl mock_engine_impl_; - MockOemDeviceCert mock_oem_device_cert_; - MockRsaPrivateKey mock_service_private_key_; }; TEST_F(ProvisioningSessionImplTest, InitializeWithInvalidPublicKey) { @@ -104,7 +58,7 @@ TEST_F(ProvisioningSessionImplTest, InitializeWithInvalidPublicKey) { CreateFromPkcs1PublicKey(kDevicePublicKey)) .WillOnce(Return(ByMove(nullptr))); EXPECT_EQ( - INVALID_DEVICE_PUBLIC_KEY, + INVALID_DRM_DEVICE_PUBLIC_KEY, session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey)); } @@ -117,7 +71,7 @@ TEST_F(ProvisioningSessionImplTest, InitializeWithInvalidPrivateKey) { CreateFromPkcs8PrivateKey(kDevicePrivateKey, IsEmpty())) .WillOnce(Return(ByMove(nullptr))); EXPECT_EQ( - INVALID_DEVICE_PRIVATE_KEY, + INVALID_DRM_DEVICE_PRIVATE_KEY, session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey)); } @@ -134,294 +88,9 @@ TEST_F(ProvisioningSessionImplTest, InitializeWithMismatchPublicPrivateKey) { EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_)) .WillOnce(Return(false)); EXPECT_EQ( - INVALID_DEVICE_PRIVATE_KEY, + INVALID_DRM_DEVICE_PRIVATE_KEY, session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey)); } -class ProvisioningSessionImplProcessTest : public ProvisioningSessionImplTest { - public: - void SetUp() override { - MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey; - EXPECT_CALL(*mock_rsa_key_factory_, - CreateFromPkcs1PublicKey(kDevicePublicKey)) - .WillOnce( - Return(ByMove(std::unique_ptr(mock_rsa_public_key)))); - EXPECT_CALL(*mock_rsa_key_factory_, - CreateFromPkcs8PrivateKey(kDevicePrivateKey, IsEmpty())) - .WillOnce(Return( - ByMove(std::unique_ptr(new MockRsaPrivateKey)))); - EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_)) - .WillOnce(Return(true)); - ASSERT_EQ(OK, session_impl_.Initialize(kDevicePublicKey, - kDevicePrivateKey)); - - // Setup Provisioning Message. - client_id_.set_type(ClientIdentification::OEM_DEVICE_CERTIFICATE); - client_id_.set_token(kClientToken); - - EncryptedClientIdentification* encrypted_client_id = - prov_request_.mutable_encrypted_client_id(); - encrypted_client_id->set_encrypted_client_id(crypto_util::EncryptAesCbc( - kPrivacyKey, kEncryptedClientIdIv, client_id_.SerializeAsString())); - encrypted_client_id->set_encrypted_client_id_iv(kEncryptedClientIdIv); - encrypted_client_id->set_encrypted_privacy_key(kEncryptedPrivacyKey); - prov_request_.set_provider_id(kProviderId); - prov_request_.set_nonce(kNonce); - - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - signed_prov_message_.set_signature("testing_signature"); - } - - ClientIdentification client_id_; - ProvisioningRequest prov_request_; - SignedProvisioningMessage signed_prov_message_; -}; - -TEST_F(ProvisioningSessionImplProcessTest, InvalidMessage) { - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage("invalid_message", &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, EmptyMessage) { - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage("", &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, MissingMessage) { - signed_prov_message_.clear_message(); - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, MissingSignature) { - signed_prov_message_.clear_signature(); - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, MissingClientId) { - prov_request_.clear_encrypted_client_id(); - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, MissingEncryptedClientId) { - prov_request_.mutable_encrypted_client_id()->clear_encrypted_client_id(); - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, MissingEncryptedClientIdIv) { - prov_request_.mutable_encrypted_client_id()->clear_encrypted_client_id_iv(); - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, MissingEncryptedPrivacyKey) { - prov_request_.mutable_encrypted_client_id()->clear_encrypted_privacy_key(); - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, InvalidNonce) { - // Nonce should be at least 4 buytes. - const char kNonceWithLessThanFourBytes[] = "xx"; - prov_request_.set_nonce(kNonceWithLessThanFourBytes); - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, PrivacyKeyDecryptionFailed) { - EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) - .WillOnce(Return(false)); - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, InvalidEncryptedClientId) { - prov_request_.mutable_encrypted_client_id()->set_encrypted_client_id( - "invalid_encrypted_client_id"); - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) - .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); - - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, VerifyCertificateChainFailed) { - EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) - .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); - EXPECT_CALL(mock_oem_device_cert_, - DoVerifyCertificateChain(kClientToken, _, _, _)) - .WillOnce(Return(false)); - - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, - ClearClientIdVerifyCertificateChainFailed) { - *prov_request_.mutable_client_id() = client_id_; - prov_request_.clear_encrypted_client_id(); - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - - EXPECT_CALL(mock_oem_device_cert_, - DoVerifyCertificateChain(kClientToken, _, _, _)) - .WillOnce(Return(false)); - - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, ClearClientIdInvalidClientIdType) { - client_id_.set_type(ClientIdentification::KEYBOX); - *prov_request_.mutable_client_id() = client_id_; - prov_request_.clear_encrypted_client_id(); - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, ClearClientIdMissingToken) { - client_id_.clear_token(); - *prov_request_.mutable_client_id() = client_id_; - prov_request_.clear_encrypted_client_id(); - signed_prov_message_.set_message(prov_request_.SerializeAsString()); - - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, VerifySignatureFailed) { - EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) - .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); - MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey; - EXPECT_CALL(mock_oem_device_cert_, - DoVerifyCertificateChain(kClientToken, _, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(mock_cert_public_key), Return(true))); - EXPECT_CALL(*mock_cert_public_key, - VerifySignature(signed_prov_message_.message(), - signed_prov_message_.signature())) - .WillOnce(Return(false)); - - std::string response; - EXPECT_EQ(INVALID_REQUEST_MESSAGE, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, GenerateDeviceCertificateFailed) { - const uint32_t kSystemId = 1234; - const char kExpectedOemSerialNumber[] = "test_oem_serial_number"; - - EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) - .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); - MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey; - EXPECT_CALL(mock_oem_device_cert_, - DoVerifyCertificateChain(kClientToken, _, _, _)) - .WillOnce(DoAll( - SetArgPointee<1>(mock_cert_public_key), SetArgPointee<2>(kSystemId), - SetArgPointee<3>(kExpectedOemSerialNumber), Return(true))); - EXPECT_CALL(*mock_cert_public_key, - VerifySignature(signed_prov_message_.message(), - signed_prov_message_.signature())) - .WillOnce(Return(true)); - - EXPECT_CALL( - mock_engine_impl_, - GenerateProviderDeviceDrmCertificate( - kSystemId, kExpectedOemSerialNumber, kProviderId, kDevicePublicKey, - DeriveSpoid(kClientToken, kProviderId, ""), _)) - .WillOnce(Return(INTERNAL_ERROR)); - - std::string response; - EXPECT_EQ(INTERNAL_ERROR, - session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); -} - -TEST_F(ProvisioningSessionImplProcessTest, Success) { - const uint32_t kSystemId = 1234; - EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _)) - .WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true))); - MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey; - EXPECT_CALL(mock_oem_device_cert_, - DoVerifyCertificateChain(kClientToken, _, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(mock_cert_public_key), - SetArgPointee<2>(kSystemId), Return(true))); - EXPECT_CALL(*mock_cert_public_key, - VerifySignature(signed_prov_message_.message(), - signed_prov_message_.signature())) - .WillOnce(Return(true)); - - EXPECT_CALL(mock_engine_impl_, - GenerateProviderDeviceDrmCertificate(kSystemId, _, _, - kDevicePublicKey, _, _)) - .WillOnce(DoAll(SetArgPointee<5>(kDeviceCertificate), Return(OK))); - - std::string message_key; - EXPECT_CALL(*mock_cert_public_key, Encrypt(_, _)) - .WillOnce(DoAll(SaveArg<0>(&message_key), - SetArgPointee<1>(kWrappingKey), Return(true))); - std::string message; - EXPECT_CALL(mock_service_private_key_, GenerateSignature(_, _)) - .WillOnce(DoAll(SaveArg<0>(&message), - SetArgPointee<1>(kSignature), Return(true))); - - std::string response; - ASSERT_EQ(OK, session_impl_.ProcessMessage( - signed_prov_message_.SerializeAsString(), &response)); - - // Verify the response. - SignedProvisioningMessage signed_prov_message; - ASSERT_TRUE(signed_prov_message.ParseFromString(response)); - EXPECT_EQ(message, signed_prov_message.message()); - EXPECT_EQ(kSignature, signed_prov_message.signature()); - - ProvisioningResponse prov_response; - ASSERT_TRUE(prov_response.ParseFromString(message)); - EXPECT_EQ( - kDevicePrivateKey, - crypto_util::DecryptAesCbc(message_key, prov_response.device_rsa_key_iv(), - prov_response.device_rsa_key())); - EXPECT_EQ(kDeviceCertificate, prov_response.device_certificate()); - EXPECT_EQ(kNonce, prov_response.nonce()); - EXPECT_EQ(kWrappingKey, prov_response.wrapping_key()); -} } // namespace widevine diff --git a/provisioning_sdk/public/BUILD b/provisioning_sdk/public/BUILD index 0030c34..e316059 100644 --- a/provisioning_sdk/public/BUILD +++ b/provisioning_sdk/public/BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -33,7 +33,11 @@ cc_library( name = "libprovisioning_sdk", srcs = [":libprovisioning_sdk.so"], hdrs = glob(["*.h"]), - deps = ["//base"], + deps = [ + "//base", + "//common:certificate_type", + "//protos/public:certificate_provisioning_proto", + ], ) cc_library( @@ -42,13 +46,17 @@ cc_library( hdrs = ["provisioning_engine.h"], copts = PUBLIC_COPTS, deps = [ - ":certificate_type", ":provisioning_session", ":provisioning_status", "//base", + "@abseil_repo//absl/memory", + "//common:certificate_type", + "//common:drm_service_certificate", "//common:rsa_key", + "//provisioning_sdk/internal:provisioning30_session_impl", "//provisioning_sdk/internal:provisioning_engine_impl", "//provisioning_sdk/internal:provisioning_session_impl", + "//protos/public:certificate_provisioning_proto", ], ) @@ -58,7 +66,7 @@ cc_test( srcs = ["provisioning_engine_test.cc"], deps = [ ":provisioning_engine", - "//external:gtest_main", + "//testing:gunit_main", ], ) @@ -71,7 +79,7 @@ cc_library( ":provisioning_status", "//base", "//provisioning_sdk/internal:provisioning_session_impl", - "//protos/public:device_certificate_proto", + "//protos/public:drm_certificate_proto", ], ) @@ -82,9 +90,3 @@ cc_library( copts = PUBLIC_COPTS, deps = ["//base"], ) - -cc_library( - name = "certificate_type", - hdrs = ["certificate_type.h"], - copts = PUBLIC_COPTS, -) diff --git a/provisioning_sdk/public/provisioning_engine.cc b/provisioning_sdk/public/provisioning_engine.cc index afb3431..0def6bd 100644 --- a/provisioning_sdk/public/provisioning_engine.cc +++ b/provisioning_sdk/public/provisioning_engine.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -11,7 +11,11 @@ #include #include "glog/logging.h" +#include "absl/memory/memory.h" +#include "common/certificate_type.h" +#include "common/drm_service_certificate.h" #include "common/rsa_key.h" +#include "provisioning_sdk/internal/provisioning30_session_impl.h" #include "provisioning_sdk/internal/provisioning_engine_impl.h" #include "provisioning_sdk/internal/provisioning_session_impl.h" #include "provisioning_sdk/public/provisioning_session.h" @@ -36,16 +40,35 @@ ProvisioningStatus ProvisioningEngine::Initialize( } std::unique_ptr impl(new ProvisioningEngineImpl); - ProvisioningStatus status = impl->Initialize( - certificate_type, service_drm_certificate, service_private_key, - service_private_key_passphrase, provisioning_drm_certificate, - provisioning_private_key, provisioning_private_key_passphrase, - secret_spoid_sauce); + ProvisioningStatus status = + impl->Initialize(certificate_type, service_drm_certificate, + service_private_key, service_private_key_passphrase, + provisioning_drm_certificate, provisioning_private_key, + provisioning_private_key_passphrase, secret_spoid_sauce); if (status != OK) return status; impl_ = std::move(impl); + + // Register Provisioning 3.0 session factory. + RegisterProtocol( + SignedProvisioningMessage::PROVISIONING_30, + [](const ProvisioningEngineImpl& engine, + std::unique_ptr* new_session) { + *new_session = absl::make_unique( + engine, engine.oem_device_cert(), + *DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() + ->private_key()); + return OK; + }); + return OK; } +void ProvisioningEngine::RegisterProtocol( + SignedProvisioningMessage::ProtocolVersion protocol, + SessionFactory session_factory) { + protocol_registry_[protocol] = std::move(session_factory); +} + ProvisioningStatus ProvisioningEngine::SetCertificateStatusList( const std::string& certificate_status_list, uint32_t expiration_period_seconds) { if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED; @@ -74,6 +97,7 @@ ProvisioningStatus ProvisioningEngine::AddDrmIntermediateCertificate( } ProvisioningStatus ProvisioningEngine::NewProvisioningSession( + SignedProvisioningMessage::ProtocolVersion protocol, const std::string& device_public_key, const std::string& device_private_key, std::unique_ptr* new_session) const { if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED; @@ -82,12 +106,56 @@ ProvisioningStatus ProvisioningEngine::NewProvisioningSession( return INTERNAL_ERROR; } - std::unique_ptr session_impl( - new ProvisioningSessionImpl(*impl_, impl_->oem_device_cert(), - *impl_->service_private_key())); - ProvisioningStatus status = - session_impl->Initialize(device_public_key, device_private_key); + auto factory = protocol_registry_.find(protocol); + if (factory == protocol_registry_.end()) { + LOG(WARNING) << "Provisioning protocol not supported (" << protocol << ")"; + return INVALID_PROTOCOL; + } + std::unique_ptr session_impl; + ProvisioningStatus status = (factory->second)(*impl_, &session_impl); if (status != OK) return status; + + status = session_impl->Initialize(device_public_key, device_private_key); + if (status != OK) return status; + + *new_session = std::unique_ptr( + new ProvisioningSession(std::move(session_impl))); + return OK; +} + +std::unique_ptr ProvisioningEngine::NewProvisioningSession( + SignedProvisioningMessage::ProtocolVersion protocol, + const std::string& device_public_key, const std::string& device_private_key, + ProvisioningStatus* status) const { + std::unique_ptr new_session; + *status = NewProvisioningSession(protocol, device_public_key, + device_private_key, &new_session); + return new_session; +} + +ProvisioningStatus ProvisioningEngine::NewKeyboxProvisioningSession( + const std::string& keybox_device_key, + std::unique_ptr* new_session) const { + if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED; + if (!new_session) { + LOG(WARNING) << "|new_session| should not be a nullptr."; + return INTERNAL_ERROR; + } + + SignedProvisioningMessage::ProtocolVersion protocol = + SignedProvisioningMessage::ARCPP_PROVISIONING; + auto factory = protocol_registry_.find(protocol); + if (factory == protocol_registry_.end()) { + LOG(WARNING) << "Provisioning protocol not supported (" << protocol << ")"; + return INVALID_PROTOCOL; + } + std::unique_ptr session_impl; + ProvisioningStatus status = (factory->second)(*impl_, &session_impl); + if (status != OK) return status; + + status = session_impl->Initialize(keybox_device_key); + if (status != OK) return status; + new_session->reset(new ProvisioningSession(std::move(session_impl))); return OK; } @@ -106,7 +174,7 @@ ProvisioningStatus ProvisioningEngine::GenerateDeviceDrmCertificate( // function which expect the input to be already validated. std::unique_ptr rsa_public_key( RsaPublicKey::Create(public_key)); - if (!rsa_public_key) return INVALID_DEVICE_PUBLIC_KEY; + if (!rsa_public_key) return INVALID_DRM_DEVICE_PUBLIC_KEY; if (serial_number.empty()) return INVALID_SERIAL_NUMBER; const std::string empty_oem_ca_serial_number; diff --git a/provisioning_sdk/public/provisioning_engine.h b/provisioning_sdk/public/provisioning_engine.h index d2f1590..34c9fa0 100644 --- a/provisioning_sdk/public/provisioning_engine.h +++ b/provisioning_sdk/public/provisioning_engine.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -10,16 +10,31 @@ #define PROVISIONING_SDK_PUBLIC_PROVISIONING_ENGINE_H_ #include +#include +#include #include #include -#include "provisioning_sdk/public/certificate_type.h" +#include "common/certificate_type.h" #include "provisioning_sdk/public/provisioning_status.h" +#include "protos/public/certificate_provisioning.pb.h" namespace widevine { class ProvisioningEngineImpl; class ProvisioningSession; +class ProvisioningSessionImpl; + +// Session factory function used to implement third-party provisioning +// protocols. +// * |engine| is the ProvisioningEngineImpl which invokes the function. +// * |new_session| will point, on successful return, to the newly created +// ProvisioningSessionImpl. +// * Returns OK if successful, or an appropriate error status code otherwise. +typedef std::function* new_session)> + SessionFactory; // Class which is used to implement a Widevine DRM device provisioning engine. // There should be only one instance of ProvisioningEngine. The engine should @@ -50,8 +65,7 @@ class ProvisioningEngine { // derivation of Stable Per-Origin IDentifiers. // * Returns OK on success, or an appropriate error status code otherwise. ProvisioningStatus Initialize( - CertificateType certificate_type, - const std::string& service_drm_certificate, + CertificateType certificate_type, const std::string& service_drm_certificate, const std::string& service_private_key, const std::string& service_private_key_passphrase, const std::string& provisioning_drm_certificate, @@ -59,6 +73,14 @@ class ProvisioningEngine { const std::string& provisioning_private_key_passphrase, const std::string& secret_spoid_sauce); + // Third-party protocol registration method. + // * |protocol| is the provisioning protocol, as defined in the + // SignedProvisioningMessage message. + // * |session_factory| is the function which instantiates the appropriate + // ProvisioningSessionImpl object for the specified protocol. + void RegisterProtocol(SignedProvisioningMessage::ProtocolVersion protocol, + SessionFactory session_factory); + // Set the certificate status list for this engine. // * |certificate_status_list| is a certificate status list generated by the // Widevine Provisioning Service. @@ -67,8 +89,7 @@ class ProvisioningEngine { // (creation_time_seconds). Zero means it will never expire. // * Returns OK on success, or an appropriate error status code otherwise. virtual ProvisioningStatus SetCertificateStatusList( - const std::string& certificate_status_list, - uint32_t expiration_period_seconds); + const std::string& certificate_status_list, uint32_t expiration_period_seconds); // Generate an intermediate DRM certificate. // * |system_id| is the Widevine system ID for the type of device. @@ -82,9 +103,7 @@ class ProvisioningEngine { // engines, including this one, by invoking // |AddIntermediatedrmcertificate| on all active ProvisioningEngine(s). ProvisioningStatus GenerateDrmIntermediateCertificate( - uint32_t system_id, - const std::string& public_key, - std::string* certificate) const; + uint32_t system_id, const std::string& public_key, std::string* certificate) const; // Add an intermediate DRM certificate to the provisioning engine. This is // usually done once for each supported device type. @@ -95,12 +114,13 @@ class ProvisioningEngine { // if any. // * Returns OK on success, or an appropriate error status code otherwise. virtual ProvisioningStatus AddDrmIntermediateCertificate( - const std::string& intermediate_cert, - const std::string& cert_private_key, + const std::string& intermediate_cert, const std::string& cert_private_key, const std::string& cert_private_key_passphrase); - // Create a session to handle a provisioning exchange between a client device - // and the provisioning server. + // Create a session to handle a certificate provisioning exchange between + // a client device and the provisioning server. + // * |protocol| is the protocol version for the session. If not sure, it + // probably ought to be |PROVISIONING_30|. // * |device_public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which // will used to create the DRM certificate to be provisioned onto the // device. @@ -114,8 +134,28 @@ class ProvisioningEngine { // NOTE: All ProvisioningSession objects must be deleted before the // ProvisioningEngine which created them. virtual ProvisioningStatus NewProvisioningSession( - const std::string& device_public_key, - const std::string& device_private_key, + SignedProvisioningMessage::ProtocolVersion protocol, + const std::string& device_public_key, const std::string& device_private_key, + std::unique_ptr* new_session) const; + + // This is the same as NewProvisioningSession above, but with outputs reversed + // To get around CLIF bug https://github.com/google/clif/issues/30. + std::unique_ptr NewProvisioningSession( + SignedProvisioningMessage::ProtocolVersion protocol, + const std::string& device_public_key, const std::string& device_private_key, + ProvisioningStatus* status) const; + + // Create a session to handle a keybox provisioning exchange between + // a client device (e.g., ChromeOS) and the provisioning server. + // It would use ARCPP_PROVISIONING protocol. + // * |keybox_device_key| is the secret device key in the keybox. + // * |new_session| will point, on successful return, to the newly created + // ProvisioningSession. + // * Returns OK if successful, or an appropriate error status code otherwise. + // NOTE: All ProvisioningSession objects must be deleted before the + // ProvisioningEngine which created them. + virtual ProvisioningStatus NewKeyboxProvisioningSession( + const std::string& keybox_device_key, std::unique_ptr* new_session) const; // Generate a new device DRM certificate to be provisioned by means other than @@ -133,13 +173,15 @@ class ProvisioningEngine { // * |certificate| will contain, upon successful return the generated // certificate. // * Returns OK on success, or an appropriate error status code otherwise. - ProvisioningStatus GenerateDeviceDrmCertificate( - uint32_t system_id, - const std::string& public_key, - const std::string& serial_number, - std::string* certificate) const; + ProvisioningStatus GenerateDeviceDrmCertificate(uint32_t system_id, + const std::string& public_key, + const std::string& serial_number, + std::string* certificate) const; private: + std::map + protocol_registry_; + #ifndef SWIGPYTHON ProvisioningEngine(const ProvisioningEngine&) = delete; ProvisioningEngine& operator=(const ProvisioningEngine&) = delete; diff --git a/provisioning_sdk/public/provisioning_engine_test.cc b/provisioning_sdk/public/provisioning_engine_test.cc index 1a5a236..151c797 100644 --- a/provisioning_sdk/public/provisioning_engine_test.cc +++ b/provisioning_sdk/public/provisioning_engine_test.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -8,7 +8,7 @@ #include "provisioning_sdk/public/provisioning_engine.h" -#include "gtest/gtest.h" +#include "testing/gunit.h" namespace { const int kSystemId = 100; diff --git a/provisioning_sdk/public/provisioning_session.cc b/provisioning_sdk/public/provisioning_session.cc index 2041e69..46ba78f 100644 --- a/provisioning_sdk/public/provisioning_session.cc +++ b/provisioning_sdk/public/provisioning_session.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -12,10 +12,16 @@ #include "glog/logging.h" #include "provisioning_sdk/internal/provisioning_session_impl.h" -#include "protos/public/device_certificate.pb.h" +#include "protos/public/drm_certificate.pb.h" namespace widevine { +ProvisioningSession::ProvisioningSession( + std::unique_ptr impl) + : impl_(std::move(impl)) { + DCHECK(impl_); +} + ProvisioningSession::ProvisioningSession() {} ProvisioningSession::~ProvisioningSession() {} @@ -32,8 +38,7 @@ ProvisioningStatus ProvisioningSession::ProcessMessage(const std::string& messag return INTERNAL_ERROR; } - ProvisioningStatus status = impl_->ProcessMessage(message, response); - *done = true; + ProvisioningStatus status = impl_->ProcessMessage(message, response, done); return status; } @@ -41,10 +46,4 @@ const ProvisionedDeviceInfo* ProvisioningSession::GetDeviceInfo() const { return impl_->GetDeviceInfo(); } -ProvisioningSession::ProvisioningSession( - std::unique_ptr impl) - : impl_(std::move(impl)) { - DCHECK(impl_); -} - } // namespace widevine diff --git a/provisioning_sdk/public/provisioning_session.h b/provisioning_sdk/public/provisioning_session.h index abdce8f..6ea2190 100644 --- a/provisioning_sdk/public/provisioning_session.h +++ b/provisioning_sdk/public/provisioning_session.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -29,7 +29,7 @@ class ProvisioningSession { // * |response| will contain, upon successful return, a message to be sent // back to the client device as a response to |message|. // * |done| will indicate, upon successful return, whether the provisioning - // exchange is complete, and the ProvisioningSession can be deleted. + // exchange is complete. // Returns OK if successful, or an appropriate error status code otherwise. virtual ProvisioningStatus ProcessMessage(const std::string& message, std::string* response, @@ -49,6 +49,7 @@ class ProvisioningSession { ProvisioningSession(const ProvisioningSession&) = delete; ProvisioningSession& operator=(const ProvisioningSession&) = delete; #endif + explicit ProvisioningSession(std::unique_ptr impl); std::unique_ptr impl_; diff --git a/provisioning_sdk/public/provisioning_status.cc b/provisioning_sdk/public/provisioning_status.cc index 9668531..fb167e7 100644 --- a/provisioning_sdk/public/provisioning_status.cc +++ b/provisioning_sdk/public/provisioning_status.cc @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -26,8 +26,8 @@ static const char* kProvisioningStatusMessage[] = { "Invalid status list", "Status list expired", "Unknown system id", - "Invalid device public key", - "Invalid device private key", + "Invalid DRM device public key", + "Invalid DRM device private key", "Invalid request message", "Invalid MAC", "Missing DRM intermediate certificate", @@ -35,10 +35,19 @@ static const char* kProvisioningStatusMessage[] = { "Device revoked", "Invalid serial number", "Internal error", - "Invalid SPOID secret sauce" + "Invalid SPOID secret sauce", + "Unsupported provisioning protocol", + "Invalid context key data", + "Provisioning context verification failed", + "Protocol error", + "Invalid keybox device key", + "Invalid session keys", + "Invalid pre-provisioning key", + "Missing pre-provisioning key", + "Remote attestation verification failure", }; -const char* GetProvisioningStatusMessage(ProvisioningStatus status) { +std::string GetProvisioningStatusMessage(ProvisioningStatus status) { static_assert( arraysize(kProvisioningStatusMessage) == NUM_PROVISIONING_STATUS, "mismatching provisioning status message and provisioning status."); diff --git a/provisioning_sdk/public/provisioning_status.h b/provisioning_sdk/public/provisioning_status.h index 158f418..342868e 100644 --- a/provisioning_sdk/public/provisioning_status.h +++ b/provisioning_sdk/public/provisioning_status.h @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 Google Inc. +// 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 @@ -9,6 +9,8 @@ #ifndef PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_ #define PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_ +#include + namespace widevine { enum ProvisioningStatus { @@ -16,8 +18,6 @@ enum ProvisioningStatus { INVALID_CERTIFICATE_TYPE = 1, PROVISIONING_ENGINE_UNINITIALIZED = 2, INVALID_SERVICE_DRM_CERTIFICATE = 3, - // Invalid service private key or private key passphrase. - INVALID_SERVICE_PRIVATE_KEY = 4, INVALID_PROVISIONER_DRM_CERTIFICATE = 5, // Invalid provisioner private key or private key passphrase. INVALID_PROVISIONER_PRIVATE_KEY = 6, @@ -28,21 +28,30 @@ enum ProvisioningStatus { INVALID_STATUS_LIST = 10, STATUS_LIST_EXPIRED = 11, UNKNOWN_SYSTEM_ID = 12, - INVALID_DEVICE_PUBLIC_KEY = 13, - INVALID_DEVICE_PRIVATE_KEY = 14, + INVALID_DRM_DEVICE_PUBLIC_KEY = 13, + INVALID_DRM_DEVICE_PRIVATE_KEY = 14, INVALID_REQUEST_MESSAGE = 15, INVALID_MAC = 16, - MISSING_DRM_INTERMEDIATE_CERT = 17, + MISSING_DEVICE_MODEL_CERT = 17, DRM_DEVICE_CERTIFICATE_NOT_SET = 18, DEVICE_REVOKED = 19, INVALID_SERIAL_NUMBER = 20, INTERNAL_ERROR = 21, INVALID_SPOID_SAUCE = 22, + INVALID_PROTOCOL = 23, + INVALID_CONTEXT_KEY_DATA = 24, + INVALID_CONTEXT = 25, + PROTOCOL_ERROR = 26, + INVALID_KEYBOX_DEVICE_KEY = 27, + INVALID_SESSION_KEYS = 28, + INVALID_PREPROV_KEY = 29, + MISSING_PREPROV_KEY = 30, + REMOTE_ATTESTATION_VERIFICATION_FAILURE = 31, NUM_PROVISIONING_STATUS, }; // Returns the message std::string for the given ProvisioningStatus. -const char* GetProvisioningStatusMessage(ProvisioningStatus status); +std::string GetProvisioningStatusMessage(ProvisioningStatus status); } // namespace widevine diff --git a/provisioning_sdk/public/python/BUILD b/provisioning_sdk/public/python/BUILD index c9fc5bc..1c56549 100644 --- a/provisioning_sdk/public/python/BUILD +++ b/provisioning_sdk/public/python/BUILD @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -69,7 +69,7 @@ py_test( deps = [ ":crypto_utility", ":test_data_utility", - "//protos/public:signed_device_certificate_py_pb2", + "//protos/public:signed_drm_certificate_py_pb2", ], ) @@ -81,6 +81,6 @@ py_test( ":crypto_utility", ":test_data_utility", "//protos/public:certificate_provisioning_py_pb2", - "//protos/public:signed_device_certificate_py_pb2", + "//protos/public:signed_drm_certificate_py_pb2", ], ) diff --git a/provisioning_sdk/public/python/base.i b/provisioning_sdk/public/python/base.i deleted file mode 100644 index 9154f77..0000000 --- a/provisioning_sdk/public/python/base.i +++ /dev/null @@ -1,27 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 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. -//////////////////////////////////////////////////////////////////////////////// - -%include "std_string.i" -%include "typemaps.i" - -%define %ignoreall %ignore ""; %enddef -%define %unignore %rename("%s") %enddef -%define %unignoreall %rename("%s") ""; %enddef - -%define COPY_TYPEMAPS(oldtype, newtype) -typedef oldtype newtype; -%apply oldtype * OUTPUT { newtype * OUTPUT }; -%apply oldtype & OUTPUT { newtype & OUTPUT }; -%apply oldtype * INPUT { newtype * INPUT }; -%apply oldtype & INPUT { newtype & INPUT }; -%apply oldtype * INOUT { newtype * INOUT }; -%apply oldtype & INOUT { newtype & INOUT }; -%enddef - -COPY_TYPEMAPS(int, int32_t); -COPY_TYPEMAPS(unsigned int, uint32_t); diff --git a/provisioning_sdk/public/python/certificate_type.i b/provisioning_sdk/public/python/certificate_type.i deleted file mode 100644 index c83aaa3..0000000 --- a/provisioning_sdk/public/python/certificate_type.i +++ /dev/null @@ -1,28 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 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. -//////////////////////////////////////////////////////////////////////////////// - -// Swig file to generate a Python library for: -// provisioning_sdk/public/certificate_type.h - -%module pywrapcertificate_type - -%include "base.i" - -%{ -#include "provisioning_sdk/public/certificate_type.h" -%} - -%ignoreall - -%unignore widevine; -%unignore widevine::CertificateType; -%unignore widevine::kCertTesting; -%unignore widevine::kCertDevelopment; -%include "provisioning_sdk/public/certificate_type.h" - -%unignoreall diff --git a/provisioning_sdk/public/python/certificate_type_setup.py b/provisioning_sdk/public/python/certificate_type_setup.py new file mode 100644 index 0000000..5fdc991 --- /dev/null +++ b/provisioning_sdk/public/python/certificate_type_setup.py @@ -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. +################################################################################ +"""Installation file for the certificate_type module.""" + +import setup_common as common +import setuptools + +if __name__ == '__main__': + setuptools.setup( + name='certificate_type', + ext_modules=[ + setuptools.Extension( + name='certificate_type', + sources=[ + '%s/certificate_type.cc' % common.WVCOMMON_SRC_DIR, + '%s/initcertificate_type.cc' % common.WVCOMMON_SRC_DIR, + '%s/clif/python/runtime.cc' % common.CLIF_PREFIX, + '%s/clif/python/slots.cc' % common.CLIF_PREFIX, + '%s/clif/python/types.cc' % common.CLIF_PREFIX, + ], + include_dirs=[ + common.SDK_ROOT_DIR, common.GEN_DIR, common.CLIF_PREFIX, '/' + ], + extra_compile_args=['-std=c++11'], + library_dirs=[common.SDK_LIBRARY_DIR], + libraries=['provisioning_sdk'], + runtime_library_dirs=[common.SDK_LIBRARY_DIR], + install_requires=['enum34;python_version<"3.4"'], + ), + ], + ) diff --git a/provisioning_sdk/public/python/crypto_utility.py b/provisioning_sdk/public/python/crypto_utility.py index a637f98..232dcdb 100644 --- a/provisioning_sdk/public/python/crypto_utility.py +++ b/provisioning_sdk/public/python/crypto_utility.py @@ -1,11 +1,10 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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. ################################################################################ - """Utility functions for cryptography.""" import logging diff --git a/provisioning_sdk/public/python/drm_intermediate_certificate_test.py b/provisioning_sdk/public/python/drm_intermediate_certificate_test.py index 5151990..940b69c 100644 --- a/provisioning_sdk/public/python/drm_intermediate_certificate_test.py +++ b/provisioning_sdk/public/python/drm_intermediate_certificate_test.py @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -8,15 +8,15 @@ import unittest -import pywrapprovisioning_engine -import pywrapprovisioning_status import test_data_utility +from provisioning_engine import ProvisioningEngine +from provisioning_status import ProvisioningStatus class AddDrmIntermediateTest(unittest.TestCase): def setUp(self): - self._engine = pywrapprovisioning_engine.ProvisioningEngine() + self._engine = ProvisioningEngine() test_data_utility.InitProvisionEngineWithTestData( self._engine, verify_success=True) @@ -30,7 +30,7 @@ class AddDrmIntermediateTest(unittest.TestCase): def testSetCertificateStatusListInvalid(self): set_cert_status_list = self._engine.SetCertificateStatusList( 'INVALID_STATUS_LIST', 0) - self.assertEqual(pywrapprovisioning_status.INVALID_STATUS_LIST, + self.assertEqual(ProvisioningStatus.INVALID_STATUS_LIST, set_cert_status_list) def testAddDrmIntermediateCertificateWithoutCertificateStatusList(self): @@ -38,7 +38,7 @@ class AddDrmIntermediateTest(unittest.TestCase): # certificate status list. status = test_data_utility.AddDrmIntermediateCertificateWithTestData( self._engine, 2001) - self.assertEqual(pywrapprovisioning_status.STATUS_LIST_EXPIRED, status) + self.assertEqual(ProvisioningStatus.STATUS_LIST_EXPIRED, status) def testAddDrmIntermediateCertificateSystemIdInvalid(self): test_data_utility.SetCertificateStatusListWithTestData( @@ -47,7 +47,7 @@ class AddDrmIntermediateTest(unittest.TestCase): # system_id 9999 is not in the sample certificate status list add_ca_status = test_data_utility.AddDrmIntermediateCertificateWithTestData( self._engine, 9999) - self.assertEqual(pywrapprovisioning_status.UNKNOWN_SYSTEM_ID, add_ca_status) + self.assertEqual(ProvisioningStatus.UNKNOWN_SYSTEM_ID, add_ca_status) if __name__ == '__main__': diff --git a/provisioning_sdk/public/python/engine_generate_certificate_test.py b/provisioning_sdk/public/python/engine_generate_certificate_test.py index a297a87..e0038c6 100644 --- a/provisioning_sdk/public/python/engine_generate_certificate_test.py +++ b/provisioning_sdk/public/python/engine_generate_certificate_test.py @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -8,19 +8,19 @@ import unittest +from certificate_type import CertificateType import crypto_utility -import pywrapcertificate_type -import pywrapprovisioning_engine -import pywrapprovisioning_status import test_data_provider import test_data_utility -from protos.public import signed_device_certificate_pb2 +from provisioning_engine import ProvisioningEngine +from provisioning_status import ProvisioningStatus +from protos.public import signed_drm_certificate_pb2 class EngineGenerateCertificateTest(unittest.TestCase): def setUp(self): - self._engine = pywrapprovisioning_engine.ProvisioningEngine() + self._engine = ProvisioningEngine() test_data_utility.InitProvisionEngineWithTestData( self._engine, verify_success=True) test_data_utility.SetCertificateStatusListWithTestData( @@ -28,14 +28,14 @@ class EngineGenerateCertificateTest(unittest.TestCase): test_data_utility.AddDrmIntermediateCertificateWithTestData( self._engine, 2001, verify_success=True) self._data_provider = test_data_provider.TestDataProvider( - pywrapcertificate_type.kCertTesting) + CertificateType.kCertificateTypeTesting) def testSuccess(self): status, signed_cert_string = self._engine.GenerateDeviceDrmCertificate( 2001, self._data_provider.device_public_key, 'DEVICE_SERIAL_NUMBER') - self.assertEqual(pywrapprovisioning_status.OK, status) + self.assertEqual(ProvisioningStatus.OK, status) - signed_cert = signed_device_certificate_pb2.SignedDrmDeviceCertificate() + signed_cert = signed_drm_certificate_pb2.SignedDrmCertificate() signed_cert.ParseFromString(signed_cert_string) crypto_utility.VerifySignature(self._data_provider.ca_public_key, signed_cert.signature, @@ -44,25 +44,24 @@ class EngineGenerateCertificateTest(unittest.TestCase): def testEmptySerialNumber(self): status, _ = self._engine.GenerateDeviceDrmCertificate( 2001, self._data_provider.device_public_key, '') - self.assertEqual(pywrapprovisioning_status.INVALID_SERIAL_NUMBER, status) + self.assertEqual(ProvisioningStatus.INVALID_SERIAL_NUMBER, status) def testEmptyPublicKey(self): status, _ = self._engine.GenerateDeviceDrmCertificate( 2001, '', 'DEVICE_SERIAL_NUMBER') - self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY, - status) + self.assertEqual(ProvisioningStatus.INVALID_DRM_DEVICE_PUBLIC_KEY, status) def testInvalidPublicKey(self): status, _ = self._engine.GenerateDeviceDrmCertificate( 2001, 'PUBLIC_KEY_MUST_BE_IN_DER_ENCODED_PKCS1_FORMAT', 'DEVICE_SERIAL_NUMBER') - self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY, - status) + self.assertEqual(ProvisioningStatus.INVALID_DRM_DEVICE_PUBLIC_KEY, status) def testMissingIntermediateCertificate(self): status, _ = self._engine.GenerateDeviceDrmCertificate( 2002, self._data_provider.device_public_key, 'DEVICE_SERIAL_NUMBER') - self.assertEqual(pywrapprovisioning_status.DEVICE_REVOKED, status) + self.assertEqual(ProvisioningStatus.UNKNOWN_SYSTEM_ID, status) + if __name__ == '__main__': unittest.main() diff --git a/provisioning_sdk/public/python/init_engine_test.py b/provisioning_sdk/public/python/init_engine_test.py index 238baa6..cb1e352 100644 --- a/provisioning_sdk/public/python/init_engine_test.py +++ b/provisioning_sdk/public/python/init_engine_test.py @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -8,83 +8,85 @@ import unittest -import pywrapcertificate_type -import pywrapprovisioning_engine -import pywrapprovisioning_status +from certificate_type import CertificateType import test_data_provider import test_data_utility +from provisioning_engine import ProvisioningEngine +from provisioning_status import ProvisioningStatus +from protos.public import certificate_provisioning_pb2 class InitEngineTest(unittest.TestCase): def setUp(self): - self._engine = pywrapprovisioning_engine.ProvisioningEngine() + self._engine = ProvisioningEngine() self._data_provider = test_data_provider.TestDataProvider( - pywrapcertificate_type.kCertTesting) + CertificateType.kCertificateTypeTesting) + self._prov30 = ( + certificate_provisioning_pb2.SignedProvisioningMessage.PROVISIONING_30) def testInitEngineSucceed(self): - test_data_utility.InitProvisionEngineWithTestData( + status = test_data_utility.InitProvisionEngineWithTestData( self._engine, verify_success=True) + self.assertEqual(ProvisioningStatus.OK, status) def testSetCertificateStatusListWithoutInit(self): status = self._engine.SetCertificateStatusList('CERTIFICATE_STATUS_LIST', 3600) - self.assertEqual( - pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status) + self.assertEqual(ProvisioningStatus.PROVISIONING_ENGINE_UNINITIALIZED, + status) def testGenerateDrmIntermediateCertificateWithoutInit(self): status, _ = self._engine.GenerateDrmIntermediateCertificate( 100, 'INTERMEDIATE_PUBLIC_KEY') - self.assertEqual( - pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status) + self.assertEqual(ProvisioningStatus.PROVISIONING_ENGINE_UNINITIALIZED, + status) def testAddDrmIntermediateCertificateWithoutInit(self): status = self._engine.AddDrmIntermediateCertificate( 'INTERMEDIATE_CERTIFICATE', 'INTERMEDIATE_PRIVATE_KEY', 'INTERMEDIATE_PRIVATE_KEY_PASSPHRASE') - self.assertEqual( - pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status) + self.assertEqual(ProvisioningStatus.PROVISIONING_ENGINE_UNINITIALIZED, + status) def testGenerateDeviceDrmCertificateWithoutInit(self): status, _ = self._engine.GenerateDeviceDrmCertificate( 100, 'DEVICE_PUBLIC_KEY', 'DEVICE_SERIAL_NUMBER') - self.assertEqual( - pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status) + self.assertEqual(ProvisioningStatus.PROVISIONING_ENGINE_UNINITIALIZED, + status) def testNewProvisioningSessionWithoutInit(self): - status, session = self._engine.NewProvisioningSession('DEVICE_PUBLIC_KEY', - 'DEVICE_PRIVATE_KEY') - self.assertEqual( - pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status) + session, status = self._engine.NewProvisioningSession( + self._prov30, 'DEVICE_PUBLIC_KEY', 'DEVICE_PRIVATE_KEY') + self.assertEqual(ProvisioningStatus.PROVISIONING_ENGINE_UNINITIALIZED, + status) self.assertIsNone(session) def testInitEngineInvalidServiceDrmCert(self): status = self._engine.Initialize( - pywrapcertificate_type.kCertTesting, 'INVALID_CERT', + CertificateType.kCertificateTypeTesting, 'INVALID_CERT', self._data_provider.service_private_key, self._data_provider.service_private_key_passphrase, self._data_provider.provisioner_drm_cert, self._data_provider.provisioner_private_key, self._data_provider.provisioner_private_key_passphrase, self._data_provider.provisioner_spoid_secret) - self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_DRM_CERTIFICATE, - status) + self.assertEqual(ProvisioningStatus.INVALID_SERVICE_DRM_CERTIFICATE, status) def testInitEngineInvalidServicePrivateKey(self): status = self._engine.Initialize( - pywrapcertificate_type.kCertTesting, + CertificateType.kCertificateTypeTesting, self._data_provider.service_drm_cert, 'INVALID_KEY', self._data_provider.service_private_key_passphrase, self._data_provider.provisioner_drm_cert, self._data_provider.provisioner_private_key, self._data_provider.provisioner_private_key_passphrase, self._data_provider.provisioner_spoid_secret) - self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY, - status) + self.assertEqual(ProvisioningStatus.INVALID_SERVICE_DRM_CERTIFICATE, status) def testInitEngineWrongServicePrivateKey(self): status = self._engine.Initialize( - pywrapcertificate_type.kCertTesting, + CertificateType.kCertificateTypeTesting, self._data_provider.service_drm_cert, self._data_provider.provisioner_private_key, self._data_provider.service_private_key_passphrase, @@ -92,48 +94,45 @@ class InitEngineTest(unittest.TestCase): self._data_provider.provisioner_private_key, self._data_provider.provisioner_private_key_passphrase, self._data_provider.provisioner_spoid_secret) - self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY, - status) + self.assertEqual(ProvisioningStatus.INVALID_SERVICE_DRM_CERTIFICATE, status) def testInitEngineInvalidServicePrivateKeyPassphrase(self): status = self._engine.Initialize( - pywrapcertificate_type.kCertTesting, + CertificateType.kCertificateTypeTesting, self._data_provider.service_drm_cert, self._data_provider.service_private_key, 'INVALID_PASSPHRASE', self._data_provider.provisioner_drm_cert, self._data_provider.provisioner_private_key, self._data_provider.provisioner_private_key_passphrase, self._data_provider.provisioner_spoid_secret) - self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY, - status) + self.assertEqual(ProvisioningStatus.INVALID_SERVICE_DRM_CERTIFICATE, status) def testInitEngineInvalidDrmCert(self): status = self._engine.Initialize( - pywrapcertificate_type.kCertTesting, + CertificateType.kCertificateTypeTesting, self._data_provider.service_drm_cert, self._data_provider.service_private_key, self._data_provider.service_private_key_passphrase, 'INVALID_CERT', self._data_provider.provisioner_private_key, self._data_provider.provisioner_private_key_passphrase, self._data_provider.provisioner_spoid_secret) - self.assertEqual( - pywrapprovisioning_status.INVALID_PROVISIONER_DRM_CERTIFICATE, status) + self.assertEqual(ProvisioningStatus.INVALID_PROVISIONER_DRM_CERTIFICATE, + status) def testInitEngineInvalidDrmPrivateKey(self): status = self._engine.Initialize( - pywrapcertificate_type.kCertTesting, + CertificateType.kCertificateTypeTesting, self._data_provider.service_drm_cert, self._data_provider.service_private_key, self._data_provider.service_private_key_passphrase, self._data_provider.provisioner_drm_cert, 'INVALID_KEY', self._data_provider.provisioner_private_key_passphrase, self._data_provider.provisioner_spoid_secret) - self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY, - status) + self.assertEqual(ProvisioningStatus.INVALID_PROVISIONER_PRIVATE_KEY, status) def testInitEngineWrongDrmPrivateKey(self): status = self._engine.Initialize( - pywrapcertificate_type.kCertTesting, + CertificateType.kCertificateTypeTesting, self._data_provider.service_drm_cert, self._data_provider.service_private_key, self._data_provider.service_private_key_passphrase, @@ -141,32 +140,30 @@ class InitEngineTest(unittest.TestCase): self._data_provider.service_private_key, self._data_provider.provisioner_private_key_passphrase, self._data_provider.provisioner_spoid_secret) - self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY, - status) + self.assertEqual(ProvisioningStatus.INVALID_PROVISIONER_PRIVATE_KEY, status) def testInitEngineInvalidDrmPrivateKeyPassphrase(self): status = self._engine.Initialize( - pywrapcertificate_type.kCertTesting, + CertificateType.kCertificateTypeTesting, self._data_provider.service_drm_cert, self._data_provider.service_private_key, self._data_provider.service_private_key_passphrase, self._data_provider.provisioner_drm_cert, self._data_provider.provisioner_private_key_passphrase, - 'INVALID_PASSPHRASE', - self._data_provider.provisioner_spoid_secret) - self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY, - status) + 'INVALID_PASSPHRASE', self._data_provider.provisioner_spoid_secret) + self.assertEqual(ProvisioningStatus.INVALID_PROVISIONER_PRIVATE_KEY, status) def testInitEngineInvalidSpoidSecret(self): status = self._engine.Initialize( - pywrapcertificate_type.kCertTesting, + CertificateType.kCertificateTypeTesting, self._data_provider.service_drm_cert, self._data_provider.service_private_key, self._data_provider.service_private_key_passphrase, self._data_provider.provisioner_drm_cert, self._data_provider.provisioner_private_key, self._data_provider.provisioner_private_key_passphrase, '') - self.assertEqual(pywrapprovisioning_status.INVALID_SPOID_SAUCE, status) + self.assertEqual(ProvisioningStatus.INVALID_SPOID_SAUCE, status) + if __name__ == '__main__': unittest.main() diff --git a/provisioning_sdk/public/python/new_session_test.py b/provisioning_sdk/public/python/new_session_test.py index 826a08c..4c6182d 100644 --- a/provisioning_sdk/public/python/new_session_test.py +++ b/provisioning_sdk/public/python/new_session_test.py @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -8,36 +8,38 @@ import unittest +from certificate_type import CertificateType import crypto_utility -import pywrapcertificate_type -import pywrapprovisioning_engine -import pywrapprovisioning_status import test_data_provider import test_data_utility +from provisioning_engine import ProvisioningEngine +from provisioning_status import ProvisioningStatus from protos.public import certificate_provisioning_pb2 -from protos.public import signed_device_certificate_pb2 +from protos.public import signed_drm_certificate_pb2 class NewSessionTest(unittest.TestCase): def setUp(self): - self._engine = pywrapprovisioning_engine.ProvisioningEngine() + self._engine = ProvisioningEngine() test_data_utility.InitProvisionEngineWithTestData( self._engine, verify_success=True) test_data_utility.SetCertificateStatusListWithTestData( self._engine, 0, verify_success=True) self._data_provider = test_data_provider.TestDataProvider( - pywrapcertificate_type.kCertTesting) + CertificateType.kCertificateTypeTesting) + self._prov30 = ( + certificate_provisioning_pb2.SignedProvisioningMessage.PROVISIONING_30) def testNewSessionSuccess(self): test_data_utility.AddDrmIntermediateCertificateWithTestData( self._engine, 2001, verify_success=True) - (_, new_session) = test_data_utility.NewProvisioningSessionWithTestData( + (new_session, _) = test_data_utility.NewProvisioningSessionWithTestData( self._engine, verify_success=True) - (status, raw_response, - _) = new_session.ProcessMessage(self._data_provider.message) - test_data_utility.AssertSuccess(status, 'Failed to create session.') + (status, raw_response, _) = new_session.ProcessMessage( + self._data_provider.message) + assert ProvisioningStatus.OK == status signed_request = test_data_utility.ConvertToSignedProvisioningMessage( self._data_provider.message) @@ -59,35 +61,44 @@ class NewSessionTest(unittest.TestCase): def testProcessInvalidMessage(self): test_data_utility.AddDrmIntermediateCertificateWithTestData( self._engine, 2001, verify_success=True) - (_, new_session - ) = test_data_utility.NewProvisioningSessionWithTestData(self._engine) + (new_session, _) = test_data_utility.NewProvisioningSessionWithTestData( + self._engine) (status, _, _) = new_session.ProcessMessage('INVALID_MESSAGE') - self.assertEqual(pywrapprovisioning_status.INVALID_REQUEST_MESSAGE, status) + self.assertEqual(ProvisioningStatus.INVALID_REQUEST_MESSAGE, status) def testNewSessionWithoutIntermediateCert(self): - (_, new_session) = test_data_utility.NewProvisioningSessionWithTestData( + (new_session, _) = test_data_utility.NewProvisioningSessionWithTestData( self._engine, verify_success=True) - (status, _, _) = new_session.ProcessMessage( - self._data_provider.message) - self.assertEqual(pywrapprovisioning_status.MISSING_DRM_INTERMEDIATE_CERT, + (status, _, _) = new_session.ProcessMessage(self._data_provider.message) + self.assertEqual(ProvisioningStatus.MISSING_DEVICE_MODEL_CERT, status) def testNewSessionInvalidDevicePublicKey(self): test_data_utility.AddDrmIntermediateCertificateWithTestData( self._engine, 2001, verify_success=True) - (session_status, _) = self._engine.NewProvisioningSession( - 'INVALID_PUBLIC_KEY', self._data_provider.device_private_key) - self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY, + (_, session_status) = self._engine.NewProvisioningSession( + self._prov30, 'INVALID_PUBLIC_KEY', + self._data_provider.device_private_key) + self.assertEqual(ProvisioningStatus.INVALID_DRM_DEVICE_PUBLIC_KEY, session_status) def testNewSessionInvalidDevicePrivateKey(self): test_data_utility.AddDrmIntermediateCertificateWithTestData( self._engine, 2001, verify_success=True) - (session_status, _) = self._engine.NewProvisioningSession( - self._data_provider.device_public_key, 'INVALID_PRIVATE_KEY') - self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PRIVATE_KEY, + (_, session_status) = self._engine.NewProvisioningSession( + self._prov30, self._data_provider.device_public_key, + 'INVALID_PRIVATE_KEY') + self.assertEqual(ProvisioningStatus.INVALID_DRM_DEVICE_PRIVATE_KEY, session_status) + def testNewSessionInvalidProtocol(self): + test_data_utility.AddDrmIntermediateCertificateWithTestData( + self._engine, 2001, verify_success=True) + (_, session_status) = self._engine.NewProvisioningSession( + 1234, self._data_provider.device_public_key, + self._data_provider.device_private_key) + self.assertEqual(ProvisioningStatus.INVALID_PROTOCOL, session_status) + def _VerifyMessageSignature(self, public_key, signed_response): crypto_utility.VerifySignature(public_key, signed_response.signature, signed_response.message) @@ -99,11 +110,11 @@ class NewSessionTest(unittest.TestCase): def _VerifyProvisioningResponse(self, request, response): self.assertEqual(request.nonce, response.nonce) - signed_cert = signed_device_certificate_pb2.SignedDrmDeviceCertificate() + signed_cert = signed_drm_certificate_pb2.SignedDrmCertificate() signed_cert.ParseFromString(response.device_certificate) - self._VerifyCertSignature(self._data_provider.ca_public_key, - signed_cert) + self._VerifyCertSignature(self._data_provider.ca_public_key, signed_cert) + if __name__ == '__main__': unittest.main() diff --git a/provisioning_sdk/public/python/provisioning_engine.clif b/provisioning_sdk/public/python/provisioning_engine.clif new file mode 100644 index 0000000..72339b5 --- /dev/null +++ b/provisioning_sdk/public/python/provisioning_engine.clif @@ -0,0 +1,46 @@ +################################################################################ +# 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. +################################################################################ + +from "common/python/certificate_type.h" import * +from "provisioning_sdk/public/python/provisioning_status.h" import * +from "provisioning_sdk/public/python/provisioning_session.h" import * +from "protos/public/certificate_provisioning_pyclif.h" import * + +from "provisioning_sdk/public/provisioning_engine.h": + namespace `widevine`: + class ProvisioningEngine: + def Initialize(self, + certificate_type: CertificateType, + service_certificate: bytes, + service_private_key: bytes, + service_private_key_passhprase: bytes, + provisioning_drm_certificate: bytes, + provisioning_private_key: bytes, + provisioning_private_key_passhprase: bytes, + secret_spoid_sauce: bytes) -> ProvisioningStatus + def SetCertificateStatusList(self, + certificate_status_list: bytes, + expiration_period_seconds: int) -> ProvisioningStatus + def GenerateDrmIntermediateCertificate(self, + system_id: int, + public_key: bytes) -> (status: ProvisioningStatus, + certificate: bytes) + def AddDrmIntermediateCertificate(self, + intermediate_cert: bytes, + cert_private_key: bytes, + cert_private_key_passhprase: bytes) -> ProvisioningStatus + def NewProvisioningSession(self, + protocol: SignedProvisioningMessage.ProtocolVersion, + device_public_key: bytes, + device_private_key: bytes) -> (new_session: ProvisioningSession, + status: ProvisioningStatus) + def GenerateDeviceDrmCertificate(self, + system_id: int, + public_key: bytes, + serial_number: bytes) -> (status: ProvisioningStatus, + certificate: bytes) diff --git a/provisioning_sdk/public/python/provisioning_engine.i b/provisioning_sdk/public/python/provisioning_engine.i deleted file mode 100644 index 7159148..0000000 --- a/provisioning_sdk/public/python/provisioning_engine.i +++ /dev/null @@ -1,46 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 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. -//////////////////////////////////////////////////////////////////////////////// - -// Swig file to generate a Python library for: -// provisioning_sdk/public/provisioning_engine.h - -%module pywrapprovisioning_engine - -%include "base.i" -%include "unique_ptr.i" -%import(module="pywrapprovisioning_session") "provisioning_sdk/public/python/provisioning_session.i" - -UNIQUE_PTR_ARGOUT(widevine::ProvisioningSession, new_session); - -%apply int { CertificateType, ProvisioningStatus }; -%apply std::string* OUTPUT { std::string* certificate }; - -%{ -#include "provisioning_sdk/public/provisioning_engine.h" -#include "provisioning_sdk/public/provisioning_session.h" -using namespace widevine; -%} - -%ignoreall - -%unignore widevine; -%unignore widevine::ProvisioningSession; - -%unignore widevine::ProvisioningEngine; -%unignore widevine::ProvisioningEngine::ProvisioningEngine; -%unignore widevine::ProvisioningEngine::~ProvisioningEngine; -%unignore widevine::ProvisioningEngine::SetCertificateStatusList; -%unignore widevine::ProvisioningEngine::Initialize; -%unignore widevine::ProvisioningEngine::GenerateDrmIntermediateCertificate; -%unignore widevine::ProvisioningEngine::AddDrmIntermediateCertificate; -%unignore widevine::ProvisioningEngine::NewProvisioningSession; -%unignore widevine::ProvisioningEngine::GenerateDeviceDrmCertificate; - -%include "provisioning_sdk/public/provisioning_engine.h" - -%unignoreall diff --git a/provisioning_sdk/public/python/provisioning_engine_setup.py b/provisioning_sdk/public/python/provisioning_engine_setup.py new file mode 100644 index 0000000..db4084e --- /dev/null +++ b/provisioning_sdk/public/python/provisioning_engine_setup.py @@ -0,0 +1,47 @@ +################################################################################ +# 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. +################################################################################ +"""Installation file for the provisioning_engine module.""" + +import certificate_type +import provisioning_session +import provisioning_status +import setup_common as common +import setuptools + +if __name__ == '__main__': + setuptools.setup( + name='provisioning_engine', + ext_modules=[ + setuptools.Extension( + name='provisioning_engine', + sources=[ + '%s/provisioning_engine.cc' % common.SDK_SRC_DIR, + '%s/initprovisioning_engine.cc' % common.SDK_SRC_DIR, + '%s/clif/python/pyproto.cc' % common.CLIF_PREFIX, + '%s/clif/python/runtime.cc' % common.CLIF_PREFIX, + '%s/clif/python/slots.cc' % common.CLIF_PREFIX, + '%s/clif/python/types.cc' % common.CLIF_PREFIX, + '%s/certificate_provisioning_pyclif.cc' % + common.WVPROTO_SRC_DIR, + ], + include_dirs=[ + common.SDK_ROOT_DIR, common.GEN_DIR, common.CLIF_PREFIX, '/' + ], + extra_compile_args=['-std=c++11'], + library_dirs=[common.SDK_LIBRARY_DIR], + libraries=['provisioning_sdk', 'protobuf'], + runtime_library_dirs=[common.SDK_LIBRARY_DIR], + install_requires=['enum34;python_version<"3.4"'], + extra_objects=[ + certificate_type.__file__, + provisioning_status.__file__, + provisioning_session.__file__, + ], + ), + ], + ) diff --git a/provisioning_sdk/public/python/provisioning_session.clif b/provisioning_sdk/public/python/provisioning_session.clif new file mode 100644 index 0000000..aca0ce6 --- /dev/null +++ b/provisioning_sdk/public/python/provisioning_session.clif @@ -0,0 +1,16 @@ +################################################################################ +# 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. +################################################################################ + +from "provisioning_sdk/public/python/provisioning_status.h" import * + +from "provisioning_sdk/public/provisioning_session.h": + namespace `widevine`: + class ProvisioningSession: + def ProcessMessage(self, message: bytes) -> (status: ProvisioningStatus, + response: bytes, + done: bool) diff --git a/provisioning_sdk/public/python/provisioning_session.i b/provisioning_sdk/public/python/provisioning_session.i deleted file mode 100644 index cbefa36..0000000 --- a/provisioning_sdk/public/python/provisioning_session.i +++ /dev/null @@ -1,37 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 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. -//////////////////////////////////////////////////////////////////////////////// - -// Swig file to generate a Python library for: -// provisioning_sdk/public/provisioning_session.h - -%module pywrapprovisioning_session - -%include "base.i" - -%apply bool* OUTPUT { bool* done }; - -%apply int { ProvisioningStatus }; - -%apply std::string* OUTPUT { std::string* response }; - -%{ -#include "provisioning_sdk/public/provisioning_session.h" -using namespace widevine; -%} - -%ignoreall - -%unignore widevine; -%unignore widevine::ProvisioningSession; -%unignore widevine::ProvisioningSession::~ProvisioningSession; -%unignore widevine::ProvisioningSession::ProcessMessage; - - -%include "provisioning_sdk/public/provisioning_session.h" - -%unignoreall diff --git a/provisioning_sdk/public/python/provisioning_session_setup.py b/provisioning_sdk/public/python/provisioning_session_setup.py new file mode 100644 index 0000000..4809994 --- /dev/null +++ b/provisioning_sdk/public/python/provisioning_session_setup.py @@ -0,0 +1,38 @@ +################################################################################ +# 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. +################################################################################ +"""Installation file for the provisioning_session module.""" + +import provisioning_status +import setup_common as common +import setuptools + +if __name__ == '__main__': + setuptools.setup( + name='provisioning_session', + ext_modules=[ + setuptools.Extension( + name='provisioning_session', + sources=[ + '%s/provisioning_session.cc' % common.SDK_SRC_DIR, + '%s/initprovisioning_session.cc' % common.SDK_SRC_DIR, + '%s/clif/python/runtime.cc' % common.CLIF_PREFIX, + '%s/clif/python/slots.cc' % common.CLIF_PREFIX, + '%s/clif/python/types.cc' % common.CLIF_PREFIX, + ], + include_dirs=[ + common.SDK_ROOT_DIR, common.GEN_DIR, common.CLIF_PREFIX, '/' + ], + extra_compile_args=['-std=c++11'], + library_dirs=[common.SDK_LIBRARY_DIR], + libraries=['provisioning_sdk'], + runtime_library_dirs=[common.SDK_LIBRARY_DIR], + install_requires=['enum34;python_version<"3.4"'], + extra_objects=[provisioning_status.__file__], + ), + ], + ) diff --git a/provisioning_sdk/public/python/provisioning_status.clif b/provisioning_sdk/public/python/provisioning_status.clif new file mode 100644 index 0000000..fea6cd9 --- /dev/null +++ b/provisioning_sdk/public/python/provisioning_status.clif @@ -0,0 +1,12 @@ +################################################################################ +# 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. +################################################################################ + +from "provisioning_sdk/public/provisioning_status.h": + namespace `widevine`: + enum ProvisioningStatus + def GetProvisioningStatusMessage(status: ProvisioningStatus) -> str diff --git a/provisioning_sdk/public/python/provisioning_status.i b/provisioning_sdk/public/python/provisioning_status.i deleted file mode 100644 index 4c6c1ae..0000000 --- a/provisioning_sdk/public/python/provisioning_status.i +++ /dev/null @@ -1,44 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 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. -//////////////////////////////////////////////////////////////////////////////// - -// Swig file to generate a Python library for: -// provisioning_sdk/public/provisioning_status.h - -%module pywrapprovisioning_status - -%include "base.i" - -%{ -#include "provisioning_sdk/public/provisioning_status.h" -%} - -%ignoreall - -%unignore widevine; -%unignore widevine::ProvisioningStatus; -%unignore widevine::OK; -%unignore widevine::PROVISIONING_ENGINE_UNINITIALIZED; -%unignore widevine::INVALID_SERVICE_DRM_CERTIFICATE; -%unignore widevine::INVALID_SERVICE_PRIVATE_KEY; -%unignore widevine::INVALID_PROVISIONER_DRM_CERTIFICATE; -%unignore widevine::INVALID_PROVISIONER_PRIVATE_KEY; -%unignore widevine::INVALID_STATUS_LIST; -%unignore widevine::STATUS_LIST_EXPIRED; -%unignore widevine::UNKNOWN_SYSTEM_ID; -%unignore widevine::INVALID_DEVICE_PUBLIC_KEY; -%unignore widevine::INVALID_DEVICE_PRIVATE_KEY; -%unignore widevine::INVALID_REQUEST_MESSAGE; -%unignore widevine::MISSING_DRM_INTERMEDIATE_CERT; -%unignore widevine::DEVICE_REVOKED; -%unignore widevine::INVALID_SERIAL_NUMBER; -%unignore widevine::INVALID_SPOID_SAUCE; -%unignore widevine::GetProvisioningStatusMessage; - -%include "provisioning_sdk/public/provisioning_status.h" - -%unignoreall diff --git a/provisioning_sdk/public/python/provisioning_status_setup.py b/provisioning_sdk/public/python/provisioning_status_setup.py new file mode 100644 index 0000000..f0c401f --- /dev/null +++ b/provisioning_sdk/public/python/provisioning_status_setup.py @@ -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. +################################################################################ +"""Installation file for the provisioning_status module.""" + +import setup_common as common +import setuptools + +if __name__ == '__main__': + setuptools.setup( + name='provisioning_status', + ext_modules=[ + setuptools.Extension( + name='provisioning_status', + sources=[ + '%s/provisioning_status.cc' % common.SDK_SRC_DIR, + '%s/initprovisioning_status.cc' % common.SDK_SRC_DIR, + '%s/clif/python/runtime.cc' % common.CLIF_PREFIX, + '%s/clif/python/slots.cc' % common.CLIF_PREFIX, + '%s/clif/python/types.cc' % common.CLIF_PREFIX, + ], + include_dirs=[ + common.SDK_ROOT_DIR, common.GEN_DIR, common.CLIF_PREFIX, '/' + ], + extra_compile_args=['-std=c++11'], + library_dirs=[common.SDK_LIBRARY_DIR], + libraries=['provisioning_sdk'], + runtime_library_dirs=[common.SDK_LIBRARY_DIR], + install_requires=['enum34;python_version<"3.4"'], + ), + ], + ) diff --git a/provisioning_sdk/public/python/set_certificate_status_list_test.py b/provisioning_sdk/public/python/set_certificate_status_list_test.py index 42bf9e9..0bb8a23 100644 --- a/provisioning_sdk/public/python/set_certificate_status_list_test.py +++ b/provisioning_sdk/public/python/set_certificate_status_list_test.py @@ -1,5 +1,5 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -8,15 +8,15 @@ import unittest -import pywrapprovisioning_engine -import pywrapprovisioning_status import test_data_utility +from provisioning_engine import ProvisioningEngine +from provisioning_status import ProvisioningStatus class SetCertificateStatusListTest(unittest.TestCase): def setUp(self): - self._engine = pywrapprovisioning_engine.ProvisioningEngine() + self._engine = ProvisioningEngine() test_data_utility.InitProvisionEngineWithTestData( self._engine, verify_success=True) @@ -27,7 +27,7 @@ class SetCertificateStatusListTest(unittest.TestCase): def testSetCertificateStatusListInvalid(self): set_cert_status_list = self._engine.SetCertificateStatusList( 'INVALID_STATUS_LIST', 0) - self.assertEqual(pywrapprovisioning_status.INVALID_STATUS_LIST, + self.assertEqual(ProvisioningStatus.INVALID_STATUS_LIST, set_cert_status_list) diff --git a/provisioning_sdk/public/python/setup.py b/provisioning_sdk/public/python/setup.py deleted file mode 100644 index 4f84aa6..0000000 --- a/provisioning_sdk/public/python/setup.py +++ /dev/null @@ -1,62 +0,0 @@ -################################################################################ -# Copyright 2016 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. -################################################################################ - -"""setup script to build Python wrappers using swig configurations.""" - -import os - -from distutils import core - -OUT_DIRNAME = 'test_genfiles' - - -def GetSdkRootDir(): - """Obtains folder containing |OUT_DIRNAME| that is considered as root dir.""" - current_dir = os.path.realpath(os.path.dirname(__file__)) - while not os.path.isdir(os.path.join(current_dir, OUT_DIRNAME)): - current_dir = os.path.dirname(current_dir) - return current_dir - - -SDK_ROOT_DIR = GetSdkRootDir() - -SWIG_CONFIG_FILE = os.path.join(SDK_ROOT_DIR, OUT_DIRNAME, '%s.i') -SWIG_CONFIG_MODULE_PATH = OUT_DIRNAME + '.%s' - -SDK_LIBRARY_DIR = os.path.join(SDK_ROOT_DIR, 'bazel-bin', 'provisioning_sdk', - 'public') - - -def ProvisioningSwigExtension(extension_name): - return core.Extension( - name=SWIG_CONFIG_MODULE_PATH % ('_pywrap' + extension_name), - sources=[SWIG_CONFIG_FILE % extension_name], - include_dirs=[SDK_ROOT_DIR], - swig_opts=['-c++'], - library_dirs=[SDK_ROOT_DIR, SDK_LIBRARY_DIR], - runtime_library_dirs=[SDK_ROOT_DIR, SDK_LIBRARY_DIR], - libraries=['provisioning_sdk'], - extra_compile_args=['-std=c++11']) - - -if __name__ == '__main__': - os.chdir(SDK_ROOT_DIR) - core.setup( - name='provisioning_sdk', - ext_modules=[ - ProvisioningSwigExtension('certificate_type'), - ProvisioningSwigExtension('provisioning_status'), - ProvisioningSwigExtension('provisioning_session'), - ProvisioningSwigExtension('provisioning_engine') - ], - py_modules=[ - SWIG_CONFIG_MODULE_PATH % 'pywrapcertificate_type', - SWIG_CONFIG_MODULE_PATH % 'pywarpprovisioning_status', - SWIG_CONFIG_MODULE_PATH % 'pywrapprovisioning_session', - SWIG_CONFIG_MODULE_PATH % 'pywrapprovisioning_engine' - ]) diff --git a/provisioning_sdk/public/python/setup_common.py b/provisioning_sdk/public/python/setup_common.py new file mode 100644 index 0000000..eddea82 --- /dev/null +++ b/provisioning_sdk/public/python/setup_common.py @@ -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. +################################################################################ +"""Common definitions for Provisioning SDK python setup files.""" + +import os + +GEN_DIRNAME = 'test_genfiles' + + +def _GetSdkRootDir(): + """Obtains folder containing |GEN_DIRNAME| that is considered as root dir.""" + current_dir = os.path.realpath(os.path.dirname(__file__)) + while not os.path.isdir(os.path.join(current_dir, GEN_DIRNAME)): + current_dir = os.path.dirname(current_dir) + + os.chdir(current_dir) + return current_dir + + +SDK_ROOT_DIR = _GetSdkRootDir() +GEN_DIR = '%s/%s' % (SDK_ROOT_DIR, GEN_DIRNAME) +SDK_LIBRARY_DIR = os.path.join(SDK_ROOT_DIR, 'bazel-bin', 'provisioning_sdk', + 'public') +CLIF_PREFIX = os.environ['CLIF_PREFIX'] +BUILD_DIR = os.environ['PYEXT_BUILD_DIR'] + +WVCOMMON_SRC_DIR = '%s/common/python' % GEN_DIR +WVPROTO_SRC_DIR = '%s/protos/public' % GEN_DIR +SDK_SRC_DIR = '%s/provisioning_sdk/public/python' % GEN_DIR diff --git a/provisioning_sdk/public/python/test_data_provider.py b/provisioning_sdk/public/python/test_data_provider.py index cb7e3d5..385f531 100644 --- a/provisioning_sdk/public/python/test_data_provider.py +++ b/provisioning_sdk/public/python/test_data_provider.py @@ -1,16 +1,14 @@ ################################################################################ -# Copyright 2017 Google Inc. +# 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. ################################################################################ - """Class that provides test data for Provisioning SDK testing.""" import os - -import pywrapcertificate_type +from certificate_type import CertificateType _TEST_CERT_DATA_FOLDER = os.path.join('example', 'example_data') _DEV_CERT_DATA_FOLDER = os.path.join('example', 'dev_cert_example_data') @@ -21,24 +19,27 @@ class TestDataProvider(object): def __init__(self, cert_type): """Initializes the TestData for Provisioning SDK tests.""" - assert (cert_type in ( - pywrapcertificate_type.kCertDevelopment, - pywrapcertificate_type.kCertTesting)) + assert (cert_type in (CertificateType.kCertificateTypeDevelopment, + CertificateType.kCertificateTypeTesting)) self._cert_type = cert_type def _GetTestData(self, filename): """Helps read test data files such as certs and keys for SDK testing.""" current_dir = os.path.realpath(os.path.dirname(__file__)) - if self._cert_type == pywrapcertificate_type.kCertDevelopment: + if self._cert_type == CertificateType.kCertificateTypeDevelopment: subfolder_path = _DEV_CERT_DATA_FOLDER - elif self._cert_type == pywrapcertificate_type.kCertTesting: + elif self._cert_type == CertificateType.kCertificateTypeTesting: subfolder_path = _TEST_CERT_DATA_FOLDER while not os.path.isdir(os.path.join(current_dir, subfolder_path)): current_dir = os.path.dirname(current_dir) filename = os.path.join(current_dir, subfolder_path, filename) - with open(filename, 'rb') as data_file: - data = data_file.read() - return data + try: + with open(filename, 'r') as data_file: + data = data_file.read() + return data + except IOError: + print 'TestDataProvider: Failed to read \'%s\'' % filename + return None @property def service_drm_cert(self): @@ -86,11 +87,11 @@ class TestDataProvider(object): @property def device_public_key(self): - return self._GetTestData('user.public') + return self._GetTestData('device.public') @property def device_private_key(self): - return self._GetTestData('user.private') + return self._GetTestData('device.private') @property def message(self): diff --git a/provisioning_sdk/public/python/test_data_utility.py b/provisioning_sdk/public/python/test_data_utility.py index c1ca62f..7656f7c 100644 --- a/provisioning_sdk/public/python/test_data_utility.py +++ b/provisioning_sdk/public/python/test_data_utility.py @@ -1,64 +1,69 @@ ################################################################################ -# Copyright 2016 Google Inc. +# 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. ################################################################################ - """Utility class for Provisioning SDK testing.""" import logging -import pywrapcertificate_type -import pywrapprovisioning_status +from certificate_type import CertificateType import test_data_provider +from provisioning_status import ProvisioningStatus from protos.public import certificate_provisioning_pb2 logging.basicConfig(level=logging.DEBUG) def InitProvisionEngineWithTestData( - engine, verify_success=False, - cert_type=pywrapcertificate_type.kCertTesting): + engine, + verify_success=False, + cert_type=CertificateType.kCertificateTypeTesting): """Initialize the provisioning engine with sample credentials. Args: - engine: a pywrapprovisioning_engine.ProvisioningEngine instance + engine: a ProvisioningEngine instance verify_success: whether to verify that resulting status code equals OK cert_type: The type of certificate to use for initializing SDK - - {kCertTesting/kCertDevelopment} + {kCertificateTypeTesting/kCertificateTypeDevelopment} Returns: OK on success, or an appropriate error status code otherwise. """ - logging.info('Initializing provisioning engine with test data.') data_provider = test_data_provider.TestDataProvider(cert_type) - status = engine.Initialize(cert_type, - data_provider.service_drm_cert, + logging.info('Adding service certificate.') + + logging.info('Initializing provisioning engine with test data.') + status = engine.Initialize(cert_type, data_provider.service_drm_cert, data_provider.service_private_key, data_provider.service_private_key_passphrase, data_provider.provisioner_drm_cert, data_provider.provisioner_private_key, data_provider.provisioner_private_key_passphrase, data_provider.provisioner_spoid_secret) + if verify_success: - AssertSuccess(status, 'Failed to initialize.') + assert ProvisioningStatus.OK == status + return status def SetCertificateStatusListWithTestData( - engine, expiration_period_seconds, verify_success=False, - cert_type=pywrapcertificate_type.kCertTesting): + engine, + expiration_period_seconds, + verify_success=False, + cert_type=CertificateType.kCertificateTypeTesting): """Set the certificate status list with sample certificate status list. Args: - engine: a pywrapprovisioning_engine.ProvisioningEngine instance + engine: a ProvisioningEngine instance expiration_period_seconds: number of seconds until certificate_status_list expires after its creation time verify_success: whether to verify that resulting status code equals OK cert_type: The type of certificate to use for initializing SDK - - {kCertTesting/kCertDevelopment} + {kCertificateTypeTesting/kCertificateTypeDevelopment} Returns: OK on success, or an appropriate error status code otherwise. @@ -71,14 +76,16 @@ def SetCertificateStatusListWithTestData( expiration_period_seconds) if verify_success: - AssertSuccess(status, 'Failed to set certificate status list.') + assert ProvisioningStatus.OK == status return status def AddDrmIntermediateCertificateWithTestData( - engine, system_id, verify_success=False, - cert_type=pywrapcertificate_type.kCertTesting): + engine, + system_id, + verify_success=False, + cert_type=CertificateType.kCertificateTypeTesting): """Generate an intermediate DRM cert and add it to provisioning engine. The intermediate DRM certificate is generated with sample public key and @@ -86,21 +93,21 @@ def AddDrmIntermediateCertificateWithTestData( passphrase. Args: - engine: a pywrapprovisioning_engine.ProvisioningEngine instance + engine: a ProvisioningEngine instance system_id: Widevine system ID for the type of device verify_success: whether to verify that resulting status code equals OK cert_type: The type of certificate to use for initializing SDK - - {kCertTesting/kCertDevelopment} + {kCertificateTypeTesting/kCertificateTypeDevelopment} Returns: OK on success, or an appropriate error status code otherwise. """ - logging.info( - 'Generating DRM intermediate certificate for system_id <%d>.', system_id) + logging.info('Generating DRM intermediate certificate for system_id <%d>.', + system_id) data_provider = test_data_provider.TestDataProvider(cert_type) gen_status, ca_certificate = engine.GenerateDrmIntermediateCertificate( system_id, data_provider.ca_public_key) - AssertSuccess(gen_status, 'Failed to generate intermediate certificate.') + assert ProvisioningStatus.OK == gen_status logging.info('Adding DRM intermediate certificate.') add_ca_status = engine.AddDrmIntermediateCertificate( @@ -108,23 +115,26 @@ def AddDrmIntermediateCertificateWithTestData( data_provider.ca_private_key_passphrase) if verify_success: - AssertSuccess(add_ca_status, 'Failed to add intermediate certificate.') + assert ProvisioningStatus.OK == add_ca_status return add_ca_status -def GenerateDeviceDrmCertificate(engine, system_id, serial_number, - verify_success=False, - cert_type=pywrapcertificate_type.kCertTesting): +def GenerateDeviceDrmCertificate( + engine, + system_id, + serial_number, + verify_success=False, + cert_type=CertificateType.kCertificateTypeTesting): """Generate a device DRM certificate. Args: - engine: a pywrapprovisioning_engine.ProvisioningEngine instance + engine: a ProvisioningEngine instance system_id: Widevine system ID for the type of device serial_number: The serial number for the device DRM certificate. verify_success: whether to verify that resulting status code equals OK cert_type: The type of certificate to use for initializing SDK - - {kCertTesting/kCertDevelopment} + {kCertificateTypeTesting/kCertificateTypeDevelopment} Returns: OK on success, or an appropriate error status code otherwise. @@ -136,39 +146,36 @@ def GenerateDeviceDrmCertificate(engine, system_id, serial_number, gen_status, ca_certificate = engine.GenerateDeviceDrmCertificate( system_id, data_provider.device_public_key, serial_number) if verify_success: - AssertSuccess(gen_status, 'Failed to generate device DRM certificate.') + assert ProvisioningStatus.OK == gen_status return ca_certificate def NewProvisioningSessionWithTestData( - engine, verify_success=False, - cert_type=pywrapcertificate_type.kCertTesting): + engine, + verify_success=False, + cert_type=CertificateType.kCertificateTypeTesting): """Create a provisioning session with sample device public and private keys. Args: - engine: a pywrapprovisioning_engine.ProvisioningEngine instance + engine: a ProvisioningEngine instance verify_success: whether to verify that resulting status code equals OK cert_type: The type of certificate to use for initializing SDK - - {kCertTesting/kCertDevelopment} + {kCertificateTypeTesting/kCertificateTypeDevelopment} Returns: - status: OK on success, or an appropriate error status code otherwise. new_session: A new provisioning_session. + status: OK on success, or an appropriate error status code otherwise. """ logging.info('Starting a new provisioning session with' 'sample device public and private keys.') data_provider = test_data_provider.TestDataProvider(cert_type) - status, new_session = engine.NewProvisioningSession( + new_session, status = engine.NewProvisioningSession( + certificate_provisioning_pb2.SignedProvisioningMessage.PROVISIONING_30, data_provider.device_public_key, data_provider.device_private_key) if verify_success: - AssertSuccess(status, 'Failed to create session.') + assert (ProvisioningStatus.OK == status), 'status = %r' % status - return status, new_session - - -def AssertSuccess(status, message=None): - """Assert status equals OK.""" - assert pywrapprovisioning_status.OK == status, message + return new_session, status def ConvertToSignedProvisioningMessage(serialized_message): diff --git a/provisioning_sdk/public/python/unique_ptr.i b/provisioning_sdk/public/python/unique_ptr.i deleted file mode 100644 index e37afe6..0000000 --- a/provisioning_sdk/public/python/unique_ptr.i +++ /dev/null @@ -1,51 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2016 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. -//////////////////////////////////////////////////////////////////////////////// - -namespace std { - template class unique_ptr {}; -} - -%define _UNIQUE_PTR_TEMPLATE(type) -template <> class std::unique_ptr {}; -%enddef - -%define UNIQUE_PTR(type) -_UNIQUE_PTR_TEMPLATE(type); - -%typemap(out) std::unique_ptr %{ - $result = SWIG_NewPointerObj( - SWIG_as_voidptr($1.release()), $descriptor(type*), SWIG_POINTER_OWN); -%} -%enddef - -%define UNIQUE_PTR_WITH_ERROR(type, err_str) -_UNIQUE_PTR_TEMPLATE(type); - -%typemap(out) std::unique_ptr %{ - if ($1) { - $result = SWIG_NewPointerObj( - SWIG_as_voidptr($1.release()), $descriptor(type*), SWIG_POINTER_OWN); - } else { - SWIG_exception(SWIG_ValueError, err_str); - } -%} -%enddef - -%define UNIQUE_PTR_ARGOUT(type, arg_name) -_UNIQUE_PTR_TEMPLATE(type) - -%typemap(in, numinputs=0) std::unique_ptr* arg_name - (std::unique_ptr temp) %{ - $1 = &temp; -%} - -%typemap(argout) std::unique_ptr* arg_name %{ - %append_output(SWIG_NewPointerObj(SWIG_as_voidptr($1->release()), - $descriptor(type*), SWIG_POINTER_OWN)); -%} -%enddef diff --git a/run_tests.sh b/run_tests.sh index 650098a..9759b1e 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,6 +1,6 @@ #!/bin/bash ################################################################################ -# Copyright 2016 Google Inc. +# 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 @@ -17,33 +17,101 @@ # - Protocol compiler: https://github.com/google/protobuf#protocol-compiler-installation # On Ubuntu: sudo apt-get install protobuf-compiler # - Protobuf Python runtime (version 3.0 or later): sudo pip install protobuf -# - swig: http://www.swig.org/download.html - -set -e - -hash protoc 2>/dev/null || { echo >&2 "protobuf is required but not installed. Aborting."; exit 1; } +# - CLIF: https://github.com/google/clif +# Environment Variables: +# - TOOLS_PATH: colon-delimited paths to additional tools such as protoc. +# - CLIF_PREFIX: prefix path to CLIF installation. +set -ex cd "$(dirname "$0")" -rm -rf test_genfiles -mkdir test_genfiles +if [[ -v TOOLS_PATH ]]; then + export PATH=$PATH:$TOOLS_PATH +fi -protoc -I="$(pwd)" --python_out="$(pwd)/test_genfiles" "$(pwd)/protos/public/client_identification.proto" -protoc -I="$(pwd)" --python_out="$(pwd)/test_genfiles" "$(pwd)/protos/public/provisioned_device_info.proto" -protoc -I="$(pwd)" --python_out="$(pwd)/test_genfiles" "$(pwd)/protos/public/certificate_provisioning.proto" -protoc -I="$(pwd)" --python_out="$(pwd)/test_genfiles" "$(pwd)/protos/public/signed_device_certificate.proto" +if ! which protoc 2>/dev/null; then + echo >&2 "Protobuf is required but not found (did you set TOOLS_PATH?). Aborting." + exit 1 +fi +if ! [[ -v CLIF_PREFIX ]]; then + echo >&2 "CLIF_PREFIX not set. Defaulting to '$HOME/opt'." + export CLIF_PREFIX=$HOME/opt +fi + +PYCLIF="$CLIF_PREFIX/clif/bin/pyclif" +PYCLIF_PROTO="$CLIF_PREFIX/clif/bin/pyclif_proto" +CLIF_INC="-I$CLIF_PREFIX" +if ! [[ -x $PYCLIF ]]; then + echo >&2 "CLIF is required but not found." + exit 1 +fi + +# Get the path of the native python C++ header files. +PYTHON_INC="-I$(printf "from distutils.sysconfig import get_python_inc\nprint(get_python_inc())\n" | python)" + +SRC_DIR="$(pwd)" +GEN_DIR="$(pwd)/test_genfiles" +PROTO_GEN_DIR="$GEN_DIR/protos/public" +COMMON_GEN_DIR="$GEN_DIR/common/python" +SDK_PYTHON_GEN_DIR="$GEN_DIR/provisioning_sdk/public/python" +export PYEXT_BUILD_DIR="$(pwd)/python_ext_build" +PYEXT_INSTALL_DIR="$(pwd)/python_env" + +rm -rf "$GEN_DIR" +rm -rf "$PYEXT_BUILD_DIR" +rm -rf "$PYEXT_INSTALL_DIR" +mkdir -p "$PROTO_GEN_DIR" +mkdir -p "$COMMON_GEN_DIR" +mkdir -p "$SDK_PYTHON_GEN_DIR" +mkdir -p "$PYEXT_BUILD_DIR" + +# Generate C++ outputs. +# The protos are needed to compile the public header provisioning_engine.h. +protoc -I="$SRC_DIR" --cpp_out="$GEN_DIR" "$SRC_DIR/protos/public/client_identification.proto" +protoc -I="$SRC_DIR" --cpp_out="$GEN_DIR" "$SRC_DIR/protos/public/remote_attestation.proto" +protoc -I="$SRC_DIR" --cpp_out="$GEN_DIR" "$SRC_DIR/protos/public/certificate_provisioning.proto" + +# Generate py outputs. +protoc -I="$SRC_DIR" --python_out="$GEN_DIR" "$SRC_DIR/protos/public/client_identification.proto" +protoc -I="$SRC_DIR" --python_out="$GEN_DIR" "$SRC_DIR/protos/public/remote_attestation.proto" +protoc -I="$SRC_DIR" --python_out="$GEN_DIR" "$SRC_DIR/protos/public/provisioned_device_info.proto" +protoc -I="$SRC_DIR" --python_out="$GEN_DIR" "$SRC_DIR/protos/public/certificate_provisioning.proto" +protoc -I="$SRC_DIR" --python_out="$GEN_DIR" "$SRC_DIR/protos/public/signed_drm_certificate.proto" + +# Generate CLIF Python wrappers. +"$PYCLIF_PROTO" --source_dir="$SRC_DIR/protos/public" --ccdeps_out="$PROTO_GEN_DIR/certificate_provisioning_pyclif.cc" --header_out="$PROTO_GEN_DIR/certificate_provisioning_pyclif.h" certificate_provisioning.proto + +"$PYCLIF" --modname certificate_type --ccdeps_out="$COMMON_GEN_DIR/certificate_type.cc" --ccinit_out="$COMMON_GEN_DIR/initcertificate_type.cc" --header_out="$COMMON_GEN_DIR/certificate_type.h" --cc_flags="-std=c++11 -I$SRC_DIR $PYTHON_INC $CLIF_INC" "$SRC_DIR/common/python/certificate_type.clif" + +"$PYCLIF" --modname provisioning_status --ccdeps_out="$SDK_PYTHON_GEN_DIR/provisioning_status.cc" --ccinit_out="$SDK_PYTHON_GEN_DIR/initprovisioning_status.cc" --header_out="$SDK_PYTHON_GEN_DIR/provisioning_status.h" --cc_flags="-std=c++11 -I$SRC_DIR $PYTHON_INC $CLIF_INC" "$SRC_DIR/provisioning_sdk/public/python/provisioning_status.clif" + +"$PYCLIF" --modname provisioning_session --ccdeps_out="$SDK_PYTHON_GEN_DIR/provisioning_session.cc" --ccinit_out="$SDK_PYTHON_GEN_DIR/initprovisioning_session.cc" --header_out="$SDK_PYTHON_GEN_DIR/provisioning_session.h" --cc_flags="-std=c++11 -I$SRC_DIR -I$GEN_DIR $PYTHON_INC $CLIF_INC" --include_paths="$GEN_DIR" "$SRC_DIR/provisioning_sdk/public/python/provisioning_session.clif" + +"$PYCLIF" --modname provisioning_engine --ccdeps_out="$SDK_PYTHON_GEN_DIR/provisioning_engine.cc" --ccinit_out="$SDK_PYTHON_GEN_DIR/initprovisioning_engine.cc" --header_out="$SDK_PYTHON_GEN_DIR/provisioning_engine.h" --cc_flags="-std=c++11 -I$SRC_DIR -I$GEN_DIR $PYTHON_INC $CLIF_INC" --include_paths="$GEN_DIR" "$SRC_DIR/provisioning_sdk/public/python/provisioning_engine.clif" + +# Create virtual Python environment, build extensions, and install onto virtual +# environment. cp -a provisioning_sdk/public/python/* test_genfiles/ cd test_genfiles -python setup.py build_ext --inplace +rm -rf "$PYEXT_INSTALL_DIR" +virtualenv "$PYEXT_INSTALL_DIR" +"$PYEXT_INSTALL_DIR/bin/python" "$PYEXT_INSTALL_DIR/bin/pip" install enum34 +"$PYEXT_INSTALL_DIR/bin/python" "$PYEXT_INSTALL_DIR/bin/pip" install protobuf +"$PYEXT_INSTALL_DIR/bin/python" "$PYEXT_INSTALL_DIR/bin/pip" install cryptography + +"$PYEXT_INSTALL_DIR/bin/python" certificate_type_setup.py build --build-base="$PYEXT_BUILD_DIR" install +"$PYEXT_INSTALL_DIR/bin/python" provisioning_status_setup.py build --build-base="$PYEXT_BUILD_DIR" install +"$PYEXT_INSTALL_DIR/bin/python" provisioning_session_setup.py build --build-base="$PYEXT_BUILD_DIR" install +"$PYEXT_INSTALL_DIR/bin/python" provisioning_engine_setup.py build --build-base="$PYEXT_BUILD_DIR" install shopt -s globstar for d in "protos"/**/; do touch -- "$d/__init__.py"; -done; +done -python init_engine_test.py -python set_certificate_status_list_test.py -python drm_intermediate_certificate_test.py -python engine_generate_certificate_test.py -python new_session_test.py +"$PYEXT_INSTALL_DIR/bin/python" init_engine_test.py +"$PYEXT_INSTALL_DIR/bin/python" set_certificate_status_list_test.py +"$PYEXT_INSTALL_DIR/bin/python" drm_intermediate_certificate_test.py +"$PYEXT_INSTALL_DIR/bin/python" engine_generate_certificate_test.py +"$PYEXT_INSTALL_DIR/bin/python" new_session_test.py diff --git a/six.BUILD b/six.BUILD new file mode 100644 index 0000000..3cb7eb8 --- /dev/null +++ b/six.BUILD @@ -0,0 +1,15 @@ +################################################################################ +# 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. +################################################################################ + +# Build target for six. Needed by py_proto_library. +py_library( + name = "six", + srcs = ["six.py"], + srcs_version = "PY2AND3", + visibility = ["//visibility:public"], +) 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..9fd8c49 --- /dev/null +++ b/testing/BUILD @@ -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. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "gunit", + hdrs = [ + "gmock.h", + "gunit.h", + ], + deps = [ + "//external:gtest", + ], +) + +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..1cb0e96 --- /dev/null +++ b/testing/gunit.h @@ -0,0 +1,17 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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(error::OK, expression.error_code()) +#define ASSERT_OK(expression) ASSERT_EQ(error::OK, expression.error_code()) + +#endif // TESTING_GUNIT_H_ diff --git a/util/BUILD b/util/BUILD new file mode 100644 index 0000000..c14212a --- /dev/null +++ b/util/BUILD @@ -0,0 +1,43 @@ +################################################################################ +# 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", + ], +) + +cc_library( + name = "error_space", + hdrs = ["error_space.h"], +) + +cc_library( + name = "proto_status", + hdrs = ["proto_status.h"], + deps = [ + "@protobuf_repo//:protobuf", + "//util:error_space", + ], +) + +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..2e66a39 --- /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::space_name(); + } + + static std::string CodeToStringImpl(const ErrorSpace* /*space*/, int code) { + return T::code_to_string(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..47f547f --- /dev/null +++ b/util/error_space_test.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 "util/error_space.h" + +#include "testing/gunit.h" + +namespace widevine { +namespace util { +namespace { + +class Space1 : public util::ErrorSpaceImpl { + public: + static std::string space_name() { return "Space1"; } + static std::string code_to_string(int code) { + return "Test" + std::to_string(code); + } +}; + +TEST(ErrorSpaceTest, Basic) { + const ErrorSpace* space1 = Space1::Get(); + EXPECT_EQ("Space1", space1->SpaceName()); + EXPECT_EQ(Space1::code_to_string(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..1878bc2 --- /dev/null +++ b/util/proto_status.h @@ -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. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_PROTO_STATUS_H_ +#define UTIL_PROTO_STATUS_H_ + +#include "google/protobuf/descriptor.h" +#include "google/protobuf/generated_enum_reflection.h" +#include "util/error_space.h" + +namespace widevine { +namespace util { + +template +class ProtoEnumErrorSpace + : public util::ErrorSpaceImpl> { + public: + static std::string space_name() { + return google::protobuf::GetEnumDescriptor()->full_name(); + } + + static std::string code_to_string(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..052caf2 --- /dev/null +++ b/util/proto_status_test.cc @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/status.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, + "provider_id_mismatch"); + EXPECT_EQ(status1, status2); +} + +TEST(StatusTest, ErrorMessageMismatch) { + 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_NE(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