From 8d17e4549aeb54520ddf37329947aa152bb23342 Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Tue, 24 Jan 2017 20:06:25 -0800 Subject: [PATCH] Export provisioning sdk Change-Id: I4d47d80444c9507f84896767dc676112ca11e901 --- LICENSE | 4 + WORKSPACE | 77 ++ base/BUILD | 24 + base/macros.h | 113 +++ base/mutex.h | 91 ++ base/thread_annotations.h | 212 +++++ common/BUILD | 164 ++++ common/aes_cbc_util.cc | 93 +++ common/aes_cbc_util.h | 38 + common/aes_cbc_util_test.cc | 137 +++ common/file_util.cc | 63 ++ common/file_util.h | 27 + common/file_util_test.cc | 29 + common/mock_rsa_key.h | 71 ++ common/openssl_util.h | 70 ++ common/random_util.cc | 22 + common/random_util.h | 22 + common/random_util_test.cc | 29 + common/rsa_key.cc | 296 +++++++ common/rsa_key.h | 131 +++ common/rsa_key_test.cc | 227 +++++ common/rsa_test_keys.cc | 682 +++++++++++++++ common/rsa_test_keys.h | 64 ++ common/rsa_util.cc | 386 +++++++++ common/rsa_util.h | 153 ++++ common/rsa_util_test.cc | 225 +++++ common/sha_util.cc | 29 + common/sha_util.h | 24 + common/sha_util_test.cc | 51 ++ example/BUILD | 40 + example/example_data/certificate_list | Bin 0 -> 456 bytes .../intermediate.encrypted.private | Bin 0 -> 1315 bytes example/example_data/intermediate.passphrase | 1 + example/example_data/intermediate.public | Bin 0 -> 270 bytes example/example_data/message | Bin 0 -> 2695 bytes example/example_data/provisioner.cert | Bin 0 -> 685 bytes .../provisioner.encrypted.private | Bin 0 -> 1315 bytes example/example_data/provisioner.passphrase | 1 + example/example_data/provisioner.spoid_secret | 4 + example/example_data/root.private | Bin 0 -> 1767 bytes example/example_data/service.cert | Bin 0 -> 685 bytes .../example_data/service.encrypted.private | Bin 0 -> 1315 bytes example/example_data/service.passphrase | 1 + example/example_data/service.public | Bin 0 -> 270 bytes example/example_data/user.private | Bin 0 -> 1191 bytes example/example_data/user.public | Bin 0 -> 270 bytes example/provisioning_example.cc | 113 +++ example/provisioning_message_generator.cc | 95 +++ glog.BUILD | 183 ++++ gtest.BUILD | 37 + oem_certificate_generator/BUILD | 24 + oem_certificate_generator/oem_certificate.py | 492 +++++++++++ .../oem_certificate_test.py | 538 ++++++++++++ protos/public/BUILD | 89 ++ protos/public/certificate_provisioning.proto | 98 +++ protos/public/client_identification.proto | 101 +++ protos/public/device_certificate.proto | 91 ++ protos/public/provisioned_device_info.proto | 47 ++ protos/public/signed_device_certificate.proto | 27 + provisioning_sdk/internal/BUILD | 112 +++ provisioning_sdk/internal/certificates/BUILD | 117 +++ .../internal/certificates/backwards-chain.der | Bin 0 -> 2210 bytes .../certificates/drm_ca_root_dev.cert | Bin 0 -> 802 bytes .../certificates/drm_ca_root_prod.cert | Bin 0 -> 802 bytes .../certificates/drm_ca_root_test.cert | Bin 0 -> 799 bytes .../certificates/expired-2000-chain.der | Bin 0 -> 2210 bytes .../internal/certificates/invalid_chain.der | Bin 0 -> 2188 bytes .../certificates/oem_ca_root_dev_cert.der | Bin 0 -> 1491 bytes .../certificates/oem_ca_root_prod_cert.der | Bin 0 -> 1493 bytes .../certificates/root_certificates.cc | 38 + .../internal/certificates/root_certificates.h | 56 ++ .../certificates/single-cert-chain.der | Bin 0 -> 950 bytes .../certificates/sysid-2001-chain.der | Bin 0 -> 2130 bytes .../certificates/sysid-2001-public-key.der | Bin 0 -> 270 bytes .../certificates/test_certificates.cc | 40 + .../internal/certificates/test_certificates.h | 63 ++ provisioning_sdk/internal/oem_device_cert.cc | 210 +++++ provisioning_sdk/internal/oem_device_cert.h | 56 ++ .../internal/oem_device_cert_test.cc | 95 +++ .../internal/provisioning_engine_impl.cc | 464 +++++++++++ .../internal/provisioning_engine_impl.h | 189 +++++ .../internal/provisioning_engine_impl_test.cc | 783 ++++++++++++++++++ .../internal/provisioning_session_impl.cc | 256 ++++++ .../internal/provisioning_session_impl.h | 88 ++ .../provisioning_session_impl_test.cc | 426 ++++++++++ provisioning_sdk/public/BUILD | 84 ++ provisioning_sdk/public/certificate_type.h | 22 + .../public/provisioning_engine.cc | 118 +++ provisioning_sdk/public/provisioning_engine.h | 153 ++++ .../public/provisioning_engine_test.cc | 58 ++ .../public/provisioning_session.cc | 48 ++ .../public/provisioning_session.h | 56 ++ .../public/provisioning_status.cc | 48 ++ provisioning_sdk/public/provisioning_status.h | 49 ++ provisioning_sdk/public/python/BUILD | 73 ++ provisioning_sdk/public/python/base.i | 27 + .../public/python/certificate_type.i | 28 + .../public/python/crypto_utility.py | 25 + .../drm_intermediate_certificate_test.py | 54 ++ .../engine_generate_certificate_test.py | 64 ++ .../public/python/init_engine_test.py | 170 ++++ .../public/python/new_session_test.py | 103 +++ .../public/python/provisioning_engine.i | 49 ++ .../public/python/provisioning_session.i | 37 + .../public/python/provisioning_status.i | 44 + .../set_certificate_status_list_test.py | 35 + provisioning_sdk/public/python/setup.py | 62 ++ .../public/python/test_data_utility.py | 151 ++++ provisioning_sdk/public/python/unique_ptr.i | 51 ++ run_tests.sh | 49 ++ 110 files changed, 10187 insertions(+) create mode 100644 LICENSE create mode 100644 WORKSPACE create mode 100644 base/BUILD create mode 100644 base/macros.h create mode 100644 base/mutex.h create mode 100644 base/thread_annotations.h create mode 100644 common/BUILD create mode 100644 common/aes_cbc_util.cc create mode 100644 common/aes_cbc_util.h create mode 100644 common/aes_cbc_util_test.cc create mode 100644 common/file_util.cc create mode 100644 common/file_util.h create mode 100644 common/file_util_test.cc create mode 100644 common/mock_rsa_key.h create mode 100644 common/openssl_util.h create mode 100644 common/random_util.cc create mode 100644 common/random_util.h create mode 100644 common/random_util_test.cc create mode 100644 common/rsa_key.cc create mode 100644 common/rsa_key.h create mode 100644 common/rsa_key_test.cc create mode 100644 common/rsa_test_keys.cc create mode 100644 common/rsa_test_keys.h create mode 100644 common/rsa_util.cc create mode 100644 common/rsa_util.h create mode 100644 common/rsa_util_test.cc create mode 100644 common/sha_util.cc create mode 100644 common/sha_util.h create mode 100644 common/sha_util_test.cc create mode 100644 example/BUILD create mode 100644 example/example_data/certificate_list create mode 100644 example/example_data/intermediate.encrypted.private create mode 100644 example/example_data/intermediate.passphrase create mode 100644 example/example_data/intermediate.public create mode 100644 example/example_data/message create mode 100644 example/example_data/provisioner.cert create mode 100644 example/example_data/provisioner.encrypted.private create mode 100644 example/example_data/provisioner.passphrase create mode 100644 example/example_data/provisioner.spoid_secret create mode 100644 example/example_data/root.private create mode 100644 example/example_data/service.cert create mode 100644 example/example_data/service.encrypted.private create mode 100644 example/example_data/service.passphrase create mode 100644 example/example_data/service.public create mode 100644 example/example_data/user.private create mode 100644 example/example_data/user.public create mode 100644 example/provisioning_example.cc create mode 100644 example/provisioning_message_generator.cc create mode 100644 glog.BUILD create mode 100644 gtest.BUILD create mode 100644 oem_certificate_generator/BUILD create mode 100644 oem_certificate_generator/oem_certificate.py create mode 100644 oem_certificate_generator/oem_certificate_test.py create mode 100644 protos/public/BUILD create mode 100644 protos/public/certificate_provisioning.proto create mode 100644 protos/public/client_identification.proto create mode 100644 protos/public/device_certificate.proto create mode 100644 protos/public/provisioned_device_info.proto create mode 100644 protos/public/signed_device_certificate.proto create mode 100644 provisioning_sdk/internal/BUILD create mode 100644 provisioning_sdk/internal/certificates/BUILD create mode 100644 provisioning_sdk/internal/certificates/backwards-chain.der create mode 100644 provisioning_sdk/internal/certificates/drm_ca_root_dev.cert create mode 100644 provisioning_sdk/internal/certificates/drm_ca_root_prod.cert create mode 100644 provisioning_sdk/internal/certificates/drm_ca_root_test.cert create mode 100644 provisioning_sdk/internal/certificates/expired-2000-chain.der create mode 100644 provisioning_sdk/internal/certificates/invalid_chain.der create mode 100644 provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.der create mode 100644 provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.der create mode 100644 provisioning_sdk/internal/certificates/root_certificates.cc create mode 100644 provisioning_sdk/internal/certificates/root_certificates.h create mode 100644 provisioning_sdk/internal/certificates/single-cert-chain.der create mode 100644 provisioning_sdk/internal/certificates/sysid-2001-chain.der create mode 100644 provisioning_sdk/internal/certificates/sysid-2001-public-key.der create mode 100644 provisioning_sdk/internal/certificates/test_certificates.cc create mode 100644 provisioning_sdk/internal/certificates/test_certificates.h create mode 100644 provisioning_sdk/internal/oem_device_cert.cc create mode 100644 provisioning_sdk/internal/oem_device_cert.h create mode 100644 provisioning_sdk/internal/oem_device_cert_test.cc create mode 100644 provisioning_sdk/internal/provisioning_engine_impl.cc create mode 100644 provisioning_sdk/internal/provisioning_engine_impl.h create mode 100644 provisioning_sdk/internal/provisioning_engine_impl_test.cc create mode 100644 provisioning_sdk/internal/provisioning_session_impl.cc create mode 100644 provisioning_sdk/internal/provisioning_session_impl.h create mode 100644 provisioning_sdk/internal/provisioning_session_impl_test.cc create mode 100644 provisioning_sdk/public/BUILD create mode 100644 provisioning_sdk/public/certificate_type.h create mode 100644 provisioning_sdk/public/provisioning_engine.cc create mode 100644 provisioning_sdk/public/provisioning_engine.h create mode 100644 provisioning_sdk/public/provisioning_engine_test.cc create mode 100644 provisioning_sdk/public/provisioning_session.cc create mode 100644 provisioning_sdk/public/provisioning_session.h create mode 100644 provisioning_sdk/public/provisioning_status.cc create mode 100644 provisioning_sdk/public/provisioning_status.h create mode 100644 provisioning_sdk/public/python/BUILD create mode 100644 provisioning_sdk/public/python/base.i create mode 100644 provisioning_sdk/public/python/certificate_type.i create mode 100644 provisioning_sdk/public/python/crypto_utility.py create mode 100644 provisioning_sdk/public/python/drm_intermediate_certificate_test.py create mode 100644 provisioning_sdk/public/python/engine_generate_certificate_test.py create mode 100644 provisioning_sdk/public/python/init_engine_test.py create mode 100644 provisioning_sdk/public/python/new_session_test.py create mode 100644 provisioning_sdk/public/python/provisioning_engine.i create mode 100644 provisioning_sdk/public/python/provisioning_session.i create mode 100644 provisioning_sdk/public/python/provisioning_status.i create mode 100644 provisioning_sdk/public/python/set_certificate_status_list_test.py create mode 100644 provisioning_sdk/public/python/setup.py create mode 100644 provisioning_sdk/public/python/test_data_utility.py create mode 100644 provisioning_sdk/public/python/unique_ptr.i create mode 100755 run_tests.sh diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fe743e5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,4 @@ +Copyright 2017 Google, Inc. + +This software is licensed under the terms defined in the Widevine Master License Agreement. +For a copy of this agreement, please contact widevine-licensing@google.com. diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..f074220 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,77 @@ +workspace(name = "provisioning_sdk") + +git_repository( + name = "protobuf_repo", + remote = "https://github.com/google/protobuf.git", + tag = "v3.0.0", +) + +git_repository( + name = "boringssl_repo", + commit = "bbcaa15b0647816b9a1a9b9e0d209cd6712f0105", # 2016-07-11 + remote = "https://github.com/google/boringssl.git", +) + +git_repository( + name = "gflags_repo", + commit = "fe57e5af4db74ab298523f06d2c43aa895ba9f98", # 2016-07-20 + remote = "https://github.com/gflags/gflags.git", +) + +new_git_repository( + name = "googletest_repo", + build_file = "gtest.BUILD", + commit = "ec44c6c1675c25b9827aacd08c02433cccde7780", # 2016-07-26 + remote = "https://github.com/google/googletest.git", +) + +new_git_repository( + name = "glog_repo", + 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", +) + +bind( + name = "six", + actual = "@six_archive//:six", +) + +bind( + name = "protobuf", + actual = "@protobuf_repo//:protobuf", +) + +bind( + name = "openssl", + actual = "@boringssl_repo//:crypto", +) + +bind( + name = "gflags", + actual = "@gflags_repo//:gflags", +) + +bind( + name = "gtest", + actual = "@googletest_repo//:gtest", +) + +bind( + name = "gtest_main", + actual = "@googletest_repo//:gtest_main", +) + +bind( + name = "glog", + actual = "@glog_repo//:glog", +) diff --git a/base/BUILD b/base/BUILD new file mode 100644 index 0000000..2af5a31 --- /dev/null +++ b/base/BUILD @@ -0,0 +1,24 @@ +################################################################################ +# 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. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "base", + hdrs = [ + "macros.h", + "mutex.h", + "thread_annotations.h", + ], + deps = [ + "//external:gflags", + "//external:glog", + ], +) diff --git a/base/macros.h b/base/macros.h new file mode 100644 index 0000000..5aff849 --- /dev/null +++ b/base/macros.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_MACROS_H_ +#define BASE_MACROS_H_ + +#include // For size_t + +// DISALLOW_COPY_AND_ASSIGN disallows the copy constructor and copy assignment +// operator. DISALLOW_IMPLICIT_CONSTRUCTORS is like DISALLOW_COPY_AND_ASSIGN, +// but also disallows the default constructor, intended to help make a +// class uninstantiable. +// +// These must be placed in the private: declarations for a class. +// +// Note: New code should prefer static_assert over COMPILE_ASSERT. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName& operator=(const TypeName&) = delete +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +// 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 + +#endif // BASE_MACROS_H_ diff --git a/base/mutex.h b/base/mutex.h new file mode 100644 index 0000000..168ea1c --- /dev/null +++ b/base/mutex.h @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 new file mode 100644 index 0000000..1bb959d --- /dev/null +++ b/base/thread_annotations.h @@ -0,0 +1,212 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +// 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__) + +#endif // BASE_THREAD_ANNOTATIONS_H_ diff --git a/common/BUILD b/common/BUILD new file mode 100644 index 0000000..cd7bbb2 --- /dev/null +++ b/common/BUILD @@ -0,0 +1,164 @@ +################################################################################ +# 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: +# Build file for code common to multiple Widevine services. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "rsa_util", + srcs = ["rsa_util.cc"], + hdrs = ["rsa_util.h"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "rsa_util_test", + size = "medium", + timeout = "short", + srcs = ["rsa_util_test.cc"], + deps = [ + ":rsa_test_keys", + ":rsa_util", + "//external:gtest", + "//external:gtest_main", + ], +) + +cc_library( + name = "openssl_util", + hdrs = ["openssl_util.h"], + deps = [ + "//external:openssl", + ], +) + +cc_library( + name = "rsa_key", + srcs = ["rsa_key.cc"], + hdrs = ["rsa_key.h"], + deps = [ + ":rsa_util", + ":sha_util", + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "rsa_key_test", + size = "medium", + timeout = "short", + srcs = ["rsa_key_test.cc"], + deps = [ + ":rsa_key", + ":rsa_test_keys", + "//external:gtest", + "//external:gtest_main", + ], +) + +cc_library( + name = "rsa_test_keys", + testonly = 1, + srcs = ["rsa_test_keys.cc"], + hdrs = ["rsa_test_keys.h"], + deps = [ + "//base", + ], +) + +cc_library( + name = "mock_rsa_key", + testonly = 1, + hdrs = ["mock_rsa_key.h"], + deps = [ + ":rsa_key", + "//external:gtest", + ], +) + +cc_library( + name = "aes_cbc_util", + srcs = ["aes_cbc_util.cc"], + hdrs = ["aes_cbc_util.h"], + visibility = ["//visibility:public"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "aes_cbc_util_test", + srcs = ["aes_cbc_util_test.cc"], + deps = [ + ":aes_cbc_util", + "//external:gtest_main", + ], +) + +cc_library( + name = "random_util", + srcs = ["random_util.cc"], + hdrs = ["random_util.h"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "random_util_test", + srcs = ["random_util_test.cc"], + deps = [ + ":random_util", + "//external:gtest_main", + ], +) + +cc_library( + name = "sha_util", + srcs = ["sha_util.cc"], + hdrs = ["sha_util.h"], + deps = [ + "//external:openssl", + ], +) + +cc_test( + name = "sha_util_test", + srcs = ["sha_util_test.cc"], + deps = [ + ":sha_util", + "//external:gtest_main", + ], +) + +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", + "//external:gtest_main", + ], +) diff --git a/common/aes_cbc_util.cc b/common/aes_cbc_util.cc new file mode 100644 index 0000000..383d93a --- /dev/null +++ b/common/aes_cbc_util.cc @@ -0,0 +1,93 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/aes_cbc_util.h" + +#include + +#include "glog/logging.h" +#include "openssl/aes.h" + +namespace widevine { +namespace crypto_util { + +// Encrypts the provided plantext std::string using AES-CBC encryption. +std::string EncryptAesCbc(const std::string& key, const std::string& iv, + const std::string& plaintext) { + 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)); + + AES_KEY aes_key; + if (AES_set_encrypt_key(reinterpret_cast(&key[0]), + key.size() * 8, &aes_key) != 0) { + return ""; + } + + std::string encrypted(padded_text); + std::vector local_iv(iv.begin(), iv.end()); + AES_cbc_encrypt(reinterpret_cast(padded_text.data()), + reinterpret_cast(&encrypted[0]), padded_text.size(), + &aes_key, &local_iv[0], AES_ENCRYPT); + return encrypted; +} + +// Decrypts the AES-CBC encrypted text. Returns an empty std::string on error or +// the plaintext on success. +std::string DecryptAesCbc(const std::string& key, const std::string& iv, + const std::string& ciphertext) { + if (ciphertext.empty()) return ""; + if (iv.size() != AES_BLOCK_SIZE) return ""; + if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) return ""; + + AES_KEY aes_key; + if (AES_set_decrypt_key(reinterpret_cast(&key[0]), + key.size() * 8, &aes_key) != 0) { + return ""; + } + + std::string cleartext(ciphertext); + 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 ""; + for (uint8_t i = 0; i < num_padding_bytes; ++i) { + if (cleartext[cleartext.size() - 1 - i] != num_padding_bytes) return ""; + } + cleartext.resize(cleartext.size() - num_padding_bytes); + return cleartext; +} + +std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& ciphertext) { + std::vector local_iv(iv.begin(), iv.end()); + if (local_iv.empty()) local_iv.resize(AES_BLOCK_SIZE, '\0'); + else if (local_iv.size() != AES_BLOCK_SIZE) return ""; + if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) return ""; + + AES_KEY aes_key; + if (AES_set_decrypt_key(reinterpret_cast(&key[0]), + key.size() * 8, &aes_key) != 0) { + return ""; + } + + std::string cleartext(ciphertext); + AES_cbc_encrypt(reinterpret_cast(ciphertext.data()), + reinterpret_cast(&cleartext[0]), ciphertext.size(), + &aes_key, &local_iv[0], AES_DECRYPT); + return cleartext; +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/aes_cbc_util.h b/common/aes_cbc_util.h new file mode 100644 index 0000000..e4bffc8 --- /dev/null +++ b/common/aes_cbc_util.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 COMMON_AES_CBC_UTIL_H_ +#define COMMON_AES_CBC_UTIL_H_ + +#include + +namespace widevine { +namespace crypto_util { + +// Helper for wrapping AES CBC encryption. Uses PKCS5 padding. +std::string EncryptAesCbc(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. +std::string DecryptAesCbc(const std::string& key, const std::string& iv, + const std::string& ciphertext); + +// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an +// empty std::string on error or the plaintext on success. +// Uses no padding; fails if the ciphertext is not a multiple of 16 bytes. +// This is used to decrypt the encrypted blob in the WVM keyboxes, with +// a zero iv. +std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& ciphertext); + +} // namespace crypto_util +} // namespace widevine + +#endif // COMMON_AES_CBC_UTIL_H_ + diff --git a/common/aes_cbc_util_test.cc b/common/aes_cbc_util_test.cc new file mode 100644 index 0000000..34d25fb --- /dev/null +++ b/common/aes_cbc_util_test.cc @@ -0,0 +1,137 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/aes_cbc_util.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace { + +const uint8_t kKey[] = { + 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e, + 0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b, +}; + +const uint8_t kIv[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, +}; + +} // namespace + +namespace widevine { +namespace crypto_util { + +TEST(CryptoUtilTest, EncryptAndDecryptAesCbc) { + std::string plain_text("Foo"); + std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), plain_text); + std::string expected_ciphertext( + "\xCF\x1A\x3\x1C\x9C\x8C\xB9Z\xEC\xC0\x17\xDCRxX\xD7"); + ASSERT_EQ(0, ciphertext.size() % 16); + ASSERT_GT(ciphertext.size(), plain_text.size()); + ASSERT_EQ(expected_ciphertext, ciphertext); + + std::string decrypted = DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), ciphertext); + ASSERT_EQ(plain_text, decrypted); +} + +TEST(CryptoUtilTest, DecryptAesCbcNoPad) { + const uint8_t kKey[] = { + 0xdd, 0x71, 0x39, 0xea, 0xfa, 0xce, 0xed, 0x7c, + 0xda, 0x9f, 0x25, 0xda, 0x8a, 0xa9, 0x15, 0xea, + }; + const uint8_t kIv[] = { + 0x5d, 0x16, 0x44, 0xea, 0xec, 0x11, 0xf9, 0x83, + 0x14, 0x75, 0x41, 0xe4, 0x6e, 0xeb, 0x27, 0x74, + }; + const uint8_t kCiphertext[] = { + 0x6d, 0xa6, 0xda, 0xe4, 0xee, 0x40, 0x09, 0x17, + 0x54, 0x7b, 0xba, 0xa5, 0x27, 0xb8, 0x82, 0x1b, + }; + const uint8_t kExpectedPlaintext[] = { + 0xb3, 0x49, 0xd4, 0x80, 0x9e, 0x91, 0x06, 0x87, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10, + }; + const uint8_t kExpectedPlaintextEmptyIv[] = { + 0xee, 0x5f, 0x90, 0x6a, 0x72, 0x80, 0xff, 0x04, + 0x14, 0x75, 0x41, 0xa4, 0x6e, 0xeb, 0x27, 0x64, + }; + + std::string decrypted = DecryptAesCbcNoPad( + std::string(kKey, kKey + sizeof(kKey)), std::string(kIv, kIv + sizeof(kIv)), + std::string(kCiphertext, kCiphertext + sizeof(kCiphertext))); + ASSERT_EQ(std::string(kExpectedPlaintext, + kExpectedPlaintext + sizeof(kExpectedPlaintext)), + decrypted); + + std::string dummy_iv; + decrypted = DecryptAesCbcNoPad( + std::string(kKey, kKey + sizeof(kKey)), dummy_iv, + std::string(kCiphertext, kCiphertext + sizeof(kCiphertext))); + ASSERT_EQ( + std::string(kExpectedPlaintextEmptyIv, + kExpectedPlaintextEmptyIv + sizeof(kExpectedPlaintextEmptyIv)), + decrypted); +} + +TEST(CryptoUtilTest, TestFailedEncrypt) { + // Test with bogus initialization vector. + std::string plain_text("Foo"); + std::string bogus_iv("bogus"); + std::string ciphertext = + EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, plain_text); + ASSERT_EQ(ciphertext.size(), 0); + + // Test with bogus key. + std::string bogus_key("bogus"); + ciphertext = + EncryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), plain_text); + ASSERT_EQ(ciphertext.size(), 0); +} + +TEST(CryptoUtilTest, TestFailedDecrypt) { + // First, encrypt the data. + std::string plain_text("Foo"); + std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), plain_text); + ASSERT_NE(ciphertext.size(), 0); + + // Test Decrypt with bogus iv. + std::string bogus_iv("bogus"); + plain_text = + DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, ciphertext); + ASSERT_EQ(plain_text.size(), 0); + + // Test Decrypt with bogus key. + std::string bogus_key("bogus"); + plain_text = + DecryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), ciphertext); + ASSERT_EQ(plain_text.size(), 0); +} + +TEST(CryptoUtilTest, TestEmptyEncrypt) { + EXPECT_EQ("\xDBx\xD9\x91\xE8\x1D\xD9\x19\x80r\x12\x89\xD7Kp\xEB", + EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), "")); +} + +TEST(CryptoUtilTest, TestEmptyDecryptAesCbc) { + EXPECT_EQ("", DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), "")); +} + +TEST(CryptoUtilTest, TestEmptyDecryptAesCbcNoPad) { + EXPECT_EQ("", DecryptAesCbcNoPad(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), "")); +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/file_util.cc b/common/file_util.cc new file mode 100644 index 0000000..99a7b32 --- /dev/null +++ b/common/file_util.cc @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/file_util.h" + +#include +#include + +#include "glog/logging.h" + +namespace widevine { + +bool GetContents(const std::string& file_name, std::string* contents) { + if (file_name.empty()) { + LOG(WARNING) << "File name is empty."; + return false; + } + FILE* file = fopen(file_name.c_str(), "r"); + if (!file) { + LOG(WARNING) << "Unable to open file " << file_name; + return false; + } + contents->clear(); + const size_t kReadSize = 0x1000; + char buffer[kReadSize]; + while (true) { + size_t size_read = fread(buffer, sizeof(char), kReadSize, file); + if (size_read == 0) break; + contents->append(buffer, size_read); + } + const bool eof = feof(file); + fclose(file); + if (!eof) { + LOG(WARNING) << "Failed to read all file contents."; + return false; + } + return true;; +} + +bool SetContents(const std::string& file_name, const std::string& contents) { + if (file_name.empty()) { + LOG(WARNING) << "File name is empty."; + return false; + } + FILE* file = fopen(file_name.c_str(), "w"); + if (!file) { + LOG(WARNING) << "Unable to open file " << file_name; + return false; + } + const size_t size_written = + fwrite(contents.data(), sizeof(char), contents.size(), file); + if (size_written != contents.size()) + LOG(WARNING) << "Failed to write to " << file_name; + fclose(file); + return size_written == contents.size(); +} + +} // namespace widevine diff --git a/common/file_util.h b/common/file_util.h new file mode 100644 index 0000000..8e09bae --- /dev/null +++ b/common/file_util.h @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +// File util wrapper to be used in partner sdks. Implemented using generic file +// apis. + +#ifndef COMMON_FILE_UTIL_H_ +#define COMMON_FILE_UTIL_H_ + +#include + +namespace widevine { + +// Read file to string. +bool GetContents(const std::string& file_name, std::string* contents); + +// Write file. +bool SetContents(const std::string& file_name, const std::string& contents); + +} // namespace widevine + +#endif // COMMON_FILE_UTIL_H_ diff --git a/common/file_util_test.cc b/common/file_util_test.cc new file mode 100644 index 0000000..02b38df --- /dev/null +++ b/common/file_util_test.cc @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/file_util.h" + +#include "gtest/gtest.h" + +namespace widevine { + +TEST(FileUtilTest, EmptyFileName) { + std::string contents; + EXPECT_FALSE(GetContents("", &contents)); + EXPECT_FALSE(SetContents("", "test content")); +} + +TEST(FileUtilTest, BasicTest) { + const std::string file_path = FLAGS_test_tmpdir + "/file_util_test"; + EXPECT_TRUE(SetContents(file_path, "test content")); + std::string contents; + EXPECT_TRUE(GetContents(file_path, &contents)); + EXPECT_EQ("test content", contents); +} + +} // namespace widevine diff --git a/common/mock_rsa_key.h b/common/mock_rsa_key.h new file mode 100644 index 0000000..3fc70e3 --- /dev/null +++ b/common/mock_rsa_key.h @@ -0,0 +1,71 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 COMMON_MOCK_RSA_KEY_H_ +#define COMMON_MOCK_RSA_KEY_H_ + +#include "common/rsa_key.h" + +namespace widevine { + +class MockRsaPrivateKey : public RsaPrivateKey { + public: + MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {} + ~MockRsaPrivateKey() override {} + + MOCK_CONST_METHOD2(Decrypt, bool(const std::string& encrypted_message, + std::string* decrypted_message)); + MOCK_CONST_METHOD2(GenerateSignature, + bool(const std::string& message, std::string* signature)); + MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key)); + MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key)); + + private: + MockRsaPrivateKey(const MockRsaPrivateKey&) = delete; + MockRsaPrivateKey& operator=(const MockRsaPrivateKey&) = delete; +}; + +class MockRsaPublicKey : public RsaPublicKey { + public: + MockRsaPublicKey() : RsaPublicKey(RSA_new()) {} + ~MockRsaPublicKey() override {} + + MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message, + std::string* encrypted_message)); + MOCK_CONST_METHOD2(VerifySignature, bool(const std::string& message, + const std::string& signature)); + MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key)); + MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key)); + + private: + MockRsaPublicKey(const MockRsaPublicKey&) = delete; + MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete; +}; + +class MockRsaKeyFactory : public RsaKeyFactory{ + public: + MockRsaKeyFactory() {} + ~MockRsaKeyFactory() override {} + + MOCK_METHOD1(CreateFromPkcs1PrivateKey, + std::unique_ptr(const std::string& private_key)); + MOCK_METHOD2( + CreateFromPkcs8PrivateKey, + std::unique_ptr(const std::string& private_key, + const std::string& private_key_passphrase)); + MOCK_METHOD1(CreateFromPkcs1PublicKey, + std::unique_ptr(const std::string& public_key)); + + private: + MockRsaKeyFactory(const MockRsaKeyFactory&) = delete; + MockRsaKeyFactory& operator=(const MockRsaKeyFactory&) = delete; +}; + +} // namespace widevine + +#endif // COMMON_MOCK_RSA_KEY_H_ diff --git a/common/openssl_util.h b/common/openssl_util.h new file mode 100644 index 0000000..23b74a2 --- /dev/null +++ b/common/openssl_util.h @@ -0,0 +1,70 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 COMMON_OPENSSL_UTIL_H__ +#define COMMON_OPENSSL_UTIL_H__ + +#include "openssl/bio.h" +#include "openssl/evp.h" +#include "openssl/rsa.h" +#include "openssl/x509v3.h" + +template +struct OpenSSLDeleter { + void operator()(T *obj) { func(obj); } +}; + +template +struct OpenSSLStackDeleter { + void operator()(StackType *obj) { + sk_pop_free(reinterpret_cast<_STACK *>(obj), + reinterpret_cast(func)); + } +}; + +template +struct OpenSSLStackOnlyDeleter { + void operator()(StackType *obj) { sk_free(reinterpret_cast<_STACK *>(obj)); } +}; + +template +using ScopedOpenSSLType = std::unique_ptr>; +template +using ScopedOpenSSLStack = + std::unique_ptr>; +template +using ScopedOpenSSLStackOnly = + std::unique_ptr>; + +using ScopedBIO = ScopedOpenSSLType; +using ScopedPKEY = ScopedOpenSSLType; +using ScopedRSA = ScopedOpenSSLType; +using ScopedX509 = ScopedOpenSSLType; +using ScopedX509Extension = + ScopedOpenSSLType; +using ScopedX509Name = ScopedOpenSSLType; +using ScopedX509NameEntry = + ScopedOpenSSLType; +using ScopedX509Store = ScopedOpenSSLType; +using ScopedX509StoreCtx = + ScopedOpenSSLType; +using ScopedAsn1UtcTime = ScopedOpenSSLType; +using ScopedAsn1Utc8String = + ScopedOpenSSLType; +using ScopedAsn1Integer = ScopedOpenSSLType; + +// XxxStack deallocates the stack and its members while XxxStackOnly deallocates +// the stack only. +using ScopedX509Stack = ScopedOpenSSLStack; +using ScopedX509StackOnly = ScopedOpenSSLStackOnly; +using ScopedX509InfoStack = + ScopedOpenSSLStack; +using ScopedX509InfoStackOnly = ScopedOpenSSLStackOnly; + +#endif // COMMON_OPENSSL_UTIL_H__ diff --git a/common/random_util.cc b/common/random_util.cc new file mode 100644 index 0000000..ae8ba33 --- /dev/null +++ b/common/random_util.cc @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/random_util.h" + +#include "glog/logging.h" +#include "openssl/rand.h" + +namespace widevine { + +bool RandomBytes(size_t num_bytes, std::string* output) { + DCHECK(output); + output->resize(num_bytes); + return RAND_bytes(reinterpret_cast(&(*output)[0]), num_bytes); +} + +} // namespace widevine diff --git a/common/random_util.h b/common/random_util.h new file mode 100644 index 0000000..1104dcd --- /dev/null +++ b/common/random_util.h @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 COMMON_RANDOM_UTIL_H_ +#define COMMON_RANDOM_UTIL_H_ + +#include + +namespace widevine { + +// Generates a random string. +// Return true on success, false otherwise. +bool RandomBytes(size_t num_bytes, std::string* output); + +} // namespace widevine + +#endif // COMMON_RANDOM_UTIL_H_ diff --git a/common/random_util_test.cc b/common/random_util_test.cc new file mode 100644 index 0000000..76bc17f --- /dev/null +++ b/common/random_util_test.cc @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/random_util.h" + +#include "gtest/gtest.h" + +namespace widevine { + +TEST(RandomUtilTest, Test) { + std::string output; + ASSERT_TRUE(RandomBytes(16u, &output)); + EXPECT_EQ(16u, output.size()); + + std::string output2; + ASSERT_TRUE(RandomBytes(16u, &output2)); + EXPECT_EQ(16u, output2.size()); + EXPECT_NE(output, output2); + + ASSERT_TRUE(RandomBytes(10u, &output2)); + EXPECT_EQ(10u, output2.size()); +} + +} // namespace widevine diff --git a/common/rsa_key.cc b/common/rsa_key.cc new file mode 100644 index 0000000..173265e --- /dev/null +++ b/common/rsa_key.cc @@ -0,0 +1,296 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// Definition of classes representing RSA private and public keys used +// for message signing, signature verification, encryption and decryption. +// +// RSA signature details: +// Algorithm: RSASSA-PSS +// Hash algorithm: SHA1 +// Mask generation function: mgf1SHA1 +// Salt length: 20 bytes +// Trailer field: 0xbc +// +// RSA encryption details: +// Algorithm: RSA-OAEP +// Mask generation function: mgf1SHA1 +// Label (encoding paramter): empty std::string + +#include "common/rsa_key.h" + +#include "glog/logging.h" +#include "openssl/bn.h" +#include "openssl/err.h" +#include "openssl/evp.h" +#include "openssl/rsa.h" +#include "openssl/sha.h" +#include "common/rsa_util.h" +#include "common/sha_util.h" + +static const int kPssSaltLength = 20; + +namespace { + +// Check if two RSA keys match. If matches, they are either a public-private key +// pair or the same public key or the same private key. +bool RsaKeyMatch(const RSA* key1, const RSA* key2) { + if (!key1 || !key2) return false; + return BN_cmp(key1->n, key2->n) == 0; +} + +} // namespace + +namespace widevine { + +RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(CHECK_NOTNULL(key)) {} + +RsaPrivateKey::~RsaPrivateKey() { RSA_free(key_); } + +RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) { + RSA* key; + if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key)) return nullptr; + if (RSA_check_key(key) != 1) { + LOG(ERROR) << "Invalid private RSA key: " + << ERR_error_string(ERR_get_error(), nullptr); + RSA_free(key); + } + return new RsaPrivateKey(key); +} + +bool RsaPrivateKey::Decrypt(const std::string& encrypted_message, + std::string* decrypted_message) const { + DCHECK(decrypted_message); + + size_t rsa_size = RSA_size(key_); + if (encrypted_message.size() != rsa_size) { + LOG(ERROR) << "Encrypted RSA message has the wrong size (expected " + << rsa_size << ", actual " << encrypted_message.size() << ")"; + return false; + } + decrypted_message->assign(rsa_size, 0); + int decrypted_size = RSA_private_decrypt( + rsa_size, + const_cast( + reinterpret_cast(encrypted_message.data())), + reinterpret_cast(&(*decrypted_message)[0]), key_, + RSA_PKCS1_OAEP_PADDING); + if (decrypted_size == -1) { + LOG(ERROR) << "RSA private decrypt failure: " + << ERR_error_string(ERR_get_error(), nullptr); + return false; + } + decrypted_message->resize(decrypted_size); + return true; +} + +bool RsaPrivateKey::GenerateSignature(const std::string& message, + std::string* signature) const { + DCHECK(signature); + + if (message.empty()) { + LOG(ERROR) << "Message to be signed is empty"; + return false; + } + // Hash the message using SHA1. + std::string message_digest = Sha1_Hash(message); + + // Add PSS padding. + size_t rsa_size = RSA_size(key_); + std::string padded_digest(rsa_size, 0); + if (!RSA_padding_add_PKCS1_PSS_mgf1( + key_, reinterpret_cast(&padded_digest[0]), + reinterpret_cast(&message_digest[0]), EVP_sha1(), + EVP_sha1(), kPssSaltLength)) { + LOG(ERROR) << "RSA padding failure: " + << ERR_error_string(ERR_get_error(), nullptr); + return false; + } + // Encrypt PSS padded digest. + signature->assign(rsa_size, 0); + if (RSA_private_encrypt(padded_digest.size(), + reinterpret_cast(&padded_digest[0]), + reinterpret_cast(&(*signature)[0]), + key_, RSA_NO_PADDING) != + static_cast(signature->size())) { + LOG(ERROR) << "RSA private encrypt failure: " + << ERR_error_string(ERR_get_error(), nullptr); + return false; + } + return true; +} + +bool RsaPrivateKey::GenerateSignatureSha256Pkcs7(const std::string& message, + std::string* signature) const { + DCHECK(signature); + if (message.empty()) { + LOG(ERROR) << "Empty signature verification message"; + return false; + } + + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast(message.data()), + message.size(), + digest); + unsigned int sig_len = RSA_size(key_); + signature->resize(sig_len); + return RSA_sign(NID_sha256, + digest, + sizeof(digest), + reinterpret_cast(&(*signature)[0]), + &sig_len, + key_) == 1; +} + +bool RsaPrivateKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const { + return RsaKeyMatch(key(), private_key.key()); +} + +bool RsaPrivateKey::MatchesPublicKey(const RsaPublicKey& public_key) const { + return RsaKeyMatch(key(), public_key.key()); +} + +RsaPublicKey::RsaPublicKey(RSA* key) : key_(CHECK_NOTNULL(key)) {} + +RsaPublicKey::~RsaPublicKey() { RSA_free(key_); } + +RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) { + RSA* key; + if (!rsa_util::DeserializeRsaPublicKey(serialized_key, &key)) return nullptr; + if (RSA_size(key) == 0) { + LOG(ERROR) << "Invalid public RSA key: " + << ERR_error_string(ERR_get_error(), nullptr); + RSA_free(key); + } + return new RsaPublicKey(key); +} + +bool RsaPublicKey::Encrypt(const std::string& clear_message, + std::string* encrypted_message) const { + DCHECK(encrypted_message); + + if (clear_message.empty()) { + LOG(ERROR) << "Message to be encrypted is empty"; + return false; + } + size_t rsa_size = RSA_size(key_); + encrypted_message->assign(rsa_size, 0); + if (RSA_public_encrypt( + clear_message.size(), + const_cast( + reinterpret_cast(clear_message.data())), + reinterpret_cast(&(*encrypted_message)[0]), key_, + RSA_PKCS1_OAEP_PADDING) != static_cast(rsa_size)) { + LOG(ERROR) << "RSA public encrypt failure: " + << ERR_error_string(ERR_get_error(), nullptr); + return false; + } + return true; +} + +bool RsaPublicKey::VerifySignature(const std::string& message, + const std::string& signature) const { + if (message.empty()) { + LOG(ERROR) << "Signed message is empty"; + return false; + } + size_t rsa_size = RSA_size(key_); + if (signature.size() != rsa_size) { + LOG(ERROR) << "Message signature is of the wrong size (expected " + << rsa_size << ", actual " << signature.size() << ")"; + return false; + } + // Decrypt the signature. + std::string padded_digest(signature.size(), 0); + if (RSA_public_decrypt( + signature.size(), + const_cast( + reinterpret_cast(signature.data())), + reinterpret_cast(&padded_digest[0]), key_, + RSA_NO_PADDING) != static_cast(rsa_size)) { + LOG(ERROR) << "RSA public decrypt failure: " + << ERR_error_string(ERR_get_error(), nullptr); + return false; + } + // Hash the message using SHA1. + std::string message_digest = Sha1_Hash(message); + + // Verify PSS padding. + return RSA_verify_PKCS1_PSS_mgf1( + key_, reinterpret_cast(&message_digest[0]), + EVP_sha1(), EVP_sha1(), + reinterpret_cast(&padded_digest[0]), + kPssSaltLength) != 0; +} + +bool RsaPublicKey::VerifySignatureSha256Pkcs7( + const std::string& message, const std::string& signature) const { + if (message.empty()) { + LOG(ERROR) << "Empty signature verification message"; + return false; + } + if (signature.empty()) { + LOG(ERROR) << "Empty signature"; + return false; + } + + if (signature.size() != RSA_size(key_)) { + LOG(ERROR) << "RSA signature has the wrong size"; + return false; + } + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast(message.data()), + message.size(), + digest); + return RSA_verify(NID_sha256, + digest, + sizeof(digest), + reinterpret_cast(signature.data()), + signature.size(), + key_) == 1; +} + +bool RsaPublicKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const { + return RsaKeyMatch(key(), private_key.key()); +} + +bool RsaPublicKey::MatchesPublicKey(const RsaPublicKey& public_key) const { + return RsaKeyMatch(key(), public_key.key()); +} + +RsaKeyFactory::RsaKeyFactory() {} + +RsaKeyFactory::~RsaKeyFactory() {} + +std::unique_ptr RsaKeyFactory::CreateFromPkcs1PrivateKey( + const std::string& private_key) { + return std::unique_ptr(RsaPrivateKey::Create(private_key)); +} + +std::unique_ptr RsaKeyFactory::CreateFromPkcs8PrivateKey( + const std::string& private_key, const std::string& private_key_passphrase) { + std::string pkcs1_key; + const bool result = + private_key_passphrase.empty() + ? rsa_util::PrivateKeyInfoToRsaPrivateKey(private_key, &pkcs1_key) + : rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey( + private_key, private_key_passphrase, &pkcs1_key); + if (!result) { + LOG(WARNING) << "Failed to get pkcs1_key."; + return std::unique_ptr(); + } + return std::unique_ptr(RsaPrivateKey::Create(pkcs1_key)); +} + +std::unique_ptr RsaKeyFactory::CreateFromPkcs1PublicKey( + const std::string& public_key) { + return std::unique_ptr(RsaPublicKey::Create(public_key)); +} + +} // namespace widevine diff --git a/common/rsa_key.h b/common/rsa_key.h new file mode 100644 index 0000000..c98f283 --- /dev/null +++ b/common/rsa_key.h @@ -0,0 +1,131 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// Declaration of classes representing RSA private and public keys used +// for message signing, signature verification, encryption and decryption. + +#ifndef COMMON_RSA_KEY_H_ +#define COMMON_RSA_KEY_H_ + +#include +#include + +#include "base/macros.h" +#include "openssl/rsa.h" + +namespace widevine { + +class RsaPublicKey; + +class RsaPrivateKey { + public: + explicit RsaPrivateKey(RSA* key); + virtual ~RsaPrivateKey(); + + // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. + // Returns NULL on failure. + static RsaPrivateKey* Create(const std::string& serialized_key); + + // Decrypt a message using RSA-OAEP. Caller retains ownership of all + // parameters. Returns true if successful, false otherwise. + virtual bool Decrypt(const std::string& encrypted_message, + std::string* decrypted_message) const; + + // Generate RSSASSA-PSS signature. Caller retains ownership of all parameters. + // Returns true if successful, false otherwise. + virtual bool GenerateSignature(const std::string& message, + std::string* signature) const; + + // Generate SHA256 digest, PKCS#7 padded signature. Caller retains ownership + // of all parameters. Returns true if successful, false otherwise. + virtual bool GenerateSignatureSha256Pkcs7(const std::string& message, + std::string* signature) const; + + // Return true if the underlying key matches with |private_key|. + virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const; + + // Return true if the underlying key is a public-private key pair with + // |public_key|. + virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const; + + const RSA* key() const { return key_; } + + private: + RSA* key_; + + DISALLOW_COPY_AND_ASSIGN(RsaPrivateKey); +}; + +class RsaPublicKey { + public: + explicit RsaPublicKey(RSA* key); + virtual ~RsaPublicKey(); + + // Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey. + // Returns NULL on failure. + static RsaPublicKey* Create(const std::string& serialized_key); + + // Encrypt a message using RSA-OAEP. Caller retains ownership of all + // parameters. Returns true if successful, false otherwise. + virtual bool Encrypt(const std::string& clear_message, + std::string* encrypted_message) const; + + // Verify RSSASSA-PSS signature. Caller retains ownership of all parameters. + // Returns true if validation succeeds, false otherwise. + virtual bool VerifySignature(const std::string& message, + const std::string& signature) const; + + + // Verify a signature. This method takes two parameters: |message| which is a + // std::string containing the data which was signed, and |signature| which is a + // std::string containing the message SHA256 digest signature with PKCS#7 + // padding. Returns true if verification succeeds, false otherwise. + virtual bool VerifySignatureSha256Pkcs7(const std::string& message, + const std::string& signature) const; + + // Return true if the underlying key is a public-private key pair with + // |private_key|. + virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const; + + // Return true if the underlying key matches with |public_key|. + virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const; + + const RSA* key() const { return key_; } + + private: + RSA* key_; + + DISALLOW_COPY_AND_ASSIGN(RsaPublicKey); +}; + +class RsaKeyFactory { + public: + RsaKeyFactory(); + virtual ~RsaKeyFactory(); + + // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. + virtual std::unique_ptr CreateFromPkcs1PrivateKey( + const std::string& private_key); + + // Create an RsaPrivateKey object using an encrypted PKCS#8 RSAPrivateKey. + virtual std::unique_ptr CreateFromPkcs8PrivateKey( + const std::string& private_key, const std::string& private_key_passphrase); + + // Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey. + virtual std::unique_ptr CreateFromPkcs1PublicKey( + const std::string& public_key); + + private: + DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory); +}; + +} // namespace widevine + +#endif // COMMON_RSA_KEY_H_ diff --git a/common/rsa_key_test.cc b/common/rsa_key_test.cc new file mode 100644 index 0000000..780a168 --- /dev/null +++ b/common/rsa_key_test.cc @@ -0,0 +1,227 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// Unit test for rsa_key RSA encryption and signing. + +#include + +#include "gtest/gtest.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" + +namespace widevine { + +static const char kTestMessage[] = + "A fool thinks himself to be wise, but a wise man knows himself to be a " + "fool."; + +class RsaKeyTest : public ::testing::Test { + protected: + void TestEncryption(std::unique_ptr private_key, + std::unique_ptr public_key); + + void TestSigning(std::unique_ptr private_key, + std::unique_ptr public_key); + + void TestSigningSha256Pkcs7(std::unique_ptr private_key, + std::unique_ptr public_key); + + RsaTestKeys test_keys_; + RsaKeyFactory factory_; +}; + +void RsaKeyTest::TestEncryption(std::unique_ptr private_key, + std::unique_ptr public_key) { + ASSERT_TRUE(private_key); + ASSERT_TRUE(public_key); + std::string encrypted_message; + EXPECT_TRUE(public_key->Encrypt(kTestMessage, &encrypted_message)); + std::string decrypted_message; + EXPECT_TRUE(private_key->Decrypt(encrypted_message, &decrypted_message)); + EXPECT_EQ(kTestMessage, decrypted_message); + // Add a byte to the encrypted message. + std::string bad_enc_message(encrypted_message); + bad_enc_message += '\0'; + EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message)); + // Remove a byte from the encrypted message. + bad_enc_message = encrypted_message.substr(0, encrypted_message.size() - 1); + EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message)); + // Change a byte in the encrypted message. + bad_enc_message = encrypted_message; + bad_enc_message[128] ^= 0x55; + EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message)); +} + +void RsaKeyTest::TestSigning(std::unique_ptr private_key, + std::unique_ptr public_key) { + ASSERT_TRUE(private_key); + ASSERT_TRUE(public_key); + std::string signature; + EXPECT_TRUE(private_key->GenerateSignature(kTestMessage, &signature)); + EXPECT_TRUE(public_key->VerifySignature(kTestMessage, signature)); + // Add a byte to the signature. + std::string bad_signature(signature); + bad_signature += '\0'; + EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature)); + // Remove a byte from the signature. + bad_signature = signature.substr(0, signature.size() - 1); + EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature)); + // Change a byte in the signature. + bad_signature = signature; + bad_signature[32] ^= 0x55; + EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature)); +} + +void RsaKeyTest::TestSigningSha256Pkcs7( + std::unique_ptr private_key, + std::unique_ptr public_key) { + ASSERT_TRUE(private_key); + ASSERT_TRUE(public_key); + std::string signature; + EXPECT_TRUE( + private_key->GenerateSignatureSha256Pkcs7(kTestMessage, &signature)); + EXPECT_TRUE(public_key->VerifySignatureSha256Pkcs7(kTestMessage, signature)); + // Add a byte to the signature. + std::string bad_signature(signature); + bad_signature += '\0'; + EXPECT_FALSE( + public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature)); + // Remove a byte from the signature. + bad_signature = signature.substr(0, signature.size() - 1); + EXPECT_FALSE( + public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature)); + // Change a byte in the signature. + bad_signature = signature; + bad_signature[32] ^= 0x55; + EXPECT_FALSE( + public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature)); +} + +TEST_F(RsaKeyTest, BadKey) { + std::unique_ptr private_key( + RsaPrivateKey::Create("bad_private_key")); + EXPECT_TRUE(!private_key); + std::unique_ptr public_key( + RsaPublicKey::Create("bad_public_key")); + EXPECT_TRUE(!public_key); +} + +TEST_F(RsaKeyTest, EncryptAndDecrypt_3072) { + const std::string& private_key = test_keys_.private_test_key_1_3072_bits(); + const std::string& public_key = test_keys_.public_test_key_1_3072_bits(); + TestEncryption( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + TestEncryption(factory_.CreateFromPkcs1PrivateKey(private_key), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, EncryptAndDecrypt_2048) { + const std::string& private_key = test_keys_.private_test_key_2_2048_bits(); + const std::string& public_key = test_keys_.public_test_key_2_2048_bits(); + TestEncryption( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + + std::string pkcs8_key; + std::string passphrase("passphrase"); + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + private_key, passphrase, &pkcs8_key)); + TestEncryption(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase), + factory_.CreateFromPkcs1PublicKey(public_key)); + + ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key)); + TestEncryption(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerify_3072) { + const std::string& private_key = test_keys_.private_test_key_1_3072_bits(); + const std::string& public_key = test_keys_.public_test_key_1_3072_bits(); + TestSigning( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + TestSigning(factory_.CreateFromPkcs1PrivateKey(private_key), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerify_2048) { + const std::string& private_key = test_keys_.private_test_key_2_2048_bits(); + const std::string& public_key = test_keys_.public_test_key_2_2048_bits(); + TestSigning( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + + std::string pkcs8_key; + std::string passphrase("passphrase"); + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + private_key, passphrase, &pkcs8_key)); + TestSigning(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase), + factory_.CreateFromPkcs1PublicKey(public_key)); + + ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key)); + TestSigning(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_3072) { + const std::string& private_key = test_keys_.private_test_key_1_3072_bits(); + const std::string& public_key = test_keys_.public_test_key_1_3072_bits(); + TestSigningSha256Pkcs7( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + TestSigningSha256Pkcs7(factory_.CreateFromPkcs1PrivateKey(private_key), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_2048) { + const std::string& private_key = test_keys_.private_test_key_2_2048_bits(); + const std::string& public_key = test_keys_.public_test_key_2_2048_bits(); + TestSigningSha256Pkcs7( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + + std::string pkcs8_key; + std::string passphrase("passphrase"); + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + private_key, passphrase, &pkcs8_key)); + TestSigningSha256Pkcs7( + factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase), + factory_.CreateFromPkcs1PublicKey(public_key)); + + ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key)); + TestSigningSha256Pkcs7( + factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, RsaKeyMatch) { + std::unique_ptr private_key2( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + std::unique_ptr private_key3( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + std::unique_ptr public_key2( + RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); + std::unique_ptr public_key3( + RsaPublicKey::Create(test_keys_.public_test_key_3_2048_bits())); + + EXPECT_TRUE(public_key2->MatchesPublicKey(*public_key2)); + EXPECT_FALSE(public_key2->MatchesPublicKey(*public_key3)); + EXPECT_TRUE(public_key2->MatchesPrivateKey(*private_key2)); + EXPECT_FALSE(public_key2->MatchesPrivateKey(*private_key3)); + + EXPECT_TRUE(private_key2->MatchesPublicKey(*public_key2)); + EXPECT_FALSE(private_key2->MatchesPublicKey(*public_key3)); + EXPECT_TRUE(private_key2->MatchesPrivateKey(*private_key2)); + EXPECT_FALSE(private_key2->MatchesPrivateKey(*private_key3)); +} + +} // namespace widevine diff --git a/common/rsa_test_keys.cc b/common/rsa_test_keys.cc new file mode 100644 index 0000000..fbed989 --- /dev/null +++ b/common/rsa_test_keys.cc @@ -0,0 +1,682 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// RSA keys generated using fake_prng for purposes of testing. + +#include "common/rsa_test_keys.h" + + +namespace widevine { + +static const unsigned char kTestRsaPrivateKey1_3072[] = { + 0x30, 0x82, 0x06, 0xe3, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x81, 0x00, 0xa5, 0x62, 0x07, 0xdf, + 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, + 0x78, 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, + 0x9d, 0x35, 0x3f, 0xf3, 0x0f, 0xe9, 0x61, 0x96, + 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, + 0xca, 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, + 0x08, 0x47, 0xe4, 0x55, 0x1b, 0x83, 0xbe, 0x10, + 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, + 0x46, 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, + 0x08, 0xc3, 0x04, 0x17, 0x01, 0xb5, 0x11, 0x53, + 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, + 0x5c, 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, + 0x82, 0xa0, 0x92, 0xf1, 0x14, 0xf1, 0x22, 0xff, + 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, + 0xcd, 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, + 0xfd, 0xa4, 0x01, 0xe3, 0xb6, 0x0e, 0x85, 0xe4, + 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, + 0xde, 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, + 0x24, 0xb9, 0xb0, 0xbb, 0x31, 0xa0, 0xee, 0x6a, + 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, + 0x3a, 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, + 0x51, 0xbb, 0xfa, 0xbb, 0x90, 0x97, 0x2c, 0x77, + 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, + 0x49, 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, + 0xda, 0xe2, 0xbd, 0xf0, 0x5f, 0x07, 0x53, 0x8a, + 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, + 0xda, 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, + 0x3a, 0xe1, 0xc8, 0xdb, 0x17, 0xe8, 0xc9, 0x3a, + 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, + 0x30, 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, + 0xf5, 0xca, 0x15, 0x26, 0xaf, 0x39, 0xf3, 0x5d, + 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, + 0x16, 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, + 0x82, 0xd0, 0x60, 0x0b, 0x42, 0x72, 0x85, 0xec, + 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, + 0xf9, 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, + 0x6b, 0xc9, 0x12, 0x64, 0xe4, 0x3a, 0x3b, 0xc9, + 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, + 0x4a, 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, + 0x82, 0xb2, 0xf0, 0xd1, 0x72, 0x71, 0x04, 0x35, + 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, + 0xde, 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, + 0xce, 0x6d, 0x87, 0x50, 0x04, 0xb5, 0xd7, 0x24, + 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, + 0x1b, 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, + 0x1e, 0x6f, 0xd7, 0xf0, 0x33, 0x61, 0x53, 0x7e, + 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, + 0x67, 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, + 0x83, 0x9c, 0xc2, 0x87, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x80, 0x5a, 0x09, 0x3f, + 0x9e, 0x2e, 0x4d, 0x26, 0x50, 0x7b, 0x70, 0x21, + 0xb0, 0x0c, 0x25, 0x21, 0x1f, 0xd9, 0x89, 0x5a, + 0xca, 0x35, 0x23, 0x0b, 0x58, 0xa9, 0x7d, 0xf6, + 0x19, 0xc4, 0x29, 0x87, 0xc7, 0xd4, 0x94, 0x85, + 0xb4, 0x2c, 0xaf, 0x62, 0xb1, 0xe8, 0x62, 0x5b, + 0xda, 0xdb, 0x70, 0x40, 0x37, 0xb1, 0x4e, 0x0c, + 0xc8, 0x62, 0xee, 0xa2, 0xfc, 0x3c, 0xd2, 0x39, + 0x90, 0x15, 0x2c, 0xba, 0x20, 0x50, 0xb7, 0x82, + 0x2a, 0xa0, 0x76, 0x83, 0x20, 0x7f, 0x56, 0x73, + 0x43, 0x8a, 0x9b, 0xa7, 0x6c, 0x63, 0xb6, 0xad, + 0x56, 0xb2, 0x8a, 0xb2, 0xbc, 0x8f, 0xe2, 0xef, + 0x83, 0x9d, 0x98, 0x0b, 0xc7, 0x62, 0x0e, 0x51, + 0x6e, 0x57, 0x1d, 0x1b, 0x0e, 0x3a, 0xea, 0x3b, + 0x76, 0x63, 0x35, 0xd0, 0xd1, 0xcf, 0xbe, 0xad, + 0xbb, 0x1d, 0xde, 0x0f, 0x05, 0x48, 0x55, 0x29, + 0xc1, 0xbc, 0x21, 0xc7, 0x87, 0xf2, 0x75, 0x12, + 0x7d, 0x92, 0x9e, 0xbf, 0xad, 0x04, 0x68, 0xc4, + 0xc9, 0x9d, 0x35, 0xd6, 0xa8, 0x62, 0xc1, 0x69, + 0x6a, 0xb6, 0x41, 0xb7, 0x37, 0x66, 0xdf, 0xb2, + 0xb9, 0x8c, 0x8b, 0x15, 0x08, 0x4c, 0x3d, 0xf1, + 0xed, 0x82, 0x0f, 0xe3, 0xd5, 0xff, 0x46, 0xbd, + 0xf7, 0x85, 0x43, 0xc0, 0x8b, 0xba, 0x47, 0xf1, + 0x41, 0x57, 0xc3, 0x7f, 0x8b, 0x0d, 0x48, 0xea, + 0xc2, 0xed, 0xc0, 0x69, 0x84, 0xb6, 0x32, 0x08, + 0x49, 0x74, 0x14, 0x84, 0xa4, 0x1b, 0x48, 0x5b, + 0xec, 0xd3, 0x0b, 0x12, 0x2b, 0x4c, 0x9e, 0x5c, + 0x01, 0x60, 0xad, 0xef, 0xcb, 0x2b, 0x56, 0x84, + 0x07, 0xfa, 0x62, 0xc6, 0x08, 0x92, 0x98, 0x70, + 0xc9, 0x5b, 0x18, 0xc8, 0xfa, 0x27, 0x0c, 0xe2, + 0xbd, 0xfb, 0x3e, 0x43, 0xa5, 0xb7, 0x06, 0x2c, + 0x4e, 0xf1, 0x07, 0x5d, 0x8d, 0xdd, 0x53, 0xc5, + 0x8c, 0x4a, 0xf2, 0x2f, 0x8e, 0x80, 0x96, 0x16, + 0xc0, 0xfc, 0xf9, 0x20, 0x4f, 0x35, 0xc7, 0x53, + 0x8b, 0x2d, 0x37, 0x43, 0x93, 0x3d, 0x74, 0x3f, + 0x63, 0xf7, 0x0b, 0xbd, 0x46, 0xe4, 0x51, 0x67, + 0x33, 0x57, 0x15, 0xf5, 0x59, 0x27, 0x66, 0xe8, + 0xe2, 0x4b, 0xa3, 0x93, 0x03, 0x8a, 0x9c, 0x05, + 0x13, 0xf2, 0xcb, 0xf7, 0x9c, 0x68, 0xe7, 0x16, + 0x4b, 0x8e, 0x59, 0x71, 0x2b, 0x73, 0x9b, 0xb9, + 0xae, 0x50, 0xfa, 0xd7, 0xd3, 0x34, 0x17, 0x1d, + 0x62, 0x88, 0xbd, 0x8c, 0xba, 0x5a, 0x6b, 0x6a, + 0x5e, 0xb3, 0xa5, 0x80, 0xca, 0xbb, 0xb9, 0xb5, + 0xa8, 0x2e, 0xb1, 0x61, 0x6e, 0xd5, 0xd6, 0x62, + 0x98, 0x4a, 0xb0, 0xb8, 0x76, 0xa9, 0x19, 0x5c, + 0xe2, 0xbe, 0xb3, 0x9b, 0x4a, 0x39, 0xf5, 0xe6, + 0xbb, 0x11, 0x6e, 0x13, 0x13, 0x38, 0xb8, 0x1f, + 0x21, 0x19, 0xf5, 0xa7, 0x76, 0x93, 0xb3, 0x56, + 0xfa, 0xcc, 0x74, 0xbc, 0x19, 0x02, 0x81, 0xc1, + 0x00, 0xd1, 0xd1, 0x72, 0x57, 0xe5, 0xb0, 0x1c, + 0x09, 0x05, 0xbb, 0x55, 0x89, 0x3c, 0x4a, 0x81, + 0x90, 0x9a, 0xf9, 0x32, 0x63, 0x41, 0xad, 0x6a, + 0x5f, 0x65, 0x94, 0x92, 0xcc, 0xf7, 0xc7, 0x53, + 0x93, 0xa0, 0xf7, 0xbe, 0x48, 0x82, 0x63, 0x31, + 0x7b, 0xd0, 0x82, 0x09, 0xbb, 0x0a, 0xbc, 0x60, + 0xc9, 0x4d, 0x83, 0xe4, 0x5d, 0x50, 0xe6, 0x5f, + 0x8b, 0x47, 0x07, 0xa3, 0x3a, 0x36, 0x97, 0xaa, + 0x21, 0x70, 0x7f, 0xd5, 0x6c, 0xb0, 0x56, 0xf5, + 0x5c, 0x48, 0x74, 0x2a, 0xdd, 0xfe, 0x94, 0x83, + 0x05, 0xe0, 0x3d, 0x5d, 0xdd, 0x5a, 0x05, 0xcb, + 0x47, 0xd7, 0xf9, 0x89, 0x55, 0xaa, 0x0b, 0x21, + 0xc0, 0x71, 0x5d, 0xe1, 0x4c, 0x6a, 0x45, 0x86, + 0x86, 0xf2, 0xb9, 0x38, 0x6a, 0x56, 0x51, 0x0d, + 0x7d, 0xac, 0x30, 0x31, 0xca, 0x2d, 0xaa, 0xaa, + 0xba, 0xcc, 0x12, 0x40, 0xc1, 0x0d, 0xa6, 0xc1, + 0x7d, 0x22, 0xec, 0xb6, 0x51, 0x45, 0xfe, 0x4e, + 0xbb, 0x4a, 0xd2, 0xba, 0x9b, 0xa2, 0xcc, 0x28, + 0x2b, 0x01, 0x53, 0x53, 0xf3, 0xa9, 0x5a, 0x8f, + 0xeb, 0xb7, 0xb8, 0x62, 0x6b, 0x8a, 0x79, 0x24, + 0xcc, 0x86, 0x34, 0x45, 0xe2, 0xad, 0x1d, 0xd0, + 0x4c, 0xc9, 0x77, 0x2a, 0xf9, 0x1a, 0xe8, 0x58, + 0x78, 0x51, 0x8a, 0xea, 0x3f, 0x90, 0x36, 0x46, + 0x2a, 0xc0, 0x71, 0x41, 0x83, 0x2c, 0x48, 0xee, + 0xc5, 0x02, 0x81, 0xc1, 0x00, 0xc9, 0xc8, 0xce, + 0xc4, 0x50, 0xb2, 0x26, 0xcb, 0x35, 0x78, 0x55, + 0x3c, 0xcc, 0xf0, 0x7e, 0xba, 0xad, 0xeb, 0x58, + 0xe9, 0xb5, 0x78, 0x2f, 0x43, 0x5f, 0x07, 0x47, + 0x56, 0x05, 0x41, 0x38, 0x71, 0xe1, 0x58, 0x62, + 0xb1, 0x8e, 0xbc, 0xf9, 0x80, 0x04, 0x22, 0x39, + 0x22, 0x24, 0x28, 0x86, 0x9c, 0x00, 0x44, 0x5f, + 0xc4, 0x97, 0xe6, 0x71, 0x5f, 0x1f, 0x58, 0xea, + 0x75, 0x18, 0x0c, 0x23, 0x63, 0x09, 0xc5, 0x98, + 0xc4, 0x6d, 0x23, 0xc2, 0x2c, 0x93, 0x6a, 0x26, + 0xe4, 0x3d, 0x8d, 0xa1, 0x39, 0x70, 0x34, 0x25, + 0xcd, 0xbc, 0x82, 0x78, 0x2b, 0xf3, 0x7e, 0x81, + 0xb6, 0x5f, 0xc5, 0x69, 0xd0, 0x81, 0x69, 0x50, + 0x2f, 0x17, 0x0c, 0x17, 0x3c, 0x0b, 0x45, 0x38, + 0xce, 0xe3, 0xbf, 0x8a, 0x50, 0x0a, 0x00, 0x74, + 0x7e, 0x7a, 0xd8, 0x55, 0x52, 0x6b, 0x82, 0xfb, + 0x34, 0x15, 0x73, 0x6a, 0xf4, 0x51, 0x9b, 0x9f, + 0xa0, 0x45, 0xb9, 0x76, 0xe5, 0xd3, 0xd5, 0xf4, + 0xa9, 0xa4, 0xcd, 0x42, 0x2f, 0x29, 0x89, 0xec, + 0x28, 0x5f, 0x03, 0x45, 0x27, 0xaf, 0x8c, 0x39, + 0x3e, 0x59, 0x9d, 0xaf, 0x27, 0x5d, 0x17, 0x53, + 0x17, 0xeb, 0x8d, 0x7f, 0x3d, 0xb8, 0x2a, 0x50, + 0x1e, 0xb5, 0xc5, 0x04, 0xab, 0x9c, 0xa7, 0xaa, + 0x86, 0x41, 0xb9, 0x36, 0x29, 0x9e, 0xd2, 0xd8, + 0xde, 0x5f, 0xde, 0x80, 0xdb, 0x02, 0x81, 0xc0, + 0x03, 0xf3, 0x5f, 0xa5, 0xcc, 0x0b, 0x5e, 0xdb, + 0xc4, 0xa1, 0xdc, 0x60, 0x73, 0x24, 0x2c, 0x00, + 0x5f, 0x0a, 0xa6, 0x2a, 0x3c, 0x48, 0x59, 0xa2, + 0x66, 0x35, 0x3f, 0xf6, 0x60, 0x0b, 0xfe, 0xc4, + 0xde, 0xd9, 0x0b, 0x5a, 0x2e, 0x2a, 0x53, 0xfa, + 0x32, 0xd8, 0xdf, 0xfa, 0x07, 0x9f, 0xb8, 0x6a, + 0xd1, 0xec, 0xd3, 0xd5, 0xf5, 0xfa, 0x00, 0x7e, + 0x8c, 0xdd, 0xd5, 0xf2, 0xf8, 0xa8, 0x2e, 0x69, + 0xe6, 0xc6, 0x61, 0x6c, 0x64, 0x7d, 0x9e, 0xad, + 0x18, 0x28, 0x27, 0xce, 0x7a, 0x46, 0xad, 0x98, + 0xe4, 0xba, 0x03, 0x14, 0x71, 0xe7, 0x7e, 0x06, + 0x62, 0x48, 0xae, 0x8f, 0x50, 0x5e, 0x59, 0x4a, + 0x58, 0x58, 0x1e, 0x2f, 0xe4, 0x28, 0x5e, 0xfa, + 0x17, 0x83, 0xe9, 0x4e, 0x07, 0x46, 0x0b, 0x6c, + 0xfc, 0x5b, 0x03, 0xf4, 0xfc, 0x9b, 0x24, 0x0f, + 0xd4, 0x5b, 0xdb, 0xa0, 0x46, 0xf3, 0x86, 0xdd, + 0x26, 0x55, 0x32, 0xb1, 0xa1, 0x11, 0xc2, 0xc5, + 0xc0, 0x08, 0xeb, 0xbe, 0x96, 0x78, 0x25, 0xa1, + 0x79, 0xaa, 0xe9, 0xff, 0xc2, 0x86, 0x94, 0x03, + 0x2a, 0x38, 0x6c, 0x91, 0xfd, 0xcf, 0x7e, 0x23, + 0xe3, 0xbb, 0x04, 0x3d, 0xda, 0x68, 0x9f, 0x4d, + 0x72, 0xd5, 0xad, 0x97, 0x77, 0x2c, 0x3c, 0xce, + 0x37, 0x2a, 0xd8, 0x72, 0x4d, 0xf2, 0xd7, 0xab, + 0x62, 0x68, 0x3f, 0x85, 0x8a, 0xc5, 0xec, 0xc9, + 0x02, 0x81, 0xc1, 0x00, 0x92, 0x43, 0x0c, 0x1d, + 0x20, 0xa1, 0x01, 0x9d, 0xaa, 0x54, 0x5e, 0xf4, + 0x83, 0x58, 0x8f, 0x83, 0xa1, 0x2d, 0x46, 0x75, + 0xa1, 0x24, 0x4c, 0x9d, 0xf8, 0xf3, 0xbd, 0xb1, + 0x8c, 0x7d, 0x89, 0xfc, 0x81, 0xeb, 0x1f, 0x1e, + 0xb4, 0xe8, 0x25, 0xb1, 0xb5, 0x4d, 0x59, 0x3c, + 0x76, 0x19, 0x29, 0xf9, 0x49, 0xf8, 0x45, 0xb2, + 0xaa, 0xa8, 0x4e, 0xe5, 0x34, 0x43, 0xaf, 0x2e, + 0xd1, 0x0f, 0x7b, 0x56, 0xfe, 0x6e, 0x4c, 0x1d, + 0x95, 0x3e, 0xa6, 0x30, 0xc9, 0x69, 0xd8, 0x66, + 0xf8, 0x77, 0x00, 0xb6, 0x31, 0xae, 0x9a, 0xf8, + 0x55, 0xfb, 0xfc, 0x3f, 0x5f, 0x70, 0x03, 0x75, + 0xbe, 0x55, 0xca, 0x2d, 0x68, 0xa0, 0x7d, 0x8e, + 0xa4, 0x96, 0x0f, 0x01, 0x66, 0xe9, 0xf6, 0x13, + 0x80, 0xe2, 0x05, 0xcf, 0x9e, 0x70, 0x56, 0x00, + 0x97, 0xea, 0xd7, 0x6d, 0xb6, 0xa0, 0x6a, 0x95, + 0x86, 0x36, 0xf2, 0xff, 0xc5, 0x67, 0x98, 0x7d, + 0x04, 0x0d, 0x3b, 0x31, 0xbc, 0x2b, 0x09, 0xfd, + 0x2d, 0x87, 0xda, 0xc1, 0x74, 0xca, 0x94, 0x73, + 0x6e, 0xeb, 0x5f, 0xe5, 0x34, 0x49, 0xdf, 0xf4, + 0x61, 0xe0, 0xfa, 0x64, 0xfe, 0x05, 0x3a, 0x25, + 0xcc, 0x87, 0xf4, 0x03, 0x38, 0xca, 0xf2, 0xe8, + 0x4f, 0xb9, 0x4f, 0x79, 0x55, 0x43, 0xf3, 0x46, + 0xfd, 0xbc, 0xd2, 0x95, 0xb8, 0x99, 0xfc, 0xb8, + 0xb3, 0xa5, 0x04, 0xa1, 0x02, 0x81, 0xc0, 0x47, + 0xc6, 0x9c, 0x18, 0x54, 0xe5, 0xbb, 0xf9, 0xf4, + 0x38, 0xd2, 0xc0, 0xd1, 0x1a, 0xcc, 0xdb, 0x06, + 0x87, 0x75, 0x1f, 0x13, 0xa2, 0x7f, 0x8b, 0x45, + 0x54, 0xcb, 0x43, 0xf8, 0xbb, 0x94, 0xd6, 0x2e, + 0x56, 0x5c, 0x69, 0x6d, 0x83, 0xb5, 0x45, 0x46, + 0x68, 0x5c, 0x76, 0x1e, 0x6c, 0x0c, 0x53, 0x59, + 0xcc, 0x19, 0xc7, 0x81, 0x62, 0x66, 0x92, 0x02, + 0x8f, 0xa6, 0xdb, 0x50, 0x1c, 0x67, 0xfc, 0x82, + 0x56, 0x2b, 0x4b, 0x1f, 0x97, 0x87, 0xc4, 0x7d, + 0x20, 0xda, 0xd3, 0x3f, 0x28, 0xf9, 0x55, 0xfe, + 0x84, 0x50, 0xc5, 0x3b, 0xd4, 0xaf, 0xf5, 0x3d, + 0x43, 0xce, 0xdc, 0x55, 0x11, 0x87, 0xdb, 0x72, + 0x66, 0xcc, 0x83, 0xc4, 0x8b, 0x20, 0xae, 0x59, + 0x4d, 0xeb, 0xac, 0xb5, 0x4a, 0xec, 0x66, 0x09, + 0x37, 0x55, 0x14, 0x21, 0x57, 0xff, 0x0a, 0xac, + 0xda, 0xb1, 0xae, 0x31, 0xab, 0x41, 0x30, 0x65, + 0x02, 0x83, 0xd1, 0xdb, 0x65, 0xb7, 0x52, 0xa7, + 0x21, 0x9f, 0x1f, 0x8f, 0x69, 0x23, 0x3b, 0xb8, + 0xf9, 0x6d, 0xe7, 0xc1, 0x53, 0x9f, 0x8f, 0x67, + 0xfc, 0x6e, 0x20, 0x18, 0x31, 0x89, 0xe7, 0xbb, + 0xd4, 0xc1, 0x03, 0x67, 0xd6, 0xa5, 0x76, 0xc9, + 0xea, 0x97, 0x93, 0x02, 0xca, 0x44, 0x52, 0x55, + 0x0f, 0xed, 0x55, 0xb5, 0x49, 0xd6, 0x94, 0x59, + 0xee, 0xcc, 0x1b, 0x5a, 0x00, 0x3d, 0xcd }; + +static const unsigned char kTestRsaPublicKey1_3072[] = { + 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, + 0x00, 0xa5, 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, + 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, 0x76, 0xbe, + 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, + 0xf3, 0x0f, 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, + 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, 0xe4, 0xdd, + 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, + 0x55, 0x1b, 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, + 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, 0xc2, 0x65, + 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, + 0x17, 0x01, 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, + 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, 0xb4, 0x7a, + 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, + 0xf1, 0x14, 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, + 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, 0xa0, 0x0a, + 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, + 0xe3, 0xb6, 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, + 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, 0xf2, 0x59, + 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, + 0xbb, 0x31, 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, + 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, 0xae, 0xb2, + 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, + 0xbb, 0x90, 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, + 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, 0x94, 0x53, + 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, + 0xf0, 0x5f, 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, + 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, 0xea, 0x1e, + 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, + 0xdb, 0x17, 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, + 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, 0xb7, 0xf5, + 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, + 0x26, 0xaf, 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, + 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, 0x9c, 0xee, + 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, + 0x0b, 0x42, 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, + 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, 0xa6, 0x82, + 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, + 0x64, 0xe4, 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, + 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, 0xe9, 0x4c, + 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, + 0xd1, 0x72, 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, + 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, 0x8f, 0xe1, + 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, + 0x50, 0x04, 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, + 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, 0x38, 0xff, + 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, + 0xf0, 0x33, 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, + 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, 0xd5, 0xf0, + 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, + 0x87, 0x02, 0x03, 0x01, 0x00, 0x01 }; + +static const unsigned char kTestRsaPrivateKey2_2048[] = { + 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, + 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4, + 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, + 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, + 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e, + 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, + 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, + 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7, + 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, + 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, + 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68, + 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, + 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, + 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, + 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, + 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, + 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e, + 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, + 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, + 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88, + 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, + 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, + 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8, + 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, + 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, + 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e, + 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, + 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, + 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29, + 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, + 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, + 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, + 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x5e, 0x79, 0x65, + 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, 0x45, 0x0f, + 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, 0xd5, 0xde, + 0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97, 0xeb, 0x3f, + 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, 0x3b, 0x5c, + 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, 0x46, 0xf5, + 0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc, 0x45, 0x38, + 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, 0x5e, 0x79, + 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, 0xc1, 0x6b, + 0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9, 0x0a, 0xfc, + 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, 0x68, 0x7b, + 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, 0xa7, 0x79, + 0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7, 0x00, 0x21, + 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, 0xbe, 0x09, + 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, 0x0e, 0x97, + 0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88, 0x7a, 0xd1, + 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, 0x25, 0x0b, + 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, 0xb6, 0xda, + 0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14, 0x46, 0xc7, + 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, 0xde, 0x00, + 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, 0x68, 0x71, + 0x17, 0x66, 0x12, 0x1a, 0x87, 0x27, 0xf7, 0xef, + 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, 0x6f, 0x35, + 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, 0x13, 0x86, + 0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64, 0xe8, 0x86, + 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, 0x1f, 0x51, + 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, 0x70, 0x98, + 0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f, 0x45, 0x6a, + 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, 0xf6, 0x06, + 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, 0x96, 0xa9, + 0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21, 0xe0, 0xac, + 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, 0xb2, 0x51, + 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, 0x81, 0x81, + 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, 0x45, 0x2d, + 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, 0x78, 0xcc, + 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, + 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, 0xb6, 0x7d, + 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, 0x84, 0x68, + 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, + 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, 0xe6, 0x4d, + 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, 0x3a, 0x83, + 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, + 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, 0x9e, 0xf7, + 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, 0x18, 0x6c, + 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, + 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, 0x06, 0x74, + 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, 0x3c, 0x2d, + 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, + 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, 0xf8, 0xcd, + 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, 0x15, 0x43, + 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, + 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, 0x23, 0x87, + 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, 0xcb, 0x43, + 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, + 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, 0x6d, 0x61, + 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, 0xa7, 0x5b, + 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, + 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, 0xd8, 0x3e, + 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, 0xc1, 0xee, + 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, + 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, 0x46, 0xcd, + 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, 0x0d, 0x6c, + 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, + 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, 0xab, 0x54, + 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, 0x53, 0xa8, + 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, + 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, 0xff, 0x73, + 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, 0x0a, 0x2d, + 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, + 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, 0x79, 0xa0, + 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, 0xeb, 0xdb, + 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, + 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, 0x33, 0xb2, + 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, 0x37, 0x43, + 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, + 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, 0x0f, 0x62, + 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, 0xf9, 0xca, + 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, + 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, 0x02, 0x71, + 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, 0xb1, 0x31, + 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, + 0x41, 0x29, 0x40, 0x19, 0x83, 0x35, 0x24, 0x69, + 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, 0xcc, 0x3b, + 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, + 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, 0xcb, 0x47, + 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, 0x6a, 0xab, + 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, + 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, 0xa8, 0xcd, + 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, 0x5f, 0xdf, + 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, + 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, 0x4b, 0x81, + 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, 0x77, 0xe6, + 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, + 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, 0x3b, 0x70, + 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, 0xd9, 0xbe, + 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, + 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, 0x6f, 0x44, + 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, 0xac, 0x21, + 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, + 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, 0xe7, 0xa6, + 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, 0x28, 0x66, + 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, + 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, 0x81, 0x7b, + 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, 0x70, 0xcf, + 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, + 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, 0xf0, 0xad, + 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, 0x50, 0xcb, + 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, + 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, 0x40, 0xda, + 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, 0xa2, 0x0d, + 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, + 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, 0x47, 0xb2, + 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, 0x72, 0x2c, + 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, + 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 }; + +static const unsigned char kTestRsaPublicKey2_2048[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, + 0x54, 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, + 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, + 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, + 0x57, 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, + 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, + 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, + 0x4e, 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, + 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, + 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, + 0xf9, 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, + 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, + 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, + 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, + 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, + 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, + 0x67, 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, + 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, + 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, + 0x8b, 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, + 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, + 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, + 0xf1, 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, + 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, + 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, + 0x40, 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, + 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, + 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, + 0x86, 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, + 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, + 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, + 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, + 0x05, 0x02, 0x03, 0x01, 0x00, 0x01 }; + +static const unsigned char kTestRsaPrivateKey3_2048[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, + 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40, + 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, + 0x3d, 0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, + 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44, + 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, + 0x32, 0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, + 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08, + 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, + 0xa3, 0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, + 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d, + 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, + 0xbc, 0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, + 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d, + 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, + 0x99, 0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, + 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55, + 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, + 0xb6, 0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, + 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, + 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, + 0x44, 0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, + 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06, + 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, + 0xea, 0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, + 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae, + 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, + 0x94, 0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, + 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84, + 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, + 0x16, 0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, + 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b, + 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x43, 0x8f, 0x19, + 0x83, 0xb1, 0x27, 0x4e, 0xee, 0x98, 0xba, 0xcb, + 0x54, 0xa0, 0x77, 0x11, 0x6d, 0xd4, 0x25, 0x31, + 0x8c, 0xb0, 0x01, 0xcf, 0xe6, 0x80, 0x83, 0x14, + 0x40, 0x67, 0x39, 0x33, 0x67, 0x03, 0x1e, 0xa0, + 0x8b, 0xd1, 0x1d, 0xfd, 0x80, 0xa4, 0xb9, 0xe7, + 0x57, 0x5e, 0xc8, 0x8e, 0x79, 0x71, 0xd5, 0x6b, + 0x09, 0xe9, 0x2b, 0x41, 0xa0, 0x33, 0x64, 0xc9, + 0x66, 0x33, 0xa1, 0xb1, 0x55, 0x07, 0x55, 0x98, + 0x53, 0x10, 0xe6, 0xc0, 0x39, 0x6d, 0x61, 0xd9, + 0xe8, 0x16, 0x52, 0x28, 0xe4, 0x2b, 0xda, 0x27, + 0x01, 0xaf, 0x21, 0x4a, 0xe8, 0x55, 0x1d, 0x0b, + 0xd1, 0x1c, 0xdc, 0xfd, 0xb3, 0x0b, 0xa6, 0x5c, + 0xcc, 0x6e, 0x77, 0xb8, 0xe0, 0xd1, 0x4e, 0x0a, + 0xd7, 0x7a, 0x5e, 0x18, 0xc3, 0xfb, 0xe9, 0xa1, + 0x9c, 0xc3, 0x9c, 0xd4, 0x4a, 0x7e, 0x70, 0x72, + 0x11, 0x18, 0x24, 0x56, 0x24, 0xdf, 0xf8, 0xba, + 0xac, 0x5b, 0x54, 0xd3, 0xc4, 0x65, 0x69, 0xc8, + 0x79, 0x94, 0x16, 0x88, 0x9a, 0x68, 0x1c, 0xbc, + 0xd4, 0xca, 0xec, 0x5e, 0x07, 0x4a, 0xc9, 0x54, + 0x7a, 0x4b, 0xdb, 0x19, 0x88, 0xf6, 0xbe, 0x50, + 0x9d, 0x9e, 0x9d, 0x88, 0x5b, 0x4a, 0x23, 0x86, + 0x2b, 0xa9, 0xa6, 0x6c, 0x70, 0x7d, 0xe1, 0x11, + 0xba, 0xbf, 0x03, 0x2e, 0xf1, 0x46, 0x7e, 0x1b, + 0xed, 0x06, 0x11, 0x57, 0xad, 0x4a, 0xcb, 0xe5, + 0xb1, 0x11, 0x05, 0x0a, 0x30, 0xb1, 0x73, 0x79, + 0xcd, 0x7a, 0x04, 0xcc, 0x70, 0xe9, 0x95, 0xe4, + 0x27, 0xc2, 0xd5, 0x2d, 0x92, 0x44, 0xdf, 0xb4, + 0x94, 0xa8, 0x73, 0xa1, 0x4a, 0xc3, 0xcc, 0xc4, + 0x0e, 0x8d, 0xa1, 0x6a, 0xc2, 0xd8, 0x03, 0x7f, + 0xfa, 0xa7, 0x76, 0x0d, 0xad, 0x87, 0x88, 0xa0, + 0x77, 0xaf, 0x3b, 0x23, 0xd1, 0x66, 0x0b, 0x31, + 0x2b, 0xaf, 0xef, 0xd5, 0x41, 0x02, 0x81, 0x81, + 0x00, 0xdb, 0xc1, 0xe7, 0xdd, 0xba, 0x3c, 0x1f, + 0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb, 0xd2, 0x47, + 0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5, 0xda, 0xc2, + 0x25, 0x64, 0x0a, 0x02, 0xbc, 0x7d, 0x7f, 0x50, + 0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d, 0x77, 0x6d, + 0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd, 0x18, 0xe9, + 0x53, 0x0b, 0x5c, 0xa2, 0x71, 0x14, 0x02, 0xfd, + 0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c, 0x2a, 0x4e, + 0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26, 0xd0, 0xd8, + 0x3d, 0xcf, 0x8c, 0x89, 0x65, 0x6c, 0x35, 0x19, + 0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49, 0xcd, 0x6d, + 0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9, 0xba, 0x43, + 0xe5, 0xdd, 0x39, 0x3a, 0x78, 0x8f, 0x07, 0xb8, + 0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8, 0x07, 0x02, + 0x9b, 0x79, 0x26, 0x32, 0x22, 0x38, 0x91, 0x01, + 0x90, 0x81, 0x29, 0x94, 0xad, 0x77, 0xeb, 0x86, + 0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x29, 0x88, + 0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77, 0x5d, 0x32, + 0x12, 0x5e, 0xdf, 0x28, 0x0c, 0x96, 0x0d, 0xa8, + 0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0, 0x41, 0xcb, + 0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2, 0xfb, 0xd2, + 0xf3, 0xf2, 0x95, 0xff, 0xd8, 0x33, 0x3f, 0x8c, + 0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe, 0x32, 0x66, + 0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe, 0x05, 0xb1, + 0xbc, 0xbe, 0x44, 0x09, 0xb4, 0xfe, 0x95, 0x06, + 0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d, 0x22, 0xa0, + 0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09, 0x2c, 0xb8, + 0xcc, 0xeb, 0x10, 0x4d, 0xa7, 0xd0, 0x4b, 0x46, + 0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47, 0x55, 0xd3, + 0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e, 0xf9, 0x5b, + 0x7b, 0xae, 0x6d, 0x08, 0x9f, 0x0c, 0x2a, 0xe1, + 0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55, 0xa3, 0x6d, + 0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02, 0x81, 0x80, + 0x61, 0x73, 0x3d, 0x64, 0xff, 0xdf, 0x05, 0x8d, + 0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d, 0x7d, 0x53, + 0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4, 0x72, 0x8f, + 0xae, 0x2b, 0x1a, 0x47, 0x87, 0xc7, 0x5b, 0x78, + 0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3, 0x8c, 0x0c, + 0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a, 0x88, 0x7d, + 0x40, 0xfe, 0x95, 0x32, 0x5b, 0xd3, 0x6f, 0x90, + 0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d, 0xc5, 0xc7, + 0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b, 0xcb, 0x78, + 0xad, 0x7e, 0xfe, 0xb6, 0xb1, 0x23, 0x63, 0x01, + 0x94, 0x9a, 0x99, 0x05, 0x63, 0xda, 0xea, 0xf1, + 0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b, 0x35, 0xec, + 0xcb, 0x0b, 0x43, 0xb8, 0x8e, 0x84, 0xb8, 0x09, + 0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c, 0x7c, 0x95, + 0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08, 0x09, 0x8f, + 0x91, 0x17, 0x65, 0x4c, 0xff, 0x6e, 0xbc, 0x51, + 0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d, 0x9d, 0xd8, + 0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f, 0x85, 0xd7, + 0xce, 0x72, 0x02, 0x38, 0xf2, 0x0f, 0x9c, 0x27, + 0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d, 0x02, 0x19, + 0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb, 0x61, 0x8e, + 0xe6, 0xb8, 0xfc, 0x07, 0x3c, 0xd1, 0x1b, 0x16, + 0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26, 0xb2, 0xbd, + 0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11, 0xc5, 0xdd, + 0xf9, 0xdb, 0x4e, 0x48, 0x52, 0xd8, 0xe6, 0x4b, + 0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1, 0x7c, 0xd3, + 0x51, 0x6b, 0x45, 0x46, 0x54, 0x11, 0x1a, 0x71, + 0xd3, 0xcd, 0x6b, 0x8f, 0x79, 0x22, 0x83, 0x02, + 0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed, 0x32, 0xd8, + 0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec, 0x2c, 0x7e, + 0xa4, 0x89, 0xdc, 0xbf, 0xf9, 0x0d, 0x32, 0xc8, + 0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc, 0x70, 0x4e, + 0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81, 0x81, 0x00, + 0xd3, 0x35, 0x1b, 0x19, 0x75, 0x3f, 0x61, 0xf2, + 0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf, 0x0c, 0x0a, + 0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b, 0x13, 0x4d, + 0x1f, 0x86, 0x58, 0x4f, 0xd8, 0xee, 0xfa, 0x76, + 0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2, 0xd2, 0x62, + 0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08, 0xa9, 0x83, + 0x07, 0xac, 0x8c, 0xa3, 0x7e, 0x18, 0xc0, 0x1c, + 0x57, 0x63, 0x8d, 0x05, 0x17, 0x47, 0x1b, 0xd3, + 0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8, 0x1a, 0x43, + 0x04, 0x36, 0xc8, 0x19, 0xbe, 0xdc, 0xa6, 0xe5, + 0x0f, 0x25, 0x62, 0x24, 0x96, 0x92, 0xb6, 0xb3, + 0x97, 0xad, 0x57, 0x9a, 0x90, 0x37, 0x4e, 0x31, + 0x44, 0x74, 0xfa, 0x7c, 0xb4, 0xea, 0xfc, 0x15, + 0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e, 0xed, 0x5b, + 0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b, + 0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8, 0xee, 0x24 }; + +static const unsigned char kTestRsaPublicKey3_2048[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, + 0x43, 0x51, 0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, + 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83, + 0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, + 0xa7, 0x5c, 0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, + 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a, + 0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, + 0x35, 0xb7, 0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, + 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74, + 0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, + 0xc2, 0xd7, 0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, + 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47, + 0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, + 0x56, 0x93, 0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, + 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03, + 0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, + 0x53, 0xfc, 0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, + 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c, + 0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, + 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, + 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0, + 0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, + 0x20, 0xec, 0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, + 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a, + 0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, + 0x59, 0x6e, 0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, + 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87, + 0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, + 0xbc, 0xe8, 0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, + 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f, + 0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, + 0xa7, 0xdd, 0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, + 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01 }; + +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)) { +} + +} // namespace widevine diff --git a/common/rsa_test_keys.h b/common/rsa_test_keys.h new file mode 100644 index 0000000..1d75a86 --- /dev/null +++ b/common/rsa_test_keys.h @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// RSA keys generated using fake_prng for purposes of testing. + +#ifndef COMMON_RSA_TEST_KEYS_H_ +#define COMMON_RSA_TEST_KEYS_H_ + +#include + +namespace widevine { + +// Container for test RSA keys +class RsaTestKeys { + public: + RsaTestKeys(); + + // Returns 3072-bit private RSA test key 1 + const std::string& private_test_key_1_3072_bits() const { + return private_key_1_3072_bits_; + } + // Returns 3072-bit public RSA test key 1 + const std::string& public_test_key_1_3072_bits() const { + return public_key_1_3072_bits_; + } + // Returns 2048-bit private RSA test key 2 + const std::string& private_test_key_2_2048_bits() const { + return private_key_2_2048_bits_; + } + // Returns 2048-bit public RSA test key 2 + const std::string& public_test_key_2_2048_bits() const { + return public_key_2_2048_bits_; + } + // Returns 2048-bit private RSA test key 3 + const std::string& private_test_key_3_2048_bits() const { + return private_key_3_2048_bits_; + } + // Returns 2048-bit public RSA test key 3 + const std::string& public_test_key_3_2048_bits() const { + return public_key_3_2048_bits_; + } + + private: + RsaTestKeys(const RsaTestKeys&) = delete; + RsaTestKeys& operator=(const RsaTestKeys&) = delete; + + std::string private_key_1_3072_bits_; + std::string public_key_1_3072_bits_; + std::string private_key_2_2048_bits_; + std::string public_key_2_2048_bits_; + std::string private_key_3_2048_bits_; + std::string public_key_3_2048_bits_; +}; + +} // namespace widevine + +#endif // COMMON_RSA_TEST_KEYS_H_ diff --git a/common/rsa_util.cc b/common/rsa_util.cc new file mode 100644 index 0000000..d9c2e30 --- /dev/null +++ b/common/rsa_util.cc @@ -0,0 +1,386 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// RSA utility functions for serializing and deserializing RSA keys, +// encryption, and signing. + +#include "common/rsa_util.h" + +#include + +#include "glog/logging.h" +#include "openssl/bio.h" +#include "openssl/evp.h" +#include "openssl/pem.h" +#include "openssl/x509.h" + +namespace widevine { +namespace rsa_util { + +static bool SerializeRsaKey(const RSA* key, std::string* serialized_key, + bool serialize_private_key) { + if (key == NULL) { + LOG(ERROR) << (serialize_private_key ? "Private" : "Public") + << " RSA key is NULL."; + return false; + } + if (serialized_key == NULL) { + LOG(ERROR) << "Pointer to hold serialized RSA" << (serialize_private_key ? + "Private" : "Public") << "Key is NULL."; + return false; + } + BIO* bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + LOG(ERROR) << "BIO_new returned NULL"; + return false; + } + bool success = false; + if ((serialize_private_key ? + i2d_RSAPrivateKey_bio(bio, const_cast(key)) : + i2d_RSAPublicKey_bio(bio, const_cast(key))) != 0) { + int serialized_size = BIO_pending(bio); + serialized_key->assign(serialized_size, 0); + if (BIO_read(bio, &(*serialized_key)[0], serialized_size) == + serialized_size) { + success = true; + } else { + LOG(ERROR) << "BIO_read failure"; + } + } else { + LOG(ERROR) << (serialize_private_key ? "Private" : "Public") << + " key serialization failure"; + } + BIO_free(bio); + return success; +} + +static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key, + bool deserialize_private_key) { + if (serialized_key.empty()) { + LOG(ERROR) << "Serialized RSA" << (deserialize_private_key ? + "Private" : "Public") << "Key is empty."; + return false; + } + if (key == NULL) { + LOG(ERROR) << "Pointer to hold new RSA " << (deserialize_private_key ? + "private" : "public") << " key is NULL."; + 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"; + return false; + } + *key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, NULL) : + d2i_RSAPublicKey_bio(bio, NULL); + BIO_free(bio); + if (*key == NULL) { + LOG(ERROR) << (deserialize_private_key ? "Private" : "Public") << + " RSA key deserialization failure"; + } + return *key != NULL; +} + +bool SerializeRsaPrivateKey(const RSA* private_key, + std::string* serialized_private_key) { + return SerializeRsaKey(private_key, serialized_private_key, true); +} + +bool DeserializeRsaPrivateKey(const std::string& serialized_private_key, + RSA** private_key) { + return DeserializeRsaKey(serialized_private_key, private_key, true); +} + +bool SerializeRsaPublicKey(const RSA* public_key, + std::string* serialized_public_key) { + return SerializeRsaKey(public_key, serialized_public_key, false); +} + +bool DeserializeRsaPublicKey(const std::string& serialized_public_key, + RSA** public_key) { + return DeserializeRsaKey(serialized_public_key, public_key, false); +} + +bool SerializePrivateKeyInfo(const RSA* private_key, + std::string* serialized_private_key) { + if (private_key == NULL) { + LOG(ERROR) << "Private RSA key is NULL."; + return false; + } + if (serialized_private_key == NULL) { + LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is NULL."; + return false; + } + // The following method of serializing a PKCS#8 PrivateKeyInfo object + // was obtained from analyzing the openssl utility code, as the official + // mechanism via i2d_PKCS8PrivateKey_bio is broken in the current openssl + // version (1.0.0c). Please refer to b/8560683. + EVP_PKEY* evp = EVP_PKEY_new(); + if (evp == NULL) { + LOG(ERROR) << "EVP_PKEY_new returned NULL."; + return false; + } + bool success = false; + PKCS8_PRIV_KEY_INFO *pkcs8_pki = NULL; + BIO* bio = NULL; + 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."; + goto cleanup; + } + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + LOG(ERROR) << "BIO_new returned NULL."; + goto cleanup; + } + if (i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8_pki) == 0) { + LOG(ERROR) << "i2d_PKCS8_PRIV_KEY_INFO_bio failed."; + goto cleanup; + } + { + int serialized_size = BIO_pending(bio); + serialized_private_key->assign(serialized_size, 0); + if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) != + serialized_size) { + LOG(ERROR) << "BIO_read failed."; + goto cleanup; + } + } + success = true; + +cleanup: + if (bio != NULL) { + BIO_free(bio); + } + if (pkcs8_pki != NULL) { + PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); + } + EVP_PKEY_free(evp); + return success; +} + +bool DeserializePrivateKeyInfo(const std::string& serialized_private_key, + RSA** private_key) { + if (serialized_private_key.empty()) { + LOG(ERROR) << "Serialized PrivateKeyInfo is empty."; + return false; + } + if (private_key == NULL) { + LOG(ERROR) << "Pointer to hold new RSA private key is NULL."; + return false; + } + // The following method of deserializing a PKCS#8 PrivateKeyInfo object + // was obtained from analyzing the openssl utility code, as the official + // mechanism via d2i_PKCS8PrivateKey_bio is broken in the current openssl + // version (1.0.0c). Please refer to b/8560683. + BIO* bio = BIO_new_mem_buf(const_cast(serialized_private_key.data()), + serialized_private_key.size()); + if (bio == NULL) { + LOG(ERROR) << "BIO_new_mem_buf returned NULL"; + 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."; + goto cleanup; + } + evp = EVP_PKCS82PKEY(pkcs8_pki); + if (evp == NULL) { + LOG(ERROR) << "EVP_PKCS82PKEY returned NULL."; + goto cleanup; + } + *private_key = EVP_PKEY_get1_RSA(evp); + if (*private_key == NULL) { + LOG(ERROR) << "PrivateKeyInfo did not contain an RSA key."; + goto cleanup; + } + success = true; + +cleanup: + if (evp != NULL) { + EVP_PKEY_free(evp); + } + if (pkcs8_pki != NULL) { + PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); + } + BIO_free(bio); + return success; +} + +bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key, + std::string* private_key_info) { + RSA* key = NULL; + if (DeserializeRsaPrivateKey(rsa_private_key, &key)) { + bool success = SerializePrivateKeyInfo(key, private_key_info); + RSA_free(key); + return success; + } + return false; +} + +bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + std::string* rsa_private_key) { + RSA* key = NULL; + if (DeserializePrivateKeyInfo(private_key_info, &key)) { + bool success = SerializeRsaPrivateKey(key, rsa_private_key); + RSA_free(key); + return success; + } + return false; +} + +bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key, + const std::string& passphrase, + std::string* serialized_private_key) { + if (private_key == NULL) { + LOG(ERROR) << "Private RSA key is NULL."; + 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."; + return false; + } + EVP_PKEY* evp = EVP_PKEY_new(); + if (evp == NULL) { + LOG(ERROR) << "EVP_PKEY_new returned NULL."; + return false; + } + bool success = false; + BIO* bio = NULL; + 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."; + goto cleanup; + } + if (i2d_PKCS8PrivateKey_bio(bio, evp, EVP_aes_256_cbc(), + const_cast(passphrase.data()), + passphrase.size(), NULL, NULL) == 0) { + LOG(ERROR) << "i2d_PKCS8PrivateKey_bio failed."; + goto cleanup; + } + { + int serialized_size = BIO_pending(bio); + serialized_private_key->assign(serialized_size, 0); + if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) != + serialized_size) { + LOG(ERROR) << "BIO_read failed."; + goto cleanup; + } + } + success = true; + +cleanup: + if (bio != NULL) { + BIO_free(bio); + } + EVP_PKEY_free(evp); + return success; +} + +namespace { +// Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below. +int get_password(char *buf, int size, int rwflag, void *u) { + CHECK(buf); + CHECK(u); + const std::string* pass(static_cast(u)); + if (!pass->empty() && size >= static_cast(pass->size())) { + memcpy(buf, pass->data(), pass->size()); + return pass->size(); + } + return 0; +} +} // namespace + +bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key, + const std::string& passphrase, + RSA** private_key) { + if (serialized_private_key.empty()) { + LOG(ERROR) << "Serialized RSAEncryptedPrivateKeyInfo is empty."; + return false; + } + if (passphrase.empty()) { + LOG(ERROR) << "Passphrase for RSA key decryption is empty."; + return false; + } + if (private_key == NULL) { + LOG(ERROR) << "Pointer to hold new RSA private key is NULL."; + 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"; + return false; + } + bool success = false; + EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, NULL, get_password, + const_cast(&passphrase)); + if (evp == NULL) { + LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned NULL."; + goto cleanup; + } + *private_key = EVP_PKEY_get1_RSA(evp); + if (*private_key == NULL) { + LOG(ERROR) << "EncryptedPrivateKeyInfo did not contain an RSA key."; + goto cleanup; + } + success = true; + +cleanup: + if (evp != NULL) { + EVP_PKEY_free(evp); + } + BIO_free(bio); + return success; +} + +bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key, + const std::string& passphrase, + std::string* private_key_info) { + RSA* key = NULL; + if (DeserializeRsaPrivateKey(rsa_private_key, &key)) { + bool success = SerializeEncryptedPrivateKeyInfo(key, + passphrase, + private_key_info); + RSA_free(key); + return success; + } + return false; +} + +bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + const std::string& passphrase, + std::string* rsa_private_key) { + RSA* key = NULL; + if (DeserializeEncryptedPrivateKeyInfo(private_key_info, passphrase, &key)) { + bool success = SerializeRsaPrivateKey(key, rsa_private_key); + RSA_free(key); + return success; + } + return false; +} + +} // namespace rsa_util +} // namespace widevine diff --git a/common/rsa_util.h b/common/rsa_util.h new file mode 100644 index 0000000..77c5921 --- /dev/null +++ b/common/rsa_util.h @@ -0,0 +1,153 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// RSA utility functions for serializing and deserializing RSA keys, +// encryption, and signing. + +#ifndef COMMON_RSA_UTIL_H_ +#define COMMON_RSA_UTIL_H_ + +#include + +#include "openssl/rsa.h" + +namespace widevine { +namespace rsa_util { + +// Serialize RSA private key into DER encoded PKCS#1 RSAPrivateKey. +// - private_key is the RSA key to be serialized, which must not be NULL. +// - serialized_private_key is a pointer to the std::string to hold the serialized +// PKCS#1 RSAPrivateKey object. Caller retains ownership of the string. This +// parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializeRsaPrivateKey(const RSA* private_key, + std::string* serialized_private_key); + +// Deserialize RSA private key from DER encoded PKCS#1 RSAPrivateKey. +// - serialized_private_key is the DER-encoded PKCS#1 RSAPrivateKey to be +// deserialized. +// - private_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializeRsaPrivateKey(const std::string& serialized_private_key, + RSA** private_key); + +// Serialize RSA key into DER encoded PKCS#1 RSAPublicKey. +// - public_key is the RSA key to be serialized, which must not be NULL. +// - serialized_public_key is a pointer to the std::string to hold the serialized +// PKCS#1 RSAPublicKey object. Caller retains ownership of the string. This +// parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializeRsaPublicKey(const RSA* public_key, + std::string* serialized_public_key); + +// Deserialize RSA public key from DER encoded PKCS#1 RSAPublicKey. +// - serialized_public_key is the DER-encoded PKCS#1 RSAPublicKey to be +// deserialized. +// - public_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializeRsaPublicKey(const std::string& serialized_public_key, + RSA** public_key); + +// Serialize RSA private key into DER encoded PKCS#8 PrivateKeyInfo. +// - private_key is the RSA key to be serialized, which must not be NULL. +// - serialized_private_key is a pointer to the std::string to hold the serialized +// PKCS#8 PrivateKeyInfo object. Caller retains ownership of the string. This +// parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializePrivateKeyInfo(const RSA* private_key, + std::string* serialized_private_key); + +// Deserialize RSA private key from DER encoded PKCS#8 PrivateKeyInfo. +// - serialized_private_key is the DER-encoded PKCS#8 PrivateKeyInfo to be +// deserialized. +// - private_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializePrivateKeyInfo(const std::string& serialized_private_key, + RSA** private_key); + +// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8 +// PrivateKeyInfo. +// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted. +// - private_key_info is a pointer to std::string to hold the PKCS#8 PrivateKeyInfo. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key, + std::string* private_key_info); + +// Convert DER-encoded PKCS#8 PrivateKeyInfo to DER-encoded PKCS#1 +// RSAPrivateKey. +// - private_key_info is the PKCS#8 PrivateKeyInfo to be converted. +// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + std::string* rsa_private_key); + + +// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo. +// - private_key is the RSA key to be serialized, which must not be NULL. +// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption. +// - serialized_private_key is a pointer to the std::string to hold the serialized +// PKCS#8 EncryptedPrivateKeyInfo object. Caller retains ownership of the +// string. This parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key, + const std::string& passphrase, + std::string* serialized_private_key); + +// Deserialize RSA private key from DER encoded PKCS#8 EncryptedPrivateKeyInfo. +// - serialized_private_key is the DER-encoded PKCS#8 EncryptedPrivateKeyInfo to +// be deserialized. +// - passphrase is the password to use for key decryption. +// - private_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key, + const std::string& passphrase, + RSA** private_key); + +// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8 +// EncryptedPrivateKeyInfo. +// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted. +// - passphrase is the password to use for PKCS#5 v2.1 AES-256-CBC encryption. +// - private_key_info is a pointer to std::string to hold the PKCS#8 +// EncryptedPrivateKeyInfo. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key, + const std::string& passphrase, + std::string* private_key_info); + +// Convert DER-encoded PKCS#8 EncryptedPrivateKeyInfo to DER-encoded PKCS#1 +// RSAPrivateKey. +// - private_key_info is the PKCS#8 EncryptedPrivateKeyInfo to be converted. +// - passphrase is the password to use for key decryption. +// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + const std::string& passphrase, + std::string* rsa_private_key); + +} // namespace rsa_util +} // namespace widevine + +#endif // COMMON_RSA_UTIL_H_ diff --git a/common/rsa_util_test.cc b/common/rsa_util_test.cc new file mode 100644 index 0000000..1856b2e --- /dev/null +++ b/common/rsa_util_test.cc @@ -0,0 +1,225 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// Unit test for rsa_util RSA utilties library. + +#include "gtest/gtest.h" +#include "testing/base/public/test_utils.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" + +namespace widevine { +namespace rsa_util { + +class RsaUtilTest : public ::testing::Test { + protected: + RsaTestKeys test_keys_; +}; + +TEST_F(RsaUtilTest, SerializeDeserializePrivateKey) { + RSA* private_key; + std::string serialized_private_key; + // Key 1 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_1_3072_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key)); + EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_1_3072_bits()); + EXPECT_EQ(RSA_check_key(private_key), 1); + RSA_free(private_key); + // Key 2 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key)); + EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_2_2048_bits()); + EXPECT_EQ(RSA_check_key(private_key), 1); + RSA_free(private_key); + // Key 3 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_3_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key)); + EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_3_2048_bits()); + EXPECT_EQ(RSA_check_key(private_key), 1); + RSA_free(private_key); + // Invalid key + EXPECT_FALSE(DeserializeRsaPrivateKey("this is a bad key", &private_key)); +} + +TEST_F(RsaUtilTest, SerializeDeserializePublicKey) { + RSA* public_key; + std::string serialized_public_key; + // Key 1 + EXPECT_TRUE(DeserializeRsaPublicKey( + test_keys_.public_test_key_1_3072_bits(), &public_key)); + ASSERT_TRUE(public_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits()); + RSA_free(public_key); + // Key 2 + EXPECT_TRUE(DeserializeRsaPublicKey( + test_keys_.public_test_key_2_2048_bits(), &public_key)); + ASSERT_TRUE(public_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits()); + RSA_free(public_key); + // Key 3 + EXPECT_TRUE(DeserializeRsaPublicKey( + test_keys_.public_test_key_3_2048_bits(), &public_key)); + ASSERT_TRUE(public_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits()); + RSA_free(public_key); + // Invalid key + EXPECT_FALSE(DeserializeRsaPublicKey("this is a bad key", &public_key)); +} + +TEST_F(RsaUtilTest, PublicKeyExtraction) { + RSA* private_key; + std::string serialized_public_key; + // Key 1 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_1_3072_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits()); + RSA_free(private_key); + // Key 2 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits()); + RSA_free(private_key); + // Key 3 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_3_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits()); + RSA_free(private_key); +} + +TEST_F(RsaUtilTest, Pkcs8PrivateKeyInfo) { + // The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8 + // serialization/deserialization functionality , so we test those. + std::string serialized_pkcs8; + std::string serialized_pkcs1; + // Key 1 + EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo( + test_keys_.private_test_key_1_3072_bits(), &serialized_pkcs8)); + EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1); + // Key 2 + EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), &serialized_pkcs8)); + EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1); + // Key 3 + EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo( + test_keys_.private_test_key_3_2048_bits(), &serialized_pkcs8)); + EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1); +} + +TEST_F(RsaUtilTest, Pkcs8EncryptedPrivateKeyInfo) { + // The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8 + // serialization/deserialization functionality , so we test those. + std::string serialized_pkcs8; + std::string serialized_pkcs1; + std::string passphrase("passphrase"); + // Key 1 + EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_1_3072_bits(), passphrase, + &serialized_pkcs8)); + EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + passphrase, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1); + // Key 2 + EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), passphrase, + &serialized_pkcs8)); + EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + passphrase, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1); + // Key 3 + EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_3_2048_bits(), passphrase, + &serialized_pkcs8)); + EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + passphrase, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1); +} + +TEST_F(RsaUtilTest, FailOnInvalidParams) { + RSA* test_input_key = NULL; + RSA* test_output_key = NULL; + std::string test_string; + std::string pass("password"); + ASSERT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &test_input_key)); + ASSERT_TRUE(test_input_key != NULL); + EXPECT_FALSE(SerializeRsaPrivateKey(NULL, &test_string)); + EXPECT_FALSE(SerializeRsaPrivateKey(test_input_key, NULL)); + EXPECT_FALSE(SerializeRsaPublicKey(NULL, &test_string)); + EXPECT_FALSE(SerializeRsaPublicKey(test_input_key, NULL)); + EXPECT_FALSE(SerializePrivateKeyInfo(NULL, &test_string)); + EXPECT_FALSE(SerializePrivateKeyInfo(test_input_key, NULL)); + EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(NULL, pass, &test_string)); + EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass, NULL)); + EXPECT_FALSE(DeserializeRsaPrivateKey("", &test_output_key)); + EXPECT_FALSE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), NULL)); + EXPECT_FALSE(DeserializeRsaPublicKey("", &test_output_key)); + EXPECT_FALSE(DeserializeRsaPublicKey( + test_keys_.public_test_key_2_2048_bits(), NULL)); + EXPECT_FALSE(DeserializePrivateKeyInfo("", &test_output_key)); + EXPECT_FALSE(DeserializePrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), NULL)); + EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo("", pass, &test_output_key)); + EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), pass, NULL)); + RSA_free(test_input_key); +} + +TEST_F(RsaUtilTest, Pkcs8FailOnInvalidPassword) { + RSA* test_input_key = NULL; + RSA* test_output_key = NULL; + std::string serialized_pkcs8; + std::string pass("password"); + ASSERT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &test_input_key)); + EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(test_input_key, "", + &serialized_pkcs8)); + ASSERT_TRUE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass, + &serialized_pkcs8)); + EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass + "a", + &test_output_key)); + EXPECT_TRUE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass, + &test_output_key)); + RSA_free(test_input_key); + RSA_free(test_output_key); +} + +} // namespace rsa_util +} // namespace widevine diff --git a/common/sha_util.cc b/common/sha_util.cc new file mode 100644 index 0000000..08079cb --- /dev/null +++ b/common/sha_util.cc @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/sha_util.h" + +#include "openssl/sha.h" + +namespace widevine { + +std::string Sha1_Hash(const std::string& message) { + return std::string(reinterpret_cast( + SHA1(reinterpret_cast(message.data()), + message.size(), nullptr)), + SHA_DIGEST_LENGTH); +} + +std::string Sha256_Hash(const std::string& message) { + return std::string(reinterpret_cast( + SHA256(reinterpret_cast(message.data()), + message.size(), nullptr)), + SHA256_DIGEST_LENGTH); +} + +} // namespace widevine diff --git a/common/sha_util.h b/common/sha_util.h new file mode 100644 index 0000000..fe703ae --- /dev/null +++ b/common/sha_util.h @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 COMMON_SHA_UTIL_H_ +#define COMMON_SHA_UTIL_H_ + +#include + +namespace widevine { + +// Calculates SHA1 hash. +std::string Sha1_Hash(const std::string& message); + +// Calculates SHA256 hash. +std::string Sha256_Hash(const std::string& message); + +} // namespace widevine + +#endif // COMMON_SHA_UTIL_H_ diff --git a/common/sha_util_test.cc b/common/sha_util_test.cc new file mode 100644 index 0000000..b765c11 --- /dev/null +++ b/common/sha_util_test.cc @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "common/sha_util.h" + +#include "gtest/gtest.h" + +namespace widevine { + +TEST(ShaUtilTest, Sha1Empty) { + const uint8_t kExpected[] = { + 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, + 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha1_Hash("")); +} + +TEST(ShaUtilTest, Sha256Empty) { + const uint8_t kExpected[] = { + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, + 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, + 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha256_Hash("")); +} + +TEST(ShaUtilTest, Sha1) { + const uint8_t kExpected[] = { + 0x5f, 0xfd, 0x3b, 0x85, 0x2c, 0xcd, 0xd4, 0x48, 0x80, 0x9a, + 0xbb, 0x17, 0x2e, 0x19, 0xbb, 0xb9, 0xc0, 0x1a, 0x43, 0xa4, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), + Sha1_Hash("random std::string")); +} + +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, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), + Sha256_Hash("random std::string")); +} + +} // namespace widevine diff --git a/example/BUILD b/example/BUILD new file mode 100644 index 0000000..ce70955 --- /dev/null +++ b/example/BUILD @@ -0,0 +1,40 @@ +################################################################################ +# 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. +################################################################################ + +# Build file for the example code. + +package( + default_visibility = ["//visibility:public"], +) + +filegroup( + name = "example_data", + srcs = glob(["example_data/*"]), +) + +cc_binary( + name = "provisioning_example", + srcs = ["provisioning_example.cc"], + deps = [ + "//provisioning_sdk/public:libprovisioning_sdk", + ], +) + +cc_binary( + name = "provisioning_message_generator", + srcs = ["provisioning_message_generator.cc"], + deps = [ + "//base", + "//common:aes_cbc_util", + "//common:file_util", + "//common:random_util", + "//common:rsa_key", + "//protos/public:certificate_provisioning_proto", + "//protos/public:client_identification_proto", + ], +) diff --git a/example/example_data/certificate_list b/example/example_data/certificate_list new file mode 100644 index 0000000000000000000000000000000000000000..17beb583119f1498046f84b9fa0b2ee6f35f2180 GIT binary patch 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! literal 0 HcmV?d00001 diff --git a/example/example_data/intermediate.encrypted.private b/example/example_data/intermediate.encrypted.private new file mode 100644 index 0000000000000000000000000000000000000000..1bab10afb895c82465fa5d1732675529c4dea0d0 GIT binary patch literal 1315 zcmV+;1>E{Df(0KiNd^fjhDe6@4FLrWFg!3D1_>&LNQU&4g?7Jo=R?%bUyt8 z0tf&w9R>+thDZTr0|Wso1P}~Z-S#kCyhh>FJJ}`r)`0|q1kh?cuj~;FYQKTs9uaE& z$rRgo*f!*n%>Uej@qhG`!8@(EM5|G$R4N$Rn-VawiQp1$0#KmTxjx=nc@U9XU%200 zJpP{=;C%|nssW>#rskrXuMJh6A#EuIk9Jd!)|?zK=nlST}R=Ud>nyr$mutY zr8W*siK|JfnvKO9>cMl0eW%dWO3k|9Y)tX2?{LsCK+6>nTs|!0y37= zN9M{C%ep7+*vDj}xOxo$F7r@BZnL9&xWiBGfS=)Yb;27T{@+uK0*pM(7K$|;Xto?x zqve2BI=#f=?iX+y((S8RkxT$R$_r~dtpC1M-U+d?Hgh~GDyJI9_L(C^dKC#AK{BDW zy)z>zv8pl$sh>*lh47o0amlA5-OqQLR}6~76ph>PDD0aJ-75Q~Tm~~yz3Z&)&R72X zq~YFKQM2YR`di=NZQW%3H=oEJdz3Hw`t$-GkkkWY{9 z5%*k8KrLrtE{;dBNGc(p*-X^Y#i*GonYUy=!_mji*Go&^d9z7*j;xCQ!q`5Jx<@TR z8Q4OK>X^Sir7)%zrJTX3su6mP%46`-BZYKXZ zE5%a3S4_qulJi)FTV${v@#JNYt3(CZZz^qr0B2l~r47`Wya+AJr&DE{#Snd!?Bp-q zcm(K^XQY6eS9Se-weSGZG+)NN#e2A#MOYU;9&`UqIHXRTq@h{jV@=(b7uYUPdtFkZ zHz4ldxL*Y2b^fKu7vf4$K&U>cgJRwy-}pZ z?frH^N8dHEHDt15fT2$bM0EH`1fyby+RwieT38_Q`{j?n>)z2lr&k^vte*ok4z|Z| z0roi#L>W;9b0;;Q)|PpCmq%CA9sr34sKt0Sz-IntCpM+V5u*n;W0HyE#*p{UpZVc} z7vyrm`DOf%kQ35v6O?`wum2o-fRcom;-n9QqPDQ+N>1Iv`^H5)MV}V5vMczi3=^LB zO9SPX^)oj`CWk+o)~2>B3lr%X}*2iKm8s(Q7YiWzsxG^hg?%XB#1MX6<`!;FC^B}6#I zw^{ZUb8EJ|wvA%7nrvVNOsHx>E;%wV*%8=NdlVl` Ufe_1Uwr`P|3#oMn0s{d60Y9vLfdBvi literal 0 HcmV?d00001 diff --git a/example/example_data/message b/example/example_data/message new file mode 100644 index 0000000000000000000000000000000000000000..df77986be19b580a185e6fa09bd182a192b6b6fc GIT binary patch literal 2695 zcmV;23V8JjffEu0+te&ND)GWe4@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$ literal 0 HcmV?d00001 diff --git a/example/example_data/provisioner.cert b/example/example_data/provisioner.cert new file mode 100644 index 0000000000000000000000000000000000000000..e98d6ff01f7d44b39033654569583d6a14eba8ec GIT binary patch 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 literal 0 HcmV?d00001 diff --git a/example/example_data/provisioner.encrypted.private b/example/example_data/provisioner.encrypted.private new file mode 100644 index 0000000000000000000000000000000000000000..b174a3e91c926e057d37125fe6a526688c944b8b GIT binary patch literal 1315 zcmV+;1>E{Df(0KiNd^fjhDe6@4FLrWFg!3D1_>&LNQU&4g?6KZvmYM+thDZTr0|Wso1Q2peYR*TR<;WpnuWhkGBqjuc1kjxjexdLaXcl5{^jC`y zKXW)Sf4EuoXv`&-GM4ljgf=KoG|lkF3tBe?($*fUZj09X|GnMD2=JebQMof#EdaAA zF8=TD`6XKT(zu6o=2c3Wj}>{AgDX8YSZ5mTRd|79D7a@8ev8CcQ0|2QXAV$L4${sd zD(g?yeKEvL!BhSWLuAHn_Ob?KV9(bI)P0YLJMsO^(1X+iI z9s`B8xJQex)b?|vUh}X99UmPr_xT?|^>E1oPKm+NAiV&wEOa`^2HY<9ae&(Y;)=Jv z2j9`7t_W|K^UFF8&$4_A0UX*F_ax8~?$yk00u~{9(2#tN_4kXvmp;0pDL8<`j|?_l z-0O_c?k7q|#EZY&!xZ)^PGsxLs%Jw?m1kY2+|hPm*th2+J07Q-h*#J97v3(l8SJ-U zaM(7AZLnE>`5vn9CnCU?(|86%R`xd6g3GlwXx9a&vgN;qw|imqnOf_nzd2<|Y)7l$ zO*&CrJ0|#dOb-OuQE+zyqb#{UG0NSNWC*kejpzy%>GIc__0QhB@CS7xxUq}bMdjA* zn6j#{+{~qh^i&NL!B;`(+WcWhu-i2UCn@f-&0@a9mrkqhT4oF+K%hE!B6f>{^(i13 zI0AT&JsUFA1)aVnT!F6mOv=f#FUuXO-tC`LUrcKm1Qu54cioukwq3lYQjKMP<0{wL zZ!Nkc@n<1sN)lb1dKI6MP2jW$L_!&U=?J*D4PPiaCdoI{Yh0ea3ggA9eYR7cg{uU` zp|&%Zc>O7jqKxlEaIPGmk9Jir9upV`nu06APvy#@J>WN!u%D7E9CZ{Hx^Y)VQ}4nx ztpmCK?f`3Ri(^B^0%o4PP<@^dSXus3l6?z$J~lVN5O zq;0*Xgr1KV8J-W3#tNpWVXb0J$0k=lRWS7W zZZcReVgro%_}Z74*3)ub8f6@~fux}L?cq`4ZJGE)NSE(wH3>-vjdDvBxaOZH+ju{J Z>rJYCVYn2B;P=)`QoE>z8MPxLif?u)c=`YU literal 0 HcmV?d00001 diff --git a/example/example_data/provisioner.passphrase b/example/example_data/provisioner.passphrase new file mode 100644 index 0000000..79ea782 --- /dev/null +++ b/example/example_data/provisioner.passphrase @@ -0,0 +1 @@ +provider_passphrase \ No newline at end of file diff --git a/example/example_data/provisioner.spoid_secret b/example/example_data/provisioner.spoid_secret new file mode 100644 index 0000000..7f3fe95 --- /dev/null +++ b/example/example_data/provisioner.spoid_secret @@ -0,0 +1,4 @@ +Twas bryllyg, and ye slythy toves +Did gyre and gymble in ye wabe: +All mimsy were ye borogoves; +And ye mome raths outgrabe. diff --git a/example/example_data/root.private b/example/example_data/root.private new file mode 100644 index 0000000000000000000000000000000000000000..03b99d9b69d75cff1417b096a4eb3a0ce98ab8f8 GIT binary patch literal 1767 zcmVCcAS&D~D@%|*X$mIVN~a{rvPIjGpc33I?C zYB^0dKbqvYcHA`M?qA&#APC$}Z%&`UHu=uRx7*-REienN3Lw%cJS-iDW$Ry)*FO>n zP!3h|@fq$IA#Uon3Q7x{ayRP>X#7$}V$h@#6iUQ4GcO^A&pt5szUXVPxACgjE@L};XeF-#7XU(`Uv&bV-pI$G4>;x(AR zlR*)(M6zVU=Lto(Z?L^?=x6$k)cQ;BsGW)m*%WsP5uU?P6l_HT0|5X50)hd67cy^Q zKNSRwH9=J&l~V}t4RXfJyiQz#iG(z^s61E+;5h}Uh} zV=%!nxKxr}A+VwIXHAv$jzQGgq<1ae*iX|i?2IpuHt*CxE$%EPb7TJ6G)ACy=VSrM zHuTM0H^_8e=ee%$5MGcZ_A(#z-svp843#`(D}D$vN!pD~jg6x?#I#X}A#g+G5}#{} ze}w>GDzc@Y>+oBv;D{pY-;iA_LLM&)zPi`;G3w(Rl7Cdjki{54(eU@_LR3ST!asyz z(zF!?McH8NoyX9g6SM_6a-M7_B5^2dzP(xy{dGC0W{qGmxI!C^aAoapZ$)dNJMQ_g z(B8&V|KZ3!W+lg}y{j=}o#XkP)x7?jlH5oM+rZgOy@rl@?fo?Ja9LWgNu^!I&pvGK zRiUIe2#9yy2zK=?y+WtjwQ9QFF6P16R6+YmzlitDX64i=cE`{KOPK7(X&xfWU6qq2 zqCR{1Zr!N&PRcDR}0`i8W=I|_N?+$`RAH?uZ z2Qqz60)fE*%OPXbmfWuoUdzZd;_sxobH;O=m=A5T6OPu=#g>o?kl$?WnR`TYi^>!p zgyrugKoggdX6}ABv;0Nq{nwWe9zOT7Y5UF+(b5AsJ3gs*cs3tsO-ab7^-8jvARTB} z`bq^?;{FJJb z7TFnjaNmRv1{M7&L4XIP0cDTq?sCw(d!;$6ZO#;jFgWsEa z6Lm{E?l9L4UEXZAmU`UlwgP0*e}#415c~XAzyk$V zR}pic)*PI$d0~iw0d>H`8P*S8KOb|Q2nmqO^r`D7G)F%XeD)VjB;W; ze`ox#!`{B!^7^OJ_2u0eAkSjpuxqAI@n`#));(_mfxs)|Mjrf*DB*}%J|uTo(%xGC z>7h@S9o*Tk2C4%n+HC#`*$t{0VVoKJi$`GY5;K`u;%QH{e%@$oy=rgQGSm;n30|t% z_ZUa;Iy4rd9-1o#e%RTeE}8RFB|dFb_}=1l6>ugI{qT@CGN_1^D2x7y zoaT;x4RzYhta;U4LKx2Y>#Cr%q?;1An`!$P}VdPt7426*N z+)#*(Q!wJ{g~t?bukBV8u#=wg&jSvWAl>c$2`UTH>jp|hJ2SJz1m|!jIKwh1H*~j- J6sR5^vg7%dc9{SG literal 0 HcmV?d00001 diff --git a/example/example_data/service.cert b/example/example_data/service.cert new file mode 100644 index 0000000000000000000000000000000000000000..b17b7d4625e310bc9c700acf1493ad015db60579 GIT binary patch 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 literal 0 HcmV?d00001 diff --git a/example/example_data/service.encrypted.private b/example/example_data/service.encrypted.private new file mode 100644 index 0000000000000000000000000000000000000000..2615dbee6dcef2846611988b19b7068a611433fd GIT binary patch literal 1315 zcmV+;1>E{Df(0KiNd^fjhDe6@4FLrWFg!3D1_>&LNQU&4g?5wP`hI@I#;0r z0tf&w9R>+thDZTr0|Wso1Q5+EsZ;)GrnF%Kn)!vTg@~SX6tea-p-b37+D%N>(riGpyrfn^u^Z+F4*CL=^$rz9r*GP z`hG*i)YuSKiEOQ@M3E}pRe_imwr@sY@Gz)bmv-h+U&H3pbKlzVvcx zC-JM^ODyx8Er8es@%`^Y2V^7W?c_vd!Uo)kq;G*74gCsd&;jh{#;kOK7Prw32zb$p z95H8E3hFw%eRye{`E%||%wj=MkawVqmgsmJEef>q&Lk^{LRSC&$>%eB4efz0Ln1wf z${p|I%ZEs$i%(L+F|jyX|04{8Fc|vXk~$+58iqN0gEHMMGIAa``6?=ahLK})o+GuT zcpoT*tSyIcT}vRU_tTNSz)0*n$pmon|KnfEC1Adm{PjTMka7Bm!LTo2?!%Y&LySCE zf#eABNV3DZ{9l9l6*Y_9K7Ko<1pm8{GaMb2z#%IWpdlDA4mgJ>MkBaUxul{b)s18+ zF>0h`%90{u${Kq!Z?4PNtvy}V&VTN`;>}OYqv7bBIw?`#Q?x+;8k?8ae$FyF|PiZf9??if?+#!I;XKA^ln7cEt4P zwVpixxzN6&$tk)|ahqnQo8$46H5hWOkYpVIi8Xchu{o6~Mzc&I#SjwFVzq5q{6g*C z`;Af2C1U9MpUx9perkV2?e&Syz_rJO5%?85z{-NYETfb$)44^HV*!!mw)rc8S|rgt zhe&};c4<8`xqT7XE_3JFjdKg&`~L-Zd(Z&YvZYQu!b#%Yo~%4Le8l0fvFmhfMST1h zYKn;9@P;(zvy8Mc0um*`A~J}eLs^WMjuT3?1}btYL)X2kt~<4__t*L6-t9`f1fqgw zL-@~ePx_b*{;%*G88x+4iuzin>dCaAt`4!XTPcah!)vjoBG)nj#(pSq<`I2i6}u1C zIPuTAGPgxj@I+v@okF!k*h@ILy?D3{+jeWxf3l;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 literal 0 HcmV?d00001 diff --git a/example/example_data/user.private b/example/example_data/user.private new file mode 100644 index 0000000000000000000000000000000000000000..5eb66de3bfedc222ecbefb7d579e6d7715592bb3 GIT binary patch literal 1191 zcmV;Y1X%kpf&`-i0RRGm0RaH*TTUbj1&PY+lYVxq?;-gDL9lX_?%p1j=-#c;G}S*` ztDdEBi97EuFPf~v2yjQ%nv_5}{r4g3{RDal74zkzu6^Qv9w3Pxf?#P-P8hk2_m(w) zXx7%!@9l5G{6C%o*qa04R&6&kS9r2DIL{I(>C!#nVRlJcQ+~^cPwTvqx?C`7lG-!j zMAiP@ZR!`17aS;-M>GqmNh&M?aDvVP{Nf%*i@9hyc9o_a&dI3H^Du^%Y*H%pC{UWG6A_wHmcwDfe!4#NQ0vKj`g_aLEMarq(C)s2H zsuqX-@F*vbgtK@#-+XH){0Iyz=JZorgW!puFD>#GcnNUifTAy253r0>w9+Hl-Ro>e z8xPE^ zl80HiY^RIlQI*j!+8wFkm$35sgIJZX*jX`C&C_eKE{`_Ff`<0>sLnf#2zW+xw;Tn7%e*B#`WuFm zLGl|qFa1Is+JmnN)Yt77&l6>&lSE%L0)c@5;uX9w9?Z+!OR;|IULQf|i^0vNidK|a z;pxERjwaA=N1bkGr)lq&_x}%tuNPag2+WVx{^;WM^9mNoXSz|lLM0ESV&lo|l335d z^(2_1d5B9Sx~0K}5y*F~UQGqkuhXZc+EvWzW}Q(CUZV*_Iu2}e&UP{WP~3`cM}p)# z0)c=aDan_b)}M+vxx9b2{+--}%yOv`)1-UM+RVYWZDcoRU*E)~janxOG>VH?mC^o8 z!axhRfvCic+(I7uOKY!0ndKx{FQl&Nft9m~wMHNtH?ey4i$aFdJzsg2D%U-?=(2U+Q*C*mbhm9)5>auIchdhwHW&I_n~G)osL$MoO9M)a znmzp}C4)t(tYr*XlI;%kjMuv|KMMQAAZb_fu+hH=NnY#!!B|&3O%IgY-54ZK&#>Ou F3Y!I`TUY=9 literal 0 HcmV?d00001 diff --git a/example/example_data/user.public b/example/example_data/user.public new file mode 100644 index 0000000000000000000000000000000000000000..0be4b27a2a681d2bfa103dd4e79e96cea85cee25 GIT binary patch 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 literal 0 HcmV?d00001 diff --git a/example/provisioning_example.cc b/example/provisioning_example.cc new file mode 100644 index 0000000..2539807 --- /dev/null +++ b/example/provisioning_example.cc @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 +#include +#include +#include +#include + +#include "provisioning_sdk/public/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::OK; +using widevine::ProvisioningEngine; +using widevine::ProvisioningSession; +using widevine::kCertTesting; + +std::string GetContents(const std::string& file_name) { + if (file_name.empty()) { + std::cout << "File name is empty." << std::endl; + return ""; + } + FILE* file = fopen(file_name.c_str(), "r"); + if (!file) { + std::cout << "Unable to open file " << file_name << std::endl; + return ""; + } + std::string contents; + const size_t kReadSize = 0x1000; + char buffer[kReadSize]; + while (true) { + size_t size_read = fread(buffer, sizeof(char), kReadSize, file); + if (size_read == 0) break; + contents.append(buffer, size_read); + } + if (!feof(file)) std::cout << "Failed to read all file contents."; + fclose(file); + return contents;; +} + +int main(int argc, char** argv) { + ProvisioningEngine engine; + + // Call Initialize to setup the engine. + if (engine.Initialize( + kCertTesting, GetContents("example_data/service.cert"), + GetContents("example_data/service.encrypted.private"), + GetContents("example_data/service.passphrase"), + GetContents("example_data/provisioner.cert"), + GetContents("example_data/provisioner.encrypted.private"), + GetContents("example_data/provisioner.passphrase"), + GetContents("example_data/provisioner.spoid_secret")) != OK) { + std::cout << "Failed to initialize." << std::endl; + return 1; + } + + // Certificate status list should be updated periodically. In this example, + // we'll just set it once. Note that in practice, the expiration should not be + // 10 years long. + if (engine.SetCertificateStatusList( + GetContents("example_data/certificate_list"), + 10 * 365 * 24 * 3600 /* 10 years */) != OK) { + std::cout << "Failed to set certificate status list." << std::endl; + return 1; + } + + // Before being able to process provisioning request for a specific type of + // device, we need to generate the intermediate certificate and add it to the + // engine first. This only needs to be done once for every new type of device. + const int kSystemId = 2001; + std::string certificate; + if (engine.GenerateDrmIntermediateCertificate( + kSystemId, GetContents("example_data/intermediate.public"), + &certificate) != OK) { + std::cout << "Failed to generate intermediate certificate." << std::endl; + return 1; + } + if (engine.AddDrmIntermediateCertificate( + certificate, + GetContents("example_data/intermediate.encrypted.private"), + GetContents("example_data/intermediate.passphrase")) != OK) { + std::cout << "Failed to add intermediate certificate." << std::endl; + return 1; + } + + // In order to process provisioning request, we need to create a session. The + // public/private key pairs should be unique - they cannot be reused if the + // 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"), + &session) != OK) { + std::cout << "Failed to create session." << std::endl; + return 1; + } + std::string response; + bool done; + if (session->ProcessMessage(GetContents("example_data/message"), &response, + &done) != OK) { + std::cout << "Failed to process message." << std::endl; + return 1; + } + std::cout << "Message processed successfully."; + return 0; +} diff --git a/example/provisioning_message_generator.cc b/example/provisioning_message_generator.cc new file mode 100644 index 0000000..d186668 --- /dev/null +++ b/example/provisioning_message_generator.cc @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 +#include + +#include "gflags/gflags.h" +#include "glog/logging.h" +#include "common/aes_cbc_util.h" +#include "common/file_util.h" +#include "common/random_util.h" +#include "common/rsa_key.h" +#include "protos/public/certificate_provisioning.pb.h" +#include "protos/public/client_identification.pb.h" + +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 " + "not be empty."); +DEFINE_string( + output_path, "", + "Specifies where to write the output message. Should not be empty."); + +namespace widevine { + +bool GenerateProvisioningMessage(const std::string& service_public_key, + const std::string& certificate, + const std::string& private_key, std::string* message) { + ClientIdentification client_id; + client_id.set_token(certificate); + client_id.set_type(ClientIdentification::OEM_DEVICE_CERTIFICATE); + + ProvisioningRequest request; + if (service_public_key.empty()) { + *request.mutable_client_id() = client_id; + } else { + auto encrypted_id = request.mutable_encrypted_client_id(); + CHECK(RandomBytes(16, encrypted_id->mutable_encrypted_client_id_iv())); + std::string privacy_key; + CHECK(RandomBytes(16, &privacy_key)); + encrypted_id->set_encrypted_client_id(crypto_util::EncryptAesCbc( + privacy_key, encrypted_id->encrypted_client_id_iv(), + client_id.SerializeAsString())); + std::unique_ptr service_key( + RsaPublicKey::Create(service_public_key)); + if (!service_key) return false; + CHECK(service_key->Encrypt(privacy_key, + encrypted_id->mutable_encrypted_privacy_key())); + } + + CHECK(RandomBytes(4, request.mutable_nonce())); + + SignedProvisioningMessage signed_message; + signed_message.set_message(request.SerializeAsString()); + std::unique_ptr signer(RsaPrivateKey::Create(private_key)); + CHECK(signer->GenerateSignature(signed_message.message(), + signed_message.mutable_signature())); + *message = signed_message.SerializeAsString(); + return true; +} + +} // namespace widevine + +using widevine::GetContents; +using widevine::SetContents; +using widevine::GenerateProvisioningMessage; + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + std::string service_public_key; + if (!FLAGS_service_public_key_path.empty()) { + if (!GetContents(FLAGS_service_public_key_path, &service_public_key)) + return 1; + } + std::string certificate; + if (!GetContents(FLAGS_certificate_path, &certificate)) return 1; + std::string private_key; + if (!GetContents(FLAGS_certificate_private_key_path, &private_key)) return 1; + std::string message; + if (!GenerateProvisioningMessage(service_public_key, certificate, private_key, + &message)) + return 1; + if (!SetContents(FLAGS_output_path, message)) return 1; + return 0; +} diff --git a/glog.BUILD b/glog.BUILD new file mode 100644 index 0000000..dc6dd25 --- /dev/null +++ b/glog.BUILD @@ -0,0 +1,183 @@ +################################################################################ +# 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. +################################################################################ + +# Bazel build file for glog. + +licenses(["notice"]) + +# Common options required by all library targets. +glog_copts = [ + "-Isrc", + "-D_START_GOOGLE_NAMESPACE_='namespace google {'", + "-D_END_GOOGLE_NAMESPACE_=}", + "-DGOOGLE_NAMESPACE=google", + "-DGOOGLE_GLOG_DLL_DECL=", + "-DHAVE_LIB_GFLAGS", + "-DHAVE_PTHREAD", + "-DHAVE_RWLOCK", + "-DHAVE_PREAD", + "-DHAVE_PWRITE", + "-DHAVE_SYS_TIME_H", + "-DHAVE_SYS_UTSNAME_H", + "-DHAVE_UNISTD_H", + "-DHAVE_GLOB_H", + "-DHAVE___ATTRIBUTE__", + "-D__NR_gettid", + "-Wno-sign-compare", +] + +cc_library( + name = "glog", + srcs = [ + "src/base/commandlineflags.h", + "src/base/googleinit.h", + "src/base/mutex.h", + "src/demangle.cc", + "src/demangle.h", + "src/glog/log_severity.h", + "src/logging.cc", + "src/raw_logging.cc", + "src/signalhandler.cc", + "src/stacktrace.h", + "src/stacktrace_generic-inl.h", + "src/stacktrace_libunwind-inl.h", + "src/stacktrace_powerpc-inl.h", + "src/stacktrace_x86-inl.h", + "src/stacktrace_x86_64-inl.h", + "src/symbolize.cc", + "src/symbolize.h", + "src/utilities.cc", + "src/utilities.h", + "src/vlog_is_on.cc", + ":config_h", + ":logging_h", + ":raw_logging_h", + ":stl_logging_h", + ":vlog_is_on_h", + ], + hdrs = glob(["glog/*.h"]), + copts = glog_copts, + includes = ["src/"], + linkopts = ["-pthread"], + visibility = ["//visibility:public"], + deps = ["//external:gflags"], +) + +genrule( + name = "config_h", + srcs = ["src/config.h.cmake.in"], + outs = ["config.h"], + cmd = "awk '{ gsub(/^#cmakedefine/, \"//cmakedefine\"); print; }' $(<) > $(@)", +) + +sub_cmd = ("awk '{ " + + "gsub(/@ac_google_start_namespace@/, \"namespace google {\"); " + + "gsub(/@ac_google_end_namespace@/, \"} // namespace google\"); " + + "gsub(/@ac_google_namespace@/, \"google\"); " + + ("gsub(/@(ac_cv_have_stdint_h|ac_cv_have_uint16_t|" + + "ac_cv_have_libgflags|ac_cv_have_unistd_h|" + + "ac_cv_have___builtin_expect|" + + "ac_cv_cxx_using_operator)@/, \"1\"); ") + + "gsub(/@ac_cv___attribute___noreturn@/, \"__attribute__ ((noreturn))\"); " + + "gsub(/@ac_cv___attribute___noinline@/, \"__attribute__ ((noinline))\"); " + + "gsub(/@(ac_cv___attribute___[a-z0-9_]+)@/, \"\"); " + + "gsub(/@([a-z0-9_]+)@/, \"0\"); " + + "print; }' $(<) > $(@)") + +genrule( + name = "logging_h", + srcs = ["src/glog/logging.h.in"], + outs = ["glog/logging.h"], + cmd = sub_cmd, +) + +genrule( + name = "raw_logging_h", + srcs = ["src/glog/raw_logging.h.in"], + outs = ["glog/raw_logging.h"], + cmd = sub_cmd, +) + +genrule( + name = "stl_logging_h", + srcs = ["src/glog/stl_logging.h.in"], + outs = ["glog/stl_logging.h"], + cmd = sub_cmd, +) + +genrule( + name = "vlog_is_on_h", + srcs = ["src/glog/vlog_is_on.h.in"], + outs = ["glog/vlog_is_on.h"], + cmd = sub_cmd, +) + +cc_library( + name = "test_hdrs", + hdrs = [ + "src/config_for_unittests.h", + "src/googletest.h", + ], +) + +cc_test( + name = "demangle_unittest", + size = "small", + srcs = ["src/demangle_unittest.cc"], + copts = glog_copts, + data = ["src/demangle_unittest.txt"], + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "logging_unittest", + size = "small", + srcs = ["src/logging_unittest.cc"], + copts = glog_copts, + data = ["src/logging_unittest.err"], + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "stacktrace_unittest", + size = "small", + srcs = ["src/stacktrace_unittest.cc"], + copts = glog_copts, + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "symbolize_unittest", + size = "small", + srcs = ["src/symbolize_unittest.cc"], + copts = glog_copts, + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "utilities_unittest", + size = "small", + srcs = ["src/utilities_unittest.cc"], + copts = glog_copts, + deps = [ + ":glog", + ":test_hdrs", + ], +) diff --git a/gtest.BUILD b/gtest.BUILD new file mode 100644 index 0000000..3131608 --- /dev/null +++ b/gtest.BUILD @@ -0,0 +1,37 @@ +################################################################################ +# 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 new file mode 100644 index 0000000..5f29565 --- /dev/null +++ b/oem_certificate_generator/BUILD @@ -0,0 +1,24 @@ +################################################################################ +# 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. +################################################################################ + +# Build file for OEM certificate generation tool. + +package(default_visibility = ["//visibility:public"]) + +py_binary( + name = "oem_certificate", + srcs = ["oem_certificate.py"], +) + +py_test( + name = "oem_certificate_test", + srcs = ["oem_certificate_test.py"], + deps = [ + ":oem_certificate", + ], +) diff --git a/oem_certificate_generator/oem_certificate.py b/oem_certificate_generator/oem_certificate.py new file mode 100644 index 0000000..50fbc81 --- /dev/null +++ b/oem_certificate_generator/oem_certificate.py @@ -0,0 +1,492 @@ +################################################################################ +# 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. +################################################################################ + +"""OEM certificate generation tool. + +Supports: + - Generating CSR (certificate signing request) + - Generating OEM intermediate certificate + - Generating OEM leaf certificate chain + - Erasing file securely + - Getting CSR/certificate/certificate chain information + +Prerequirements (if running the script directly): + - Install pip: https://pip.pypa.io/en/stable/installing/ + - Install python cryptography: https://cryptography.io/en/latest/installation/ + +Run 'python oem_certificate.py --help' to see available commands. + +The arguments can be partially or fully loaded from a configuration file, for +example, if file "location.cfg" is, + + -C=US + -ST=CA + -L=Kirkland + -O=Some Company + -OU=Some Unit + +A command of + "python oem_certificate.py generate_csr @location.cfg " + "--output_csr_file=csr.pem --output_private_key_file=key.der", +is equivalent to + "python oem_certificate.py generate_csr -C=US -ST=CA -L=Kirkland " + "-O='Some Company' -OU='Some Unit' --output_csr_file=csr.pem " + "--output_private_key_file=key.der". + +Note that (1) the arguments in the config file must be one per line; (2) the +arguments should not be quoted in the config file. + +The script uses a default configuration file 'oem_certificate.cfg', which will +be loaded automatically if exists. +""" + +import argparse +import datetime +import os +import sys + +from cryptography import utils +from cryptography import x509 +from cryptography.hazmat import backends +from cryptography.hazmat.backends import openssl +from cryptography.hazmat.backends.openssl import backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509 import oid +from pyasn1.codec.der import decoder +from pyasn1.codec.der import encoder +from pyasn1.type import univ +import six + + +class WidevineSystemId(x509.UnrecognizedExtension): + """Implement WidevineSystemId x509 extension.""" + + # oid of Widevine system id. + oid = oid.ObjectIdentifier('1.3.6.1.4.1.11129.4.1.1') + + def __init__(self, value): + """Inits from raw bytes.""" + super(WidevineSystemId, self).__init__(WidevineSystemId.oid, value) + + @classmethod + def from_int_value(cls, int_value): + """Construct from integer system id value.""" + return cls(encoder.encode(univ.Integer(int_value))) + + def int_value(self): + """Return the integer value of the system id.""" + return int(decoder.decode(self.value)[0]) + + +class X509CertificateChain(object): + """Implement x509 certificate chain object. + + cryptography does not support certificate chain (pkcs7 container) right + now, so we have to implement it using low level functions directly. + """ + + # Disable pylint on protected-access in class X509CertificateChain. + # pylint: disable=protected-access + + def __init__(self, certificates): + """Inits from certificate list.""" + self._certificates = certificates + + def __iter__(self): + """Iterates through certificates.""" + return self._certificates.__iter__() + + @classmethod + def load_der(cls, certificate_chain_data): + """Load from DER formatted data.""" + bio = backend._bytes_to_bio(certificate_chain_data) + pkcs7 = backend._lib.d2i_PKCS7_bio(bio.bio, backend._ffi.NULL) + if pkcs7 == backend._ffi.NULL: + raise ValueError('Unable to load certificate chain') + pkcs7 = backend._ffi.gc(pkcs7, backend._lib.PKCS7_free) + if not backend._lib.PKCS7_type_is_signed(pkcs7): + raise ValueError('Invalid certificate chain') + + x509_stack = pkcs7.d.sign.cert + certificates = [] + for i in xrange(backend._lib.sk_X509_num(x509_stack)): + x509_value = backend._ffi.gc( + backend._lib.X509_dup(backend._lib.sk_X509_value(x509_stack, i)), + backend._lib.X509_free) + certificates.append(openssl.x509._Certificate(backend, x509_value)) + return cls(certificates) + + def der_bytes(self): + """Return DER formatted bytes.""" + x509_stack = backend._ffi.gc(backend._lib.sk_X509_new_null(), + backend._lib.sk_X509_free) + 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) + p7 = backend._ffi.gc(p7, backend._lib.PKCS7_free) + + bio = backend._create_mem_bio_gc() + backend.openssl_assert(backend._lib.i2d_PKCS7_bio(bio, p7) == 1) + return backend._read_mem_bio(bio) + + +def _multiple_of_1024(key_size_str): + """argparse custom type function for key size.""" + key_size = int(key_size_str) + if key_size % 1024: + msg = '%r is not multiple of 1024' % key_size_str + raise argparse.ArgumentTypeError(msg) + return key_size + + +def _valid_date(date_str): + """argparse custom type function dates.""" + return datetime.datetime.strptime(date_str, '%Y-%m-%d') + + +def _get_encryption_algorithm(passphrase): + """Convenient function to get the encryption algorithm.""" + if passphrase: + return serialization.BestAvailableEncryption(passphrase) + else: + return serialization.NoEncryption() + + +def generate_csr(args): + """Subparser handler for generating certificate signing request.""" + key = rsa.generate_private_key( + public_exponent=65537, + key_size=args.key_size, + backend=backends.default_backend()) + args.output_private_key_file.write( + key.private_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=_get_encryption_algorithm(args.passphrase))) + + 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()) + args.output_csr_file.write(csr.public_bytes(serialization.Encoding.PEM)) + + +def _random_serial_number(): + """Utility function to generate random serial number.""" + return utils.int_from_bytes(os.urandom(16), byteorder='big') + + +def _build_certificate(subject_name, issuer_name, system_id, not_valid_before, + valid_duration, public_key, signing_key, ca): + """Utility function to build certificate.""" + builder = x509.CertificateBuilder() + builder = builder.subject_name(subject_name).issuer_name(issuer_name) + + if ca: + builder = builder.add_extension( + x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False) + builder = builder.add_extension( + x509.AuthorityKeyIdentifier.from_issuer_public_key( + signing_key.public_key()), + critical=False) + builder = builder.add_extension( + x509.BasicConstraints(True, 0), critical=True) + if system_id: + builder = builder.add_extension( + WidevineSystemId.from_int_value(system_id), critical=False) + + builder = builder.not_valid_before(not_valid_before).not_valid_after( + not_valid_before + datetime.timedelta(valid_duration)).serial_number( + _random_serial_number()).public_key(public_key) + return builder.sign( + private_key=signing_key, + algorithm=hashes.SHA256(), + backend=backends.default_backend()) + + +def generate_intermediate_certificate(args): + """Subparser handler for generating intermediate certificate.""" + root_cert = x509.load_der_x509_certificate(args.root_certificate_file.read(), + backends.default_backend()) + root_private_key = serialization.load_der_private_key( + args.root_private_key_file.read(), + password=args.root_private_key_passphrase, + backend=backends.default_backend()) + if (root_private_key.public_key().public_numbers() != + root_cert.public_key().public_numbers()): + raise ValueError('Root certificate does not match with root private key') + csr = x509.load_pem_x509_csr(args.csr_file.read(), backends.default_backend()) + + certificate = _build_certificate(csr.subject, root_cert.subject, + args.system_id, args.not_valid_before, + args.valid_duration, + csr.public_key(), root_private_key, True) + args.output_certificate_file.write( + certificate.public_bytes(serialization.Encoding.DER)) + + +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()) + intermediate_private_key = serialization.load_der_private_key( + args.intermediate_private_key_file.read(), + password=args.intermediate_private_key_passphrase, + backend=backends.default_backend()) + if (intermediate_private_key.public_key().public_numbers() != + intermediate_cert.public_key().public_numbers()): + raise ValueError( + 'Intermediate certificate does not match with intermediate private key') + + system_id_raw_bytes = intermediate_cert.extensions.get_extension_for_oid( + WidevineSystemId.oid).value.value + system_id = WidevineSystemId(system_id_raw_bytes).int_value() + + name_attributes = [ + x509.NameAttribute(oid.NameOID.COMMON_NAME, u'{0}-leaf'.format(system_id)) + ] + name_attributes.extend([ + attribute for attribute in intermediate_cert.subject + if attribute.oid != oid.NameOID.COMMON_NAME + ]) + subject_name = x509.Name(name_attributes) + + leaf_private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=args.key_size, + backend=backends.default_backend()) + args.output_private_key_file.write( + leaf_private_key.private_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=_get_encryption_algorithm(args.passphrase))) + + certificate = _build_certificate(subject_name, intermediate_cert.subject, + system_id, args.not_valid_before, + args.valid_duration, + leaf_private_key.public_key(), + intermediate_private_key, False) + args.output_certificate_file.write( + X509CertificateChain([certificate, intermediate_cert]).der_bytes()) + + +def secure_erase(args): + """Subparser handler for secure erasing of a file.""" + length = args.file.tell() + for _ in xrange(args.passes): + args.file.seek(0) + for _ in xrange(length): + args.file.write(os.urandom(1)) + args.file.close() + os.remove(args.file.name) + + +def _certificate_as_string(cert): + """Utility function to format certificate as string.""" + lines = ['Certificate Subject Name:'] + lines.extend([' {0}'.format(attribute) for attribute in cert.subject]) + lines.append('Issuer Name:') + lines.extend([' {0}'.format(attribute) for attribute in cert.issuer]) + lines.append('Key Size: {0.key_size}'.format(cert.public_key())) + try: + system_id_raw_bytes = cert.extensions.get_extension_for_oid( + WidevineSystemId.oid).value.value + lines.append('Widevine System Id: {0}'.format( + WidevineSystemId(system_id_raw_bytes).int_value())) + except x509.ExtensionNotFound: + pass + lines.append('Not valid before: {0.not_valid_before}'.format(cert)) + lines.append('Not valid after: {0.not_valid_after}'.format(cert)) + return '\n'.join(lines) + + +def _csr_as_string(csr): + """Utility function to format csr as string.""" + lines = ['CSR Subject Name:'] + lines.extend([' {0}'.format(attribute) for attribute in csr.subject]) + lines.append('Key Size: {0.key_size}'.format(csr.public_key())) + return '\n'.join(lines) + + +def _handle_csr(data): + """Utility function for get_info to parse csr.""" + return _csr_as_string( + x509.load_pem_x509_csr(data, backends.default_backend())) + + +def _handle_certificate(data): + """Utility function for get_info to parse certificate.""" + return _certificate_as_string( + x509.load_der_x509_certificate(data, backends.default_backend())) + + +def _handle_certificate_chain(data): + """Utility function for get_info to parse certificate chain.""" + return '\n\n'.join([ + _certificate_as_string(certificate) + for certificate in X509CertificateChain.load_der(data) + ]) + + +def get_info(args, out=sys.stdout): + """Subparser handler to get csr or certificate information.""" + # 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]: + try: + out.write(handler(data)) + return + except ValueError: + pass + print('Error occurred. The input file is not a valid CSR, nor certificate, ' + 'nor certificate chain.') + + +def create_parser(): + """Command line parsing.""" + parser = argparse.ArgumentParser(fromfile_prefix_chars='@') + subparsers = parser.add_subparsers() + + # Subparser for certificate signing request generation. + parser_csr = subparsers.add_parser( + 'generate_csr', help='generate certificate signing request') + parser_csr.add_argument( + '--key_size', + type=_multiple_of_1024, + default=3072, + 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( + '--output_csr_file', type=argparse.FileType('wb'), required=True) + parser_csr.add_argument( + '--output_private_key_file', type=argparse.FileType('wb'), required=True) + parser_csr.add_argument( + '--passphrase', + help=('specify an optional passphrase to encrypt the private key. The ' + 'private key is not encrypted if omitted.')) + parser_csr.set_defaults(func=generate_csr) + + # Subparser for intermediate certificate generation. + parser_intermediate_cert = subparsers.add_parser( + 'generate_intermediate_certificate', + help=('generate intermediate certificate from csr. This should only be ' + 'used for testing.')) + parser_intermediate_cert.add_argument( + '--not_valid_before', + type=_valid_date, + default=datetime.datetime.today(), + help='certificate validity start date - format YYYY-MM-DD') + parser_intermediate_cert.add_argument( + '--valid_duration', + type=int, + default=3650, + help='the certificate will expire after the specified number of days.') + parser_intermediate_cert.add_argument('--system_id', type=int, required=True) + parser_intermediate_cert.add_argument( + '--csr_file', type=argparse.FileType('rb'), required=True) + parser_intermediate_cert.add_argument( + '--root_certificate_file', type=argparse.FileType('rb'), required=True) + parser_intermediate_cert.add_argument( + '--root_private_key_file', type=argparse.FileType('rb'), required=True) + parser_intermediate_cert.add_argument('--root_private_key_passphrase') + parser_intermediate_cert.add_argument( + '--output_certificate_file', type=argparse.FileType('wb'), required=True) + parser_intermediate_cert.set_defaults(func=generate_intermediate_certificate) + + # Subparser for leaf certificate generation. + parser_leaf_cert = subparsers.add_parser( + 'generate_leaf_certificate', help='generate leaf certificate') + parser_leaf_cert.add_argument( + '--key_size', + type=_multiple_of_1024, + default=3072, + help='specify RSA key size.') + parser_leaf_cert.add_argument( + '--not_valid_before', + type=_valid_date, + default=datetime.datetime.today(), + help='certificate validity start date - format YYYY-MM-DD') + parser_leaf_cert.add_argument( + '--valid_duration', + type=int, + default=3650, + help='the certificate will expire after the specified number of days.') + parser_leaf_cert.add_argument( + '--intermediate_certificate_file', + type=argparse.FileType('rb'), + required=True) + parser_leaf_cert.add_argument( + '--intermediate_private_key_file', + type=argparse.FileType('rb'), + required=True) + parser_leaf_cert.add_argument('--intermediate_private_key_passphrase') + parser_leaf_cert.add_argument( + '--output_certificate_file', type=argparse.FileType('wb'), required=True) + parser_leaf_cert.add_argument( + '--output_private_key_file', type=argparse.FileType('wb'), required=True) + parser_leaf_cert.add_argument( + '--passphrase', + help=('specify an optional passphrase to encrypt the private key. The ' + 'private key is not encrypted if omitted.')) + parser_leaf_cert.set_defaults(func=generate_leaf_certificate) + + # Subparser for secure file erase. + parser_erase = subparsers.add_parser('erase', help='erase a file securely') + parser_erase.add_argument( + '-F', '--file', type=argparse.FileType('a'), required=True) + parser_erase.add_argument( + '--passes', + type=int, + default=3, + help=('the file will be overwritten with random patterns for the ' + 'specified number of passes')) + parser_erase.set_defaults(func=secure_erase) + + # Subparser to get CSR or certificate or certificate chain metadata. + parser_info = subparsers.add_parser( + 'info', help='get CSR or certificate metadata') + parser_info.add_argument( + '-F', '--file', type=argparse.FileType('rb'), required=True) + parser_info.set_defaults(func=get_info) + + return parser + + +def main(): + args = sys.argv[1:] + config_file_name = 'oem_certificate.cfg' + if os.path.isfile(config_file_name): + print 'Load from args default configuration file: ', config_file_name + args.append('@' + config_file_name) + parser_args = create_parser().parse_args(args) + parser_args.func(parser_args) + + +if __name__ == '__main__': + main() diff --git a/oem_certificate_generator/oem_certificate_test.py b/oem_certificate_generator/oem_certificate_test.py new file mode 100644 index 0000000..d696a61 --- /dev/null +++ b/oem_certificate_generator/oem_certificate_test.py @@ -0,0 +1,538 @@ +################################################################################ +# 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. +################################################################################ + +import datetime +import os +import shutil +import StringIO +import tempfile +import textwrap +import unittest + +from cryptography import x509 +from cryptography.hazmat import backends +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509 import oid + +import oem_certificate + + +class ArgParseObject(object): + """A convenient object to allow adding arbitrary attribute to it.""" + pass + + +class OemCertificateTest(unittest.TestCase): + + def _setup_csr_args(self, key_size=4096, passphrase=None): + args = ArgParseObject() + args.key_size = key_size + args.country_name = 'US' + args.state_or_province_name = 'WA' + args.locality_name = 'Kirkland' + args.organization_name = 'CompanyXYZ' + args.organizational_unit_name = 'ContentProtection' + args.output_csr_file = StringIO.StringIO() + args.output_private_key_file = StringIO.StringIO() + args.passphrase = passphrase + return args + + def test_widevine_system_id(self): + system_id = 1234567890123 + self.assertEqual( + oem_certificate.WidevineSystemId.from_int_value(system_id).int_value(), + system_id) + + def test_generate_csr(self): + args = self._setup_csr_args() + 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) + + 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_with_keysize4096_and_passphrase(self): + args = self._setup_csr_args(key_size=4096, passphrase='passphrase_4096') + oem_certificate.generate_csr(args) + private_key = serialization.load_der_private_key( + args.output_private_key_file.getvalue(), + 'passphrase_4096', + backend=backends.default_backend()) + csr = x509.load_pem_x509_csr(args.output_csr_file.getvalue(), + backends.default_backend()) + self.assertEqual(private_key.key_size, 4096) + self.assertEqual(csr.public_key().key_size, 4096) + # Verify csr and private key match. + self.assertEqual(csr.public_key().public_numbers(), + private_key.public_key().public_numbers()) + + def _create_root_certificate_and_key(self): + key = rsa.generate_private_key( + public_exponent=65537, + key_size=3072, + backend=backends.default_backend()) + subject_name = x509.Name( + [x509.NameAttribute(oid.NameOID.COMMON_NAME, u'root_cert')]) + certificate = oem_certificate._build_certificate( + subject_name, subject_name, None, + datetime.datetime(2001, 8, 9), 1000, key.public_key(), key, True) + return (key, certificate) + + def _setup_intermediate_cert_args(self, csr_bytes, root_key, + root_certificate): + args = ArgParseObject() + args.not_valid_before = datetime.datetime(2001, 8, 9) + args.valid_duration = 100 + args.system_id = 1234554321 + args.csr_file = StringIO.StringIO(csr_bytes) + args.root_private_key_passphrase = 'root_passphrase' + args.output_certificate_file = StringIO.StringIO() + + serialized_private_key = root_key.private_bytes( + serialization.Encoding.DER, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.BestAvailableEncryption( + args.root_private_key_passphrase)) + serialized_certificate = root_certificate.public_bytes( + serialization.Encoding.DER) + + args.root_certificate_file = StringIO.StringIO(serialized_certificate) + args.root_private_key_file = StringIO.StringIO(serialized_private_key) + return args + + def test_generate_intermediate_certificate(self): + csr_args = self._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 = self._create_root_certificate_and_key() + args = self._setup_intermediate_cert_args(csr_bytes, root_key, + root_certificate) + oem_certificate.generate_intermediate_certificate(args) + + cert = x509.load_der_x509_certificate( + args.output_certificate_file.getvalue(), backends.default_backend()) + self.assertEqual(cert.issuer, root_certificate.subject) + self.assertEqual(cert.subject, csr.subject) + + system_id_raw_bytes = cert.extensions.get_extension_for_oid( + oem_certificate.WidevineSystemId.oid).value.value + self.assertEqual( + oem_certificate.WidevineSystemId(system_id_raw_bytes).int_value(), + args.system_id) + + self.assertEqual(cert.not_valid_before, datetime.datetime(2001, 8, 9)) + self.assertEqual(cert.not_valid_after, datetime.datetime(2001, 11, 17)) + + root_key.public_key().verify(cert.signature, cert.tbs_certificate_bytes, + padding.PKCS1v15(), + cert.signature_hash_algorithm) + + def test_generate_intermediate_with_cert_mismatch_root_cert_and_key(self): + root_key1, _ = self._create_root_certificate_and_key() + _, root_certificate2 = self._create_root_certificate_and_key() + args = self._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 _setup_leaf_cert_args(self, + intermediate_key_bytes, + intermediate_certificate_bytes, + key_size=1024, + passphrase=None): + args = ArgParseObject() + args.key_size = key_size + args.not_valid_before = datetime.datetime(2001, 8, 9) + args.valid_duration = 8000 + args.intermediate_private_key_passphrase = None + args.output_certificate_file = StringIO.StringIO() + args.output_private_key_file = StringIO.StringIO() + args.passphrase = passphrase + + args.intermediate_private_key_file = StringIO.StringIO( + intermediate_key_bytes) + args.intermediate_certificate_file = StringIO.StringIO( + intermediate_certificate_bytes) + return args + + def _create_intermediate_certificate_and_key_bytes(self, + key_size=4096, + passphrase=None): + csr_args = self._setup_csr_args(key_size, passphrase) + oem_certificate.generate_csr(csr_args) + csr_bytes = csr_args.output_csr_file.getvalue() + + root_key, root_certificate = self._create_root_certificate_and_key() + args = self._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()) + + def test_generate_leaf_certificate(self): + intermediate_key_bytes, intermediate_certificate_bytes = ( + self._create_intermediate_certificate_and_key_bytes()) + args = self._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) + + self.assertEqual(leaf_cert.not_valid_before, datetime.datetime(2001, 8, 9)) + self.assertEqual(leaf_cert.not_valid_after, datetime.datetime(2023, 7, 5)) + + system_id_raw_bytes = leaf_cert.extensions.get_extension_for_oid( + oem_certificate.WidevineSystemId.oid).value.value + self.assertEqual( + oem_certificate.WidevineSystemId(system_id_raw_bytes).int_value(), + 1234554321) + + leaf_key = serialization.load_der_private_key( + args.output_private_key_file.getvalue(), + args.passphrase, + backend=backends.default_backend()) + self.assertEqual(leaf_key.key_size, args.key_size) + self.assertEqual(leaf_cert.public_key().key_size, args.key_size) + # Verify cert and private key match. + self.assertEqual(leaf_cert.public_key().public_numbers(), + leaf_key.public_key().public_numbers()) + + def test_generate_leaf_certificate_with_keysize4096_and_passphrase(self): + intermediate_key_bytes, intermediate_certificate_bytes = ( + self._create_intermediate_certificate_and_key_bytes()) + args = self._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( + args.output_private_key_file.getvalue(), + 'leaf passphrase', + backend=backends.default_backend()) + self.assertEqual(4096, args.key_size) + + def test_get_csr_info(self): + args = self._setup_csr_args() + oem_certificate.generate_csr(args) + args.file = StringIO.StringIO(args.output_csr_file.getvalue()) + output = StringIO.StringIO() + oem_certificate.get_info(args, output) + expected_info = """\ + CSR Subject Name: + , value=u'US')> + , value=u'WA')> + , value=u'Kirkland')> + , value=u'CompanyXYZ')> + , value=u'ContentProtection')> + Key Size: 4096""" + self.assertEqual(output.getvalue(), textwrap.dedent(expected_info)) + + def test_get_certificate_info(self): + _, intermediate_certificate_bytes = ( + self._create_intermediate_certificate_and_key_bytes()) + 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: 1234554321 + 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_certificate_chain_info(self): + intermediate_key_bytes, intermediate_certificate_bytes = ( + self._create_intermediate_certificate_and_key_bytes()) + args = self._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()) + output = StringIO.StringIO() + oem_certificate.get_info(args, output) + expected_info = """\ + Certificate Subject Name: + , value=u'1234554321-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: 1234554321 + 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: 1234554321 + 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) + args.passes = 2 + self.assertTrue(os.path.exists(args.file.name)) + oem_certificate.secure_erase(args) + self.assertFalse(os.path.exists(args.file.name)) + + +class OemCertificateArgParseTest(unittest.TestCase): + + def setUp(self): + self.parser = oem_certificate.create_parser() + self.test_dir = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.test_dir) + + def test_generate_csr(self): + cmds = ('generate_csr --key_size 4096 -C USA -ST WA ' + '-L Kirkland -O Company -OU Widevine').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.func, oem_certificate.generate_csr) + + def _fill_file_with_dummy_contents(self, file_name): + with open(file_name, 'wb') as f: + f.write('dummy') + + def test_generate_csr_invalid_key_size(self): + cmds = ('generate_csr --key_size unknown -C USA -ST WA ' + '-L Kirkland -O Company -OU Widevine').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' + ]) + + with self.assertRaises(SystemExit) as context: + self.parser.parse_args(cmds) + self.assertEqual(context.exception.code, 2) + + def test_generate_intermediate_cert(self): + cmds = ( + 'generate_intermediate_certificate --valid_duration 10 --system_id 100' + ).split() + + csr_file = os.path.join(self.test_dir, 'csr') + self._fill_file_with_dummy_contents(csr_file) + root_certificate_file = os.path.join(self.test_dir, 'root_cert') + self._fill_file_with_dummy_contents(root_certificate_file) + root_private_key_file = os.path.join(self.test_dir, 'root_private_key') + self._fill_file_with_dummy_contents(root_private_key_file) + output_certificate_file = os.path.join(self.test_dir, 'cert') + cmds.extend([ + '--csr_file', csr_file, '--root_certificate_file', + root_certificate_file, '--root_private_key_file', root_private_key_file, + '--root_private_key_passphrase', 'root_key', + '--output_certificate_file', output_certificate_file + ]) + + args = self.parser.parse_args(cmds) + self.assertAlmostEqual( + args.not_valid_before, + datetime.datetime.today(), + delta=datetime.timedelta(seconds=60)) + self.assertEqual(args.valid_duration, 10) + self.assertEqual(args.system_id, 100) + self.assertEqual(args.csr_file.name, csr_file) + self.assertEqual(args.csr_file.mode, 'rb') + self.assertEqual(args.root_certificate_file.name, root_certificate_file) + self.assertEqual(args.root_certificate_file.mode, 'rb') + self.assertEqual(args.root_private_key_file.name, root_private_key_file) + self.assertEqual(args.root_private_key_file.mode, 'rb') + self.assertEqual(args.root_private_key_passphrase, 'root_key') + self.assertEqual(args.output_certificate_file.name, output_certificate_file) + self.assertEqual(args.output_certificate_file.mode, 'wb') + self.assertEqual(args.func, + oem_certificate.generate_intermediate_certificate) + + def test_generate_leaf_cert(self): + cmds = ('generate_leaf_certificate --not_valid_before 2016-01-02 ' + '--valid_duration 10').split() + + intermediate_certificate_file = os.path.join(self.test_dir, + 'intermediate_cert') + self._fill_file_with_dummy_contents(intermediate_certificate_file) + intermediate_private_key_file = os.path.join(self.test_dir, + 'intermediate_private_key') + self._fill_file_with_dummy_contents(intermediate_private_key_file) + + output_certificate_file = os.path.join(self.test_dir, 'cert') + output_private_key_file = os.path.join(self.test_dir, 'key') + cmds.extend([ + '--intermediate_certificate_file', intermediate_certificate_file, + '--intermediate_private_key_file', intermediate_private_key_file, + '--intermediate_private_key_passphrase', 'intermediate_key', + '--output_certificate_file', output_certificate_file, + '--output_private_key_file', output_private_key_file, '--passphrase', + 'leaf_key' + ]) + + args = self.parser.parse_args(cmds) + self.assertEqual(args.not_valid_before, datetime.datetime(2016, 1, 2)) + self.assertEqual(args.valid_duration, 10) + self.assertEqual(args.intermediate_certificate_file.name, + intermediate_certificate_file) + self.assertEqual(args.intermediate_certificate_file.mode, 'rb') + self.assertEqual(args.intermediate_private_key_file.name, + intermediate_private_key_file) + self.assertEqual(args.intermediate_private_key_file.mode, 'rb') + self.assertEqual(args.intermediate_private_key_passphrase, + 'intermediate_key') + self.assertEqual(args.output_certificate_file.name, output_certificate_file) + self.assertEqual(args.output_certificate_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, 'leaf_key') + self.assertEqual(args.func, oem_certificate.generate_leaf_certificate) + + def test_generate_leaf_cert_invalid_date(self): + cmds = ('generate_leaf_certificate --not_valid_before invaid-date ' + '--valid_duration 10').split() + + intermediate_certificate_file = os.path.join(self.test_dir, + 'intermediate_cert') + self._fill_file_with_dummy_contents(intermediate_certificate_file) + intermediate_private_key_file = os.path.join(self.test_dir, + 'intermediate_private_key') + self._fill_file_with_dummy_contents(intermediate_private_key_file) + + output_certificate_file = os.path.join(self.test_dir, 'cert') + output_private_key_file = os.path.join(self.test_dir, 'key') + cmds.extend([ + '--intermediate_certificate_file', intermediate_certificate_file, + '--intermediate_private_key_file', intermediate_private_key_file, + '--intermediate_private_key_passphrase', 'intermediate_key', + '--output_certificate_file', output_certificate_file, + '--output_private_key_file', output_private_key_file, '--passphrase', + 'leaf_key' + ]) + + with self.assertRaises(SystemExit) as context: + self.parser.parse_args(cmds) + self.assertEqual(context.exception.code, 2) + + def test_secure_erase(self): + file_path = os.path.join(self.test_dir, 'file') + self._fill_file_with_dummy_contents(file_path) + cmds = ['erase', '-F', file_path, '--passes', '2'] + args = self.parser.parse_args(cmds) + self.assertEqual(args.passes, 2) + self.assertEqual(args.file.name, file_path) + self.assertEqual(args.file.mode, 'a') + self.assertEqual(args.func, oem_certificate.secure_erase) + + def test_get_info(self): + file_path = os.path.join(self.test_dir, 'file') + self._fill_file_with_dummy_contents(file_path) + cmds = ['info', '-F', file_path] + args = self.parser.parse_args(cmds) + self.assertEqual(args.file.name, file_path) + self.assertEqual(args.file.mode, 'rb') + self.assertEqual(args.func, oem_certificate.get_info) + + def test_arbitrary_commands(self): + with self.assertRaises(SystemExit) as context: + self.parser.parse_args(['unsupport', '--commands']) + self.assertEqual(context.exception.code, 2) + + def test_no_argument(self): + with self.assertRaises(SystemExit) as context: + self.parser.parse_args([]) + self.assertEqual(context.exception.code, 2) + + +if __name__ == '__main__': + unittest.main() diff --git a/protos/public/BUILD b/protos/public/BUILD new file mode 100644 index 0000000..f47d3a0 --- /dev/null +++ b/protos/public/BUILD @@ -0,0 +1,89 @@ +################################################################################ +# 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: +# Public protocol buffer definitions for Widevine Services. + +package(default_visibility = ["//visibility:public"]) + +load("@protobuf_repo//:protobuf.bzl", "cc_proto_library", "py_proto_library") + +cc_proto_library( + name = "certificate_provisioning_proto", + srcs = ["certificate_provisioning.proto"], + default_runtime = "@protobuf_repo//:protobuf", + protoc = "@protobuf_repo//:protoc", + deps = [":client_identification_proto"], +) + +py_proto_library( + name = "certificate_provisioning_py_pb2", + srcs = ["certificate_provisioning.proto"], + default_runtime = "@protobuf_repo//:protobuf_python", + protoc = "@protobuf_repo//:protoc", + deps = [":client_identification_py_pb2"], +) + +cc_proto_library( + name = "client_identification_proto", + srcs = ["client_identification.proto"], + default_runtime = "@protobuf_repo//:protobuf", + protoc = "@protobuf_repo//:protoc", +) + +py_proto_library( + name = "client_identification_py_pb2", + srcs = ["client_identification.proto"], + default_runtime = "@protobuf_repo//:protobuf_python", + protoc = "@protobuf_repo//:protoc", +) + +cc_proto_library( + name = "device_certificate_proto", + srcs = ["device_certificate.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"], + 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"], + default_runtime = "@protobuf_repo//:protobuf", + protoc = "@protobuf_repo//:protoc", +) + +py_proto_library( + name = "signed_device_certificate_py_pb2", + srcs = ["signed_device_certificate.proto"], + default_runtime = "@protobuf_repo//:protobuf_python", + protoc = "@protobuf_repo//:protoc", +) + +cc_proto_library( + name = "provisioned_device_info_proto", + srcs = ["provisioned_device_info.proto"], + default_runtime = "@protobuf_repo//:protobuf", + protoc = "@protobuf_repo//:protoc", +) + +py_proto_library( + name = "provisioned_device_info_py_pb2", + srcs = ["provisioned_device_info.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 new file mode 100644 index 0000000..3aacf45 --- /dev/null +++ b/protos/public/certificate_provisioning.proto @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// Public protocol buffer definitions for Widevine Device Certificate +// Provisioning protocol. + +syntax = "proto2"; + +package widevine; +option java_package = "com.google.video.widevine.protos"; + +import "protos/public/client_identification.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. + } + + optional CertificateType certificate_type = 1 [default = WIDEVINE_DRM]; + + // Contains the application-specific name used to identify the certificate + // authority for signing the generated certificate. This is required iff the + // certificate type is X509. + optional string certificate_authority = 2; +} + +// Provisioning request sent by client devices to provisioning service. +message ProvisioningRequest { + oneof clear_or_encrypted_client_id { + // Device root of trust and other client identification. Required. + ClientIdentification client_id = 1; + EncryptedClientIdentification encrypted_client_id = 5; + } + // Nonce value used to prevent replay attacks. Required. + optional bytes nonce = 2; + // Options for type of certificate to generate. Optional. + optional ProvisioningOptions options = 3; + oneof spoid_param { + // Stable identifier, unique for each device + application (or origin). + // To be deprecated. + bytes stable_id = 4; + // Service provider ID from the service certificate's provider_id field. + // Preferred parameter. + bytes provider_id = 6; + // Client-generated stable per-origin identifier to be copied directly + // to the client certificate serial number. + bytes spoid = 7; + } +} + +// 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 { + // 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 + // proto message. + optional bytes device_rsa_key = 1; + // 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 + // encoded X.509 certificate. Required. + optional bytes device_certificate = 3; + // Nonce value matching nonce in ProvisioningRequest. Required. + optional bytes nonce = 4; + // Key used to wrap device_rsa_key when DRM provisioning an OEM factory + // provisioned device. Encrypted with the device OEM public key using + // RSA-OAEP. + optional bytes wrapping_key = 5; +} + +// Serialized ProvisioningRequest or ProvisioningResponse signed with +// The message authentication key. +message SignedProvisioningMessage { + enum ProtocolVersion { + VERSION_2 = 2; // Keybox factory-provisioned devices. + VERSION_3 = 3; // OEM certificate factory-provisioned devices. + } + + // Serialized ProvisioningRequest or ProvisioningResponse. Required. + optional bytes message = 1; + // HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required. + optional bytes signature = 2; + // Version number of provisioning protocol. + optional ProtocolVersion protocol_version = 3 [default = VERSION_2]; +} diff --git a/protos/public/client_identification.proto b/protos/public/client_identification.proto new file mode 100644 index 0000000..f2281ff --- /dev/null +++ b/protos/public/client_identification.proto @@ -0,0 +1,101 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// ClientIdentification messages used by provisioning and license protocols. + +syntax = "proto2"; + +package widevine; +option java_package = "com.google.video.widevine.protos"; + +option java_outer_classname = "ClientIdentificationProtos"; + +// ClientIdentification message used to authenticate the client device. +message ClientIdentification { + enum TokenType { + KEYBOX = 0; + DRM_DEVICE_CERTIFICATE = 1; + REMOTE_ATTESTATION_CERTIFICATE = 2; + OEM_DEVICE_CERTIFICATE = 3; + } + + message NameValue { + optional string name = 1; + optional string value = 2; + } + + // Capabilities which not all clients may support. Used for the license + // exchange protocol only. + message ClientCapabilities { + enum HdcpVersion { + HDCP_NONE = 0; + HDCP_V1 = 1; + HDCP_V2 = 2; + HDCP_V2_1 = 3; + HDCP_V2_2 = 4; + HDCP_NO_DIGITAL_OUTPUT = 0xff; + } + + enum CertificateKeyType { + RSA_2048 = 0; + RSA_3072 = 1; + } + + optional bool client_token = 1 [default = false]; + optional bool session_token = 2 [default = false]; + optional bool video_resolution_constraints = 3 [default = false]; + optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE]; + optional uint32 oem_crypto_api_version = 5; + // Client has hardware support for protecting the usage table, such as + // storing the generation number in secure memory. For Details, see: + // https://docs.google.com/document/d/1Mm8oB51SYAgry62mEuh_2OEkabikBiS61kN7HsDnh9Y/edit#heading=h.xgjl2srtytjt + optional bool anti_rollback_usage_table = 6 [default = false]; + // The client shall report |srm_version| if available. + optional uint32 srm_version = 7; + // A device may have SRM data, and report a version, but may not be capable + // of updating SRM data. + optional bool can_update_srm = 8 [default = false]; + repeated CertificateKeyType supported_certificate_key_type = 9; + } + + // Type of factory-provisioned device root of trust. Optional. + optional TokenType type = 1 [default = KEYBOX]; + // Factory-provisioned device root of trust. Required. + optional bytes token = 2; + // Optional client information name/value pairs. + repeated NameValue client_info = 3; + // Client token generated by the content provider. Optional. + optional bytes provider_client_token = 4; + // Number of licenses received by the client to which the token above belongs. + // Only present if client_token is specified. + optional uint32 license_counter = 5; + // List of non-baseline client capabilities. + optional ClientCapabilities client_capabilities = 6; + // Serialized VmpData message. Optional. + optional bytes vmp_data = 7; +} + +// EncryptedClientIdentification message used to hold ClientIdentification +// messages encrypted for privacy purposes. +message EncryptedClientIdentification { + // Provider ID for which the ClientIdentifcation is encrypted (owner of + // service certificate). + optional string provider_id = 1; + // Serial number for the service certificate for which ClientIdentification is + // encrypted. + optional bytes service_certificate_serial_number = 2; + // Serialized ClientIdentification message, encrypted with the privacy key using + // AES-128-CBC with PKCS#5 padding. + optional bytes encrypted_client_id = 3; + // Initialization vector needed to decrypt encrypted_client_id. + optional bytes encrypted_client_id_iv = 4; + // AES-128 privacy key, encrypted with the service public key using RSA-OAEP. + optional bytes encrypted_privacy_key = 5; +} diff --git a/protos/public/device_certificate.proto b/protos/public/device_certificate.proto new file mode 100644 index 0000000..041825e --- /dev/null +++ b/protos/public/device_certificate.proto @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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/provisioned_device_info.proto b/protos/public/provisioned_device_info.proto new file mode 100644 index 0000000..c5c5b97 --- /dev/null +++ b/protos/public/provisioned_device_info.proto @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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: +// Provisioned device info format definitions. + +syntax = "proto2"; + +package widevine; + +option java_package = "com.google.video.widevine.protos"; +option java_outer_classname = "ProvisionedDeviceInfoProto"; + +// Contains device model information for a provisioned device. +message ProvisionedDeviceInfo { + enum WvSecurityLevel { + // Defined in Widevine Security Integration Guide for DASH on Android: + // http://doc/1Zum-fcJeoIw6KG1kDP_KepIE5h9gAZg0PaMtemBvk9c/edit#heading=h.1t3h5sf + LEVEL_UNSPECIFIED = 0; + LEVEL_1 = 1; + LEVEL_2 = 2; + LEVEL_3 = 3; + } + + // Widevine system ID for the device. Mandatory. + optional uint32 system_id = 1; + // Name of system-on-a-chip. Optional. + optional string soc = 2; + // Name of manufacturer. Optional. + optional string manufacturer = 3; + // Manufacturer's model name. Matches "brand" in device metadata. Optional. + optional string model = 4; + // Type of device (Phone, Tablet, TV, etc). + optional string device_type = 5; + // Device model year. Optional. + optional uint32 model_year = 6; + // Widevine-defined security level. Optional. + optional WvSecurityLevel security_level = 7 [default = LEVEL_UNSPECIFIED]; + // True if the certificate corresponds to a test (non production) device. + // Optional. + optional bool test_device = 8 [default = false]; +} diff --git a/protos/public/signed_device_certificate.proto b/protos/public/signed_device_certificate.proto new file mode 100644 index 0000000..23a9fbe --- /dev/null +++ b/protos/public/signed_device_certificate.proto @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +// Signed device certificate definition. + +syntax = "proto2"; + +package widevine; + +option java_outer_classname = "SignedDeviceCertificateProtos"; +option java_package = "com.google.video.widevine.protos"; + +// DrmDeviceCertificate signed by a higher (CA) DRM certificate. +message SignedDrmDeviceCertificate { + // 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; +} diff --git a/provisioning_sdk/internal/BUILD b/provisioning_sdk/internal/BUILD new file mode 100644 index 0000000..4f04960 --- /dev/null +++ b/provisioning_sdk/internal/BUILD @@ -0,0 +1,112 @@ +################################################################################ +# 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. +################################################################################ + +# Build file for provisioning 3.0 SDK internal library. + +# Only accessible by public provisioning_sdk apis. +package_group( + name = "internal", + packages = [ + "//provisioning_sdk/...", + ], +) + +package( + default_visibility = [":internal"], +) + +cc_library( + name = "provisioning_engine_impl", + srcs = ["provisioning_engine_impl.cc"], + hdrs = ["provisioning_engine_impl.h"], + deps = [ + ":oem_device_cert", + "//base", + "//common:random_util", + "//common:rsa_key", + "//provisioning_sdk/internal/certificates:root_certificates", + "//provisioning_sdk/public:certificate_type", + "//provisioning_sdk/public:provisioning_status", + "//protos/public:device_certificate_proto", + "//protos/public:provisioned_device_info_proto", + "//protos/public:signed_device_certificate_proto", + ], +) + +cc_test( + name = "provisioning_engine_impl_test", + size = "small", + srcs = ["provisioning_engine_impl_test.cc"], + deps = [ + ":provisioning_engine_impl", + "//base", + "//external:gtest_main", + "//common:mock_rsa_key", + "//provisioning_sdk/public:certificate_type", + ], +) + +cc_library( + name = "provisioning_session_impl", + 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", + ], +) + +cc_test( + name = "provisioning_session_impl_test", + size = "small", + srcs = ["provisioning_session_impl_test.cc"], + deps = [ + ":oem_device_cert", + ":provisioning_engine_impl", + ":provisioning_session_impl", + "//external:gtest_main", + "//common:aes_cbc_util", + "//common:mock_rsa_key", + "//common:sha_util", + ], +) + +cc_library( + name = "oem_device_cert", + srcs = ["oem_device_cert.cc"], + hdrs = ["oem_device_cert.h"], + deps = [ + "//base", + "//external:openssl", + "//common:openssl_util", + "//common:rsa_key", + "//provisioning_sdk/internal/certificates:root_certificates", + "//provisioning_sdk/public:certificate_type", + ], +) + +cc_test( + name = "oem_device_cert_test", + size = "small", + srcs = ["oem_device_cert_test.cc"], + deps = [ + ":oem_device_cert", + "//external:gtest_main", + "//provisioning_sdk/internal/certificates:test_certificates", + ], +) diff --git a/provisioning_sdk/internal/certificates/BUILD b/provisioning_sdk/internal/certificates/BUILD new file mode 100644 index 0000000..3f8d044 --- /dev/null +++ b/provisioning_sdk/internal/certificates/BUILD @@ -0,0 +1,117 @@ +################################################################################ +# 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. +################################################################################ + +# Build file for test certificates. + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "root_certificates", + srcs = [ + "root_certificates.cc", + ":drm_ca_root_dev_cert", + ":drm_ca_root_prod_cert", + ":drm_ca_root_test_cert", + ":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/$@", +) + +genrule( + name = "oem_ca_root_dev_der", + srcs = ["oem_ca_root_dev_cert.der"], + outs = ["oem_ca_root_dev_cert.h"], + cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", +) + +genrule( + name = "oem_ca_root_prod_der", + srcs = ["oem_ca_root_prod_cert.der"], + outs = ["oem_ca_root_prod_cert.h"], + cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", +) + +cc_library( + name = "test_certificates", + srcs = [ + "test_certificates.cc", + ":backwards_chain_der", + ":expired_2000_chain_der", + ":invalid_chain_der", + ":single_cert_chain_der", + ":sysid_2001_chain_der", + ":sysid_2001_public_key_der", + ], + hdrs = ["test_certificates.h"], +) + +genrule( + name = "single_cert_chain_der", + srcs = ["single-cert-chain.der"], + outs = ["single_cert_chain.h"], + cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", +) + +genrule( + name = "sysid_2001_chain_der", + srcs = ["sysid-2001-chain.der"], + outs = ["sysid_2001_chain.h"], + cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", +) + +genrule( + name = "sysid_2001_public_key_der", + srcs = ["sysid-2001-public-key.der"], + outs = ["sysid_2001_public_key.h"], + cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", +) + +genrule( + name = "expired_2000_chain_der", + srcs = ["expired-2000-chain.der"], + outs = ["expired_2000_chain.h"], + cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", +) + +genrule( + name = "backwards_chain_der", + srcs = ["backwards-chain.der"], + outs = ["backwards_chain.h"], + cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", +) + +genrule( + name = "invalid_chain_der", + srcs = ["invalid_chain.der"], + outs = ["invalid_chain.h"], + cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@", +) diff --git a/provisioning_sdk/internal/certificates/backwards-chain.der b/provisioning_sdk/internal/certificates/backwards-chain.der new file mode 100644 index 0000000000000000000000000000000000000000..8cceb0ae0f3ef979d283cb369e559e9d5b22e5e3 GIT binary patch literal 2210 zcmc(gX;jl!8piWaLI_!82?VgLRX`%kKP)1kAPhp4%|O^SG(ZRulE@OMAP8ZL1;K(K zn`}`QwK^=7qbvdn2#V}TL6ps+m8C#s7bYC3)9Htqew&Z)^FHU^=f3xz-vznIe1r%g z;hTgNEFcUmO-f61S_3jd212zpyZB>#$au!ERHYLJA@M?I3ZCMl>VGk%>KBo=D8qtC?lA` zW(is#7x?T3t$JkIlf*o;nyq7!?bt3*VZ*9aT+A1N%u257ss!(rKp7}@Rhe~(&j6Z48TKja_*I1fCBF^R(o^tyu4!X8Rt%t9CUA2CQJHp zS2}h!s2@&`0k)=!nB6U*N+PNOj4ip&KJVZy+{7OkJY8=riw$=_5{+@WO*P zv750HmM@p@U|i3k)Ixun^m<4rQL2XMPm5bU&g{H3^c3-YaK`&G^@eMLL+tDOPJI^L z;fS>QdRdMa7t$d&S@wuwyh4}`dc<1@xC(FRt6F~(KgqwRyc|qQnHFC)3V1C)Y?Ktc zr!mG;{b1@6dGbVkWp|wBp=)cH9Wc!a$CH$Fxo{pt|LG zfv(taWvgY<5C5zl;S6$NF#^^xKC@07(%foG`GLPY*W(an!lRTf|K-j6ziILXb~;2| zUERY1*a_Hon5eT`g5Lkh;=c#GU86uNZMR-}w!b~xvB|DrQg7vGO}m(t{efZA z7sQB92(b?<4QXrlHcb46=l1&8EU7OB=Nk?!vxh39xq&G^-B@fk=|pwhhj-@SlE}ei z6>@SzRyh5~;VQeTb#B>7dX}>bIj~dW;w&F%Je#y1mdR_l=1Zt^Y>!2d+<7#d%#_ir z{{q#+=BdK(&3BO$P0v+{i^ZF@$}-^a*0BwsL(5>#i_y?Tt|i(iEVVI6SmWY#g#?dT zHT5^JYhLpfeI7?g@+eC9QBgCVLaUx!yRVRUz68A5(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 literal 0 HcmV?d00001 diff --git a/provisioning_sdk/internal/certificates/drm_ca_root_prod.cert b/provisioning_sdk/internal/certificates/drm_ca_root_prod.cert new file mode 100644 index 0000000000000000000000000000000000000000..01987e395379bfe189cffce274205df1002d4626 GIT binary patch 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 literal 0 HcmV?d00001 diff --git a/provisioning_sdk/internal/certificates/drm_ca_root_test.cert b/provisioning_sdk/internal/certificates/drm_ca_root_test.cert new file mode 100644 index 0000000000000000000000000000000000000000..175ada4f04000afbf8ccfd074630fd99c42ddf49 GIT binary patch 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?c5GX`TOmtmrH-9jdp-wYCpIAY7KVccu%V2sI_$uMk0jN7zCOXQZ@ zluNaBTsBX+ghG_uWl6cUYI9j^?y+ODXM6hNob#MN&Yz$6=l%OW@8|vfy}lqF}X7~8mENUzS z!YW*F_qp-Q{THE!t%TBfrLDX>H8 z!VDj1IFq;!mcgsP>Wiy&Y>Pn<-FZ}u?4;3*|2)OR=859(O?O1dTAnLX7m7A&RAj-S z&7Kd?s1fcw|f zXnJ;5h6zDo00iI*S_P#L$O8xf4h)=w0{|Qjg$j3l-kjiv5;O-uGT=n*;vDB+vP~TLTgiD zT69@uGvlm&NlKZ6gQsjqmf zHAqZo`Y!GL2XG2|J`|4&{B2dyGsi%JXVc!#YF2(~(%RbgAdV{0$lTjIS6p4O+fwPs z$Lpv639Wp5^`2rfVMYF&4X0;xq$h5;#zHw|5i5N1HmS+PtWb9Up8F@V?xzv@Edkjm zGsG=0uJ(pUEdX(uES_C!i#l#r%vyYdYJXL1Fsf#Rcd5Ng=|D#iB8& ze|f}EA}(|vY6zW4#eWSde?m_#bVt(mW(DZ5r~z7R7AsgwVEdavydmKbXdqyau#F^s zXZ$kW|NDvfPjn81<-aWaS)LXo^k^SnQ8QTW4azzD=c3W-##2vLRzqK_y>2M2tLcyR zILH5CBPu?UhVOo+8Xjq0Eo{_xxS$lx&~cM}+(M!&_C&z`QCi1Nf=hktTinAS_ZPXU z&bY*-1C8Qz`(zF5-n_Y9dv#8F!`b>I?Y&82E$t0!WAa?}Oz--Zjp-iJKy>J;U5+|oPGg<5Q1*0mz@b~W zb^7kun%}?~PQW+cCCk)X`2?o4S>VpkCv-cOGUg;s(qyh>4dfDYF8;Gmj!JYEGmm;Rv3dto+FtL4%YxV_ z=}6ccgqN|V$_j>nyS~O3Y3Y!osUly2M za@{Dp!;YCQ3!aUR&A;Xr*q~%iQeZ_fIeG-A)`yL`)f(7~wdaS6%kA9{Fdpa|iyB~F?p=a2Aa9T95IawIc)f}QMs>Yyi%6Ig72W?`;e<2Z}&N=BU z@`!%Nlvnepb)$CS{08))mv>hVj96jdsV`gRJf(A5&PmCk8p+9;lk-|t%E#KBobvKq zYD6yVm%Ldb^nHj_xVAwEa6R{B@q>5orRQZWu*WiMTJ7l8saA{Wr^>mmw_LjWtYgNS za(<=Cj1TpU^AnRqF29wrN;5y)a&tW3V1Pxcy3kiXuz-jNzXybYKHgR-sh3F$WIeq4{*iGRS^9tNvlU09XAJ`Af63O zdtahlca3+5d3DdJ*P<&7ky=+L$MNDq+T|xoAJUDNaZ~Jvym`hIczti>+UvLp{#})& zAY$^A8XwbB@6ZPwb!qj@+jmG8N?nJdH}GjH2* k!^!jg+5YWQgd#v~)AKw{ss8e2^MoJvX&u735Q2yN0oz|zkpKVy literal 0 HcmV?d00001 diff --git a/provisioning_sdk/internal/certificates/invalid_chain.der b/provisioning_sdk/internal/certificates/invalid_chain.der new file mode 100644 index 0000000000000000000000000000000000000000..686af57e3f7a5af04a2052c1a995766a81759d94 GIT binary patch literal 2188 zcmbW1c~Fz*7RK{^Sy&P_K?E0wP%aXKd|{C)5SA*4kf2sv?vc zE`39~Tmw)6iv+51kU*6O0ssL9q5tXticTOTfTuzNkXZx=0T9%I)!iWlXP=rHZSzSY zih{*ckj8%=0B9r(+Dkw~YACor0;LQ3`x0~@EoGsKQ6&d3BBIrYUN*RsOAW_)75DGPh$7Qk^1Vd<%Qio%-H?1dGMshiOn9)oQ zThW4y7HJSxL-qt4Yl4l9&9^)LEWagK&zBJOpV*uCp^nj*w}=0q{vZKh@?|Ul5(XCl zS}=vK4hjGOCWfuvMenWg+ktbD3@tpoO=r{LZGHV>V@Ld;NX>#0zzE)&G-{r;-S!yn zOo4R?XhV^w{r_2;R8zhrM&j1_ZMf0q1~g4Y2M=&W6_0c z9#3%|UrrH39g_YsR!3}Bzjp=Ed`$O%U$mc@-@)3vc*gne3UbAapmZZ6Z)<>GRP%!5 z*QbCRVF$i}9~IT+hTy7w8~35M>=4m)4Mx_!4u3}p^uB8}QFlQ#*mCFeg5;C`mMu4c zx<9(i0{2$gYqs?5$q=}s*YdM&a8>Zh1xD$Cugxt6;JLfstm_DL>Ny^~Bw>vN%|e9B zWcZz}74b+A4#0p@id3NmDD{;n00DHxApih@06~=pUnZwmC`EDrY$*`@{?*WquJQG9 zSHy8q=D%vUZDjgB}txSTkPem z!bmNvsE>%W6K9qAtTSZ&+T-P%WENE4sNp8t%X&~AYTJtR)peLXIyxd;2a19i_d~RV zUeK0|y4akTlhAYhL_Pr*_4d8d&U|}qk@vk$PCfQMv7w={DvhpvlWp4Z=5*!dMefE< zAAb4%5?FCN_lnUW>*=L&-n{m~zV@^p**a|2xW3BfbAEM1*J6Vei?40``C5*3r#oPf z;)*&8rB#;%Ry>7L#?qgQE?BC$)*Gy4NK!K>I;6d~6Y{f&jgSD*u7I%W6NK7^lNsc! z8p`i>WkyxBv?~3tBKw*_T}bB>gX#g4=uibEF&y?66nZha;Sm%zji9CY=T8bz9vluM zg8q31jZqC?(&&4bY&yXN!hS-}kUuZ2qH=+7@jor$m^9cr!{wI%Go+L^*eIt(@J`dyk zrBRsmir~>0%46{VI4rpvvwf=g`hgd*%??hQegp4rrFWinnS^B_Dv1@*dh$xgGe;+T zK`Z~?C8#53vRh`$_Z$g5*mX_Y&@?gn$-R^b?<+;-59lU!9Kf^%;*lOU07kirF_VnG`q(UyVJEaH#b0Cd#H50Y2tKE zTWjpY$ylp$k%OZnT6eqe%8nVHfY#$Ulm1ND*bkOu9BmMKDArisu;=yC5D8}{XYbUr!f_ojM9tK~i(i47Sm+olLb z_$hix@Moyq4*2CIKE*Fa@$Eh%Q!+M$Q4Z)LkPsXI=Kc%SZC|Y@TmS;FYwvr@-165o zBpa5Wi|E^514AFI_NbnbEZnx=!Gu#&5pQ4{aKzDmT54#1g(u59y~H56&_pc8BBkA| z$Sdy1&CNH+X{SzIOR*R%l}q16$v(1h{M&B_kXP~3;9^s^qVpxds@TIKxMOPV9S(vu z1&;pqXqY->aouguwf)R(S`#`lp-4Y;e4;W_@FroTJS>V&xIcwCX4LmIj~^s%zkAlq z{Reii@YZWOIDlB9vtY)G(p#WmGWV;6mu#DH#7gO|(JhX{+b^q1&?pYmu=uT zbnN5jSoWdV0$;()afsZXqdjr6yt-nMRwDX& z_xh@m`Z#fh2W~4uc$`YaKM0Jv(|<78p8R<9 zK(YPx_=XoxkJeTd#=rv9-QMu7bRQ(;sO literal 0 HcmV?d00001 diff --git a/provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.der b/provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.der new file mode 100644 index 0000000000000000000000000000000000000000..7f5e237054874cffac04ba7832e1867c6469f811 GIT binary patch literal 1491 zcmXqLVm)ur#Jqh0GZP~d6DPye>s$Yv?>il6z{|#|)#lOmotKf3o0Y+!){xtPlZ`o) zg-w_#G}utsKoG>?;Nc2SEY8TxOE1aKGZZx72MMzCaCm1HW#=U3r5N%9%>fE=@vyn) z=cnhS!qjl{aD->3q?Tpor5Y+3D1g*3^GKFMh4hm1bM^C6b9IaI^GkGr$_y05c@51B z%#19Jj14S|Ors?DjSNu)2HJs5OiIW>#>mRR+{DDsV9>By8gAm@P(iM?fm~~>O{{I75lmfMq>pS>ojFk^#y8+9|NNQkvky-co%HzcLsw%KKAVR#YfMe& zur7%G=z49gg692+=KGR-437Q`>R%V8`21v9zw)=2cRBZNsC_XnZbr7H>*tEF-AgCL zec0ro;!r3hG}lCFU0S?kt%$#W_s<3;`u7>lhXX~dt+T42h*n8zN(Oc!W zW>1fGsCy|Ga5#07tU<_a-rq6Lgj7sdCJ1; zcg%SHr0>LDhh0HkCuJfyb88(sL^2vK&uTosPUrxuxah-&-}HXjb4MvC@A;*4^#6n$ z^Z%zOl`Ax-p4}95)p6O~b(Y$_#gC&SSOPOUA2RK^G$AZ=*PU}5PIf=6+&HgDmilct z{PC#9t+_jTzTb47FSTp)M*Tm_&r3V(y3r-^d{4dA-@3k=TjzIGCN7pwSd-LsIPBo_ zn_Q9nHU=u28eC?~%kmbLI<;W^&482YJQGdVZd&!jJ!(nj(pw2<4pj&;WJOCZ+SoJG zw?9LvbjIUrhjbX3m>C%u7Y7*l8OQh_9^Ha7YYaPX#tz1ldl#~ufAy2+ z(}W#c9JBMXT8w^`a|C#YtPQ#+9a!?$MeFc3p2m^QaZ-|XHK7wIat6#s&}HBY;>zU(pgFUhn1`hSgV`m&#*zH)kV zwpFfN8Y*L4%A}jz&N`!WsTWU}Rd`EQu!&fLCx%Yu&0;Z9qXf9ot=ZEz-}rIW3Y<#Hjb-;1uxi_G2z znjiS7{A$PAbBg}#1(kuOOHSVkF@DYvoNsIGD5Sn>@3z8;%DZP+d_G_<#JTFhow?VR zHJ&V1*s)Jkss7aI1O@loacsHi3^zi`Z94Pk7f)Ngb%)fcR&NDq E0AC?)*Z=?k literal 0 HcmV?d00001 diff --git a/provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.der b/provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.der new file mode 100644 index 0000000000000000000000000000000000000000..1befb68a9e70f0992854d0f9a4796814b89b361c GIT binary patch literal 1493 zcmXqLV!de4#JqC>GZP~d6DPy{HdaH%eY0j6@Un4gwRyCC=VfH%W@Rv_GvqelWMd9x zVH0Kw4K@@u5Cm~Jc(}q7i!(Cw(o6F53bpiT-~Dl{1V-QqWlyC1#wx_9F)=cndwRLo7w$fHV#c0mgADPCa%RV5BO#yZv2u%=-Fl}+&fC>{`J%0b znYn@~K=MlW{ zl%T+iBVYdPD$R)4)pp?NsR~K10}~_P$Czldh^)MJ=JmmM$sc%b@vV3$t-bc1%>P5a zP1#)E5B`=l`@rbbpz`Ev_SOA?mrn)w>nDBND^m6*aixoijmjpgn_`N7Pk1EW8MiKK z*m#%A@W}4Rax%VO{MLrtk(vDAr>AZP8ahrwcBU)MY^`t?y->}aUfg)KQwUb79|Jr_?djs3Cl zLqHPU_|hop6L=?z;IAu%J7zkSBnRm&Pw++*)H9$NC&X5Q2%XH>(t9{#%a zZ}0ZtIExFF=cX=OJJ0A;Nbh1>6D6_yv{zLr{AYCM|Ngjwm1X;t>uT&)^+|`mzTums z$me;vgV&F#{Oo_3Z;bBCMdD+k8PzTveUvKd;oaNHt0MB|&-9oi=l9O9PL*k$J^#b+ zp#GOEwoD;U#}gf?(&gJ%D;ctXIlXE{>hF)ic`rFP6|az5l3gKkBV6|sYxtI=Sb0xhyEuK{1CiLa_f^_kXpP7z)+>^5AxbB=% z%Z@|r=B6SWE~?l^O?uV7KjKlCp;-^l*VTQh_Hi|#8vNTsOIn?r8lI@{kXXR!rMxfg z&8rm!S0W6&1HFY5aJLlTVD+R|;mbom4ea<}!MDo$E_x{=_x?7cafe zp1io~{SwxPFC-jmH_SA#SZ+SO)Hqz!!{Pb`4%^Ey!8S`iNF}F+PAhj#xc=?@szU4A zG5e-J+_6L?!0fcOK(VEUjcok>XOB;)7BpORaopNIb9QQKDRb&+i=!JEedhiA`0rpn E0RJjzy8r+H literal 0 HcmV?d00001 diff --git a/provisioning_sdk/internal/certificates/root_certificates.cc b/provisioning_sdk/internal/certificates/root_certificates.cc new file mode 100644 index 0000000..d3848c4 --- /dev/null +++ b/provisioning_sdk/internal/certificates/root_certificates.cc @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/certificates/root_certificates.h" + +#include "provisioning_sdk/internal/certificates/drm_ca_root_dev_cert.h" +#include "provisioning_sdk/internal/certificates/drm_ca_root_prod_cert.h" +#include "provisioning_sdk/internal/certificates/drm_ca_root_test_cert.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 { + +RootCertificates::RootCertificates() + : drm_root_test_certificate_( + drm_ca_root_test_cert, + drm_ca_root_test_cert + drm_ca_root_test_cert_len), + drm_root_dev_certificate_( + drm_ca_root_dev_cert, + drm_ca_root_dev_cert + drm_ca_root_dev_cert_len), + drm_root_prod_certificate_( + drm_ca_root_prod_cert, + drm_ca_root_prod_cert + drm_ca_root_prod_cert_len), + 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) {} + +RootCertificates::~RootCertificates() {} + +} // namespace widevine diff --git a/provisioning_sdk/internal/certificates/root_certificates.h b/provisioning_sdk/internal/certificates/root_certificates.h new file mode 100644 index 0000000..7ee1ed8 --- /dev/null +++ b/provisioning_sdk/internal/certificates/root_certificates.h @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_CERTIFICATES_H_ +#define PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_CERTIFICATES_H_ + +#include + +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/single-cert-chain.der b/provisioning_sdk/internal/certificates/single-cert-chain.der new file mode 100644 index 0000000000000000000000000000000000000000..813880e988109a0000e304d8bf0faa029897ad47 GIT binary patch literal 950 zcmXqLV&25YsnzDu_MMlJooPW6^J0T0=J`yFjD`#b+z1&)piC=JrqQ5@DRTic6C)Fo zAj6?!zHt*5e|vW^sLrk5C@4f^9cC6`sx-G<(Fj^XXfVtr3~c6c@51B%ngl=42_MAEuz3&3qvC)*C3f3 z=Rw`U#ls7Ao(^9388k5}AqNX1D+6;ABR>OBoQtW6k&$6JgIPlAoxLGZS`J$ticX0T z^q&%O_ug`O8=VIooSKQ@>2YCo@6`JvO=fAWn!CmC*Yd0@C#!-F`ptiA^4)g(Ndb3% zja&BT46FaL3B3NkZ_>~8zwI(^y^~7u`KDf1DIHLdVUpAi-wQg~LV{_SGM16kcF%+osdO;6L>6MV9n zEikGhRaoMQ)!XckiP1hMN)$wF^?_VmV;n~&xc zm@e;M8RR0F`|P&2eVuB|(UZJRyK8eVY|Jn@&l)Uj{d?NGCoL{a9Wm*r)A(Dw4FcN_ zRZM>U)!?~UO}U{~;fJ4c@w3eZI($xD%|EJe+V1%Ale=0|1rFvZUw*%A`zCQWdE38B zVmB~tJ~DlqT%XYonM$A1OAqc|YQ4SNMWO$*7<<5)kVAHk3net9c6!a+Ioar{8>2*n vBim|&);&w3H$O6H_|*2iW1TLi<53B#^ev6;iGsehN2+J{+nrp!f7l=Y_y2yM|MlGO{d+(*(heb_onV!) zDh3Ec^Vvvqkc~8j0stNYqW*9IfujzxVV)oxYMl>*0#K+Fq}4a{Wn=r_iG7K6jxZK5 z4~qT%0e}*MfNppcD1v}F!VxmiqxN`l5WOvsXrYtzfJpi`E&NR@fePEcC|cUikHMe^ zsU11z?G?hL2l;sg<0V1yZ4-qSw)FH0p)(^v6?rkd5l940@B{+haKDSZ*#2#4{8fUq z|1Dh^RQi%G`!(IxGbq$YkQK`G3-ZOwfwEun#r{+W*#LIuwE-arj15RY1a(m;8vr0v zj3pc)bL4NZ{2KG#$XtnqIIK7&NyplH5n|Jg*|J3sER|aJckMkLv3~nO0?}UVW|3pn zBb;N!_>_aj;qz%ueH_EQ@Ah||m^}mwzG(EO9TwP9#Ty!^{gvo}YT7N?1$DA-kx%T? zqf^9C1+4~-QK^H-rFRSGA6}8Yd8KAVU3lEC=t&!P=*B7>mY+@{?_p@hD3Xc-Fq#Ld zXER6dzVKh}bt7gNH`5L`*Y%1-c{smQ!K!Ip4xTsO+dI%-@(JI$wjMY)$)r0+sNRu` z#agxIRuGOEx@8URr4fx}epUoZm1uR`18T{MFK&*84=pJl<#EDq{eR(d5gQ&TR)C&{a}?0xm4EBzaIMZA?BAsA0y@G%d+4&hs%_( zv+#0v_w$-P0hWePbtf)0{WPS%^2?a(VdCrNXw2n!)wjIv7wi)43wH;nLh3Q!C(lWz zI7mSeKfk%2k$kd^IM35Nk~~#NZRpUH*Pm1`D@GR_OnXmS&RMkPK9IXwge$K*MM|*s z%P&{CMP3*yoj)|o+sDYBs6YO$;9_B(6*C0L!tq=gSyZ1|IGYL?+$dx2qbCs9)0dE$Jv{ zj%)_#QJq5vo{ZP1%_?UauOSI%`RTT45{;D{gVUmYJl_e9>n)spNs`WYJ+zd;F|0C! zXHre>aPKWTrA`=k5YlrOQhFtqomllQX_?aH@?p54O+%-0aXqe7zVn`jUp#q*^<%c; zM9b3>4f+U1J!4|o;6@VXjq^3@t{kV5eshdFo4va&?qft2*RvS*g*F;^22>XONo8@k z99(vs;S(FpC1)lVcF2&PKRC?eU>(?{$Z$Q!9-@ zpPy=F8MqJ#1AtHe6}wOX(o|ua<&ZAV;-oeww)yO_QF_9i@Q-~|NPB=*X26ztK@jGCY@Uq zTDYrCc6L+3IYaxttSBne5a`b~ml|q87uL#;1L7x7+Qxlr@4TIW{%a>b+#|+J#qJ zJ-S@+V=CR94v?H@4&;Kuq<&w6pWr#|I5)Agk<%Q`9p^D3mfe{}P-CS0{2E%QwupIN)zL9 Q6dSY9Tuq88fZ!qj2C7~?^8f$< literal 0 HcmV?d00001 diff --git a/provisioning_sdk/internal/certificates/sysid-2001-public-key.der b/provisioning_sdk/internal/certificates/sysid-2001-public-key.der new file mode 100644 index 0000000000000000000000000000000000000000..cacfae9e02883401a65b735056cfb1260c511c13 GIT binary patch literal 270 zcmV+p0rCDYf&mHwf&l>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 literal 0 HcmV?d00001 diff --git a/provisioning_sdk/internal/certificates/test_certificates.cc b/provisioning_sdk/internal/certificates/test_certificates.cc new file mode 100644 index 0000000..a03172a --- /dev/null +++ b/provisioning_sdk/internal/certificates/test_certificates.cc @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/certificates/test_certificates.h" + +#include "provisioning_sdk/internal/certificates/backwards_chain.h" +#include "provisioning_sdk/internal/certificates/expired_2000_chain.h" +#include "provisioning_sdk/internal/certificates/invalid_chain.h" +#include "provisioning_sdk/internal/certificates/single_cert_chain.h" +#include "provisioning_sdk/internal/certificates/sysid_2001_chain.h" +#include "provisioning_sdk/internal/certificates/sysid_2001_public_key.h" + +namespace widevine { + +TestCertificates::TestCertificates() + : single_certificate_chain_der_( + single_cert_chain_der, + single_cert_chain_der + single_cert_chain_der_len), + valid_certificate_chain_der_( + sysid_2001_chain_der, + sysid_2001_chain_der + sysid_2001_chain_der_len), + valid_certificate_public_key_der_( + sysid_2001_public_key_der, + sysid_2001_public_key_der + sysid_2001_public_key_der_len), + expired_certificate_chain_der_( + expired_2000_chain_der, + expired_2000_chain_der + expired_2000_chain_der_len), + backwards_certificate_chain_der_( + backwards_chain_der, backwards_chain_der + backwards_chain_der_len), + invalid_certificate_chain_der_( + invalid_chain_der, invalid_chain_der + invalid_chain_der_len) {} + +TestCertificates::~TestCertificates() {} + +} // namespace widevine diff --git a/provisioning_sdk/internal/certificates/test_certificates.h b/provisioning_sdk/internal/certificates/test_certificates.h new file mode 100644 index 0000000..543257d --- /dev/null +++ b/provisioning_sdk/internal/certificates/test_certificates.h @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_ +#define PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_ + +#include + +namespace widevine { + +// This class contains DER-encoded test certificates. The keys for these +// certificates came from common/rsa_test_keys.h. +class TestCertificates { + public: + TestCertificates(); + ~TestCertificates(); + + const std::string& single_certificate_chain_der() const { + return single_certificate_chain_der_; + } + + const std::string& valid_certificate_chain_der() const { + return valid_certificate_chain_der_; + } + + const std::string& valid_certificate_public_key_der() const { + return valid_certificate_public_key_der_; + } + + const std::string& expired_certificate_chain_der() const { + return expired_certificate_chain_der_; + } + + const std::string& backwards_certificate_chain_der() const { + return backwards_certificate_chain_der_; + } + + const std::string& invalid_certificate_chain_der() const { + return invalid_certificate_chain_der_; + } + + private: + TestCertificates(const TestCertificates&) = delete; + TestCertificates& operator=(const TestCertificates&) = delete; + + std::string single_certificate_chain_der_; + // leaf + intermediate certificates. + std::string valid_certificate_chain_der_; + std::string valid_certificate_public_key_der_; + std::string expired_certificate_chain_der_; + // intermediate + leaf certificates. + std::string backwards_certificate_chain_der_; + std::string invalid_certificate_chain_der_; +}; + +} // namespace widevine + +#endif // PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_ diff --git a/provisioning_sdk/internal/oem_device_cert.cc b/provisioning_sdk/internal/oem_device_cert.cc new file mode 100644 index 0000000..edc99f2 --- /dev/null +++ b/provisioning_sdk/internal/oem_device_cert.cc @@ -0,0 +1,210 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/oem_device_cert.h" + +#include + +#include "glog/logging.h" +#include "openssl/asn1.h" +#include "openssl/x509.h" +#include "openssl/x509v3.h" +#include "provisioning_sdk/internal/certificates/root_certificates.h" + +namespace widevine { +namespace { + +const int kExtensionOidSize = 64; +const char kWidevineSystemIdExtensionOid[] = "1.3.6.1.4.1.11129.4.1.1"; + +bool ExtractPublicKey(X509* x509, std::unique_ptr* public_key) { + DCHECK(x509); + ScopedPKEY pkey(X509_get_pubkey(x509)); + if (!pkey) { + LOG(WARNING) << "X509_get_pubkey failed."; + return false; + } + public_key->reset(new RsaPublicKey(EVP_PKEY_get1_RSA(pkey.get()))); + return true; +} + +bool ExtractSystemId(X509* x509, uint32_t* system_id) { + DCHECK(x509); + const int num_of_extensions = X509_get_ext_count(x509); + for (int i = 0; i < num_of_extensions; ++i) { + X509_EXTENSION* extension = X509_get_ext(x509, i); + if (!extension) { + LOG(WARNING) << "X509_get_ext failed."; + return false; + } + ASN1_OBJECT* extension_object = X509_EXTENSION_get_object(extension); + if (!extension_object) { + LOG(WARNING) << "X509_EXTENSION_get_object failed."; + return false; + } + char extension_name[kExtensionOidSize + 1]; + OBJ_obj2txt(extension_name, kExtensionOidSize, extension_object, 1); + if (strcmp(extension_name, kWidevineSystemIdExtensionOid)) continue; + + ASN1_OCTET_STRING* octet_str = X509_EXTENSION_get_data(extension); + if (!octet_str) { + LOG(WARNING) << "X509_EXTENSION_get_data failed."; + return false; + } + const unsigned char* data = octet_str->data; + if (!data) { + LOG(WARNING) << "null data in octet string."; + return false; + } + ScopedAsn1Integer asn1_integer( + d2i_ASN1_INTEGER(nullptr, &data, octet_str->length)); + if (!asn1_integer) { + LOG(WARNING) << "d2i_ASN1_INTEGER failed."; + return false; + } + int64_t system_id_long = ASN1_INTEGER_get(asn1_integer.get()); + if (system_id_long == -1) { + LOG(WARNING) << "ASN1_INTEGER_get failed."; + return false; + } + *system_id = system_id_long; + return true; + } + LOG(WARNING) << "Widevine system ID extension not found."; + return false; +} + +bool ExtractSerialNumber(X509* x509, std::string* oem_ca_serial_number) { + ASN1_INTEGER* serial = X509_get_serialNumber(x509); + if (!serial) { + LOG(WARNING) << "X509_get_serialNumber failed."; + return false; + } + uint8_t* buffer = ASN1_STRING_data(serial); + if (!buffer) { + LOG(WARNING) << "ASN1_STRING_data failed."; + return false; + } + int length = ASN1_STRING_length(serial); + if (length <= 0) { + LOG(WARNING) << "ASN1_STRING_length returns " << length; + } + oem_ca_serial_number->assign(reinterpret_cast(buffer), length); + return true; +} + +} // namespace + +OemDeviceCert::OemDeviceCert() {} + +OemDeviceCert::~OemDeviceCert() {} + +bool OemDeviceCert::Initialize(CertificateType certificate_type) { + RootCertificates root_certificates; + switch (certificate_type) { + case kCertTesting: + case kCertDevelopment: + return Initialize(root_certificates.oem_root_dev_certificate()); + case kCertProduction: + return Initialize(root_certificates.oem_root_prod_certificate()); + default: + LOG(WARNING) << "Invalid certificate type " << certificate_type; + return false; + } +} + +bool OemDeviceCert::VerifyCertificateChain( + const std::string& certificate_chain, + std::unique_ptr* leaf_public_key, uint32_t* system_id, + std::string* oem_ca_serial_number) const { + if (certificate_chain.empty()) { + LOG(WARNING) << "Empty certificate."; + return false; + } + + ScopedX509Stack x509_stack(sk_X509_new_null()); + if (!x509_stack) { + LOG(WARNING) << "sk_X509_new_null returned NULL."; + return false; + } + CBS pkcs7; + CBS_init(&pkcs7, reinterpret_cast(certificate_chain.data()), + certificate_chain.size()); + if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) { + LOG(WARNING) << "PKCS7_get_certificates failed."; + return false; + } + if (sk_X509_num(x509_stack.get()) != 2) { + LOG(WARNING) << "Expecting one and only one intermediate certificate."; + return false; + } + X509* leaf_certificate = sk_X509_value(x509_stack.get(), 0); + X509* intermediate_certificate = sk_X509_value(x509_stack.get(), 1); + DCHECK(leaf_certificate); + DCHECK(intermediate_certificate); + + // Verify the cert. + // TODO(user): Implement some form of caching mechanism so that we do not + // have to verify the same intermediate certs over and over. + ScopedX509StoreCtx context(X509_STORE_CTX_new()); + if (!context) { + LOG(WARNING) << "X509_STORE_CTX_new returned NULL."; + return false; + } + if (!X509_STORE_CTX_init(context.get(), store_.get(), leaf_certificate, + x509_stack.get())) { + LOG(WARNING) << "X509_STORE_CTX_init failed."; + return false; + } + if (X509_verify_cert(context.get()) != 1) { + LOG(WARNING) << "Verification error: " + << X509_verify_cert_error_string( + X509_STORE_CTX_get_error(context.get())); + return false; + } + if (sk_X509_num(context->chain) < 3) { + LOG(WARNING) << "The leaf certificate should not be signed by the root " + "certificate directly."; + return false; + } + + // Extract public key from the leaf certificate and system_id extension and + // oem serial number from the intermediate certificate. + return ExtractPublicKey(leaf_certificate, leaf_public_key) && + ExtractSystemId(intermediate_certificate, system_id) && + ExtractSerialNumber(intermediate_certificate, oem_ca_serial_number); +} + +bool OemDeviceCert::Initialize(const std::string& serialized_root_certificate) { + if (serialized_root_certificate.empty()) { + LOG(WARNING) << "Empty root certificate."; + return false; + } + + store_.reset(X509_STORE_new()); + if (!store_) { + LOG(WARNING) << "X509_STORE_new returned NULL"; + return false; + } + const char* data = serialized_root_certificate.data(); + ScopedX509 root_certificate(d2i_X509(nullptr, + reinterpret_cast(&data), + serialized_root_certificate.size())); + if (!root_certificate) { + LOG(WARNING) << "d2i_X509 returned NULL."; + return false; + } + + if (!X509_STORE_add_cert(store_.get(), root_certificate.get())) { + LOG(WARNING) << "X509_STORE_add_cert failed."; + return false; + } + return true; +} + +} // namespace widevine diff --git a/provisioning_sdk/internal/oem_device_cert.h b/provisioning_sdk/internal/oem_device_cert.h new file mode 100644 index 0000000..17d5cf3 --- /dev/null +++ b/provisioning_sdk/internal/oem_device_cert.h @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 PROVISIONING_SDK_INTERNAL_OEM_DEVICE_CERT_H_ +#define PROVISIONING_SDK_INTERNAL_OEM_DEVICE_CERT_H_ + +#include + +#include +#include + +#include "common/openssl_util.h" +#include "common/rsa_key.h" +#include "provisioning_sdk/public/certificate_type.h" + +namespace widevine { + +// Implements a class to handle OEM certificate: verifies the validity of the +// certificate and extracts leaf public key and system id. +class OemDeviceCert { + public: + OemDeviceCert(); + virtual ~OemDeviceCert(); + + // Initialize with root certificate. + bool Initialize(CertificateType certificate_type); + + // Verify the given certificate chain (in DER encoded pkcs7 format), which + // includes the leaf certificate (a device unique certificate containing the + // device public OEM key) and the intermediate certificate (OEM model + // intermediate CA certificate for a specific device make + model), and + // extract public key from the leaf certificate and system id extension and + // oem ca serial number from the intermediate certificate. + virtual bool VerifyCertificateChain( + const std::string& certificate_chain, + std::unique_ptr* leaf_public_key, uint32_t* system_id, + std::string* oem_ca_serial_number) const; + + private: + OemDeviceCert(const OemDeviceCert&) = delete; + OemDeviceCert& operator=(const OemDeviceCert&) = delete; + + // Internal implementation of Initialize function. + bool Initialize(const std::string& serialized_root_certificate); + + ScopedX509Store store_; +}; + +} // namespace widevine + +#endif // PROVISIONING_SDK_INTERNAL_OEM_DEVICE_CERT_H_ diff --git a/provisioning_sdk/internal/oem_device_cert_test.cc b/provisioning_sdk/internal/oem_device_cert_test.cc new file mode 100644 index 0000000..69405e8 --- /dev/null +++ b/provisioning_sdk/internal/oem_device_cert_test.cc @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/oem_device_cert.h" + +#include "gtest/gtest.h" +#include "provisioning_sdk/internal/certificates/test_certificates.h" + +namespace widevine { + +class OemDeviceCertTest : public ::testing::Test { + protected: + void SetUp() override { + ASSERT_TRUE(oem_device_cert_.Initialize(kCertTesting)); + } + + OemDeviceCert oem_device_cert_; + TestCertificates test_certificates_; +}; + +TEST_F(OemDeviceCertTest, EmptyCertificateChain) { + std::unique_ptr leaf_public_key; + uint32_t system_id; + std::string oem_ca_serial_number; + EXPECT_FALSE(oem_device_cert_.VerifyCertificateChain( + "", &leaf_public_key, &system_id, &oem_ca_serial_number)); +} + +TEST_F(OemDeviceCertTest, InvalidCertificateChain) { + std::unique_ptr leaf_public_key; + uint32_t system_id; + std::string oem_ca_serial_number; + EXPECT_FALSE(oem_device_cert_.VerifyCertificateChain( + "invalid certificte chain", &leaf_public_key, &system_id, + &oem_ca_serial_number)); +} + +TEST_F(OemDeviceCertTest, OnlyOneCertificateInCertificateChain) { + std::unique_ptr leaf_public_key; + 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, + &system_id, &oem_ca_serial_number)); +} + +TEST_F(OemDeviceCertTest, ValidCertificateChain) { + std::unique_ptr leaf_public_key; + 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, + &system_id, &oem_ca_serial_number)); + + std::unique_ptr public_key(RsaPublicKey::Create( + test_certificates_.valid_certificate_public_key_der())); + ASSERT_TRUE(public_key); + EXPECT_TRUE(leaf_public_key->MatchesPublicKey(*public_key)); + EXPECT_EQ(2001u, system_id); + EXPECT_EQ("\x1", oem_ca_serial_number); +} + +TEST_F(OemDeviceCertTest, ExpiredCertificateChain) { + std::unique_ptr leaf_public_key; + 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, + &system_id, &oem_ca_serial_number)); +} + +TEST_F(OemDeviceCertTest, OutOfOrderCertificateChain) { + std::unique_ptr leaf_public_key; + 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_F(OemDeviceCertTest, CertificateChainNotSignedByRoot) { + std::unique_ptr leaf_public_key; + 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, + &system_id, &oem_ca_serial_number)); +} + +} // namespace widevine diff --git a/provisioning_sdk/internal/provisioning_engine_impl.cc b/provisioning_sdk/internal/provisioning_engine_impl.cc new file mode 100644 index 0000000..46bb20e --- /dev/null +++ b/provisioning_sdk/internal/provisioning_engine_impl.cc @@ -0,0 +1,464 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/provisioning_engine_impl.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "glog/logging.h" +#include "common/random_util.h" +#include "common/rsa_key.h" +#include "provisioning_sdk/internal/certificates/root_certificates.h" +#include "provisioning_sdk/public/provisioning_status.h" + +#define LOG_WITH_PROTO(message, proto) \ + LOG(WARNING) << (message) << " [proto: " << (proto).ShortDebugString() << "]" + +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; + } + + 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, + const RsaPrivateKey& signing_key, + const SignedDrmDeviceCertificate& signer, + std::string* certificate) { + DCHECK(type == DrmDeviceCertificate::DRM_INTERMEDIATE || + type == DrmDeviceCertificate::DRM_USER_DEVICE); + if (serial_number.empty()) { + LOG(WARNING) << "Require an non-empty serial number."; + return false; + } + + DrmDeviceCertificate 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); + drm_cert.set_serial_number(serial_number); + 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."; + return false; + } + if (!signing_key.GenerateSignature(signed_cert.drm_certificate(), + signed_cert.mutable_signature())) { + LOG(WARNING) << "Failed to generate signature for DrmDeviceCertificate."; + return false; + } + *signed_cert.mutable_signer() = signer; + if (!signed_cert.SerializeToString(certificate)) { + LOG(WARNING) << "Failed to serialize SignedDrmDeviceCertificate to string."; + return false; + } + return true; +} + +// Compares oem serial number, which is a 16-byte big number. +bool IsSerialNumberEq(const std::string& a, const std::string& b) { + // An empty serial number indicates that it does not need to be matched. + if (a.empty() || b.empty()) return true; + int a_index = a.size() - 1; + int b_index = b.size() - 1; + // Matching a and b backwards. + for (; a_index >= 0 && b_index >= 0; --a_index, --b_index) + if (a[a_index] != b[b_index]) return false; + // The remaining characters should be 0. + for (; a_index >= 0; --a_index) + if (a[a_index] != 0) return false; + for (; b_index >= 0; --b_index) + if (b[b_index] != 0) return false; + return true; +} + +} // namespace + +ProvisioningEngineImpl::ProvisioningEngineImpl() + : rsa_key_factory_(new RsaKeyFactory) {} + +ProvisioningEngineImpl::~ProvisioningEngineImpl() {} + +ProvisioningStatus ProvisioningEngineImpl::Initialize( + CertificateType certificate_type, const std::string& drm_service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_phassphrase, + const std::string& provisioning_drm_certificate, + const std::string& provisioning_private_key, + const std::string& provisioning_private_key_phassphrase, + const std::string& secret_spoid_sauce) { + if (!LoadDrmRootPublicKey(certificate_type)) return INVALID_CERTIFICATE_TYPE; + + 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_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_phassphrase); + 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; + } + + if (!VerifyAndExtractCertificate(root_public_key_.get(), + provisioning_drm_certificate, + &signed_provisioning_cert_, &drm_cert)) { + return INVALID_PROVISIONER_DRM_CERTIFICATE; + } + if (drm_cert.type() != DrmDeviceCertificate::ROOT && + drm_cert.type() != DrmDeviceCertificate::PROVISIONER) { + LOG(WARNING) << "Expecting ROOT or PROVISIONER certificate."; + return INVALID_PROVISIONER_DRM_CERTIFICATE; + } + + provisioning_public_key_ = + rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key()); + if (!provisioning_public_key_) return INVALID_PROVISIONER_DRM_CERTIFICATE; + provisioning_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey( + provisioning_private_key, provisioning_private_key_phassphrase); + 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."; + return INVALID_PROVISIONER_PRIVATE_KEY; + } + + if (secret_spoid_sauce.empty()) { + LOG(WARNING) << "SPOID secret sauce is empty!"; + return INVALID_SPOID_SAUCE; + } + secret_spoid_sauce_ = secret_spoid_sauce; + + if (!oem_device_cert_.Initialize(certificate_type)) return INTERNAL_ERROR; + return OK; +} + +ProvisioningStatus ProvisioningEngineImpl::SetCertificateStatusList( + const std::string& certificate_status_list, uint32_t expiration_period_seconds) { + if (certificate_status_list.empty()) { + LOG(WARNING) << "Empty certificate_status_list."; + return INVALID_STATUS_LIST; + } + + SignedCertificateStatusList signed_cert_status_list; + if (!signed_cert_status_list.ParseFromString(certificate_status_list)) { + LOG(WARNING) << "Error parsing SignedCertificateStatusList."; + return INVALID_STATUS_LIST; + } + if (!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); + return INVALID_STATUS_LIST; + } + + DeviceCertificateStatusList cert_status_list; + if (!cert_status_list.ParseFromString( + signed_cert_status_list.certificate_status_list())) { + LOG_WITH_PROTO("Error parsing DeviceCertificateStatusList", + signed_cert_status_list); + return INVALID_STATUS_LIST; + } + + WriterMutexLock writer_lock(&mutex_); + + if (expiration_period_seconds == 0) { + certificate_expiration_seconds_utc_ = std::numeric_limits::max(); + } else { + certificate_expiration_seconds_utc_ = + cert_status_list.creation_time_seconds() + expiration_period_seconds; + } + + certificate_status_map_.clear(); + for (DeviceCertificateStatus& cert_status : + *cert_status_list.mutable_certificate_status()) { + const uint32_t system_id = cert_status.device_info().system_id(); + certificate_status_map_[system_id].Swap(&cert_status); + } + + // Remove items that are no longer valid. + for (auto it = intermediate_certs_info_.begin(); + it != intermediate_certs_info_.end();) { + auto certificate_status_it = certificate_status_map_.find(it->first); + if (certificate_status_it == certificate_status_map_.end()) + intermediate_certs_info_.erase(it++); + else + ++it; + } + // Set / Update device info. + for (const auto& cert_status : certificate_status_map_) { + auto device_info = std::make_shared(); + *device_info = cert_status.second.device_info(); + intermediate_certs_info_[cert_status.first].device_info = device_info; + // intermediate certificate and private key are not changed if exists. + } + return OK; +} + +ProvisioningStatus ProvisioningEngineImpl::GenerateDrmIntermediateCertificate( + uint32_t system_id, const std::string& public_key, std::string* certificate) const { + auto intermediate_public_key = + rsa_key_factory_->CreateFromPkcs1PublicKey(public_key); + if (!intermediate_public_key) return INVALID_INTERMEDIATE_PUBLIC_KEY; + + const size_t kCertificateSerialNumberSize = 16; + std::string serial_number; + if (!RandomBytes(kCertificateSerialNumberSize, &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, + *provisioning_private_key_, + signed_provisioning_cert_, certificate)) { + return INTERNAL_ERROR; + } + return OK; +} + +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)) { + return INVALID_INTERMEDIATE_DRM_CERTIFICATE; + } + if (intermediate_drm_cert.type() != + DrmDeviceCertificate::DRM_INTERMEDIATE) { + LOG_WITH_PROTO("Invalid device certificate type", intermediate_drm_cert); + return INVALID_INTERMEDIATE_DRM_CERTIFICATE; + } + if (!intermediate_drm_cert.has_system_id()) { + LOG_WITH_PROTO("Missing system_id", intermediate_drm_cert); + return UNKNOWN_SYSTEM_ID; + } + + const std::string empty_oem_ca_serial_number; + ProvisioningStatus status = CheckDeviceStatus( + intermediate_drm_cert.system_id(), empty_oem_ca_serial_number); + if (status != OK) return status; + + auto intermediate_public_key = rsa_key_factory_->CreateFromPkcs1PublicKey( + intermediate_drm_cert.public_key()); + if (!intermediate_public_key) return INVALID_INTERMEDIATE_DRM_CERTIFICATE; + std::unique_ptr intermediate_private_key = + rsa_key_factory_->CreateFromPkcs8PrivateKey(cert_private_key, + cert_private_key_passphrase); + if (!intermediate_private_key) return INVALID_INTERMEDIATE_PRIVATE_KEY; + if (!intermediate_public_key->MatchesPrivateKey(*intermediate_private_key)) { + LOG(WARNING) << "Intermediate public key and private key do not match."; + return INVALID_INTERMEDIATE_PRIVATE_KEY; + } + + WriterMutexLock writer_lock(&mutex_); + auto& certificate_info = + intermediate_certs_info_[intermediate_drm_cert.system_id()]; + certificate_info.signed_drm_certificate.Swap(&intermediate_signed_cert); + certificate_info.private_key = std::move(intermediate_private_key); + return OK; +} + +ProvisioningStatus ProvisioningEngineImpl::GenerateDeviceDrmCertificate( + uint32_t system_id, const std::string& oem_ca_serial_number, + const std::string& public_key, const std::string& certificate_serial_number, + std::string* certificate) const { + // |oem_ca_serial_number| could be empty if it is called directly from + // ProvisioningEngine::GenerateDeviceDrmCertificate. + DCHECK(!certificate_serial_number.empty()); + + return GenerateProviderDeviceDrmCertificate( + system_id, oem_ca_serial_number, std::string(), public_key, + certificate_serial_number, certificate); +} + +ProvisioningStatus ProvisioningEngineImpl::GenerateProviderDeviceDrmCertificate( + 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) const { + // |oem_ca_serial_number| could be empty if it is called directly from + // ProvisioningEngine::GenerateDeviceDrmCertificate. + DCHECK(!certificate_serial_number.empty()); + + ProvisioningStatus status = + CheckDeviceStatus(system_id, oem_ca_serial_number); + if (status != OK) return status; + + std::shared_ptr intermediate_private_key; + const SignedDrmDeviceCertificate* intermediate_cert = nullptr; + { + ReaderMutexLock reader_lock(&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; + } + 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)) { + return INTERNAL_ERROR; + } + return OK; +} + +std::shared_ptr ProvisioningEngineImpl::GetDeviceInfo( + uint32_t system_id) const { + ReaderMutexLock reader_lock(&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: " + << system_id; + return std::shared_ptr(); + } + 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; + } + + 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; + } + if (root_cert.type() != DrmDeviceCertificate::ROOT) { + LOG(WARNING) << "Expecting ROOT certificate."; + return false; + } + root_public_key_ = + rsa_key_factory_->CreateFromPkcs1PublicKey(root_cert.public_key()); + CHECK(root_public_key_); + return true; +} + +ProvisioningStatus ProvisioningEngineImpl::CheckDeviceStatus( + uint32_t system_id, const std::string& oem_ca_serial_number) const { + ReaderMutexLock reader_lock(&mutex_); + + if (certificate_expiration_seconds_utc_ < time(nullptr)) + return STATUS_LIST_EXPIRED; + + auto certificate_status_it = certificate_status_map_.find(system_id); + if (certificate_status_it == certificate_status_map_.end()) { + LOG(WARNING) << "Cannot find the system id in device certificate list: " + << system_id; + return UNKNOWN_SYSTEM_ID; + } + if (!IsSerialNumberEq(certificate_status_it->second.oem_serial_number(), + oem_ca_serial_number)) { + LOG(WARNING) << "Provided serial number does not match with stored serial " + "number. It may come from a revoked device. System Id: " + << system_id; + return DEVICE_REVOKED; + } + if (certificate_status_it->second.status() != + DeviceCertificateStatus::VALID) { + LOG(WARNING) << "Device revoked " << system_id; + return DEVICE_REVOKED; + } + return OK; +} + +} // namespace widevine diff --git a/provisioning_sdk/internal/provisioning_engine_impl.h b/provisioning_sdk/internal/provisioning_engine_impl.h new file mode 100644 index 0000000..7c90c78 --- /dev/null +++ b/provisioning_sdk/internal/provisioning_engine_impl.h @@ -0,0 +1,189 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +// ProvisioningEngine internal implementation. This class is thread-safe. + +#ifndef PROVISIONING_SDK_INTERNAL_PROVISIONING_ENGINE_IMPL_H_ +#define PROVISIONING_SDK_INTERNAL_PROVISIONING_ENGINE_IMPL_H_ + +#include +#include +#include +#include +#include + +#include "base/mutex.h" +#include "base/thread_annotations.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/provisioned_device_info.pb.h" +#include "protos/public/signed_device_certificate.pb.h" + +namespace widevine { + +class ProvisioningSession; + +class ProvisioningEngineImpl { + public: + ProvisioningEngineImpl(); + virtual ~ProvisioningEngineImpl(); + + // Initializes the provisioning engine with required credentials. + // * |certificate_type| indicates which type of certificate chains will be + // used for device provisioning via this engine. + // * |drm_service_certificate| is a Google-generated certificate used to + // authenticate the service provider for purposes of user 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|, if any. + // * |provisioning_drm_certificate| is a Google-generated certificate used to + // sign intermediate DRM certificates. + // * |provisioning_private_key| is the encrypted PKCS#8 private RSA key + // corresponding to the provisioning certificate. + // * |provisioning_private_key_passphrase| is the password required to + // decrypt |provisioning_private_key|, if any. + // * |secret_spoid_sauce| is a stable secret used as a factor in the + // 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, + const std::string& service_private_key, + const std::string& service_private_key_passphrase, + const std::string& provisioning_drm_certificate, + const std::string& provisioning_private_key, + const std::string& provisioning_private_key_passphrase, + const std::string& secret_spoid_sauce); + + // Set the certificate status list for this engine. + // * |certificate_status_list| is a certificate status list generated by the + // Widevine Provisioning Service. + // * |expiration_period| is the number of seconds until the + // |certificate_status_list| expires after its creation time + // (creation_time_seconds). Zero means it will never expire. + // * Returns OK on success, or an appropriate error status code otherwise. + ProvisioningStatus SetCertificateStatusList( + 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. + // * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will + // be embedded in the generated certificate. + // * |certificate| will contain the new intermediate certificate, upon + // successful return. + // * Returns OK on success, or an appropriate error status code otherwise. + // NOTE: The generated certificate and associated private key should be stored + // securely to be reused. They should also be propagated to all + // 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; + + // Add an intermediate DRM certificate to the provisioning engine. This is + // usually done once for each supported device type. + // * |intermediate_cert| is the intermediate DRM certificate to be added. + // * |cert_private_key| is a PKCS#8 private key corresponding to + // |intermediate_cert|. + // * |cert_private_key_passphrase| is the passphrase for cert_private_key, + // if any. + // * Returns OK on success, or an appropriate error status code otherwise. + ProvisioningStatus AddDrmIntermediateCertificate( + const std::string& intermediate_cert, const std::string& cert_private_key, + const std::string& cert_private_key_passphrase); + + // Generate a new device DRM certificate to be provisioned. + // * |system_id| is the Widevine system ID for the type of device being + // provisioned. + // * |oem_ca_serial_number| is the oem serial number for the type of device + // being provisioned. ProvisioningEngine uses |system_id| and + // |oem_ca_serial_number| to determine if the device is valid, e.g. whether + // it has been revoked. |oem_ca_serial_number| can be empty if we do not + // care about serial number matches or not. + // * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will + // be embedded in the generated certificate. + // * |certificate_serial_number| is a binary std::string to be used as the + // generated DRM certificate serial number. + // * |certificate| will contain, upon successful return the generated + // certificate. + // * Returns OK on success, or an appropriate error status code otherwise. + // Virtual for mocking. + virtual ProvisioningStatus GenerateDeviceDrmCertificate( + uint32_t system_id, const std::string& oem_ca_serial_number, + const std::string& public_key, const std::string& certificate_serial_number, + std::string* certificate) const; + + // Internal version of the method above. Allows specifying |provider_id|. + virtual ProvisioningStatus GenerateProviderDeviceDrmCertificate( + 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) const; + + // Get the device info for the given |system_id|. + 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(); + } + 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; + + 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. + // * Returns OK on success, or an appropriate error status code otherwise. + ProvisioningStatus CheckDeviceStatus( + uint32_t system_id, const std::string& oem_ca_serial_number) const; + + // 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 provisioning_public_key_; + std::unique_ptr provisioning_private_key_; + std::string secret_spoid_sauce_; + OemDeviceCert oem_device_cert_; + + mutable Mutex mutex_; + // POSIX time, in seconds, when the list would be expired. + uint32_t certificate_expiration_seconds_utc_ GUARDED_BY(mutex_) = 0; + // Maps with system_id as the key. + std::map certificate_status_map_ + GUARDED_BY(mutex_); + struct IntermediateCertificateInfo { + SignedDrmDeviceCertificate signed_drm_certificate; + std::shared_ptr device_info; + std::shared_ptr private_key; + }; + std::map intermediate_certs_info_; +}; + +} // namespace widevine + +#endif // PROVISIONING_SDK_INTERNAL_PROVISIONING_ENGINE_IMPL_H_ diff --git a/provisioning_sdk/internal/provisioning_engine_impl_test.cc b/provisioning_sdk/internal/provisioning_engine_impl_test.cc new file mode 100644 index 0000000..c3856cb --- /dev/null +++ b/provisioning_sdk/internal/provisioning_engine_impl_test.cc @@ -0,0 +1,783 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/provisioning_engine_impl.h" + +#include +#include + +#include "glog/logging.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "common/mock_rsa_key.h" +#include "provisioning_sdk/public/certificate_type.h" + +using ::testing::_; +using ::testing::ByMove; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::SetArgPointee; +using ::testing::StrEq; + +namespace { +const int kSystemId = 100; +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 kOemSerialNumber0[] = "oem_serial_number_0"; +const char kOemSerialNumber1[] = "oem_serial_number_1"; +const char kDevicePublicKey[] = "device_public_key"; +const char kSignature[] = "mock_signature"; +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 kProvisioningDrmCertificate[] = "provisioning_drm_certificate"; +const char kProvisioningPrivateKey[] = "provisioning_private_key"; +const char kProvisioningPrivateKeyPassphrase[] = + "provisioning_private_key_phassphrase"; +const char kProvisioningPublicKey[] = "provisioning_public_key"; +const char kProvisioningSignature[] = "provisioning_signature"; + +// A simple std::string concatenation function. This assumes i within [0, 9]. +std::string StrCat(const std::string& in, int i) { + DCHECK_LE(i, 9); + DCHECK_GE(i, 0); + std::string out = in + "0"; + out[out.size() - 1] += i; + return out; +} + +} // namespace + +namespace widevine { + +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_)); + } + + ProvisioningStatus CheckDeviceStatus(uint32_t system_id, + const std::string& oem_ca_serial_number) { + return engine_impl_.CheckDeviceStatus(system_id, oem_ca_serial_number); + } + + ProvisioningEngineImpl engine_impl_; + MockRsaKeyFactory* mock_rsa_key_factory_ = nullptr; +}; + +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)); +} + +class ProvisioningEngineImplServiceTest : 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); + signed_prov_cert_.set_drm_certificate(prov_cert_.SerializeAsString()); + signed_prov_cert_.set_signature(kProvisioningSignature); + } + + 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"); + } + + DrmDeviceCertificate prov_cert_; + SignedDrmDeviceCertificate signed_prov_cert_; + MockRsaPublicKey* mock_service_public_key_ = nullptr; + MockRsaPrivateKey* mock_service_private_key_ = nullptr; +}; + +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); + 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)); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kProvisioningPublicKey)) + .WillOnce(Return(ByMove(nullptr))); + EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE, + Initialize(signed_prov_cert_.SerializeAsString())); +} + +TEST_F(ProvisioningEngineImplProvTest, InvalidPrivateKey) { + EXPECT_CALL(*mock_root_public_key_, + VerifySignature(_, kProvisioningSignature)) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kProvisioningPublicKey)) + .WillOnce( + Return(ByMove(std::unique_ptr(new MockRsaPublicKey)))); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs8PrivateKey(kProvisioningPrivateKey, + kProvisioningPrivateKeyPassphrase)) + .WillOnce(Return(ByMove(nullptr))); + EXPECT_EQ(INVALID_PROVISIONER_PRIVATE_KEY, + Initialize(signed_prov_cert_.SerializeAsString())); +} + +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)) + .WillOnce( + Return(ByMove(std::unique_ptr(mock_rsa_public_key)))); + MockRsaPrivateKey* mock_rsa_private_key = new MockRsaPrivateKey; + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs8PrivateKey(kProvisioningPrivateKey, + kProvisioningPrivateKeyPassphrase)) + .WillOnce( + Return(ByMove(std::unique_ptr(mock_rsa_private_key)))); + EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_)) + .WillOnce(Return(false)); + EXPECT_EQ(INVALID_PROVISIONER_PRIVATE_KEY, + Initialize(signed_prov_cert_.SerializeAsString())); +} + +class ProvisioningEngineImplGeneralTest + : public ProvisioningEngineImplProvTest { + protected: + void SetUp() override { + ProvisioningEngineImplProvTest::SetUp(); + + // Provisioning certificate expectations. + EXPECT_CALL(*mock_root_public_key_, + VerifySignature(_, kProvisioningSignature)) + .WillOnce(Return(true)); + 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( + 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. + cert_status_list_.set_creation_time_seconds(time(nullptr)); + for (int i = 0; i < 2; ++i) { + DeviceCertificateStatus* cert_status = + cert_status_list_.add_certificate_status(); + cert_status->set_oem_serial_number(StrCat("oem_serial_number_", i)); + ProvisionedDeviceInfo* device_info = cert_status->mutable_device_info(); + device_info->set_system_id(kSystemId + i); + device_info->set_model(StrCat("model_", i)); + } + cert_status_list_.mutable_certificate_status(0)->set_status( + DeviceCertificateStatus::VALID); + cert_status_list_.mutable_certificate_status(1)->set_status( + DeviceCertificateStatus::REVOKED); + + SignedCertificateStatusList signed_cert_status_list; + signed_cert_status_list.set_certificate_status_list( + cert_status_list_.SerializeAsString()); + EXPECT_CALL(*mock_root_public_key_, + VerifySignature( + StrEq(signed_cert_status_list.certificate_status_list()), + "cert_status_list_signature")) + .WillOnce(Return(true)); + 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); + } + + MockRsaPublicKey* mock_prov_public_key_ = nullptr; + MockRsaPrivateKey* mock_prov_private_key_ = nullptr; + DeviceCertificateStatusList cert_status_list_; + DrmDeviceCertificate intermediate_cert_; + SignedDrmDeviceCertificate signed_intermediate_cert_; +}; + +TEST_F(ProvisioningEngineImplGeneralTest, InvalidCertificateStatusList) { + EXPECT_EQ(INVALID_STATUS_LIST, engine_impl_.SetCertificateStatusList( + "", kExpirationPeriodSeconds)); + EXPECT_EQ(INVALID_STATUS_LIST, + engine_impl_.SetCertificateStatusList( + "invalid_certificate_status_list", kExpirationPeriodSeconds)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + CertificateStatusListIncorrectSignature) { + SignedCertificateStatusList signed_cert_status_list; + signed_cert_status_list.set_certificate_status_list( + cert_status_list_.SerializeAsString()); + EXPECT_CALL(*mock_root_public_key_, + VerifySignature(_, "cert_status_list_signature")) + .WillOnce(Return(false)); + signed_cert_status_list.set_signature("cert_status_list_signature"); + ASSERT_EQ(INVALID_STATUS_LIST, + engine_impl_.SetCertificateStatusList( + signed_cert_status_list.SerializeAsString(), + kExpirationPeriodSeconds)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, GetDeviceInfoAndCheckDeviceStatus) { + EXPECT_EQ(OK, CheckDeviceStatus(kSystemId, kEmptyOemSerialNumber)); + auto device_info = engine_impl_.GetDeviceInfo(kSystemId); + ASSERT_NE(nullptr, device_info); + EXPECT_EQ("model_0", device_info->model()); + + EXPECT_EQ(DEVICE_REVOKED, + CheckDeviceStatus(kSystemId + 1, kEmptyOemSerialNumber)); + // We can still query device info for revoked device. + device_info = engine_impl_.GetDeviceInfo(kSystemId + 1); + ASSERT_NE(nullptr, device_info); + EXPECT_EQ("model_1", device_info->model()); + + EXPECT_EQ(UNKNOWN_SYSTEM_ID, + CheckDeviceStatus(kSystemId + 2, kEmptyOemSerialNumber)); + EXPECT_EQ(nullptr, engine_impl_.GetDeviceInfo(kSystemId + 2)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, UpdateCertificateStatusList) { + cert_status_list_.mutable_certificate_status(0)->set_status( + DeviceCertificateStatus::REVOKED); + + DeviceCertificateStatus* cert_status = + cert_status_list_.add_certificate_status(); + ProvisionedDeviceInfo* device_info = cert_status->mutable_device_info(); + device_info->set_system_id(kSystemId + 2); + device_info->set_model("model_2"); + + SignedCertificateStatusList signed_cert_status_list; + signed_cert_status_list.set_certificate_status_list( + cert_status_list_.SerializeAsString()); + EXPECT_CALL(*mock_root_public_key_, + VerifySignature(_, "cert_status_list_signature")) + .WillOnce(Return(true)); + signed_cert_status_list.set_signature("cert_status_list_signature"); + ASSERT_EQ(OK, engine_impl_.SetCertificateStatusList( + signed_cert_status_list.SerializeAsString(), + kInfiniteExpirationPeriodSeconds)); + + // Now device with system id = kSystemId is revoked. + EXPECT_EQ(DEVICE_REVOKED, + CheckDeviceStatus(kSystemId, kEmptyOemSerialNumber)); + EXPECT_EQ("model_0", engine_impl_.GetDeviceInfo(kSystemId)->model()); + EXPECT_EQ(DEVICE_REVOKED, + CheckDeviceStatus(kSystemId + 1, kEmptyOemSerialNumber)); + EXPECT_EQ("model_1", engine_impl_.GetDeviceInfo(kSystemId + 1)->model()); + EXPECT_EQ(OK, CheckDeviceStatus(kSystemId + 2, kEmptyOemSerialNumber)); + EXPECT_EQ("model_2", engine_impl_.GetDeviceInfo(kSystemId + 2)->model()); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + GenerateDrmIntermediateCertificateInvalidPublicKey) { + std::string drm_certificate; + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kIntermediatePublicKey)) + .WillOnce(Return(ByMove(nullptr))); + std::string certificate; + ASSERT_EQ(INVALID_INTERMEDIATE_PUBLIC_KEY, + engine_impl_.GenerateDrmIntermediateCertificate( + kSystemId, kIntermediatePublicKey, &certificate)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, GenerateDrmIntermediateCertificate) { + std::string drm_certificate; + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kIntermediatePublicKey)) + .WillOnce( + Return(ByMove(std::unique_ptr(new MockRsaPublicKey)))); + EXPECT_CALL(*mock_prov_private_key_, GenerateSignature(_, _)) + .WillOnce(DoAll(SaveArg<0>(&drm_certificate), + SetArgPointee<1>(kSignature), Return(true))); + std::string certificate; + ASSERT_EQ(OK, engine_impl_.GenerateDrmIntermediateCertificate( + kSystemId, kIntermediatePublicKey, &certificate)); + + SignedDrmDeviceCertificate 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; + ASSERT_TRUE(drm_cert_proto.ParseFromString(drm_certificate)); + EXPECT_EQ(DrmDeviceCertificate::DRM_INTERMEDIATE, 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()); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + AddDrmIntermediateCertificateInvalidCert) { + EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE, + engine_impl_.AddDrmIntermediateCertificate( + "", kIntermediatePrivateKey, + kIntermediatePrivateKeyPassphrase)); + EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE, + engine_impl_.AddDrmIntermediateCertificate( + "invalid_intermediate_cert", kIntermediatePrivateKey, + kIntermediatePrivateKeyPassphrase)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + AddDrmIntermediateCertificateIncorrectCertType) { + intermediate_cert_.set_type(DrmDeviceCertificate::DRM_USER_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)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + AddDrmIntermediateCertificateMissingSystemId) { + intermediate_cert_.clear_system_id(); + 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)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + AddDrmIntermediateCertificateRevokedCert) { + 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)); +} + +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)); + 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)); +} + +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)); + + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kIntermediatePublicKey)) + .WillOnce( + Return(ByMove(std::unique_ptr(new MockRsaPublicKey)))); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs8PrivateKey(kIntermediatePrivateKey, + kIntermediatePrivateKeyPassphrase)) + .WillOnce(Return(ByMove(nullptr))); + + 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)); + + MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey; + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kIntermediatePublicKey)) + .WillOnce(Return( + ByMove(std::unique_ptr(mock_intermediate_public_key)))); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs8PrivateKey(kIntermediatePrivateKey, + kIntermediatePrivateKeyPassphrase)) + .WillOnce(Return( + ByMove(std::unique_ptr(new MockRsaPrivateKey)))); + 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)); +} + +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)); + + MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey; + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kIntermediatePublicKey)) + .WillOnce(Return( + ByMove(std::unique_ptr(mock_intermediate_public_key)))); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs8PrivateKey(kIntermediatePrivateKey, + kIntermediatePrivateKeyPassphrase)) + .WillOnce(Return( + ByMove(std::unique_ptr(new MockRsaPrivateKey)))); + EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_)) + .WillOnce(Return(true)); + + 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; + signed_cert_status_list.set_certificate_status_list( + cert_status_list_.SerializeAsString()); + EXPECT_CALL(*mock_root_public_key_, + VerifySignature(_, "cert_status_list_signature")) + .WillOnce(Return(true)); + signed_cert_status_list.set_signature("cert_status_list_signature"); + ASSERT_EQ(OK, engine_impl_.SetCertificateStatusList( + 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)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + GenerateDeviceDrmCertificateRevokedDevice) { + std::string certificate; + EXPECT_EQ(DEVICE_REVOKED, + engine_impl_.GenerateDeviceDrmCertificate( + kSystemId + 1, kOemSerialNumber1, kDevicePublicKey, + kCertificateSerialNumber, &certificate)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + GenerateDeviceDrmCertificateWithMismatchingOemSerialNumber) { + 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)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + GenerateDeviceDrmCertificateWithoutIntermediateCert) { + std::string certificate; + EXPECT_EQ(MISSING_DRM_INTERMEDIATE_CERT, + engine_impl_.GenerateDeviceDrmCertificate( + kSystemId, kOemSerialNumber0, kDevicePublicKey, + kCertificateSerialNumber, &certificate)); +} + +TEST_F(ProvisioningEngineImplGeneralTest, + GenerateDeviceDrmCertificate) { + // 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_, + CreateFromPkcs1PublicKey(kIntermediatePublicKey)) + .WillOnce(Return( + ByMove(std::unique_ptr(mock_intermediate_public_key)))); + MockRsaPrivateKey* mock_intermediate_private_key = new MockRsaPrivateKey; + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs8PrivateKey(kIntermediatePrivateKey, + kIntermediatePrivateKeyPassphrase)) + .WillOnce(Return(ByMove( + std::unique_ptr(mock_intermediate_private_key)))); + EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_)) + .WillOnce(Return(true)); + + EXPECT_EQ(OK, engine_impl_.AddDrmIntermediateCertificate( + signed_intermediate_cert_.SerializeAsString(), + kIntermediatePrivateKey, + kIntermediatePrivateKeyPassphrase)); + + // Intermediate private key expectation. + std::string drm_certificate; + EXPECT_CALL(*mock_intermediate_private_key, GenerateSignature(_, _)) + .WillOnce(DoAll(SaveArg<0>(&drm_certificate), + SetArgPointee<1>(kSignature), Return(true))); + std::string certificate; + EXPECT_EQ(OK, engine_impl_.GenerateDeviceDrmCertificate( + kSystemId, kOemSerialNumber0, kDevicePublicKey, + kCertificateSerialNumber, &certificate)); + + SignedDrmDeviceCertificate 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; + 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(kSystemId, drm_cert_proto.system_id()); + EXPECT_EQ(kDevicePublicKey, drm_cert_proto.public_key()); +} + +} // namespace widevine diff --git a/provisioning_sdk/internal/provisioning_session_impl.cc b/provisioning_sdk/internal/provisioning_session_impl.cc new file mode 100644 index 0000000..5d06126 --- /dev/null +++ b/provisioning_sdk/internal/provisioning_session_impl.cc @@ -0,0 +1,256 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/provisioning_session_impl.h" + +#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(provisioning_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_provisioning_log_every_n) \ + << (message) << " [proto: " << (proto).ShortDebugString() << "]" + +namespace widevine { + +ProvisioningSessionImpl::ProvisioningSessionImpl( + const ProvisioningEngineImpl& engine, const OemDeviceCert& oem_device_cert, + const RsaPrivateKey& service_private_key) + : 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) { + auto rsa_public_key = + rsa_key_factory_->CreateFromPkcs1PublicKey(device_public_key); + if (!rsa_public_key) return INVALID_DEVICE_PUBLIC_KEY; + auto rsa_private_key = + rsa_key_factory_->CreateFromPkcs1PrivateKey(device_private_key); + if (!rsa_private_key) return INVALID_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; + } + + device_public_key_ = device_public_key; + device_private_key_ = device_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; + } + 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_provisioning_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; +} + +} // namespace widevine diff --git a/provisioning_sdk/internal/provisioning_session_impl.h b/provisioning_sdk/internal/provisioning_session_impl.h new file mode 100644 index 0000000..75214ca --- /dev/null +++ b/provisioning_sdk/internal/provisioning_session_impl.h @@ -0,0 +1,88 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +// ProvisioningSession internal implementation. + +#ifndef PROVISIONING_SDK_INTERNAL_PROVISIONING_SESSION_IMPL_H_ +#define PROVISIONING_SDK_INTERNAL_PROVISIONING_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_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" + +namespace widevine { + +class ProvisioningSessionImpl { + public: + ProvisioningSessionImpl(const ProvisioningEngineImpl& engine, + const OemDeviceCert& oem_device_cert, + const RsaPrivateKey& service_private_key); + ~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); + + // 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); + + // * Returns a ProvisioneddeviceInfo message containing information about the + // type of device being provisioned. May return nullptr. + const ProvisionedDeviceInfo* GetDeviceInfo() const { + return device_info_.get(); + } + + private: + friend class ProvisioningSessionImplTest; + + 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_; +}; + +} // namespace widevine + +#endif // PROVISIONING_SDK_INTERNAL_PROVISIONING_SESSION_IMPL_H_ diff --git a/provisioning_sdk/internal/provisioning_session_impl_test.cc b/provisioning_sdk/internal/provisioning_session_impl_test.cc new file mode 100644 index 0000000..aa921f5 --- /dev/null +++ b/provisioning_sdk/internal/provisioning_session_impl_test.cc @@ -0,0 +1,426 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/internal/provisioning_session_impl.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.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::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 ProvisioningSessionImplTest : public ::testing::Test { + protected: + ProvisioningSessionImplTest() + : 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_)); + } + + ProvisioningSessionImpl 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) { + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kDevicePublicKey)) + .WillOnce(Return(ByMove(nullptr))); + EXPECT_EQ( + INVALID_DEVICE_PUBLIC_KEY, + session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey)); +} + +TEST_F(ProvisioningSessionImplTest, InitializeWithInvalidPrivateKey) { + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PublicKey(kDevicePublicKey)) + .WillOnce( + Return(ByMove(std::unique_ptr(new MockRsaPublicKey)))); + EXPECT_CALL(*mock_rsa_key_factory_, + CreateFromPkcs1PrivateKey(kDevicePrivateKey)) + .WillOnce(Return(ByMove(nullptr))); + EXPECT_EQ( + INVALID_DEVICE_PRIVATE_KEY, + session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey)); +} + +TEST_F(ProvisioningSessionImplTest, InitializeWithMismatchPublicPrivateKey) { + 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_, + CreateFromPkcs1PrivateKey(kDevicePrivateKey)) + .WillOnce(Return( + ByMove(std::unique_ptr(new MockRsaPrivateKey)))); + EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_)) + .WillOnce(Return(false)); + EXPECT_EQ( + INVALID_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_, + CreateFromPkcs1PrivateKey(kDevicePrivateKey)) + .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 new file mode 100644 index 0000000..7c57daa --- /dev/null +++ b/provisioning_sdk/public/BUILD @@ -0,0 +1,84 @@ +################################################################################ +# 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. +################################################################################ + +# Build file for provisioning 3.0 SDK. + +package( + default_visibility = ["//visibility:public"], +) + +# Defines the common copts for cc_library targets. The main option here is +# -fvisibility=default, which exports symbols from the public APIs in the shared +# library. +# Note that the shared library should be built with -fvisibility=hidden. +PUBLIC_COPTS = ["-fvisibility=default"] + +cc_binary( + name = "libprovisioning_sdk.so", + linkshared = 1, + deps = [":provisioning_engine"], +) + +cc_library( + name = "libprovisioning_sdk", + srcs = [":libprovisioning_sdk.so"], + hdrs = glob(["*.h"]), +) + +cc_library( + name = "provisioning_engine", + srcs = ["provisioning_engine.cc"], + hdrs = ["provisioning_engine.h"], + copts = PUBLIC_COPTS, + deps = [ + ":certificate_type", + ":provisioning_session", + ":provisioning_status", + "//base", + "//common:rsa_key", + "//provisioning_sdk/internal:provisioning_engine_impl", + "//provisioning_sdk/internal:provisioning_session_impl", + ], +) + +cc_test( + name = "provisioning_engine_test", + size = "small", + srcs = ["provisioning_engine_test.cc"], + deps = [ + ":provisioning_engine", + "//external:gtest_main", + ], +) + +cc_library( + name = "provisioning_session", + srcs = ["provisioning_session.cc"], + hdrs = ["provisioning_session.h"], + copts = PUBLIC_COPTS, + deps = [ + ":provisioning_status", + "//base", + "//provisioning_sdk/internal:provisioning_session_impl", + "//protos/public:device_certificate_proto", + ], +) + +cc_library( + name = "provisioning_status", + srcs = ["provisioning_status.cc"], + hdrs = ["provisioning_status.h"], + copts = PUBLIC_COPTS, + deps = ["//base"], +) + +cc_library( + name = "certificate_type", + hdrs = ["certificate_type.h"], + copts = PUBLIC_COPTS, +) diff --git a/provisioning_sdk/public/certificate_type.h b/provisioning_sdk/public/certificate_type.h new file mode 100644 index 0000000..f4640d7 --- /dev/null +++ b/provisioning_sdk/public/certificate_type.h @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_ +#define PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_ + +namespace widevine { + +enum CertificateType { + kCertTesting = 0, + kCertDevelopment, + kCertProduction, +}; + +} // namespace widevine + +#endif // PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_ diff --git a/provisioning_sdk/public/provisioning_engine.cc b/provisioning_sdk/public/provisioning_engine.cc new file mode 100644 index 0000000..afb3431 --- /dev/null +++ b/provisioning_sdk/public/provisioning_engine.cc @@ -0,0 +1,118 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/public/provisioning_engine.h" + +#include + +#include "glog/logging.h" +#include "common/rsa_key.h" +#include "provisioning_sdk/internal/provisioning_engine_impl.h" +#include "provisioning_sdk/internal/provisioning_session_impl.h" +#include "provisioning_sdk/public/provisioning_session.h" + +namespace widevine { + +ProvisioningEngine::ProvisioningEngine() {} + +ProvisioningEngine::~ProvisioningEngine() {} + +ProvisioningStatus ProvisioningEngine::Initialize( + 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, + const std::string& provisioning_private_key, + const std::string& provisioning_private_key_passphrase, + const std::string& secret_spoid_sauce) { + if (impl_) { + LOG(WARNING) << "ProvisioningEngine is already initialized."; + return INTERNAL_ERROR; + } + + 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); + if (status != OK) return status; + impl_ = std::move(impl); + return OK; +} + +ProvisioningStatus ProvisioningEngine::SetCertificateStatusList( + const std::string& certificate_status_list, uint32_t expiration_period_seconds) { + if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED; + return impl_->SetCertificateStatusList(certificate_status_list, + expiration_period_seconds); +} + +ProvisioningStatus ProvisioningEngine::GenerateDrmIntermediateCertificate( + uint32_t system_id, const std::string& public_key, std::string* certificate) const { + if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED; + if (!certificate) { + LOG(WARNING) << "|certificate| should not be a nullptr."; + return INTERNAL_ERROR; + } + + return impl_->GenerateDrmIntermediateCertificate(system_id, public_key, + certificate); +} + +ProvisioningStatus ProvisioningEngine::AddDrmIntermediateCertificate( + const std::string& intermediate_cert, const std::string& cert_private_key, + const std::string& cert_private_key_passphrase) { + if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED; + return impl_->AddDrmIntermediateCertificate( + intermediate_cert, cert_private_key, cert_private_key_passphrase); +} + +ProvisioningStatus ProvisioningEngine::NewProvisioningSession( + const std::string& device_public_key, const std::string& device_private_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; + } + + 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); + if (status != OK) return status; + new_session->reset(new ProvisioningSession(std::move(session_impl))); + return OK; +} + +ProvisioningStatus ProvisioningEngine::GenerateDeviceDrmCertificate( + uint32_t system_id, const std::string& public_key, const std::string& serial_number, + std::string* certificate) const { + if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED; + if (!certificate) { + LOG(WARNING) << "|certificate| should not be a nullptr."; + return INTERNAL_ERROR; + } + + // Validate public key. This validation is done here instead of inside + // |impl_->GenerateDeviceDrmCertificate| is because the latter is an internal + // 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 (serial_number.empty()) return INVALID_SERIAL_NUMBER; + + const std::string empty_oem_ca_serial_number; + return impl_->GenerateDeviceDrmCertificate( + system_id, empty_oem_ca_serial_number, public_key, serial_number, + certificate); +} + +} // namespace widevine diff --git a/provisioning_sdk/public/provisioning_engine.h b/provisioning_sdk/public/provisioning_engine.h new file mode 100644 index 0000000..c6c88b2 --- /dev/null +++ b/provisioning_sdk/public/provisioning_engine.h @@ -0,0 +1,153 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 PROVISIONING_SDK_PUBLIC_PROVISIONING_ENGINE_H_ +#define PROVISIONING_SDK_PUBLIC_PROVISIONING_ENGINE_H_ + +#include +#include +#include + +#include "provisioning_sdk/public/certificate_type.h" +#include "provisioning_sdk/public/provisioning_status.h" + +namespace widevine { + +class ProvisioningEngineImpl; +class ProvisioningSession; + +// Class which is used to implement a Widevine DRM device provisioning engine. +// There should be only one instance of ProvisioningEngine. The engine should +// be "Initialized" before being used. ProvisioningEngine::Initialize is the +// only method that is not thread-safe. After initializing the engine, it can +// be safely used in different threads. +class ProvisioningEngine { + public: + ProvisioningEngine(); + ~ProvisioningEngine(); + + // Initializes the provisioning engine with required credentials. + // * |certificate_type| indicates which type of certificate chains will be + // used for device provisioning via this engine. + // * |service_drm_certificate| is a Google-generated certificate used to + // authenticate the service provider for purposes of user 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|, if any. + // * |provisioning_drm_certificate| is a Google-generated certificate used to + // sign intermediate DRM certificates. + // * |provisioning_private_key| is the encrypted PKCS#8 private RSA key + // corresponding to the provisioning certificate. + // * |provisioning_private_key_passphrase| is the password required to + // decrypt |provisioning_private_key|, if any. + // * |secret_spoid_sauce| is a stable secret used as a factor in the + // 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, + const std::string& service_private_key, + const std::string& service_private_key_passphrase, + const std::string& provisioning_drm_certificate, + const std::string& provisioning_private_key, + const std::string& provisioning_private_key_passphrase, + const std::string& secret_spoid_sauce); + + // Set the certificate status list for this engine. + // * |certificate_status_list| is a certificate status list generated by the + // Widevine Provisioning Service. + // * |expiration_period| is the number of seconds until the + // |certificate_status_list| expires after its creation time + // (creation_time_seconds). Zero means it will never expire. + // * Returns OK on success, or an appropriate error status code otherwise. + ProvisioningStatus SetCertificateStatusList( + 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. + // * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will + // be embedded in the generated certificate. + // * |certificate| will contain the new intermediate certificate, upon + // successful return. + // * Returns OK on success, or an appropriate error status code otherwise. + // NOTE: The generated certificate and associated private key should be stored + // securely to be reused. They should also be propagated to all + // 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; + + // Add an intermediate DRM certificate to the provisioning engine. This is + // usually done once for each supported device type. + // * |intermediate_cert| is the intermediate DRM certificate to be added. + // * |cert_private_key| is a PKCS#8 private key corresponding to + // |intermediate_cert|. + // * |cert_private_key_passphrase| is the passphrase for cert_private_key, + // if any. + // * Returns OK on success, or an appropriate error status code otherwise. + ProvisioningStatus AddDrmIntermediateCertificate( + 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. + // * |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. + // * |device_private_key| is a DER-encoded PKCS#8 PrivateKeyInfo message + // which contains the private key matching |device_public_key|. + // * |new_session| will point, on successful return, to the newly created + // ProvisioningSession. + // * Returns OK if successful, or an appropriate error status code otherwise. + // The key pairs can be re-used if the created session failed to process the + // message. + // NOTE: All ProvisioningSession objects must be deleted before the + // ProvisioningEngine which created them. + ProvisioningStatus NewProvisioningSession( + const std::string& device_public_key, + const std::string& device_private_key, + std::unique_ptr* new_session) const; + + // Generate a new device DRM certificate to be provisioned by means other than + // the Widevine provisioning protocol. + // NOTE: This API should only be used to provision devices which were + // manufactured without Widevine DRM support. It is meant to be used as + // an exception, and not the norm. Most devices should be provisioned + // by means of a ProvisioningSession. + // * |system_id| is the Widevine system ID for the type of device being + // provisioned. + // * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will + // be embedded in the generated certificate. + // * |serial_number| is a binary std::string to be used as the generated DRM + // certificate serial number. + // * |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; + + private: +#ifndef SWIGPYTHON + ProvisioningEngine(const ProvisioningEngine&) = delete; + ProvisioningEngine& operator=(const ProvisioningEngine&) = delete; +#endif + + std::unique_ptr impl_; +}; + +} // namespace widevine + +#endif // PROVISIONING_SDK_PUBLIC_PROVISIONING_ENGINE_H_ diff --git a/provisioning_sdk/public/provisioning_engine_test.cc b/provisioning_sdk/public/provisioning_engine_test.cc new file mode 100644 index 0000000..1a5a236 --- /dev/null +++ b/provisioning_sdk/public/provisioning_engine_test.cc @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/public/provisioning_engine.h" + +#include "gtest/gtest.h" + +namespace { +const int kSystemId = 100; +const int kExpirationPeriodSeconds = 3600; +} // namespace + +namespace widevine { + +class ProvisioningEngineTest : public ::testing::Test { + protected: + ProvisioningEngine engine_; +}; + +TEST_F(ProvisioningEngineTest, SetCertificateStatusListWithoutInit) { + EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED, + engine_.SetCertificateStatusList("certificate_status_list", + kExpirationPeriodSeconds)); +} + +TEST_F(ProvisioningEngineTest, GenerateDrmIntermediateCertificateWithoutInit) { + std::string certificate; + EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED, + engine_.GenerateDrmIntermediateCertificate( + kSystemId, "intermediate_public_key", &certificate)); +} + +TEST_F(ProvisioningEngineTest, AddDrmIntermediateCertificateWithoutInit) { + std::string certificate; + EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED, + engine_.AddDrmIntermediateCertificate( + "intermediate_certificate", "intermediate_private_key", + "intermediate_private_key_passphrase")); +} + +TEST_F(ProvisioningEngineTest, GenerateDeviceDrmCertificateWithoutInit) { + std::string certificate; + EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED, + engine_.GenerateDeviceDrmCertificate(kSystemId, "device_public_key", + "device_serial_number", + &certificate)); +} + +// ProvisioningEngine is basically a wrapper of ProvisioningEngineImpl, so there +// is no need to test the functionalities as ProvisioningEngineImpl is already +// tested. + +} // namespace widevine diff --git a/provisioning_sdk/public/provisioning_session.cc b/provisioning_sdk/public/provisioning_session.cc new file mode 100644 index 0000000..fc41db9 --- /dev/null +++ b/provisioning_sdk/public/provisioning_session.cc @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/public/provisioning_session.h" + +#include + +#include "glog/logging.h" +#include "provisioning_sdk/internal/provisioning_session_impl.h" +#include "protos/public/device_certificate.pb.h" + +namespace widevine { + +ProvisioningSession::~ProvisioningSession() {} + +ProvisioningStatus ProvisioningSession::ProcessMessage(const std::string& message, + std::string* response, + bool* done) { + if (!response) { + LOG(WARNING) << "|response| should not be a nullptr."; + return INTERNAL_ERROR; + } + if (!done) { + LOG(WARNING) << "|done| should not be a nullptr."; + return INTERNAL_ERROR; + } + + ProvisioningStatus status = impl_->ProcessMessage(message, response); + *done = true; + return status; +} + +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 new file mode 100644 index 0000000..c83455d --- /dev/null +++ b/provisioning_sdk/public/provisioning_session.h @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 PROVISIONING_SDK_PUBLIC_PROVISIONING_SESSION_H_ +#define PROVISIONING_SDK_PUBLIC_PROVISIONING_SESSION_H_ + +#include +#include + +#include "provisioning_sdk/public/provisioning_status.h" + +namespace widevine { + +class ProvisionedDeviceInfo; +class ProvisioningSessionImpl; + +// Class which is used to implement the provisioning session state machine. +class ProvisioningSession { + public: + ~ProvisioningSession(); + + // 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, and the ProvisioningSession can be deleted. + // Returns OK if successful, or an appropriate error status code otherwise. + ProvisioningStatus ProcessMessage(const std::string& message, + std::string* response, + bool* done); + + // * Returns a ProvisioneddeviceInfo message containing information about the + // type of device being provisioned. May return nullptr. + const ProvisionedDeviceInfo* GetDeviceInfo() const; + + private: +#ifndef SWIGPYTHON + friend class ProvisioningEngine; + + ProvisioningSession(const ProvisioningSession&) = delete; + ProvisioningSession& operator=(const ProvisioningSession&) = delete; +#endif + explicit ProvisioningSession(std::unique_ptr impl); + + std::unique_ptr impl_; +}; + +} // namespace widevine + +#endif // PROVISIONING_SDK_PUBLIC_PROVISIONING_SESSION_H_ diff --git a/provisioning_sdk/public/provisioning_status.cc b/provisioning_sdk/public/provisioning_status.cc new file mode 100644 index 0000000..9668531 --- /dev/null +++ b/provisioning_sdk/public/provisioning_status.cc @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 "provisioning_sdk/public/provisioning_status.h" + +#include "base/macros.h" + +namespace widevine { + +static const char* kProvisioningStatusMessage[] = { + "OK", + "Invalid certificate type", + "Provisioning engine uninitialized", + "Invalid service drm certificate", + "Invalid service private key", + "Invalid provisioning drm certificate", + "Invalid provisioning private key", + "Invalid intermediate drm certificate", + "Invalid intermediate public key", + "Invalid intermediate private key", + "Invalid status list", + "Status list expired", + "Unknown system id", + "Invalid device public key", + "Invalid device private key", + "Invalid request message", + "Invalid MAC", + "Missing DRM intermediate certificate", + "DRM device certificate not set", + "Device revoked", + "Invalid serial number", + "Internal error", + "Invalid SPOID secret sauce" +}; + +const char* GetProvisioningStatusMessage(ProvisioningStatus status) { + static_assert( + arraysize(kProvisioningStatusMessage) == NUM_PROVISIONING_STATUS, + "mismatching provisioning status message and provisioning status."); + return kProvisioningStatusMessage[status]; +} + +} // namespace widevine diff --git a/provisioning_sdk/public/provisioning_status.h b/provisioning_sdk/public/provisioning_status.h new file mode 100644 index 0000000..158f418 --- /dev/null +++ b/provisioning_sdk/public/provisioning_status.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_ +#define PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_ + +namespace widevine { + +enum ProvisioningStatus { + OK = 0, + 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, + INVALID_INTERMEDIATE_DRM_CERTIFICATE = 7, + INVALID_INTERMEDIATE_PUBLIC_KEY = 8, + // Invalid intermediate private key or private key passphrase. + INVALID_INTERMEDIATE_PRIVATE_KEY = 9, + INVALID_STATUS_LIST = 10, + STATUS_LIST_EXPIRED = 11, + UNKNOWN_SYSTEM_ID = 12, + INVALID_DEVICE_PUBLIC_KEY = 13, + INVALID_DEVICE_PRIVATE_KEY = 14, + INVALID_REQUEST_MESSAGE = 15, + INVALID_MAC = 16, + MISSING_DRM_INTERMEDIATE_CERT = 17, + DRM_DEVICE_CERTIFICATE_NOT_SET = 18, + DEVICE_REVOKED = 19, + INVALID_SERIAL_NUMBER = 20, + INTERNAL_ERROR = 21, + INVALID_SPOID_SAUCE = 22, + NUM_PROVISIONING_STATUS, +}; + +// Returns the message std::string for the given ProvisioningStatus. +const char* GetProvisioningStatusMessage(ProvisioningStatus status); + +} // namespace widevine + +#endif // PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_ diff --git a/provisioning_sdk/public/python/BUILD b/provisioning_sdk/public/python/BUILD new file mode 100644 index 0000000..0db32cf --- /dev/null +++ b/provisioning_sdk/public/python/BUILD @@ -0,0 +1,73 @@ +################################################################################ +# 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. +################################################################################ + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "test_data_utility", + srcs = ["test_data_utility.py"], + data = ["//example:example_data"], + deps = [ + "//protos/public:certificate_provisioning_py_pb2", + ], +) + +py_library( + name = "crypto_utility", + srcs = ["crypto_utility.py"], +) + +py_test( + name = "init_engine_test", + size = "small", + srcs = ["init_engine_test.py"], + deps = [ + ":test_data_utility", + ], +) + +py_test( + name = "set_certificate_status_list_test", + size = "small", + srcs = ["set_certificate_status_list_test.py"], + deps = [ + ":test_data_utility", + ], +) + +py_test( + name = "drm_intermediate_certificate_test", + size = "small", + srcs = ["drm_intermediate_certificate_test.py"], + deps = [ + ":test_data_utility", + ], +) + +py_test( + name = "engine_generate_certificate_test", + size = "small", + srcs = ["engine_generate_certificate_test.py"], + deps = [ + ":crypto_utility", + ":test_data_utility", + "//protos/public:signed_device_certificate_py_pb2", + ], +) + +py_test( + name = "new_session_test", + size = "small", + srcs = ["new_session_test.py"], + deps = [ + ":crypto_utility", + ":test_data_utility", + "//protos/public:certificate_provisioning_py_pb2", + "//protos/public:signed_device_certificate_py_pb2", + ], +) diff --git a/provisioning_sdk/public/python/base.i b/provisioning_sdk/public/python/base.i new file mode 100644 index 0000000..db4d783 --- /dev/null +++ b/provisioning_sdk/public/python/base.i @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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); +COPY_TYPEMAPS(unsigned int, uint32); diff --git a/provisioning_sdk/public/python/certificate_type.i b/provisioning_sdk/public/python/certificate_type.i new file mode 100644 index 0000000..c83aaa3 --- /dev/null +++ b/provisioning_sdk/public/python/certificate_type.i @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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/crypto_utility.py b/provisioning_sdk/public/python/crypto_utility.py new file mode 100644 index 0000000..fde1f05 --- /dev/null +++ b/provisioning_sdk/public/python/crypto_utility.py @@ -0,0 +1,25 @@ +################################################################################ +# 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. +################################################################################ + +"""Utility functions for cryptography.""" + +from cryptography.hazmat import backends +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import padding + + +def VerifySignature(public_key, signature, data): + hash_algorithm = hashes.SHA1() + salt_len = 20 + + key = serialization.load_der_public_key( + public_key, backend=backends.default_backend()) + key.verify(signature, data, + padding.PSS(padding.MGF1(hash_algorithm), salt_len), + hash_algorithm) diff --git a/provisioning_sdk/public/python/drm_intermediate_certificate_test.py b/provisioning_sdk/public/python/drm_intermediate_certificate_test.py new file mode 100644 index 0000000..5151990 --- /dev/null +++ b/provisioning_sdk/public/python/drm_intermediate_certificate_test.py @@ -0,0 +1,54 @@ +################################################################################ +# 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. +################################################################################ + +import unittest + +import pywrapprovisioning_engine +import pywrapprovisioning_status +import test_data_utility + + +class AddDrmIntermediateTest(unittest.TestCase): + + def setUp(self): + self._engine = pywrapprovisioning_engine.ProvisioningEngine() + test_data_utility.InitProvisionEngineWithTestData( + self._engine, verify_success=True) + + def testGenerateDrmIntermediateCertificateWithValidExpirationPeriod(self): + test_data_utility.SetCertificateStatusListWithTestData( + self._engine, 0, verify_success=True) + + test_data_utility.AddDrmIntermediateCertificateWithTestData( + self._engine, 2001, verify_success=True) + + def testSetCertificateStatusListInvalid(self): + set_cert_status_list = self._engine.SetCertificateStatusList( + 'INVALID_STATUS_LIST', 0) + self.assertEqual(pywrapprovisioning_status.INVALID_STATUS_LIST, + set_cert_status_list) + + def testAddDrmIntermediateCertificateWithoutCertificateStatusList(self): + # Users should not be able to add DRM certificate without having + # certificate status list. + status = test_data_utility.AddDrmIntermediateCertificateWithTestData( + self._engine, 2001) + self.assertEqual(pywrapprovisioning_status.STATUS_LIST_EXPIRED, status) + + def testAddDrmIntermediateCertificateSystemIdInvalid(self): + test_data_utility.SetCertificateStatusListWithTestData( + self._engine, 0, verify_success=True) + + # 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) + + +if __name__ == '__main__': + unittest.main() diff --git a/provisioning_sdk/public/python/engine_generate_certificate_test.py b/provisioning_sdk/public/python/engine_generate_certificate_test.py new file mode 100644 index 0000000..8c74cc3 --- /dev/null +++ b/provisioning_sdk/public/python/engine_generate_certificate_test.py @@ -0,0 +1,64 @@ +################################################################################ +# 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. +################################################################################ + +import unittest + +import crypto_utility +import pywrapprovisioning_engine +import pywrapprovisioning_status +import test_data_utility +from protos.public import signed_device_certificate_pb2 + + +class EngineGenerateCertificateTest(unittest.TestCase): + + def setUp(self): + self._engine = pywrapprovisioning_engine.ProvisioningEngine() + test_data_utility.InitProvisionEngineWithTestData( + self._engine, verify_success=True) + test_data_utility.SetCertificateStatusListWithTestData( + self._engine, 0, verify_success=True) + test_data_utility.AddDrmIntermediateCertificateWithTestData( + self._engine, 2001, verify_success=True) + + def testSuccess(self): + status, signed_cert_string = self._engine.GenerateDeviceDrmCertificate( + 2001, test_data_utility.DEVICE_PUBLIC_KEY, 'DEVICE_SERIAL_NUMBER') + self.assertEqual(pywrapprovisioning_status.OK, status) + + signed_cert = signed_device_certificate_pb2.SignedDrmDeviceCertificate() + signed_cert.ParseFromString(signed_cert_string) + crypto_utility.VerifySignature(test_data_utility.CA_PUBLIC_KEY, + signed_cert.signature, + signed_cert.drm_certificate) + + def testEmptySerialNumber(self): + status, _ = self._engine.GenerateDeviceDrmCertificate( + 2001, test_data_utility.DEVICE_PUBLIC_KEY, '') + self.assertEqual(pywrapprovisioning_status.INVALID_SERIAL_NUMBER, status) + + def testEmptyPublicKey(self): + status, _ = self._engine.GenerateDeviceDrmCertificate( + 2001, '', 'DEVICE_SERIAL_NUMBER') + self.assertEqual(pywrapprovisioning_status.INVALID_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) + + def testMissingIntermediateCertificate(self): + status, _ = self._engine.GenerateDeviceDrmCertificate( + 2002, test_data_utility.DEVICE_PUBLIC_KEY, 'DEVICE_SERIAL_NUMBER') + self.assertEqual(pywrapprovisioning_status.DEVICE_REVOKED, 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 new file mode 100644 index 0000000..5306208 --- /dev/null +++ b/provisioning_sdk/public/python/init_engine_test.py @@ -0,0 +1,170 @@ +################################################################################ +# 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. +################################################################################ + +import unittest + +import pywrapcertificate_type +import pywrapprovisioning_engine +import pywrapprovisioning_status +import test_data_utility + + +class InitEngineTest(unittest.TestCase): + + def setUp(self): + self._engine = pywrapprovisioning_engine.ProvisioningEngine() + + def testInitEngineSucceed(self): + test_data_utility.InitProvisionEngineWithTestData( + self._engine, verify_success=True) + + def testSetCertificateStatusListWithoutInit(self): + status = self._engine.SetCertificateStatusList('CERTIFICATE_STATUS_LIST', + 3600) + self.assertEqual( + pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status) + + def testGenerateDrmIntermediateCertificateWithoutInit(self): + status, _ = self._engine.GenerateDrmIntermediateCertificate( + 100, 'INTERMEDIATE_PUBLIC_KEY') + self.assertEqual( + pywrapprovisioning_status.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) + + def testGenerateDeviceDrmCertificateWithoutInit(self): + status, _ = self._engine.GenerateDeviceDrmCertificate( + 100, 'DEVICE_PUBLIC_KEY', 'DEVICE_SERIAL_NUMBER') + self.assertEqual( + pywrapprovisioning_status.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) + self.assertIsNone(session) + + def testInitEngineInvalidServiceDrmCert(self): + status = self._engine.Initialize( + pywrapcertificate_type.kCertTesting, 'INVALID_CERT', + test_data_utility.SERVICE_PRIVATE_KEY, + test_data_utility.SERVICE_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_DRM_CERT, + test_data_utility.PROVISIONER_PRIVATE_KEY, + test_data_utility.PROVISIONER_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_SPOID_SECRET) + self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_DRM_CERTIFICATE, + status) + + def testInitEngineInvalidServicePrivateKey(self): + status = self._engine.Initialize( + pywrapcertificate_type.kCertTesting, + test_data_utility.SERVICE_DRM_CERT, 'INVALID_KEY', + test_data_utility.SERVICE_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_DRM_CERT, + test_data_utility.PROVISIONER_PRIVATE_KEY, + test_data_utility.PROVISIONER_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_SPOID_SECRET) + self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY, + status) + + def testInitEngineWrongServicePrivateKey(self): + status = self._engine.Initialize( + pywrapcertificate_type.kCertTesting, + test_data_utility.SERVICE_DRM_CERT, + test_data_utility.PROVISIONER_PRIVATE_KEY, + test_data_utility.SERVICE_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_DRM_CERT, + test_data_utility.PROVISIONER_PRIVATE_KEY, + test_data_utility.PROVISIONER_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_SPOID_SECRET) + self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY, + status) + + def testInitEngineInvalidServicePrivateKeyPassphrase(self): + status = self._engine.Initialize( + pywrapcertificate_type.kCertTesting, + test_data_utility.SERVICE_DRM_CERT, + test_data_utility.SERVICE_PRIVATE_KEY, 'INVALID_PASSPHRASE', + test_data_utility.PROVISIONER_DRM_CERT, + test_data_utility.PROVISIONER_PRIVATE_KEY, + test_data_utility.PROVISIONER_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_SPOID_SECRET) + self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY, + status) + + def testInitEngineInvalidDrmCert(self): + status = self._engine.Initialize( + pywrapcertificate_type.kCertTesting, + test_data_utility.SERVICE_DRM_CERT, + test_data_utility.SERVICE_PRIVATE_KEY, + test_data_utility.SERVICE_PRIVATE_KEY_PASS, 'INVALID_CERT', + test_data_utility.PROVISIONER_PRIVATE_KEY, + test_data_utility.PROVISIONER_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_SPOID_SECRET) + self.assertEqual( + pywrapprovisioning_status.INVALID_PROVISIONER_DRM_CERTIFICATE, status) + + def testInitEngineInvalidDrmPrivateKey(self): + status = self._engine.Initialize( + pywrapcertificate_type.kCertTesting, + test_data_utility.SERVICE_DRM_CERT, + test_data_utility.SERVICE_PRIVATE_KEY, + test_data_utility.SERVICE_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_DRM_CERT, 'INVALID_KEY', + test_data_utility.PROVISIONER_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_SPOID_SECRET) + self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY, + status) + + def testInitEngineWrongDrmPrivateKey(self): + status = self._engine.Initialize( + pywrapcertificate_type.kCertTesting, + test_data_utility.SERVICE_DRM_CERT, + test_data_utility.SERVICE_PRIVATE_KEY, + test_data_utility.SERVICE_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_DRM_CERT, + test_data_utility.SERVICE_PRIVATE_KEY, + test_data_utility.PROVISIONER_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_SPOID_SECRET) + self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY, + status) + + def testInitEngineInvalidDrmPrivateKeyPassphrase(self): + status = self._engine.Initialize( + pywrapcertificate_type.kCertTesting, + test_data_utility.SERVICE_DRM_CERT, + test_data_utility.SERVICE_PRIVATE_KEY, + test_data_utility.SERVICE_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_DRM_CERT, + test_data_utility.PROVISIONER_PRIVATE_KEY, + 'INVALID_PASSPHRASE', + test_data_utility.PROVISIONER_SPOID_SECRET) + self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY, + status) + + def testInitEngineInvalidSpoidSecret(self): + status = self._engine.Initialize( + pywrapcertificate_type.kCertTesting, + test_data_utility.SERVICE_DRM_CERT, + test_data_utility.SERVICE_PRIVATE_KEY, + test_data_utility.SERVICE_PRIVATE_KEY_PASS, + test_data_utility.PROVISIONER_DRM_CERT, + test_data_utility.PROVISIONER_PRIVATE_KEY, + test_data_utility.PROVISIONER_PRIVATE_KEY_PASS, + '') + self.assertEqual(pywrapprovisioning_status.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 new file mode 100644 index 0000000..8702b41 --- /dev/null +++ b/provisioning_sdk/public/python/new_session_test.py @@ -0,0 +1,103 @@ +################################################################################ +# 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. +################################################################################ + +import unittest + +import crypto_utility +import pywrapprovisioning_engine +import pywrapprovisioning_status +import test_data_utility +from protos.public import certificate_provisioning_pb2 +from protos.public import signed_device_certificate_pb2 + + +class NewSessionTest(unittest.TestCase): + + def setUp(self): + self._engine = pywrapprovisioning_engine.ProvisioningEngine() + test_data_utility.InitProvisionEngineWithTestData( + self._engine, verify_success=True) + test_data_utility.SetCertificateStatusListWithTestData( + self._engine, 0, verify_success=True) + + def testNewSessionSuccess(self): + test_data_utility.AddDrmIntermediateCertificateWithTestData( + self._engine, 2001, verify_success=True) + + (_, new_session) = test_data_utility.NewProvisioningSessionWithTestData( + self._engine, verify_success=True) + (status, raw_response, + _) = new_session.ProcessMessage(test_data_utility.MESSAGE) + test_data_utility.AssertSuccess(status, 'Failed to create session.') + + signed_request = test_data_utility.ConvertToSignedProvisioningMessage( + test_data_utility.MESSAGE) + + unsigned_request = certificate_provisioning_pb2.ProvisioningRequest() + unsigned_request.ParseFromString(signed_request.message) + + signed_response = test_data_utility.ConvertToSignedProvisioningMessage( + raw_response) + + self._VerifyMessageSignature(test_data_utility.SERVICE_PUBLIC_KEY, + signed_response) + + unsigned_response = certificate_provisioning_pb2.ProvisioningResponse() + unsigned_response.ParseFromString(signed_response.message) + + self._VerifyProvisioningResponse(unsigned_request, unsigned_response) + + def testProcessInvalidMessage(self): + test_data_utility.AddDrmIntermediateCertificateWithTestData( + self._engine, 2001, verify_success=True) + (_, new_session + ) = test_data_utility.NewProvisioningSessionWithTestData(self._engine) + (status, _, _) = new_session.ProcessMessage('INVALID_MESSAGE') + self.assertEqual(pywrapprovisioning_status.INVALID_REQUEST_MESSAGE, status) + + def testNewSessionWithoutIntermediateCert(self): + (_, new_session) = test_data_utility.NewProvisioningSessionWithTestData( + self._engine, verify_success=True) + (status, _, _) = new_session.ProcessMessage(test_data_utility.MESSAGE) + self.assertEqual(pywrapprovisioning_status.MISSING_DRM_INTERMEDIATE_CERT, + status) + + def testNewSessionInvalidDevicePublicKey(self): + test_data_utility.AddDrmIntermediateCertificateWithTestData( + self._engine, 2001, verify_success=True) + (session_status, _) = self._engine.NewProvisioningSession( + 'INVALID_PUBLIC_KEY', test_data_utility.DEVICE_PRIVATE_KEY) + self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY, + session_status) + + def testNewSessionInvalidDevicePrivateKey(self): + test_data_utility.AddDrmIntermediateCertificateWithTestData( + self._engine, 2001, verify_success=True) + (session_status, _) = self._engine.NewProvisioningSession( + test_data_utility.DEVICE_PUBLIC_KEY, 'INVALID_PRIVATE_KEY') + self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PRIVATE_KEY, + session_status) + + def _VerifyMessageSignature(self, public_key, signed_response): + crypto_utility.VerifySignature(public_key, signed_response.signature, + signed_response.message) + + def _VerifyCertSignature(self, public_key, signed_cert): + crypto_utility.VerifySignature(public_key, signed_cert.signature, + signed_cert.drm_certificate) + + def _VerifyProvisioningResponse(self, request, response): + self.assertEqual(request.nonce, response.nonce) + + signed_cert = signed_device_certificate_pb2.SignedDrmDeviceCertificate() + signed_cert.ParseFromString(response.device_certificate) + + self._VerifyCertSignature(test_data_utility.CA_PUBLIC_KEY, signed_cert) + +if __name__ == '__main__': + unittest.main() diff --git a/provisioning_sdk/public/python/provisioning_engine.i b/provisioning_sdk/public/python/provisioning_engine.i new file mode 100644 index 0000000..64c7c1e --- /dev/null +++ b/provisioning_sdk/public/python/provisioning_engine.i @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 int32 { int32_t }; +%apply uint32 { uint32_t }; + +%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_session.i b/provisioning_sdk/public/python/provisioning_session.i new file mode 100644 index 0000000..cbefa36 --- /dev/null +++ b/provisioning_sdk/public/python/provisioning_session.i @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_status.i b/provisioning_sdk/public/python/provisioning_status.i new file mode 100644 index 0000000..4c6c1ae --- /dev/null +++ b/provisioning_sdk/public/python/provisioning_status.i @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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/set_certificate_status_list_test.py b/provisioning_sdk/public/python/set_certificate_status_list_test.py new file mode 100644 index 0000000..42bf9e9 --- /dev/null +++ b/provisioning_sdk/public/python/set_certificate_status_list_test.py @@ -0,0 +1,35 @@ +################################################################################ +# 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. +################################################################################ + +import unittest + +import pywrapprovisioning_engine +import pywrapprovisioning_status +import test_data_utility + + +class SetCertificateStatusListTest(unittest.TestCase): + + def setUp(self): + self._engine = pywrapprovisioning_engine.ProvisioningEngine() + test_data_utility.InitProvisionEngineWithTestData( + self._engine, verify_success=True) + + def testSetCertificateStatusListSuccess(self): + test_data_utility.SetCertificateStatusListWithTestData( + self._engine, 0, verify_success=True) + + def testSetCertificateStatusListInvalid(self): + set_cert_status_list = self._engine.SetCertificateStatusList( + 'INVALID_STATUS_LIST', 0) + self.assertEqual(pywrapprovisioning_status.INVALID_STATUS_LIST, + set_cert_status_list) + + +if __name__ == '__main__': + unittest.main() diff --git a/provisioning_sdk/public/python/setup.py b/provisioning_sdk/public/python/setup.py new file mode 100644 index 0000000..4f84aa6 --- /dev/null +++ b/provisioning_sdk/public/python/setup.py @@ -0,0 +1,62 @@ +################################################################################ +# 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/test_data_utility.py b/provisioning_sdk/public/python/test_data_utility.py new file mode 100644 index 0000000..f794132 --- /dev/null +++ b/provisioning_sdk/public/python/test_data_utility.py @@ -0,0 +1,151 @@ +################################################################################ +# 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. +################################################################################ + +"""Utility class for Provisioning SDK testing.""" + +import os + +import pywrapcertificate_type +import pywrapprovisioning_status +from protos.public import certificate_provisioning_pb2 + +TEST_DATA_FOLDER = os.path.join('example', 'example_data') + + +def GetTestData(filename): + current_dir = os.path.realpath(os.path.dirname(__file__)) + while not os.path.isdir(os.path.join(current_dir, TEST_DATA_FOLDER)): + current_dir = os.path.dirname(current_dir) + filename = os.path.join(current_dir, TEST_DATA_FOLDER, filename) + with open(filename, 'rb') as data_file: + data = data_file.read() + return data + + +SERVICE_DRM_CERT = GetTestData('service.cert') +SERVICE_PUBLIC_KEY = GetTestData('service.public') +SERVICE_PRIVATE_KEY = GetTestData('service.encrypted.private') +SERVICE_PRIVATE_KEY_PASS = GetTestData('service.passphrase') +PROVISIONER_DRM_CERT = GetTestData('provisioner.cert') +PROVISIONER_PRIVATE_KEY = GetTestData('provisioner.encrypted.private') +PROVISIONER_PRIVATE_KEY_PASS = GetTestData('provisioner.passphrase') +PROVISIONER_SPOID_SECRET = GetTestData('provisioner.spoid_secret') +CA_PUBLIC_KEY = GetTestData('intermediate.public') +DEVICE_PUBLIC_KEY = GetTestData('user.public') +DEVICE_PRIVATE_KEY = GetTestData('user.private') +MESSAGE = GetTestData('message') + + +def InitProvisionEngineWithTestData(engine, verify_success=False): + """Initialize the provisioning engine with sample credentials. + + Args: + engine: a pywrapprovisioning_engine.ProvisioningEngine instance + verify_success: whether to verify that resulting status code equals OK + + Returns: + OK on success, or an appropriate error status code otherwise. + """ + status = engine.Initialize(pywrapcertificate_type.kCertTesting, + SERVICE_DRM_CERT, SERVICE_PRIVATE_KEY, + SERVICE_PRIVATE_KEY_PASS, PROVISIONER_DRM_CERT, + PROVISIONER_PRIVATE_KEY, + PROVISIONER_PRIVATE_KEY_PASS, + PROVISIONER_SPOID_SECRET) + if verify_success: + AssertSuccess(status, 'Failed to initialize.') + return status + + +def SetCertificateStatusListWithTestData(engine, + expiration_period_seconds, + verify_success=False): + """Set the certificate status list with sample certificate status list. + + Args: + engine: a pywrapprovisioning_engine.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 + + Returns: + OK on success, or an appropriate error status code otherwise. + """ + certificate_status_list = GetTestData('certificate_list') + + status = engine.SetCertificateStatusList(certificate_status_list, + expiration_period_seconds) + + if verify_success: + AssertSuccess(status, 'Failed to set certificate status list.') + + return status + + +def AddDrmIntermediateCertificateWithTestData(engine, + system_id, + verify_success=False): + """Generate an intermediate DRM cert and add it to provisioning engine. + + The intermediate DRM certificate is generated with sample public key and + is added to the provisioning engine with sample certificate private key and + passphrase. + + Args: + engine: a pywrapprovisioning_engine.ProvisioningEngine instance + system_id: Widevine system ID for the type of device + verify_success: whether to verify that resulting status code equals OK + + Returns: + OK on success, or an appropriate error status code otherwise. + """ + ca_private_key = GetTestData('intermediate.encrypted.private') + ca_private_key_passphrase = GetTestData('intermediate.passphrase') + + gen_status, ca_certificate = engine.GenerateDrmIntermediateCertificate( + system_id, CA_PUBLIC_KEY) + AssertSuccess(gen_status, 'Failed to generate intermediate certificate.') + + add_ca_status = engine.AddDrmIntermediateCertificate( + ca_certificate, ca_private_key, ca_private_key_passphrase) + + if verify_success: + AssertSuccess(add_ca_status, 'Failed to add intermediate certificate.') + + return add_ca_status + + +def NewProvisioningSessionWithTestData(engine, verify_success=False): + """Create a provisioning session with sample device public and private keys. + + Args: + engine: a pywrapprovisioning_engine.ProvisioningEngine instance + verify_success: whether to verify that resulting status code equals OK + + Returns: + status: OK on success, or an appropriate error status code otherwise. + new_session: A new provisioning_session. + """ + status, new_session = engine.NewProvisioningSession(DEVICE_PUBLIC_KEY, + DEVICE_PRIVATE_KEY) + + if verify_success: + AssertSuccess(status, 'Failed to create session.') + + return (status, new_session) + + +def AssertSuccess(status, message=None): + """Assert status equals OK.""" + assert pywrapprovisioning_status.OK == status, message + + +def ConvertToSignedProvisioningMessage(serialized_message): + signed_message = certificate_provisioning_pb2.SignedProvisioningMessage() + signed_message.ParseFromString(serialized_message) + return signed_message diff --git a/provisioning_sdk/public/python/unique_ptr.i b/provisioning_sdk/public/python/unique_ptr.i new file mode 100644 index 0000000..e37afe6 --- /dev/null +++ b/provisioning_sdk/public/python/unique_ptr.i @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 new file mode 100755 index 0000000..650098a --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,49 @@ +#!/bin/bash +################################################################################ +# 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. +################################################################################ +# +# This script generates a directory that stores the intermediate artifacts +# needed for testing. +# +# Prerequirements (if running the script directly): +# - Python 2.7 or later +# - pip: https://pip.pypa.io/en/latest/installing/ +# - Python cryptography package: https://cryptography.io/en/latest/installation/ +# - 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; } + +cd "$(dirname "$0")" + +rm -rf test_genfiles +mkdir test_genfiles + +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" + +cp -a provisioning_sdk/public/python/* test_genfiles/ +cd test_genfiles +python setup.py build_ext --inplace + +shopt -s globstar +for d in "protos"/**/; do + touch -- "$d/__init__.py"; +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