diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..1575231 --- /dev/null +++ b/BUILD @@ -0,0 +1,27 @@ +################################################################################ +# Copyright 2018 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +# Build media cas packager sdk tar package. + +load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") + +pkg_tar( + name = "media_cas_packager_sdk_files", + strip_prefix = "/", + files = [ + "//media_cas_packager_sdk/public:binary_release_files", + ], + mode = "0644", +) + +pkg_tar( + name = "media_cas_packager_sdk-bin", + deps = [":media_cas_packager_sdk_files"], + files = ["//media_cas_packager_sdk/public:libmedia_cas_packager_sdk.so"], + mode = "0755", +) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fe743e5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,4 @@ +Copyright 2017 Google, Inc. + +This software is licensed under the terms defined in the Widevine Master License Agreement. +For a copy of this agreement, please contact widevine-licensing@google.com. diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..3ed01b1 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,75 @@ +workspace(name = "media_cas_packager_sdk") + +# CCTZ (Time-zone framework), needed by abseil. +git_repository( + name = "com_googlesource_code_cctz", + remote = "https://github.com/google/cctz.git", + tag = "v2.2", +) + +git_repository( + name = "abseil_repo", + commit = "475d64f2de7403a01b1b36c487328ed41d29c20c", #2018-04-10 + remote = "https://github.com/abseil/abseil-cpp.git", +) + +git_repository( + name = "protobuf_repo", + remote = "https://github.com/google/protobuf.git", + tag = "v3.5.0", +) + +git_repository( + name = "boringssl_repo", + commit = "27ae6cadd74fd054208730827a8de3fe9bc648f0", # 2017-12-07 + remote = "https://github.com/google/boringssl.git", +) + +git_repository( + name = "gflags_repo", + commit = "fe57e5af4db74ab298523f06d2c43aa895ba9f98", # 2016-07-20 + remote = "https://github.com/gflags/gflags.git", +) + +git_repository( + name = "googletest_repo", + commit = "9816b96a6ddc0430671693df90192bbee57108b6", # 2017-08-11 + remote = "https://github.com/google/googletest.git", +) + +new_git_repository( + name = "glog_repo", + build_file = "glog.BUILD", + commit = "0472b91c5defdf90cff7292e3bf7bd86770a9a0a", # 2016-07-13 + remote = "https://github.com/google/glog.git", +) + +bind( + name = "protobuf", + actual = "@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..0612c2d --- /dev/null +++ b/base/BUILD @@ -0,0 +1,25 @@ +################################################################################ +# Copyright 2016 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "base", + hdrs = [ + "macros.h", + "thread_annotations.h", + "timestamp.h", + ], + deps = [ + "@abseil_repo//absl/base", + "//external:gflags", + "//external:glog", + ], +) diff --git a/base/macros.h b/base/macros.h new file mode 100644 index 0000000..e2ec936 --- /dev/null +++ b/base/macros.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef BASE_MACROS_H_ +#define BASE_MACROS_H_ + +#include "absl/base/macros.h" + +// DISALLOW_COPY_AND_ASSIGN disallows the copy constructor and copy assignment +// operator. DISALLOW_IMPLICIT_CONSTRUCTORS is like DISALLOW_COPY_AND_ASSIGN, +// but also disallows the default constructor, intended to help make a +// class uninstantiable. +// +// These must be placed in the private: declarations for a class. +// +// Note: New code should prefer static_assert over COMPILE_ASSERT. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName& operator=(const TypeName&) = delete +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +#define arraysize(array) ABSL_ARRAYSIZE(array) + +#endif // BASE_MACROS_H_ diff --git a/base/thread_annotations.h b/base/thread_annotations.h new file mode 100644 index 0000000..ce3535a --- /dev/null +++ b/base/thread_annotations.h @@ -0,0 +1,14 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef BASE_THREAD_ANNOTATIONS_H_ +#define BASE_THREAD_ANNOTATIONS_H_ + +#include "absl/base/thread_annotations.h" + +#endif // BASE_THREAD_ANNOTATIONS_H_ diff --git a/base/timestamp.h b/base/timestamp.h new file mode 100644 index 0000000..81b1a77 --- /dev/null +++ b/base/timestamp.h @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef BASE_TIMESTAMP_H_ +#define BASE_TIMESTAMP_H_ + +namespace widevine { + +class BuildData { + public: + static const char* Timestamp() { return __DATE__ " " __TIME__; } +}; + +} // namespace widevine + +#endif // BASE_TIMESTAMP_H_ diff --git a/common/BUILD b/common/BUILD new file mode 100644 index 0000000..5b086ac --- /dev/null +++ b/common/BUILD @@ -0,0 +1,564 @@ +################################################################################ +# Copyright 2016 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +# +# Description: +# Build file for code common to multiple Widevine services. + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "binary_release_files", + srcs = [ + "certificate_type.h", + ], +) + +cc_library( + name = "certificate_type", + hdrs = ["certificate_type.h"], +) + +cc_library( + name = "drm_root_certificate", + srcs = ["drm_root_certificate.cc"], + hdrs = ["drm_root_certificate.h"], + deps = [ + ":certificate_type", + ":error_space", + ":rsa_key", + "//base", + "@abseil_repo//absl/strings", + "//external:openssl", + "//util:status", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_test( + name = "drm_root_certificate_test", + timeout = "short", + srcs = ["drm_root_certificate_test.cc"], + deps = [ + ":drm_root_certificate", + "//base", + "//testing:gunit_main", + "//common:rsa_key", + "//common:rsa_test_keys", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_library( + name = "certificate_util", + srcs = ["certificate_util.cc"], + hdrs = ["certificate_util.h"], + deps = [ + ":certificate_type", + ":drm_service_certificate", + ":verified_media_pipeline", + ":vmp_checker", + "//license_server_sdk/internal:sdk", + ], +) + +cc_library( + name = "client_id_util", + srcs = ["client_id_util.cc"], + hdrs = ["client_id_util.h"], + deps = [ + ":aes_cbc_util", + ":drm_service_certificate", + ":error_space", + "//base", + "@abseil_repo//absl/strings", + "//util:status", + "//protos/public:client_identification_proto", + "//protos/public:errors_proto", + ], +) + +cc_library( + name = "rsa_util", + srcs = ["rsa_util.cc"], + hdrs = ["rsa_util.h"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "rsa_util_test", + size = "medium", + timeout = "short", + srcs = ["rsa_util_test.cc"], + deps = [ + ":rsa_test_keys", + ":rsa_util", + "//testing:gunit", + "//testing:gunit_main", + "//external:openssl", + ], +) + +cc_library( + name = "openssl_util", + hdrs = ["openssl_util.h"], + deps = [ + "//external:openssl", + ], +) + +cc_library( + name = "rsa_key", + srcs = ["rsa_key.cc"], + hdrs = ["rsa_key.h"], + deps = [ + ":rsa_util", + ":sha_util", + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "rsa_key_test", + size = "medium", + timeout = "short", + srcs = ["rsa_key_test.cc"], + deps = [ + ":rsa_key", + ":rsa_test_keys", + "//testing:gunit", + "//testing:gunit_main", + ], +) + +cc_library( + name = "rsa_test_keys", + testonly = 1, + srcs = ["rsa_test_keys.cc"], + hdrs = ["rsa_test_keys.h"], + deps = [ + "//base", + ], +) + +cc_library( + name = "mock_rsa_key", + testonly = 1, + hdrs = ["mock_rsa_key.h"], + deps = [ + ":rsa_key", + "//testing:gunit", + ], +) + +cc_library( + name = "aes_cbc_util", + srcs = ["aes_cbc_util.cc"], + hdrs = ["aes_cbc_util.h"], + visibility = ["//visibility:public"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "aes_cbc_util_test", + srcs = ["aes_cbc_util_test.cc"], + deps = [ + ":aes_cbc_util", + "//testing:gunit_main", + ], +) + +cc_library( + name = "crypto_util", + srcs = ["crypto_util.cc"], + hdrs = ["crypto_util.h"], + visibility = ["//visibility:public"], + deps = [ + "//base", + "@abseil_repo//absl/strings", + "//external:openssl", + "//util/endian", + ], +) + +cc_test( + name = "crypto_util_test", + size = "medium", + srcs = ["crypto_util_test.cc"], + deps = [ + ":crypto_util", + "//testing:gunit", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "ecb_util", + srcs = ["ecb_util.cc"], + hdrs = ["ecb_util.h"], + visibility = ["//visibility:public"], + deps = [ + "//base", + "@abseil_repo//absl/strings", + "//external:openssl", + ], +) + +cc_test( + name = "ecb_util_test", + size = "small", + srcs = ["ecb_util_test.cc"], + deps = [ + ":ecb_util", + "//testing:gunit", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "file_util", + srcs = ["file_util.cc"], + hdrs = ["file_util.h"], + deps = [ + "//base", + ], +) + +cc_test( + name = "file_util_test", + srcs = ["file_util_test.cc"], + deps = [ + ":file_util", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "random_util", + srcs = ["random_util.cc"], + hdrs = ["random_util.h"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "random_util_test", + srcs = ["random_util_test.cc"], + deps = [ + ":random_util", + "//testing:gunit_main", + ], +) + +cc_library( + name = "sha_util", + srcs = ["sha_util.cc"], + hdrs = ["sha_util.h"], + deps = [ + "//base", + "//external:openssl", + ], +) + +cc_test( + name = "sha_util_test", + srcs = ["sha_util_test.cc"], + deps = [ + ":sha_util", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "signature_util", + srcs = ["signature_util.cc"], + hdrs = ["signature_util.h"], + deps = [ + ":aes_cbc_util", + ":rsa_key", + ":sha_util", + "//base", + "//util:status", + ], +) + +cc_library( + name = "signing_key_util", + srcs = ["signing_key_util.cc"], + hdrs = ["signing_key_util.h"], + deps = [ + ":crypto_util", + "//base", + "//protos/public:license_protocol_proto", + ], +) + +cc_test( + name = "signing_key_util_test", + size = "small", + srcs = ["signing_key_util_test.cc"], + deps = [ + ":signing_key_util", + "//testing:gunit", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//protos/public:license_protocol_proto", + ], +) + +cc_library( + name = "test_certificates", + testonly = 1, + srcs = ["test_certificates.cc"], + hdrs = ["test_certificates.h"], + deps = [ + "//base", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "wvm_token_handler", + srcs = ["wvm_token_handler.cc"], + hdrs = ["wvm_token_handler.h"], + deps = [ + ":aes_cbc_util", + ":ecb_util", + ":sha_util", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util/endian", + "//util/gtl:map_util", + "//util:status", + ], +) + +cc_test( + name = "wvm_token_handler_test", + size = "small", + srcs = ["wvm_token_handler_test.cc"], + deps = [ + ":wvm_test_keys", + ":wvm_token_handler", + "//testing:gunit", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "wvm_test_keys", + testonly = 1, + srcs = ["wvm_test_keys.cc"], + hdrs = ["wvm_test_keys.h"], + deps = [ + ":wvm_token_handler", + "//base", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "error_space", + srcs = ["error_space.cc"], + hdrs = ["error_space.h"], + deps = [ + "//util:status", + "//util:proto_status", + "//protos/public:errors_proto", + ], +) + +cc_library( + name = "remote_attestation_verifier", + srcs = ["remote_attestation_verifier.cc"], + hdrs = ["remote_attestation_verifier.h"], + deps = [ + ":client_id_util", + ":drm_service_certificate", + ":error_space", + ":rsa_key", + ":x509_cert", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util:status", + "//protos/public:client_identification_proto", + "//protos/public:errors_proto", + "//protos/public:remote_attestation_proto", + ], +) + +cc_library( + name = "drm_service_certificate", + srcs = ["drm_service_certificate.cc"], + hdrs = ["drm_service_certificate.h"], + deps = [ + ":aes_cbc_util", + ":certificate_type", + ":drm_root_certificate", + ":error_space", + ":rsa_key", + ":rsa_util", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//util/gtl:map_util", + "//util:status", + "//protos/public:client_identification_proto", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_test( + name = "drm_service_certificate_test", + timeout = "short", + srcs = ["drm_service_certificate_test.cc"], + deps = [ + ":aes_cbc_util", + ":drm_service_certificate", + ":rsa_key", + ":rsa_test_keys", + ":rsa_util", + ":test_certificates", + "//base", + "//external:protobuf", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//protos/public:client_identification_proto", + "//protos/public:drm_certificate_proto", + "//protos/public:errors_proto", + "//protos/public:license_server_sdk_proto", + "//protos/public:signed_drm_certificate_proto", + ], +) + +cc_library( + name = "verified_media_pipeline", + srcs = ["verified_media_pipeline.cc"], + hdrs = ["verified_media_pipeline.h"], + deps = [ + ":vmp_checker", + "//base", + "@abseil_repo//absl/strings", + "//util:status", + "//protos/public:license_protocol_proto", + ], +) + +cc_library( + name = "x509_cert", + srcs = ["x509_cert.cc"], + hdrs = ["x509_cert.h"], + deps = [ + ":openssl_util", + ":rsa_key", + "//base", + "@abseil_repo//absl/strings", + "@abseil_repo//absl/synchronization", + "//external:openssl", + "//util:status", + ], +) + +cc_library( + name = "test_utils", + testonly = 1, + srcs = ["test_utils.cc"], + hdrs = ["test_utils.h"], + deps = [ + "//base", + "//external:openssl", + "//util:status", + ], +) + +cc_test( + name = "x509_cert_test", + timeout = "short", + srcs = ["x509_cert_test.cc"], + deps = [ + ":rsa_key", + ":test_utils", + ":x509_cert", + "//base", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "vmp_checker", + srcs = ["vmp_checker.cc"], + hdrs = ["vmp_checker.h"], + deps = [ + ":certificate_type", + ":error_space", + ":x509_cert", + "//base", + "@abseil_repo//absl/strings", + "//protos/public:errors_proto", + "//protos/public:verified_media_pipeline_proto", + ], +) + +cc_test( + name = "vmp_checker_test", + timeout = "short", + srcs = ["vmp_checker_test.cc"], + deps = [ + ":rsa_key", + ":vmp_checker", + "//base", + "//testing:gunit_main", + "@abseil_repo//absl/strings", + "//protos/public:errors_proto", + "//protos/public:verified_media_pipeline_proto", + ], +) + +cc_library( + name = "string_util", + srcs = ["string_util.cc"], + hdrs = ["string_util.h"], + deps = [ + "//base", + "//util:status", + ], +) + +cc_test( + name = "string_util_test", + srcs = ["string_util_test.cc"], + deps = [ + ":string_util", + "//base", + "//testing:gunit_main", + ], +) + diff --git a/common/aes_cbc_util.cc b/common/aes_cbc_util.cc new file mode 100644 index 0000000..66e6552 --- /dev/null +++ b/common/aes_cbc_util.cc @@ -0,0 +1,132 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/aes_cbc_util.h" + +#include + +#include +#include "glog/logging.h" +#include "openssl/aes.h" + +namespace widevine { +namespace crypto_util { + +// Encrypts the provided plantext std::string using AES-CBC encryption. +std::string EncryptAesCbc(const std::string& key, const std::string& iv, + const std::string& plaintext) { + const size_t num_padding_bytes = + AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE); + std::string padded_text = plaintext; + padded_text.append(num_padding_bytes, static_cast(num_padding_bytes)); + return EncryptAesCbcNoPad(key, iv, padded_text); +} + +std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& plaintext) { + if (iv.size() != AES_BLOCK_SIZE) { + LOG(WARNING) << "Invalid CBC IV size: " << iv.size(); + return std::string(); + } + if (plaintext.size() % AES_BLOCK_SIZE) { + LOG(WARNING) << "Invalid AEC-CBC plaintext size: " << plaintext.size(); + return std::string(); + } + + AES_KEY aes_key; + if (AES_set_encrypt_key(reinterpret_cast(&key[0]), + key.size() * 8, &aes_key) != 0) { + LOG(WARNING) << "Invalid AES key."; + return std::string(); + } + + std::string encrypted(plaintext.size(), 0); + std::vector local_iv(iv.begin(), iv.end()); + AES_cbc_encrypt(reinterpret_cast(plaintext.data()), + reinterpret_cast(&encrypted[0]), plaintext.size(), + &aes_key, &local_iv[0], AES_ENCRYPT); + return encrypted; +} + +// Decrypts the AES-CBC encrypted text. Returns an empty std::string on error or +// the plaintext on success. +std::string DecryptAesCbc(const std::string& key, const std::string& iv, + const std::string& ciphertext) { + if (ciphertext.empty()) { + LOG(WARNING) << "Empty ciphertext."; + return std::string(); + } + if (iv.size() != AES_BLOCK_SIZE) { + LOG(WARNING) << "Invalid CBC IV size: " << iv.size(); + return std::string(); + } + if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) { + LOG(WARNING) << "Ciphertext not a multiple of AES block size: " + << ciphertext.size(); + return std::string(); + } + + AES_KEY aes_key; + if (AES_set_decrypt_key(reinterpret_cast(&key[0]), + key.size() * 8, &aes_key) != 0) { + LOG(WARNING) << "Invalid AES key."; + return std::string(); + } + + std::string cleartext(ciphertext.size(), 0); + std::vector local_iv(iv.begin(), iv.end()); + AES_cbc_encrypt(reinterpret_cast(ciphertext.data()), + reinterpret_cast(&cleartext[0]), ciphertext.size(), + &aes_key, &local_iv[0], AES_DECRYPT); + + const uint8_t num_padding_bytes = cleartext[cleartext.size() - 1]; + if (num_padding_bytes > AES_BLOCK_SIZE) { + LOG(WARNING) << "AES padding too long: " << num_padding_bytes; + return std::string(); + } + for (uint8_t i = 0; i < num_padding_bytes; ++i) { + if (cleartext[cleartext.size() - 1 - i] != num_padding_bytes) { + LOG(WARNING) << "Padding verification failure."; + return std::string(); + } + } + cleartext.resize(cleartext.size() - num_padding_bytes); + return cleartext; +} + +std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& ciphertext) { + std::vector local_iv(iv.begin(), iv.end()); + if (local_iv.empty()) { + local_iv.resize(AES_BLOCK_SIZE, '\0'); + } else if (local_iv.size() != AES_BLOCK_SIZE) { + LOG(WARNING) << "Invalid CBC IV size: " << iv.size(); + return std::string(); + } + if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) { + LOG(WARNING) << "Ciphertext not a multiple of AES block size: " + << ciphertext.size(); + return std::string(); + } + + AES_KEY aes_key; + if (AES_set_decrypt_key(reinterpret_cast(&key[0]), + key.size() * 8, &aes_key) != 0) { + LOG(WARNING) << "Invalid AES key."; + return std::string(); + } + + std::string cleartext(ciphertext.size(), 0); + AES_cbc_encrypt(reinterpret_cast(ciphertext.data()), + reinterpret_cast(&cleartext[0]), ciphertext.size(), + &aes_key, &local_iv[0], AES_DECRYPT); + return cleartext; +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/aes_cbc_util.h b/common/aes_cbc_util.h new file mode 100644 index 0000000..a1ad59b --- /dev/null +++ b/common/aes_cbc_util.h @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_AES_CBC_UTIL_H_ +#define COMMON_AES_CBC_UTIL_H_ + +#include + +namespace widevine { +namespace crypto_util { + +// Helper for wrapping AES CBC encryption. Uses PKCS7 padding. +std::string EncryptAesCbc(const std::string& key, const std::string& iv, + const std::string& plaintext); + +// Helper for wrapping AES CBC encryption. Adds no padding, so the input +// must be an multiple of the 16-byte AES block size. Returns empty std::string +// on error. +std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& plaintext); + +// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an +// empty std::string on error or the plaintext on success. Expects PKCS7 padding. +std::string DecryptAesCbc(const std::string& key, const std::string& iv, + const std::string& ciphertext); + +// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an +// empty std::string on error or the plaintext on success. +// Uses no padding; fails if the ciphertext is not a multiple of 16 bytes. +// This is used to decrypt the encrypted blob in the WVM keyboxes, with +// a zero iv. +std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv, + const std::string& ciphertext); + +} // namespace crypto_util +} // namespace widevine + +#endif // COMMON_AES_CBC_UTIL_H_ diff --git a/common/aes_cbc_util_test.cc b/common/aes_cbc_util_test.cc new file mode 100644 index 0000000..b6b7ba9 --- /dev/null +++ b/common/aes_cbc_util_test.cc @@ -0,0 +1,162 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/aes_cbc_util.h" + +#include "testing/gmock.h" +#include "testing/gunit.h" + +namespace { + +const uint8_t kKey[] = { + 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e, + 0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b, +}; + +const uint8_t kIv[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, +}; + +} // namespace + +namespace widevine { +namespace crypto_util { + +TEST(CryptoUtilTest, EncryptAndDecryptAesCbc) { + std::string plain_text("Foo"); + std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), plain_text); + std::string expected_ciphertext( + "\xCF\x1A\x3\x1C\x9C\x8C\xB9Z\xEC\xC0\x17\xDCRxX\xD7"); + ASSERT_EQ(0, ciphertext.size() % 16); + ASSERT_GT(ciphertext.size(), plain_text.size()); + ASSERT_EQ(expected_ciphertext, ciphertext); + + std::string decrypted = DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), ciphertext); + ASSERT_EQ(plain_text, decrypted); +} + +TEST(CryptoUtilTest, DecryptAesCbcNoPad) { + const uint8_t kKey[] = { + 0xdd, 0x71, 0x39, 0xea, 0xfa, 0xce, 0xed, 0x7c, + 0xda, 0x9f, 0x25, 0xda, 0x8a, 0xa9, 0x15, 0xea, + }; + const uint8_t kIv[] = { + 0x5d, 0x16, 0x44, 0xea, 0xec, 0x11, 0xf9, 0x83, + 0x14, 0x75, 0x41, 0xe4, 0x6e, 0xeb, 0x27, 0x74, + }; + const uint8_t kCiphertext[] = { + 0x6d, 0xa6, 0xda, 0xe4, 0xee, 0x40, 0x09, 0x17, + 0x54, 0x7b, 0xba, 0xa5, 0x27, 0xb8, 0x82, 0x1b, + }; + const uint8_t kExpectedPlaintext[] = { + 0xb3, 0x49, 0xd4, 0x80, 0x9e, 0x91, 0x06, 0x87, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10, + }; + const uint8_t kExpectedPlaintextEmptyIv[] = { + 0xee, 0x5f, 0x90, 0x6a, 0x72, 0x80, 0xff, 0x04, + 0x14, 0x75, 0x41, 0xa4, 0x6e, 0xeb, 0x27, 0x64, + }; + + std::string decrypted = DecryptAesCbcNoPad( + std::string(kKey, kKey + sizeof(kKey)), std::string(kIv, kIv + sizeof(kIv)), + std::string(kCiphertext, kCiphertext + sizeof(kCiphertext))); + ASSERT_EQ(std::string(kExpectedPlaintext, + kExpectedPlaintext + sizeof(kExpectedPlaintext)), + decrypted); + + std::string dummy_iv; + decrypted = DecryptAesCbcNoPad( + std::string(kKey, kKey + sizeof(kKey)), dummy_iv, + std::string(kCiphertext, kCiphertext + sizeof(kCiphertext))); + ASSERT_EQ( + std::string(kExpectedPlaintextEmptyIv, + kExpectedPlaintextEmptyIv + sizeof(kExpectedPlaintextEmptyIv)), + decrypted); +} + +TEST(CryptoUtilTest, TestFailedEncrypt) { + // Test with bogus initialization vector. + std::string plain_text("Foo"); + std::string bogus_iv("bogus"); + std::string ciphertext = + EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, plain_text); + ASSERT_EQ(ciphertext.size(), 0); + + // Test with bogus key. + std::string bogus_key("bogus"); + ciphertext = + EncryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), plain_text); + ASSERT_EQ(ciphertext.size(), 0); +} + +TEST(CryptoUtilTest, TestFailedEncryptNoPad) { + std::string plaintext("0123456789abcdef"); + std::string key(kKey, kKey + sizeof(kKey)); + std::string iv(kIv, kIv + sizeof(kIv)); + + // Control. + std::string ciphertext = EncryptAesCbcNoPad(key, iv, plaintext); + ASSERT_EQ(plaintext.size(), ciphertext.size()); + + // Bogus key. + std::string bogus_key("bogus"); + ciphertext = EncryptAesCbcNoPad(bogus_key, iv, plaintext); + EXPECT_EQ(ciphertext.size(), 0); + + // Bogus IV. + std::string bogus_iv("bogus"); + ciphertext = EncryptAesCbcNoPad(key, bogus_iv, plaintext); + EXPECT_EQ(ciphertext.size(), 0); + + // Incorrectly-sized plaintext. + std::string bad_plaintext("Foo"); + ciphertext = EncryptAesCbcNoPad(key, iv, bad_plaintext); + EXPECT_EQ(ciphertext.size(), 0); +} + +TEST(CryptoUtilTest, TestFailedDecrypt) { + // First, encrypt the data. + std::string plain_text("Foo"); + std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), plain_text); + ASSERT_NE(ciphertext.size(), 0); + + // Test Decrypt with bogus iv. + std::string bogus_iv("bogus"); + plain_text = + DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, ciphertext); + ASSERT_EQ(plain_text.size(), 0); + + // Test Decrypt with bogus key. + std::string bogus_key("bogus"); + plain_text = + DecryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), ciphertext); + ASSERT_EQ(plain_text.size(), 0); +} + +TEST(CryptoUtilTest, TestEmptyEncrypt) { + EXPECT_EQ("\xDBx\xD9\x91\xE8\x1D\xD9\x19\x80r\x12\x89\xD7Kp\xEB", + EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), "")); +} + +TEST(CryptoUtilTest, TestEmptyDecryptAesCbc) { + EXPECT_EQ("", DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), "")); +} + +TEST(CryptoUtilTest, TestEmptyDecryptAesCbcNoPad) { + EXPECT_EQ("", DecryptAesCbcNoPad(std::string(kKey, kKey + sizeof(kKey)), + std::string(kIv, kIv + sizeof(kIv)), "")); +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/certificate_type.h b/common/certificate_type.h new file mode 100644 index 0000000..b63a639 --- /dev/null +++ b/common/certificate_type.h @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_CERTIFICATE_TYPE_H_ +#define COMMON_CERTIFICATE_TYPE_H_ + +namespace widevine { + +enum CertificateType { + kCertificateTypeTesting, + kCertificateTypeDevelopment, + kCertificateTypeProduction, +}; + +} // namespace widevine + +#endif // COMMON_CERTIFICATE_TYPE_H_ diff --git a/common/certificate_util.cc b/common/certificate_util.cc new file mode 100644 index 0000000..ad5c312 --- /dev/null +++ b/common/certificate_util.cc @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/certificate_util.h" + +#include "common/drm_root_certificate.h" +#include "common/drm_service_certificate.h" +#include "common/verified_media_pipeline.h" +#include "common/vmp_checker.h" +#include "license_server_sdk/internal/client_cert.h" +#include "license_server_sdk/internal/device_status_list.h" + +namespace widevine { +util::Status SetCertificateStatusList( + CertificateType cert_type, const std::string& signed_certificate_status_list, + uint32_t expiration_period_seconds, bool allow_unknown_devices) { + util::Status status = + VmpChecker::Instance()->SelectDrmCertificateType(cert_type); + if (!status.ok()) return status; + + std::unique_ptr root_cert; + status = DrmRootCertificate::CreateByType(cert_type, &root_cert); + if (!status.ok()) { + return status; + } + status = CertificateClientCert::SetDrmRootCertificatePublicKey( + root_cert->public_key()); + if (!status.ok()) { + return status; + } + DeviceStatusList::Instance()->set_allow_unknown_devices( + allow_unknown_devices); + return DeviceStatusList::Instance()->UpdateStatusList( + root_cert->public_key(), signed_certificate_status_list, + expiration_period_seconds); +} + +util::Status AddDrmServiceCertificate( + CertificateType cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + util::Status status = + VmpChecker::Instance()->SelectDrmCertificateType(cert_type); + if (!status.ok()) return status; + return DrmServiceCertificate::AddDrmServiceCertificate( + cert_type, service_certificate, service_private_key, + service_private_key_passphrase); +} +} // namespace widevine diff --git a/common/certificate_util.h b/common/certificate_util.h new file mode 100644 index 0000000..b6fe1d0 --- /dev/null +++ b/common/certificate_util.h @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_CERTIFICATE_UTIL_H_ +#define COMMON_CERTIFICATE_UTIL_H_ + +#include + +#include "util/status.h" +#include "common/certificate_type.h" + +namespace widevine { +// Set the certificate status list system-wide. |cert_type| specifies +// whether to use development or production root certificates. +// |expiration_period| is the number of seconds until the +// certificate_status_list expires after its creation time +// (creation_time_seconds). If |allow_unknown_devices| is false, an error is +// returned if the device does not appear in the certificate_status_list. +util::Status SetCertificateStatusList(CertificateType cert_type, + const std::string& certificate_status_list, + uint32_t expiration_period_seconds, + bool allow_unknown_devices); + +// Add a service certificate system-wide. |cert_type| indicates the type of +// root certificate used to sign the service certificate; +// |service_certificate| is a Google-generated certificate used to +// authenticate the service provider for purposes of device privacy; +// |service_private_key| is the encrypted PKCS#8 private RSA key corresponding +// to the service certificate; and |service_private_key_passphrase| is the +// password required to decrypt |service_private_key|. +util::Status AddDrmServiceCertificate( + CertificateType cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); +} // namespace widevine + +#endif // COMMON_CERTIFICATE_UTIL_H_ diff --git a/common/client_id_util.cc b/common/client_id_util.cc new file mode 100644 index 0000000..35013a2 --- /dev/null +++ b/common/client_id_util.cc @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/client_id_util.h" + +#include "glog/logging.h" +#include "common/aes_cbc_util.h" +#include "common/drm_service_certificate.h" +#include "common/error_space.h" +#include "protos/public/errors.pb.h" + +namespace widevine { + +void AddClientInfo(ClientIdentification* client_id, absl::string_view name, + absl::string_view value) { + ClientIdentification_NameValue* nv = client_id->add_client_info(); + nv->set_name(std::string(name)); + nv->set_value(std::string(value)); +} + +bool SetClientInfo(ClientIdentification* client_id, absl::string_view name, + absl::string_view value) { + int n = client_id->client_info_size(); + for (int i = 0; i < n; i++) { + if (client_id->client_info(i).name() == name) { + client_id->mutable_client_info(i)->set_value(std::string(value)); + return true; + } + } + AddClientInfo(client_id, name, value); + return false; +} + +std::string GetClientInfo(const ClientIdentification& client_id, + absl::string_view name) { + return GetClientInfo(client_id, name, std::string()); +} + +std::string GetClientInfo(const ClientIdentification& client_id, + absl::string_view name, const std::string& default_value) { + for (const auto& nv : client_id.client_info()) { + if (nv.name() == name) { + return nv.value(); + } + } + return default_value; +} + +util::Status DecryptEncryptedClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id) { + return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id, + client_id); +} + +util::Status DecryptEncryptedClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + const std::string& privacy_key, ClientIdentification* client_id) { + DCHECK(client_id); + if (!encrypted_client_id.has_encrypted_client_id() || + encrypted_client_id.encrypted_client_id().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id"); + } + if (!encrypted_client_id.has_encrypted_client_id_iv() || + encrypted_client_id.encrypted_client_id_iv().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id-iv"); + } + std::string serialized_client_id(crypto_util::DecryptAesCbc( + privacy_key, encrypted_client_id.encrypted_client_id_iv(), + encrypted_client_id.encrypted_client_id())); + if (serialized_client_id.empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-decryption-failed"); + } + if (!client_id->ParseFromString(serialized_client_id)) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-parse-failed"); + } + return util::OkStatus(); +} + +} // namespace widevine diff --git a/common/client_id_util.h b/common/client_id_util.h new file mode 100644 index 0000000..fc998bd --- /dev/null +++ b/common/client_id_util.h @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Utilities for manipulating the ClientIdentification proto. +// ClientIdentification.client_info() contains a sequence of +// arbitrary name-value pairs; this code consolidates the +// accessors for them in one place. +#ifndef COMMON_CLIENT_ID_UTIL_H_ +#define COMMON_CLIENT_ID_UTIL_H_ + +#include "absl/strings/string_view.h" +#include "util/status.h" +#include "protos/public/client_identification.pb.h" + +namespace widevine { + +// Append the given name/value pair to client_id->client_info(). Does not +// check for duplicates. +void AddClientInfo(ClientIdentification* client_id, absl::string_view name, + absl::string_view value); + +// Append the given name/value pair to client_id->client_info(). If the +// given name already had a value, replaces it and returns true. +bool SetClientInfo(ClientIdentification* client_id, absl::string_view name, + absl::string_view value); + +// Return the value from client_id.client_info() matching the given name, +// or the empty std::string if not found. +std::string GetClientInfo(const ClientIdentification& client_id, + absl::string_view name); + +// Return the value from client_id.client_info() matching the given name, +// or the given default value if not found. +std::string GetClientInfo(const ClientIdentification& client_id, + absl::string_view name, const std::string& default_value); + +// Decrypts the encrypted client identification in |encrypted_client_id| into +// |client_id| using the private key for the service certificate which was +// used to encrypt the information. +// |client_id| is owned by caller. +// Returns util::Status::OK, if successful, else an error. +util::Status DecryptEncryptedClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id); + +// Decrypts the encrypted client identification in |encrypted_client_id| into +// |client_id| using |privacy_key|. +// |client_id| is owned by caller. +// Returns util::Status::OK, if successful, else an error. +util::Status DecryptEncryptedClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + const std::string& privacy_key, ClientIdentification* client_id); + +} // namespace widevine + +#endif // COMMON_CLIENT_ID_UTIL_H_ diff --git a/common/crypto_util.cc b/common/crypto_util.cc new file mode 100644 index 0000000..785e95f --- /dev/null +++ b/common/crypto_util.cc @@ -0,0 +1,174 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Implementation of Common crypto utilities used by Widevine services. + +#include "common/crypto_util.h" + +#include +#include + +#include "glog/logging.h" +#include "absl/strings/string_view.h" +#include "openssl/aes.h" +#include "openssl/cmac.h" +#include "openssl/evp.h" +#include "util/endian/endian.h" + +namespace widevine { +namespace crypto_util { + +const char kEncryptionKeyLabel[] = "ENCRYPTION"; +const int kEncryptionKeySizeBits = 128; +const char kSigningKeyLabel[] = "AUTHENTICATION"; +const int kSigningKeySizeBits = 256; +const size_t kSigningKeySizeBytes = 32; +const char kIvMasterKey[] = "1234567890123456"; +const char kIvLabel[] = "IV_ENCRYPTION"; +const int kIvSizeBits = 128; +const char kGroupKeyLabel[] = "GROUP_ENCRYPTION"; +// TODO(user): This is a temporary key for development. Replace this with +// a real group master key in keystore. +// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy +// like VerifySignatureHmacSha1. +const char kPhonyGroupMasterKey[] = "fedcba9876543210"; +const int kAes128KeySizeBits = 128; +const int kAes128KeySizeBytes = 16; + +const uint32_t kCENCSchemeID = 0x63656E63; // 'cenc' (AES-CTR): 0x63656E63 +const uint32_t kCBC1SchemeID = 0x63626331; // 'cbc1' (AES-CBC): 0x63626331 +const uint32_t kCENSSchemeID = + 0x63656E73; // 'cens' (AES-CTR subsample): 0x63656E73 +const uint32_t kCBCSSchemeID = + 0x63626373; // 'cbcs' (AES-CBC subsample): 0x63626373 + +// Creates a SHA-256 HMAC signature for the given message. +std::string CreateSignatureHmacSha256(absl::string_view key, + absl::string_view message) { + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init(&ctx, key.data(), key.size(), EVP_sha256()); + HMAC_Update(&ctx, reinterpret_cast(message.data()), + message.size()); + unsigned char digest[SHA256_DIGEST_LENGTH]; + unsigned int digest_len; + HMAC_Final(&ctx, digest, &digest_len); + HMAC_CTX_cleanup(&ctx); + std::string s(reinterpret_cast(digest), digest_len); + return s; +} + +// Compares the SHA-256 HMAC against the provided signature. +bool VerifySignatureHmacSha256(absl::string_view key, + absl::string_view signature, + absl::string_view message) { + return CreateSignatureHmacSha256(key, message) == signature; +} + +// Creates a SHA-1 HMAC signature for the given message. +std::string CreateSignatureHmacSha1(absl::string_view key, + absl::string_view message) { + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init(&ctx, key.data(), key.size(), EVP_sha1()); + HMAC_Update(&ctx, reinterpret_cast(message.data()), + message.size()); + unsigned char digest[SHA_DIGEST_LENGTH]; + unsigned int digest_len; + HMAC_Final(&ctx, digest, &digest_len); + HMAC_CTX_cleanup(&ctx); + std::string s(reinterpret_cast(digest), digest_len); + return s; +} + +// Compares the SHA-1 HMAC against the provided signature. +bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature, + absl::string_view message) { + return CreateSignatureHmacSha1(key, message) == signature; +} + +// Derives an AES 128 key from the provided key and additional info. +std::string DeriveKey(absl::string_view key, absl::string_view label, + absl::string_view context, const uint32_t size_bits) { + if (key.size() != kAes128KeySizeBytes) return ""; + + // We only handle even multiples of 16 bytes (128 bits) right now. + if ((size_bits % 128) || (size_bits > (128 * 255))) { + return ""; + } + + std::string result; + + const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + + for (unsigned char counter = 1; counter <= (size_bits / 128); counter++) { + if (CMAC_Init(cmac_ctx, key.data(), key.size(), cipher, 0)) { + std::string message; + message.append(1, counter); + message.append(label.data(), label.size()); + message.append(1, '\0'); + message.append(context.data(), context.size()); + char size_string[4]; + BigEndian::Store32(&size_string, size_bits); + message.append(&size_string[0], &size_string[0] + 4); + if (CMAC_Update(cmac_ctx, reinterpret_cast(message.data()), + message.size())) { + size_t reslen; + unsigned char res[AES_BLOCK_SIZE]; + if (CMAC_Final(cmac_ctx, res, &reslen)) { + result.append(reinterpret_cast(res), reslen); + } + DCHECK(reslen == AES_BLOCK_SIZE); + } + } + } + + CMAC_CTX_free(cmac_ctx); + return result; +} + +// Derives an IV from the provided info. +std::string DeriveIv(absl::string_view context) { + return DeriveKey(kIvMasterKey, kIvLabel, context, kIvSizeBits); +} + +std::string DeriveGroupSessionKey(absl::string_view context, + const uint32_t size_bits) { + return DeriveKey(kPhonyGroupMasterKey, kGroupKeyLabel, context, size_bits); +} + +std::string DeriveSigningKey(absl::string_view key, absl::string_view context, + const uint32_t size_bits) { + return DeriveKey(key, kSigningKeyLabel, context, size_bits); +} + +bool FourCCEncryptionSchemeIDFromString(const std::string& requested, + uint32_t* four_cc_code) { + if (requested.size() != 4 || four_cc_code == nullptr) return false; + + uint32_t result = 0; + for (auto i = 0; i < 4; ++i) { + result <<= 8; + result |= requested[i]; + } + + switch (result) { + case kCENCSchemeID: + case kCBC1SchemeID: + case kCENSSchemeID: + case kCBCSSchemeID: + *four_cc_code = result; + return true; + default: + return false; + } +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/crypto_util.h b/common/crypto_util.h new file mode 100644 index 0000000..05eaa41 --- /dev/null +++ b/common/crypto_util.h @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Contains common crypto routines for widevine protocols. These routines are +// used as part of licensing and provisioning request handling. + +#ifndef COMMON_CRYPTO_UTIL_H_ +#define COMMON_CRYPTO_UTIL_H_ + +#include + +#include "base/macros.h" +#include "absl/strings/string_view.h" + +namespace widevine { +namespace crypto_util { + +// Default constants used for key derivation for encryption and signing. +// TODO(user): These are duplicated in session.cc in the sdk. de-dup. +extern const char kEncryptionKeyLabel[]; +extern const int kEncryptionKeySizeBits; +extern const char kSigningKeyLabel[]; +extern const int kSigningKeySizeBits; +extern const size_t kSigningKeySizeBytes; +extern const char kIvMasterKey[]; +extern const char kIvLabel[]; +extern const int kIvSizeBits; +extern const int kAes128KeySizeBits; +extern const int kAes128KeySizeBytes; + +extern const uint32_t kCENCSchemeID; // 'cenc' (AES-CTR): 0x63656E63 +extern const uint32_t kCBC1SchemeID; // 'cbc1' (AES-CBC): 0x63626331 +extern const uint32_t kCENSSchemeID; // 'cens' (AES-CTR subsample): 0x63656E73 +extern const uint32_t kCBCSSchemeID; // 'cbcs' (AES-CBC subsample): 0x63626373 + +// DeriveKey uses the NIST 800-108 KDF recommendation, using AES-CMAC PRF. +// NIST 800-108: +// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf +// AES-CMAC: +// http://tools.ietf.org/html/rfc4493 +std::string DeriveKey(absl::string_view key, absl::string_view label, + absl::string_view context, const uint32_t size_bits); + +// Derives an IV from the provided |context|. +std::string DeriveIv(absl::string_view context); + +// Helper function to derive a key using the group master key and context. +std::string DeriveGroupSessionKey(absl::string_view context, const uint32_t size_bits); + +// Helper function to derive a signing key for from the signing context. +std::string DeriveSigningKey(absl::string_view key, absl::string_view context, + const uint32_t size_bits); + +// Helper function to create a SHA-256 HMAC signature for the given message. +std::string CreateSignatureHmacSha256(absl::string_view key, + absl::string_view message); + +// Helper function which compares the SHA-256 HMAC against the provided +// signature. +bool VerifySignatureHmacSha256(absl::string_view key, + absl::string_view signature, + absl::string_view message); + +// Helper function to create a SHA-1 HMAC signature for the given message. +std::string CreateSignatureHmacSha1(absl::string_view key, + absl::string_view message); + +// Helper function which compares the SHA-1 HMAC against the provided +// signature. +bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature, + absl::string_view message); + +// Converts a requested 4CC encryption scheme ID from a std::string to a uint32_t and +// verifies it is a correct value. +bool FourCCEncryptionSchemeIDFromString(const std::string& requested, + uint32_t* four_cc_code); + +} // namespace crypto_util +} // namespace widevine + +#endif // COMMON_CRYPTO_UTIL_H_ diff --git a/common/crypto_util_test.cc b/common/crypto_util_test.cc new file mode 100644 index 0000000..c6ade77 --- /dev/null +++ b/common/crypto_util_test.cc @@ -0,0 +1,222 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Unit tests for the crypto_util helper functions. + +#include "common/crypto_util.h" + +#include + +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace widevine { +namespace crypto_util { + +const char kCENCStr[] = "cenc"; +const char kCBC1Str[] = "cbc1"; +const char kCENSStr[] = "cens"; +const char kCBCSStr[] = "cbcs"; + +static unsigned char key_data[] = + { 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e, + 0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b }; + +static std::string key_str(key_data, key_data + sizeof(key_data)); + +static unsigned char iv_data[] = + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + +static std::string iv_str(iv_data, iv_data + sizeof(iv_data)); + +TEST(CryptoUtilTest, DeriveAes128KeyTest) { + unsigned char label[] = { 0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1, + 0x92, 0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08, + 0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60, + 0xd1, 0xcf, 0x6a, 0x4d, 0x40, 0xa1, 0xe3, 0xfe, + 0xd3, 0xe9, 0xa6, 0x58, 0x4c, 0xd4, 0xad, 0xa4, + 0xa2 }; + + unsigned char context[] = { 0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d, 0x6d, + 0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5, 0x5b, 0x80, + 0x81, 0x91, 0xff }; + + unsigned char output0[] = { 0xd5, 0xad, 0x2d, 0xb1, 0x5a, 0x06, 0xcb, 0x50, + 0xf2, 0x59, 0x5a, 0xb2, 0xb2, 0x0d, 0x44, 0x4e }; + + unsigned char output1[] = { 0xdf, 0x38, 0x45, 0x97, 0x5d, 0x7a, 0x81, 0xb4, + 0x94, 0x86, 0xaf, 0x0c, 0xdc, 0x4d, 0xeb, 0x62, + 0x31, 0x39, 0x67, 0x8f, 0xff, 0x5d, 0x68, 0x35, + 0xdc, 0x89, 0x5f, 0x47, 0xca, 0xe0, 0x2d, 0x3a, + 0x10, 0x24, 0xf8, 0x7e, 0x5b, 0x70, 0xe1, 0xa3, + 0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22 }; + + std::string label_str(label, label + sizeof(label)); + std::string key_str(key_data, key_data + sizeof(key_data)); + std::string context_str(context, context + sizeof(context)); + std::string result = DeriveKey(key_str, label_str, context_str, 128); + + std::string output_128(output0, output0 + sizeof(output0)); + + ASSERT_EQ(result, output_128); + + result = DeriveKey(key_str, label_str, context_str, 384); + + std::string output_384(output1, output1 + sizeof(output1)); + + ASSERT_EQ(result, output_384); +} + +TEST(CryptoUtilTest, DeriveGroupSesionKey) { + unsigned char output[] = { 0x92, 0x6c, 0x2f, 0x5, 0xa6, 0x4f, 0xff, 0xb1, + 0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1 }; + std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128); + EXPECT_EQ(crypto_util::kAes128KeySizeBytes, group_session_key.size()); + const std::string output_128(output, output + sizeof(output)); + ASSERT_EQ(output_128, group_session_key); +} + +TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) { + unsigned char message_data[] = { + 0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, + 0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2, + 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91, + 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, + 0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, + 0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5, + 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54, + 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda }; + + std::string message(message_data, message_data + sizeof(message_data)); + std::string signature(CreateSignatureHmacSha256(key_str, message)); + + ASSERT_EQ(signature.size(), 32); + + ASSERT_TRUE(VerifySignatureHmacSha256(key_str, signature, message)); +} + +TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) { + unsigned char message_data[] = { + 0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, + 0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2, + 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91, + 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, + 0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, + 0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5, + 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54, + 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda }; + + std::string message(message_data, message_data + sizeof(message_data)); + // Test with bogus key; + std::string bogus_key("bogus"); + std::string signature(CreateSignatureHmacSha256(bogus_key, message)); + + // This should still produce an hmac signature. + ASSERT_EQ(signature.size(), 32); + + // Create valid signature to compare. + signature = CreateSignatureHmacSha256(key_str, message); + + // Test with bogus key. + ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message)); + + // Test with munged signature. + signature[0] = 0xFF; + ASSERT_FALSE(VerifySignatureHmacSha256(key_str, signature, message)); + + // Test with bogus signature. + ASSERT_FALSE(VerifySignatureHmacSha256(key_str, "bogus", message)); +} + +TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) { + unsigned char message_data[] = { + 0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, + 0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2, + 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91, + 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, + 0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, + 0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5, + 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54, + 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda }; + + std::string message(message_data, message_data + sizeof(message_data)); + std::string signature(CreateSignatureHmacSha1(key_str, message)); + + ASSERT_EQ(20, signature.size()); + ASSERT_TRUE(VerifySignatureHmacSha1(key_str, signature, message)); +} + +TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) { + unsigned char message_data[] = { + 0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, + 0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2, + 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91, + 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, + 0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, + 0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5, + 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54, + 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda }; + + std::string message(message_data, message_data + sizeof(message_data)); + // Test with bogus key; + std::string bogus_key("bogus"); + std::string signature(CreateSignatureHmacSha1(bogus_key, message)); + + // This should still produce an hmac signature. + ASSERT_EQ(20, signature.size()); + // Create valid signature to compare. + signature = CreateSignatureHmacSha1(key_str, message); + // Test with bogus key. + ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message)); + // Test with munged signature. + signature[0] = 0xFF; + ASSERT_FALSE(VerifySignatureHmacSha1(key_str, signature, message)); + // Test with bogus signature. + ASSERT_FALSE(VerifySignatureHmacSha1(key_str, "bogus", message)); +} + +TEST(CryptoUtilTest, DeriveIv) { + // First value in the pair is the key_id, second value is the expected IV. + std::pair id_iv_pairs[] = { + {"1234567890123456", "3278234c7682d1a2e153af4912975f5f"}, + {"0987654321098765", "cf09abd30f04b60544910791a6b904cf"}}; + for (const auto& id_iv_pair : id_iv_pairs) { + SCOPED_TRACE(absl::StrCat("test case:", id_iv_pair.first)); + EXPECT_EQ(id_iv_pair.second, + absl::BytesToHexString(DeriveIv(id_iv_pair.first))); + // Repeat same call to verify derivied result is repeatable. + EXPECT_EQ(id_iv_pair.second, + absl::BytesToHexString(DeriveIv(id_iv_pair.first))); + } +} + +TEST(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) { + uint32_t cc_code; + ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code)); + ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code)); + ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code)); +} + +TEST(CryptoUtilTest, Verify4CCEncryptionIDFromString) { + uint32_t cc_code = 0; + ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code)); + ASSERT_EQ(kCENCSchemeID, cc_code); + ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBC1Str, &cc_code)); + ASSERT_EQ(kCBC1SchemeID, cc_code); + ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENSStr, &cc_code)); + ASSERT_EQ(kCENSSchemeID, cc_code); + ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBCSStr, &cc_code)); + ASSERT_EQ(kCBCSSchemeID, cc_code); +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/drm_root_certificate.cc b/common/drm_root_certificate.cc new file mode 100644 index 0000000..bd5eb43 --- /dev/null +++ b/common/drm_root_certificate.cc @@ -0,0 +1,319 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/drm_root_certificate.h" + +#include + +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "openssl/sha.h" +#include "common/error_space.h" +#include "common/rsa_key.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +// From common::TestCertificates. +// TODO(user): common::test_certificates is a testonly target, consider +// how to use instead of dupliciating the test cert here. +static const unsigned char kTestRootCertificate[] = { + 0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22, + 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5, + 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, + 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f, + 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, + 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b, + 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, + 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01, + 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, + 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14, + 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, + 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6, + 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, + 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31, + 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, + 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90, + 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, + 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f, + 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, + 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17, + 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, + 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf, + 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, + 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42, + 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, + 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4, + 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, + 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72, + 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, + 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04, + 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, + 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33, + 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, + 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a, + 0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35, + 0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe, + 0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49, + 0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6, + 0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58, + 0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce, + 0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27, + 0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5, + 0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c, + 0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e, + 0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12, + 0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54, + 0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8, + 0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1, + 0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1, + 0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8, + 0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4, + 0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9, + 0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd, + 0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9, + 0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f, + 0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92, + 0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15, + 0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0, + 0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9, + 0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1, + 0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2, + 0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c, + 0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36, + 0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35, + 0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7, + 0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44}; + +static const unsigned char kDevRootCertificate[] = { + 0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xc3, 0x94, 0x88, + 0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, + 0x81, 0x00, 0xc0, 0x00, 0x36, 0x6f, 0x8e, 0xe9, 0xcf, 0x86, 0xdb, 0xcd, + 0xdd, 0x4e, 0xfd, 0xcd, 0x45, 0xbf, 0x6d, 0x96, 0x05, 0x00, 0xb8, 0x72, + 0xff, 0x9c, 0xb4, 0x39, 0xa8, 0xd8, 0xc0, 0x09, 0x73, 0xc0, 0x24, 0x6a, + 0x39, 0x4d, 0x36, 0x3f, 0x9a, 0xe4, 0xb8, 0x76, 0xdc, 0x34, 0xe3, 0xee, + 0x5f, 0xdd, 0x13, 0x20, 0x08, 0xdc, 0x4e, 0x6f, 0x4e, 0x9f, 0xc0, 0x36, + 0xf9, 0xce, 0xc6, 0xb7, 0xdb, 0xe0, 0x51, 0x2d, 0x30, 0x0b, 0xae, 0x0a, + 0x20, 0xd2, 0x29, 0x3c, 0x2c, 0x1d, 0x87, 0x65, 0xeb, 0x5f, 0x93, 0xd7, + 0x3f, 0x12, 0x08, 0x50, 0x0e, 0x55, 0xf3, 0xf1, 0x19, 0xee, 0x18, 0x21, + 0x6e, 0xea, 0xb6, 0x0a, 0x4a, 0x0b, 0x9c, 0x72, 0x37, 0xeb, 0x0b, 0x68, + 0xfc, 0x52, 0x46, 0x62, 0xd0, 0xa2, 0x99, 0x66, 0xe2, 0x2b, 0x74, 0xdd, + 0x5c, 0xaf, 0x9a, 0x03, 0xc4, 0x5d, 0x93, 0xfb, 0xcd, 0x45, 0x9a, 0xee, + 0xfb, 0x7b, 0x18, 0x94, 0xc1, 0x8c, 0x82, 0x34, 0x7f, 0x02, 0x12, 0x21, + 0xfc, 0x40, 0xc1, 0x50, 0xc9, 0xf4, 0x7c, 0xd5, 0x96, 0xbe, 0x55, 0x7f, + 0x3c, 0x1d, 0x70, 0x34, 0xb4, 0xa2, 0x03, 0xc4, 0x3f, 0x89, 0x60, 0xe4, + 0x24, 0x09, 0x1a, 0x74, 0xc4, 0xb6, 0x39, 0xf0, 0x34, 0x60, 0x8e, 0xa7, + 0x5f, 0x02, 0x7f, 0xb9, 0x2a, 0xc5, 0xaa, 0xb2, 0x4c, 0x34, 0xd3, 0x5a, + 0x5d, 0xfa, 0x07, 0xf2, 0xb9, 0xb3, 0xc1, 0xba, 0xab, 0xbe, 0x89, 0x99, + 0xe3, 0x6d, 0x9b, 0xa9, 0xd3, 0xaf, 0x2a, 0x08, 0x76, 0xf3, 0x0e, 0xc9, + 0xe0, 0xb3, 0xbf, 0x51, 0x0c, 0xc5, 0xf4, 0xf3, 0x15, 0x7b, 0x08, 0x11, + 0x8f, 0x61, 0x1f, 0x61, 0x64, 0xdb, 0x15, 0x84, 0x5b, 0x8a, 0xd1, 0x28, + 0x40, 0xde, 0xc5, 0x32, 0xb5, 0xad, 0xad, 0x65, 0x4c, 0xf5, 0xf7, 0xd1, + 0x90, 0x14, 0x5d, 0xc2, 0x85, 0x98, 0xcc, 0xe9, 0xe6, 0x95, 0x42, 0xe1, + 0x3e, 0xfc, 0x7f, 0xc4, 0x49, 0xed, 0x9c, 0xe4, 0x49, 0x3f, 0x03, 0x1b, + 0x0d, 0xa0, 0xfb, 0xf5, 0x38, 0x49, 0xd2, 0xdf, 0xa3, 0x88, 0xb2, 0x76, + 0x93, 0x08, 0x20, 0x18, 0xfe, 0xdc, 0x72, 0x6c, 0x6e, 0xbf, 0x61, 0x37, + 0x03, 0xdb, 0xe5, 0x72, 0x68, 0xe0, 0x99, 0x2f, 0xb9, 0xe0, 0x2e, 0xbb, + 0x9f, 0x96, 0x36, 0x61, 0xaa, 0x2d, 0xa4, 0x93, 0xe8, 0x50, 0x58, 0xe6, + 0x61, 0xe1, 0x14, 0xcf, 0xac, 0x86, 0x98, 0x7f, 0x3c, 0x67, 0x16, 0xce, + 0xb8, 0x70, 0x90, 0x3a, 0x5a, 0xd4, 0xe1, 0xe2, 0x35, 0x98, 0xbf, 0x93, + 0x41, 0x11, 0xb2, 0x44, 0xb2, 0x64, 0xc2, 0xe7, 0x09, 0x45, 0xb7, 0x6f, + 0xb0, 0xbd, 0x6e, 0xe8, 0x67, 0xfa, 0x8d, 0xd4, 0xfa, 0x4b, 0xef, 0xa8, + 0x9d, 0x8a, 0x0a, 0xd9, 0x14, 0x77, 0x09, 0x11, 0x9e, 0xc3, 0x50, 0x14, + 0x6c, 0x45, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x17, 0x01, + 0x60, 0x24, 0xe1, 0xfd, 0x75, 0x60, 0x17, 0x5c, 0x5e, 0x6f, 0x9f, 0x7f, + 0xdf, 0xee, 0xf0, 0xf7, 0x7d, 0xb2, 0x50, 0x65, 0x36, 0x26, 0x14, 0x19, + 0x01, 0x5e, 0x98, 0x94, 0x65, 0x97, 0x83, 0xaa, 0x4a, 0x2b, 0x98, 0x2e, + 0x02, 0xf3, 0xb2, 0xc9, 0xb2, 0xed, 0xd3, 0x1b, 0x20, 0x27, 0x9e, 0xe1, + 0x25, 0xc7, 0x86, 0xf0, 0x66, 0x68, 0x5d, 0xd2, 0x3d, 0xa7, 0xbb, 0xbc, + 0x22, 0xfc, 0x29, 0xfa, 0x17, 0x16, 0xf4, 0xa2, 0x00, 0x10, 0x87, 0xb4, + 0x5d, 0x51, 0x45, 0x6b, 0xc8, 0xf4, 0x6b, 0xcc, 0x92, 0x91, 0xe7, 0xa7, + 0x93, 0xbc, 0xc7, 0x2e, 0xdc, 0xac, 0x82, 0x2b, 0x85, 0x56, 0x7b, 0xae, + 0xf2, 0xd8, 0xda, 0xa6, 0xd7, 0xfa, 0x6d, 0x70, 0x2a, 0x2e, 0xcf, 0x69, + 0xef, 0x57, 0x91, 0xa7, 0xaa, 0x40, 0x15, 0x4a, 0x49, 0x1b, 0xbc, 0x36, + 0xbb, 0x1c, 0x94, 0x33, 0x36, 0x61, 0x22, 0x9d, 0x22, 0x66, 0xf0, 0x88, + 0x5e, 0x7c, 0x3c, 0xa5, 0xff, 0x81, 0xcf, 0x1a, 0x44, 0xa1, 0x2b, 0xdf, + 0xc9, 0x3d, 0xd5, 0xc7, 0xc7, 0x3a, 0x75, 0xac, 0x29, 0xfa, 0xfd, 0x5b, + 0xda, 0xf5, 0x8f, 0xd9, 0xdf, 0x08, 0xa4, 0x8d, 0x19, 0x4a, 0xa4, 0x79, + 0x6e, 0x47, 0xf6, 0x07, 0xe0, 0xbd, 0xbf, 0x30, 0x3a, 0xf9, 0xf5, 0xc0, + 0x90, 0x6d, 0x70, 0x27, 0x44, 0xa8, 0x5e, 0x70, 0xcd, 0x43, 0x3e, 0xaf, + 0xf0, 0xd7, 0x20, 0xd3, 0x5e, 0x97, 0x2d, 0x32, 0x1a, 0x3d, 0x2d, 0x0f, + 0x0f, 0xcf, 0xac, 0x4e, 0x88, 0x75, 0x98, 0x6c, 0xfa, 0xe8, 0x42, 0x58, + 0x99, 0xaa, 0x45, 0x0c, 0x41, 0x0c, 0x6e, 0x27, 0x58, 0x57, 0xd2, 0x5b, + 0x82, 0x3d, 0x75, 0x2f, 0x9e, 0xf3, 0xe4, 0x00, 0xcf, 0x91, 0x48, 0x25, + 0xca, 0x98, 0xf2, 0x91, 0x6b, 0x41, 0xa5, 0xe8, 0xcd, 0x64, 0xa7, 0x2e, + 0x78, 0xc7, 0x76, 0x82, 0x3f, 0xf8, 0x57, 0x8a, 0x9d, 0x78, 0x25, 0xad, + 0xf3, 0x1a, 0x8b, 0xfc, 0x83, 0x9a, 0x98, 0x87, 0xe4, 0x55, 0x3e, 0x1c, + 0xa7, 0x80, 0x8f, 0xd6, 0x76, 0xab, 0x03, 0xc7, 0x05, 0x66, 0xc3, 0xa0, + 0x4c, 0x33, 0x1f, 0x39, 0x74, 0x1b, 0x2a, 0xbf, 0xe6, 0xb0, 0x9f, 0x6b, + 0xc1, 0xd6, 0xd3, 0xf4, 0x46, 0x9b, 0xf3, 0xab, 0xca, 0x2e, 0x88, 0x3d, + 0x84, 0x5f, 0xc9, 0x9b, 0x47, 0xbb, 0x57, 0x64, 0x08, 0x0e, 0x18, 0x74, + 0x83, 0x44, 0xd4, 0xc3, 0x18, 0x97, 0xcf, 0x89, 0x6a, 0x49, 0x51, 0xc6, + 0xff, 0x8d, 0x39, 0xc5, 0x23, 0xf9, 0xd5, 0x01, 0xd7, 0x2f, 0xa9, 0xa5, + 0x5d, 0xa9, 0xf3, 0xc9, 0xfd, 0xc4, 0x52, 0x19, 0x7d, 0xf6, 0xa4, 0x2c, + 0x0c, 0xa0, 0x07, 0xdf, 0x7b, 0x44, 0xd7, 0xe5, 0xbf, 0x57, 0x87, 0xc9, + 0x8c, 0xfe, 0x30, 0xb2, 0x89, 0x5d, 0x00, 0x03, 0x3b, 0xe5}; + +static const unsigned char kProdRootCertificate[] = { + 0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xdd, 0x94, 0x88, + 0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, + 0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, + 0x97, 0x09, 0xe8, 0x68, 0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, + 0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0, + 0x57, 0x03, 0x53, 0x4c, 0xf6, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, + 0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe, + 0x66, 0xdf, 0xc5, 0x21, 0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, + 0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, + 0xbe, 0x34, 0x23, 0x8b, 0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, + 0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, + 0x0f, 0x92, 0xd6, 0x4c, 0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, + 0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, + 0x9a, 0x97, 0xeb, 0x84, 0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, + 0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, + 0x72, 0xa0, 0xfa, 0xc1, 0xbd, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, + 0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc, + 0xfd, 0x82, 0x48, 0x2b, 0xb7, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, + 0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e, + 0x65, 0xf5, 0xc8, 0x6c, 0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, + 0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98, + 0x79, 0x52, 0x5e, 0x45, 0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, + 0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, + 0x4f, 0x5e, 0x48, 0x77, 0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, + 0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, + 0xe4, 0x29, 0x06, 0x5e, 0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, + 0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, + 0xec, 0x1f, 0x11, 0xb3, 0x24, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, + 0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, + 0x68, 0x15, 0x84, 0xff, 0xa5, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4, + 0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12, + 0xd2, 0x88, 0x6d, 0x41, 0x3d, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4, + 0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5, + 0xce, 0xa6, 0x96, 0x55, 0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, + 0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x58, 0xf1, + 0xd6, 0x4d, 0x04, 0x09, 0x7b, 0xdf, 0xd7, 0xef, 0x5d, 0x3b, 0x02, 0x39, + 0x17, 0xfa, 0x14, 0x36, 0x75, 0x4a, 0x38, 0x67, 0x85, 0x57, 0x12, 0xa7, + 0x14, 0xee, 0x35, 0x16, 0xd5, 0x3d, 0xbf, 0x42, 0x86, 0xf6, 0x69, 0x00, + 0x76, 0xcd, 0x93, 0xf4, 0x7c, 0xb2, 0xdf, 0x9e, 0x44, 0xcd, 0x4c, 0xd4, + 0xae, 0x09, 0x18, 0x53, 0x44, 0x32, 0xec, 0xe0, 0x61, 0x1b, 0xe5, 0xda, + 0x13, 0xd3, 0x55, 0xc5, 0xdd, 0x1a, 0xcb, 0x90, 0x1e, 0x7e, 0x5b, 0xc6, + 0xe9, 0x0f, 0x22, 0x9f, 0xbe, 0x85, 0x02, 0xfe, 0x90, 0x31, 0xcc, 0x6b, + 0x03, 0x84, 0xbd, 0x22, 0xc4, 0x55, 0xfa, 0xf5, 0xf2, 0x08, 0xcd, 0x65, + 0x41, 0x58, 0xe8, 0x7d, 0x29, 0xda, 0x04, 0x58, 0x82, 0xf5, 0x37, 0x69, + 0xbc, 0xf3, 0x5a, 0x57, 0x84, 0x17, 0x7b, 0x32, 0x87, 0x70, 0xb2, 0xb0, + 0x76, 0x9c, 0xb2, 0xc3, 0x15, 0xd1, 0x11, 0x26, 0x2a, 0x23, 0x75, 0x99, + 0x3e, 0xb9, 0x77, 0x22, 0x32, 0x0d, 0xbc, 0x1a, 0x19, 0xc1, 0xd5, 0x65, + 0x90, 0x76, 0x55, 0x74, 0x0f, 0x0e, 0x69, 0x4d, 0x5f, 0x4d, 0x8f, 0x19, + 0xaf, 0xdf, 0xd6, 0x16, 0x31, 0x94, 0xa8, 0x92, 0x5f, 0x4f, 0xbc, 0x7a, + 0x31, 0xf8, 0xae, 0x8e, 0xad, 0x33, 0xb7, 0xe9, 0x30, 0xd0, 0x8c, 0x0a, + 0x8a, 0x6c, 0x83, 0x35, 0xf8, 0x8a, 0x81, 0xb2, 0xfe, 0x1c, 0x88, 0xac, + 0x2a, 0x66, 0xc5, 0xff, 0xbd, 0xe6, 0x17, 0xd0, 0x62, 0x0b, 0xdc, 0x8a, + 0x45, 0xf7, 0xb0, 0x3e, 0x5a, 0xc8, 0x1e, 0x4a, 0x24, 0x2f, 0x6c, 0xa5, + 0xe3, 0x1c, 0x88, 0x14, 0x83, 0xd5, 0xc5, 0xef, 0x5e, 0x9f, 0x3d, 0x85, + 0x45, 0x73, 0xe2, 0x6b, 0x50, 0x52, 0x57, 0x4c, 0xfb, 0x92, 0x6c, 0x66, + 0x75, 0x8a, 0xd6, 0x0d, 0x1b, 0xae, 0xf3, 0xec, 0xaf, 0x51, 0x22, 0x03, + 0x5d, 0x0a, 0x2e, 0x63, 0x93, 0x9c, 0x0b, 0x01, 0x20, 0xa8, 0xa9, 0x84, + 0x2e, 0x17, 0xca, 0xae, 0x73, 0xec, 0x22, 0x1b, 0x79, 0xae, 0xf6, 0xa0, + 0x72, 0x2c, 0xdf, 0x07, 0x47, 0xdb, 0x88, 0x86, 0x30, 0x14, 0x78, 0x21, + 0x11, 0x22, 0x88, 0xac, 0xd7, 0x54, 0x74, 0xf9, 0xf3, 0x26, 0xc2, 0xa5, + 0x56, 0xc8, 0x56, 0x4f, 0x00, 0x29, 0x1d, 0x08, 0x7b, 0x7a, 0xfb, 0x95, + 0x89, 0xc3, 0xee, 0x98, 0x54, 0x9e, 0x3c, 0x6b, 0x94, 0x05, 0x13, 0x12, + 0xf6, 0x71, 0xb9, 0xab, 0x13, 0xc3, 0x0c, 0x9b, 0x46, 0x08, 0x7b, 0x3d, + 0x32, 0x6a, 0x68, 0xca, 0x1e, 0x9c, 0x90, 0x62, 0xc5, 0xed, 0x10, 0xb9, + 0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd, 0xc4, 0x46, 0xf5, 0xa3, + 0x62, 0x13, 0x74, 0x02, 0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf, + 0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5, 0x02, 0x0c}; + +util::Status DrmRootCertificate::Create( + const std::string& signed_drm_certificate, + std::unique_ptr* cert) { + CHECK(cert); + + SignedDrmCertificate signed_root_cert; + if (!signed_root_cert.ParseFromString(signed_drm_certificate)) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "signed-root-cert-deserialize-fail"); + } + DrmCertificate root_cert; + if (!signed_root_cert.has_drm_certificate()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-root-device-certificate"); + } + if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "root-cert-deserialize-fail"); + } + if (!root_cert.has_public_key()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-root-cert-public-key"); + } + if (!signed_root_cert.has_signature()) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "missing-root-certificate-signature"); + } + std::unique_ptr public_key( + RsaPublicKey::Create(root_cert.public_key())); + if (!public_key) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-root-public-key"); + } + if (!public_key->VerifySignature(signed_root_cert.drm_certificate(), + signed_root_cert.signature())) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "invalid-root-certificate-signature"); + } + cert->reset(new DrmRootCertificate(root_cert.public_key())); + return util::OkStatus(); +} + +util::Status DrmRootCertificate::CreateByType( + CertificateType cert_type, std::unique_ptr* cert) { + CHECK(cert); + return Create(GetDrmRootCertificate(cert_type), cert); +} + +std::string DrmRootCertificate::GetDrmRootCertificate(CertificateType cert_type) { + std::string root_cert; + switch (cert_type) { + case kCertificateTypeProduction: { + root_cert.assign(kProdRootCertificate, + kProdRootCertificate + sizeof(kProdRootCertificate)); + break; + } + case kCertificateTypeDevelopment: { + root_cert.assign(kDevRootCertificate, + kDevRootCertificate + sizeof(kDevRootCertificate)); + break; + } + case kCertificateTypeTesting: { + root_cert.assign(kTestRootCertificate, + kTestRootCertificate + sizeof(kTestRootCertificate)); + break; + } + default: + // TODO(user): Consider returning util::Status indicating unsupported + // cert type. + break; + } + return root_cert; +} + +std::string DrmRootCertificate::GetDigest(CertificateType cert_type) { + std::string cert(GetDrmRootCertificate(cert_type)); + if (cert.empty()) { + return std::string(); + } + std::string hash(SHA256_DIGEST_LENGTH, 0); + SHA256(reinterpret_cast(cert.data()), cert.size(), + reinterpret_cast(&hash[0])); + return absl::BytesToHexString(hash); +} + +} // namespace widevine diff --git a/common/drm_root_certificate.h b/common/drm_root_certificate.h new file mode 100644 index 0000000..d700cec --- /dev/null +++ b/common/drm_root_certificate.h @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Root device certificate holder class which deserializes, validates, +// and extracts the root certificate public key. + +#ifndef COMMON_DRM_ROOT_CERTIFICATE_H__ +#define COMMON_DRM_ROOT_CERTIFICATE_H__ + +#include +#include + +#include "base/macros.h" +#include "util/status.h" + +#include "common/certificate_type.h" + +namespace widevine { + +class DrmRootCertificate { + public: + virtual ~DrmRootCertificate() {} + // Creates a DrmRootCertificate object given a certificate type. + // |cert| may not be nullptr, and it points to a + // std::unique_ptr which will be used to return a newly + // created DrmRootCertificate* if successful. The caller assumes ownership of + // the new DrmRootCertificate. This method returns util::Status::OK on + // success, or appropriate error status otherwise. + static util::Status CreateByType(CertificateType cert_type, + std::unique_ptr* cert); + // Returns the hex-encoded SHA-256 digest for the specified root certificate. + static std::string GetDigest(CertificateType cert_type); + // Given |cert_type|, the appropiate root certificate is returned as + // a serialized SignedDrmCertificates. + static std::string GetDrmRootCertificate(CertificateType cert_type); + const std::string& public_key() const { return public_key_; } + + // Verifies a DRM certificate. + private: + friend class DrmRootCertificateTest; + + // Creates a DrmRootCertificate object given a serialized + // SignedDrmCertificate. |cert| may not be nullptr, and it points to a + // std::unique_ptr which will be used to return a newly + // created DrmRootCertificate* if successful. The caller assumes ownership of + // the new DrmRootCertificate. This method returns util::Status::OK on + // success, or appropriate error status otherwise. + // TODO(user): Consider moving to private. + static util::Status Create(const std::string& signed_drm_certificate, + std::unique_ptr* cert); + explicit DrmRootCertificate(const std::string& public_key) + : public_key_(public_key) {} + + std::string public_key_; + DISALLOW_IMPLICIT_CONSTRUCTORS(DrmRootCertificate); +}; + +} // namespace widevine + +#endif // COMMON_DRM_ROOT_CERTIFICATE_H__ diff --git a/common/drm_root_certificate_test.cc b/common/drm_root_certificate_test.cc new file mode 100644 index 0000000..498d91e --- /dev/null +++ b/common/drm_root_certificate_test.cc @@ -0,0 +1,101 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Unit tests for drm_root_certificate.cc + +#include "common/drm_root_certificate.h" + +#include + +#include "testing/gunit.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +class DrmRootCertificateTest : public testing::Test { + protected: + DrmRootCertificateTest() {} + util::Status DrmRootCertificateCreate( + const std::string& signed_drm_certificate, + std::unique_ptr* cert) { + return DrmRootCertificate::Create(signed_drm_certificate, cert); + } +}; + +TEST_F(DrmRootCertificateTest, DrmRootCertificateCreation) { + RsaTestKeys test_keys; + std::unique_ptr root_cert; + + // First, invalid serialized cert. Should fail. + EXPECT_EQ(INVALID_DRM_CERTIFICATE, + DrmRootCertificateCreate("bad_cert", &root_cert).error_code()); + SignedDrmCertificate signed_cert; + std::string serialized; + // Serialized empty cert. Should fail. + ASSERT_TRUE(signed_cert.SerializeToString(&serialized)); + EXPECT_NE(util::OkStatus(), + DrmRootCertificateCreate(serialized, &root_cert)); + // Add public key. Should still fail. + DrmCertificate drm_cert; + drm_cert.set_public_key(test_keys.public_test_key_1_3072_bits()); + ASSERT_TRUE( + drm_cert.SerializeToString(signed_cert.mutable_drm_certificate())); + ASSERT_TRUE(signed_cert.SerializeToString(&serialized)); + EXPECT_EQ(INVALID_DRM_CERTIFICATE, + DrmRootCertificateCreate(serialized, &root_cert).error_code()); + // Now self-sign the cert. Should succeed. + std::unique_ptr private_key( + RsaPrivateKey::Create(test_keys.private_test_key_1_3072_bits())); + ASSERT_TRUE(private_key.get()); + ASSERT_TRUE(private_key->GenerateSignature(signed_cert.drm_certificate(), + signed_cert.mutable_signature())); + ASSERT_TRUE(signed_cert.SerializeToString(&serialized)); + EXPECT_EQ(util::OkStatus(), + DrmRootCertificateCreate(serialized, &root_cert)); + ASSERT_TRUE(root_cert); + // Verify the public key. + EXPECT_EQ(test_keys.public_test_key_1_3072_bits(), root_cert->public_key()); +} + +TEST_F(DrmRootCertificateTest, DrmRootCertificateCreationByType) { + std::unique_ptr root_cert; + EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeTesting, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeDevelopment, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); + EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( + kCertificateTypeProduction, &root_cert)); + ASSERT_TRUE(root_cert != nullptr); +} + +TEST_F(DrmRootCertificateTest, DrmRootCertificateDigest) { + const std::string test_cert_hash( + "49f917b1bdfed78002a58e799a58e940" + "1fffaaed9d8d80752782b066757e2c8c"); + const std::string dev_cert_hash( + "0e25ee95476a770f30b98ac5ef778b3f" + "137b66c29385b84f547a361b4724b17d"); + const std::string prod_cert_hash( + "d62fdabc9286648a81f7d3bedaf2f5a5" + "27bbad39bc38da034ba98a21569adb9b"); + EXPECT_EQ(test_cert_hash, + DrmRootCertificate::GetDigest(kCertificateTypeTesting)); + EXPECT_EQ(dev_cert_hash, + DrmRootCertificate::GetDigest(kCertificateTypeDevelopment)); + EXPECT_EQ(prod_cert_hash, + DrmRootCertificate::GetDigest(kCertificateTypeProduction)); +} + +} // namespace widevine diff --git a/common/drm_service_certificate.cc b/common/drm_service_certificate.cc new file mode 100644 index 0000000..4a3dc94 --- /dev/null +++ b/common/drm_service_certificate.cc @@ -0,0 +1,314 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/drm_service_certificate.h" + +#include +#include +#include + +#include "glog/logging.h" +#include "base/thread_annotations.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" +#include "util/gtl/map_util.h" +#include "common/aes_cbc_util.h" +#include "common/drm_root_certificate.h" +#include "common/error_space.h" +#include "common/rsa_util.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +namespace { + +// Class used to hold global service certificate map. +class DrmServiceCertificateMap { + public: + DrmServiceCertificateMap(); + ~DrmServiceCertificateMap(); + + DrmServiceCertificateMap(const DrmServiceCertificateMap&) = delete; + DrmServiceCertificateMap& operator=(const DrmServiceCertificateMap&) = delete; + + void Reset(); + void AddCert(std::unique_ptr new_cert); + void ClearDefaultDrmServiceCertificate(); + const DrmServiceCertificate* GetDefaultCert(); + const DrmServiceCertificate* GetCert(const std::string& serial_number); + + static DrmServiceCertificateMap* GetInstance(); + + private: + absl::Mutex mutex_; + // Certificate serial number to certificate map. + std::map> map_ + GUARDED_BY(mutex_); + DrmServiceCertificate* default_cert_ GUARDED_BY(mutex_); +}; + +DrmServiceCertificateMap::DrmServiceCertificateMap() : default_cert_(nullptr) {} +DrmServiceCertificateMap::~DrmServiceCertificateMap() { Reset(); } + +void DrmServiceCertificateMap::Reset() { + absl::WriterMutexLock lock(&mutex_); + map_.clear(); + default_cert_ = nullptr; +} + +void DrmServiceCertificateMap::AddCert( + std::unique_ptr new_cert) { + absl::WriterMutexLock lock(&mutex_); + + std::unique_ptr* previous_cert = + gtl::FindOrNull(map_, new_cert->serial_number()); + if (previous_cert != nullptr) { + if (default_cert_ == previous_cert->get()) { + default_cert_ = nullptr; + } + } + + if (default_cert_ == nullptr) { + default_cert_ = new_cert.get(); + } + const std::string& serial_number = new_cert->serial_number(); + map_[serial_number] = std::move(new_cert); +} + +void DrmServiceCertificateMap::ClearDefaultDrmServiceCertificate() { + absl::WriterMutexLock lock(&mutex_); + default_cert_ = nullptr; +} + +const DrmServiceCertificate* DrmServiceCertificateMap::GetDefaultCert() { + absl::ReaderMutexLock lock(&mutex_); + return default_cert_; +} + +const DrmServiceCertificate* DrmServiceCertificateMap::GetCert( + const std::string& serial_number) { + absl::ReaderMutexLock lock(&mutex_); + return map_[serial_number].get(); +} + +DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() { + static auto* const kInstance = new DrmServiceCertificateMap(); + return kInstance; +} + +} // namespace + +util::Status DrmServiceCertificate::AddDrmServiceCertificate( + const std::string& root_public_key, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + std::unique_ptr root_key(RsaPublicKey::Create(root_public_key)); + if (root_key == nullptr) { + return util::Status(error_space, INVALID_DRM_CERTIFICATE, + "root-certificate-rsa-public-key-failed"); + } + SignedDrmCertificate signed_cert; + if (!signed_cert.ParseFromString(service_certificate)) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "signed-certificate-parse-failed"); + } + if (!root_key->VerifySignature(signed_cert.drm_certificate(), + signed_cert.signature())) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "certificate-signature-verification-failed"); + } + DrmCertificate drm_cert; + if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "certificate-parse-failed"); + } + if (drm_cert.type() != DrmCertificate::SERVICE) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "not-service-certificate"); + } + if (drm_cert.serial_number().empty()) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing-certificate-serial-number"); + } + if (drm_cert.provider_id().empty()) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing-certificate-service-id"); + } + if (!drm_cert.has_creation_time_seconds()) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing-certificate-creation-time"); + } + if (drm_cert.public_key().empty()) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "missing-certificate-public-key"); + } + std::unique_ptr public_key( + RsaPublicKey::Create(drm_cert.public_key())); + if (!public_key) { + return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, + "invalid-certificate-public-key"); + } + std::string pkcs1_key; + if (!rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey( + service_private_key, service_private_key_passphrase, &pkcs1_key)) { + return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY, + "key-decryption-failed"); + } + std::unique_ptr private_key(RsaPrivateKey::Create(pkcs1_key)); + if (private_key == nullptr) { + return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY, + "invalid-private-key"); + } + + std::unique_ptr new_cert(new DrmServiceCertificate( + service_certificate, drm_cert.provider_id(), drm_cert.serial_number(), + drm_cert.creation_time_seconds(), std::move(public_key), + std::move(private_key))); + DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert)); + + return util::OkStatus(); +} + +util::Status DrmServiceCertificate::AddDrmServiceCertificate( + CertificateType root_cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + std::unique_ptr root_cert; + util::Status status = + DrmRootCertificate::CreateByType(root_cert_type, &root_cert); + if (!status.ok()) { + return status; + } + return AddDrmServiceCertificate(root_cert->public_key(), service_certificate, + service_private_key, + service_private_key_passphrase); +} + +const DrmServiceCertificate* +DrmServiceCertificate::GetDefaultDrmServiceCertificate() { + return DrmServiceCertificateMap::GetInstance()->GetDefaultCert(); +} + +const DrmServiceCertificate* +DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() { + const DrmServiceCertificate* default_cert = + DrmServiceCertificateMap::GetInstance()->GetDefaultCert(); + CHECK(default_cert) << "Service Certificate not set!"; + return default_cert; +} + +const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate( + const std::string& serial_number) { + return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number); +} + +util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate( + const std::string& root_public_key, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + DrmServiceCertificateMap::GetInstance()->ClearDefaultDrmServiceCertificate(); + return AddDrmServiceCertificate(root_public_key, service_certificate, + service_private_key, + service_private_key_passphrase); +} + +util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate( + CertificateType root_cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase) { + std::unique_ptr root_cert; + util::Status status = + DrmRootCertificate::CreateByType(root_cert_type, &root_cert); + if (!status.ok()) { + return status; + } + return SetDefaultDrmServiceCertificate( + root_cert->public_key(), service_certificate, service_private_key, + service_private_key_passphrase); +} + +util::Status DrmServiceCertificate::DecryptClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id) { + DCHECK(client_id); + if (encrypted_client_id.service_certificate_serial_number().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-service-certificate-serial-number"); + } + if (encrypted_client_id.provider_id().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-service-id"); + } + if (encrypted_client_id.encrypted_client_id().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id"); + } + if (encrypted_client_id.encrypted_client_id_iv().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-client-id-iv"); + } + if (encrypted_client_id.encrypted_privacy_key().empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "missing-encrypted-privacy-key"); + } + std::string privacy_key; + std::string provider_id; + const DrmServiceCertificate* cert = GetDrmServiceCertificate( + encrypted_client_id.service_certificate_serial_number()); + if (!cert) { + return util::Status( + error_space, SERVICE_CERTIFICATE_NOT_FOUND, + "service-certificate-not-found (SN " + + absl::BytesToHexString( + encrypted_client_id.service_certificate_serial_number()) + + ")"); + } + if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(), + &privacy_key)) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "privacy-key-decryption-failed"); + } + if (cert->provider_id() != encrypted_client_id.provider_id()) { + return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND, + std::string("provider-id-mismatch (") + cert->provider_id() + + " / " + encrypted_client_id.provider_id() + ")"); + } + std::string serialized_client_id(crypto_util::DecryptAesCbc( + privacy_key, encrypted_client_id.encrypted_client_id_iv(), + encrypted_client_id.encrypted_client_id())); + if (serialized_client_id.empty()) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-decryption-failed"); + } + if (!client_id->ParseFromString(serialized_client_id)) { + return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, + "client-id-parse-failed"); + } + return util::OkStatus(); +} + +void DrmServiceCertificate::ResetServiceCertificates() { + DrmServiceCertificateMap::GetInstance()->Reset(); +} + +DrmServiceCertificate::DrmServiceCertificate( + const std::string& service_certificate, const std::string& provider_id, + const std::string& serial_number, const uint32_t creation_time_seconds, + std::unique_ptr public_key, + std::unique_ptr private_key) + : certificate_(service_certificate), + provider_id_(provider_id), + serial_number_(serial_number), + creation_time_seconds_(creation_time_seconds), + public_key_(std::move(public_key)), + private_key_(std::move(private_key)) {} + +} // namespace widevine diff --git a/common/drm_service_certificate.h b/common/drm_service_certificate.h new file mode 100644 index 0000000..f1fc7e1 --- /dev/null +++ b/common/drm_service_certificate.h @@ -0,0 +1,123 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Service certificate holder used to decrypt encrypted client credentials. + +#ifndef COMMON_DRM_SERVICE_CERTIFICATE_H__ +#define COMMON_DRM_SERVICE_CERTIFICATE_H__ + +#include +#include +#include + +#include +#include "base/macros.h" +#include "util/status.h" +#include "common/certificate_type.h" +#include "common/rsa_key.h" + +namespace widevine { +class RequestInspectorTest; +} // namespace widevine + +namespace widevine { + +class ClientIdentification; +class EncryptedClientIdentification; + +class DrmServiceCertificate { + public: + // Create a new DrmServiceCertificate object and add it to the list of valid + // service certificates. |root_cert_type| indicates which root public key to + // use to verify |service_certificate|, |service_certificate| is a + // Google-generated certificate used to authenticate the service provider for + // purposes of device privacy, |service_private_key| is the encrypted PKCS#8 + // private RSA key corresponding to the service certificate, + // |service_private_key_passphrase| is the password required to decrypt + // |service_private_key|. + // Returns status::OK if successful, or appropriate error code otherwise. + // If the default service certificate is not set, this certificate will be + // used as the default service certificate. + // This method is thread-safe. + static util::Status AddDrmServiceCertificate( + CertificateType root_cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + // Same as AddDrmServiceCertificate(), but will clear the default service + // certificate if it's set. This will result in this service certificate + // being set as the default service certificate. + static util::Status SetDefaultDrmServiceCertificate( + CertificateType root_cert_type, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + // Returns the default service certificate. Will return null if no default + // Service Certificate is set. This method is thread-safe. + static const DrmServiceCertificate* GetDefaultDrmServiceCertificate(); + + // Returns the default service certificate. Will abort if no default Service + // Certificate is set. This method is thread-safe. + static const DrmServiceCertificate* GetDefaultDrmServiceCertificateOrDie(); + + // Returns the service certificate with the given serial number if found, or + // null otherwise. + static const DrmServiceCertificate* GetDrmServiceCertificate( + const std::string& cert_serial_number); + + // Decrypts the EncryptedClientIdentification message passed in + // |encrypted_client_id| into |client_id| using the private key for the + // certificate which was used to encrypt the information. |client_id| must + // not be NULL. Returns status::OK if successful, or an appropriate error + // otherwise. This method is thread-safe. + static util::Status DecryptClientIdentification( + const EncryptedClientIdentification& encrypted_client_id, + ClientIdentification* client_id); + + const std::string& certificate() const { return certificate_; } + const std::string& provider_id() const { return provider_id_; } + const std::string& serial_number() const { return serial_number_; } + const RsaPrivateKey* const private_key() const { return private_key_.get(); } + const RsaPublicKey* const public_key() const { return public_key_.get(); } + + private: + friend class DrmServiceCertificateTest; + friend class widevine::RequestInspectorTest; + + static util::Status AddDrmServiceCertificate( + const std::string& root_public_key, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + static util::Status SetDefaultDrmServiceCertificate( + const std::string& root_public_key, const std::string& service_certificate, + const std::string& service_private_key, + const std::string& service_private_key_passphrase); + + DrmServiceCertificate(const std::string& service_certificate, + const std::string& provider_id, const std::string& serial_number, + const uint32_t creation_time_seconds, + std::unique_ptr public_key, + std::unique_ptr private_key); + + static void ResetServiceCertificates(); + + std::string certificate_; + std::string provider_id_; + std::string serial_number_; + uint32_t creation_time_seconds_; + std::unique_ptr public_key_; + std::unique_ptr private_key_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DrmServiceCertificate); +}; + +} // namespace widevine + +#endif // COMMON_DRM_SERVICE_CERTIFICATE_H__ diff --git a/common/drm_service_certificate_test.cc b/common/drm_service_certificate_test.cc new file mode 100644 index 0000000..789ef39 --- /dev/null +++ b/common/drm_service_certificate_test.cc @@ -0,0 +1,369 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/drm_service_certificate.h" + +#include + +#include "glog/logging.h" +#include "google/protobuf/util/message_differencer.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "common/aes_cbc_util.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" +#include "common/test_certificates.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/drm_certificate.pb.h" +#include "protos/public/errors.pb.h" // IWYU pragma: keep +#include "protos/public/license_server_sdk.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" + +namespace widevine { + +const char kPrivacyKey[] = "f7538b38acc78ec68c732ac665c55c65"; +const char kIv[] = "09e9cda133ff5140bd2793173a04b5a3"; +const char kPassphrase[] = "passphrase"; + +class DrmServiceCertificateTest : public ::testing::Test { + public: + DrmServiceCertificateTest() + : privacy_key_(absl::HexStringToBytes(kPrivacyKey)), + iv_(absl::HexStringToBytes(kIv)), + root_private_key_( + RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())) { + CHECK(root_private_key_ != nullptr); + client_id_.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE); + client_id_.set_token(test_certs_.test_user_device_certificate()); + } + + void SetUp() override { DrmServiceCertificate::ResetServiceCertificates(); } + + protected: + std::string GenerateDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds, + const std::string& public_key) { + DrmCertificate cert; + cert.set_type(DrmCertificate::SERVICE); + cert.set_serial_number(serial_number); + cert.set_provider_id(provider_id); + cert.set_public_key(public_key); + cert.set_creation_time_seconds(creation_time_seconds); + SignedDrmCertificate signed_cert; + cert.SerializeToString(signed_cert.mutable_drm_certificate()); + root_private_key_->GenerateSignature(signed_cert.drm_certificate(), + signed_cert.mutable_signature()); + std::string serialized_cert; + signed_cert.SerializeToString(&serialized_cert); + return serialized_cert; + } + + util::Status SetDefaultDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds) { + std::string signed_cert(GenerateDrmServiceCertificate( + serial_number, provider_id, creation_time_seconds, + test_keys_.public_test_key_2_2048_bits())); + std::string encrypted_private_key; + if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), kPassphrase, + &encrypted_private_key)) { + return util::Status(util::error::INTERNAL, ""); + } + return DrmServiceCertificate::SetDefaultDrmServiceCertificate( + test_keys_.public_test_key_1_3072_bits(), signed_cert, + encrypted_private_key, kPassphrase); + } + + util::Status AddDrmServiceCertificate(const std::string& serial_number, + const std::string& provider_id, + uint32_t creation_time_seconds) { + std::string signed_cert(GenerateDrmServiceCertificate( + serial_number, provider_id, creation_time_seconds, + test_keys_.public_test_key_2_2048_bits())); + std::string encrypted_private_key; + if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), kPassphrase, + &encrypted_private_key)) { + return util::Status(util::error::INTERNAL, ""); + } + return DrmServiceCertificate::AddDrmServiceCertificate( + test_keys_.public_test_key_1_3072_bits(), signed_cert, + encrypted_private_key, kPassphrase); + } + + void EncryptClientIdentification( + const std::string& serial_number, const std::string& provider_id, + const std::string& public_key, + EncryptedClientIdentification* encrypted_client_id) { + CHECK(encrypted_client_id); + encrypted_client_id->set_provider_id(provider_id); + encrypted_client_id->set_service_certificate_serial_number(serial_number); + std::string serial_client_id; + client_id_.SerializeToString(&serial_client_id); + encrypted_client_id->set_encrypted_client_id( + crypto_util::EncryptAesCbc(privacy_key_, iv_, serial_client_id)); + encrypted_client_id->set_encrypted_client_id_iv(iv_); + std::unique_ptr rsa_key(RsaPublicKey::Create(public_key)); + ASSERT_TRUE(rsa_key.get()); + rsa_key->Encrypt(privacy_key_, + encrypted_client_id->mutable_encrypted_privacy_key()); + } + + RsaTestKeys test_keys_; + TestCertificates test_certs_; + std::string privacy_key_; + std::string iv_; + std::unique_ptr root_private_key_; + ClientIdentification client_id_; +}; + +TEST_F(DrmServiceCertificateTest, BasicClientIdDecrypt) { + std::string serial_number("serial_number"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + EXPECT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); +} + +TEST_F(DrmServiceCertificateTest, NoDefaultDrmServiceCertificate) { + ASSERT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate()); + const auto& get_default_sc_or_die = []() { + DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie(); + }; + EXPECT_DEATH(get_default_sc_or_die(), "Service Certificate not set!"); +} + +TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) { + std::string serial_number1("serial_number1"); + std::string provider_id1("someservice.com"); + uint32_t creation_time_seconds1(1234); + std::string serial_number2("serial_number2"); + uint32_t creation_time_seconds2(1234); + std::string bogus_serial_number("bogus-serial-number2"); + + EXPECT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate()); + + EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id1, + creation_time_seconds1)); + + // Expect this to pass because the serial number is allowed to change as long + // as the service Id is the same as before. + EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id1, + creation_time_seconds2)); + + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number1, provider_id1, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptClientIdentification(serial_number2, provider_id1, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptClientIdentification(bogus_serial_number, provider_id1, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND, + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id) + .error_code()); +} + +TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) { + std::string serial_number1("serial_number1"); + std::string serial_number2("serial_number2"); + std::string serial_number3("serial_number3"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id, + creation_time_seconds)); + EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id, + creation_time_seconds + 1)); + EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id, + creation_time_seconds - 1)); + + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number1, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + EXPECT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptClientIdentification(serial_number2, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + EXPECT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptClientIdentification(serial_number3, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + EXPECT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + const DrmServiceCertificate* default_cert( + DrmServiceCertificate::GetDefaultDrmServiceCertificate()); + ASSERT_TRUE(default_cert); + SignedDrmCertificate signed_cert; + ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate())); + DrmCertificate drm_cert; + ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate())); + EXPECT_EQ(serial_number1, drm_cert.serial_number()); + + EXPECT_OK(SetDefaultDrmServiceCertificate(serial_number2, provider_id, + creation_time_seconds)); + default_cert = DrmServiceCertificate::GetDefaultDrmServiceCertificate(); + ASSERT_TRUE(default_cert); + ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate())); + ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate())); + EXPECT_EQ(serial_number2, drm_cert.serial_number()); +} + +TEST_F(DrmServiceCertificateTest, DrmServiceCertificateNotFound) { + std::string serial_number("serial_number"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification("invalid_serial_number", provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND, + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id) + .error_code()); +} + +TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) { + std::string serial_number("serial_number"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + ASSERT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptedClientIdentification invalid; + invalid = encrypted_client_id; + invalid.clear_encrypted_privacy_key(); + EXPECT_EQ( + "Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: " + "missing-encrypted-privacy-key", + DrmServiceCertificate::DecryptClientIdentification(invalid, + &decrypted_client_id) + .ToString()); + + invalid = encrypted_client_id; + ++(*invalid.mutable_encrypted_client_id_iv())[4]; + EXPECT_NE(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + invalid, &decrypted_client_id)); + + invalid.clear_encrypted_client_id_iv(); + EXPECT_EQ( + "Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: " + "missing-encrypted-client-id-iv", + DrmServiceCertificate::DecryptClientIdentification(invalid, + &decrypted_client_id) + .ToString()); + + invalid = encrypted_client_id; + ++(*invalid.mutable_encrypted_client_id())[0]; + EXPECT_NE(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + invalid, &decrypted_client_id)); + + invalid.clear_encrypted_client_id(); + EXPECT_EQ( + "Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: " + "missing-encrypted-client-id", + DrmServiceCertificate::DecryptClientIdentification(invalid, + &decrypted_client_id) + .ToString()); +} + +TEST_F(DrmServiceCertificateTest, PrivateKeyDecryptError) { + std::string serial_number("serial_number"); + std::string provider_id("someservice.com"); + uint32_t creation_time_seconds(1234); + + ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id, + creation_time_seconds)); + EncryptedClientIdentification encrypted_client_id; + EncryptClientIdentification(serial_number, provider_id, + test_keys_.public_test_key_2_2048_bits(), + &encrypted_client_id); + ClientIdentification decrypted_client_id; + ASSERT_EQ(util::OkStatus(), + DrmServiceCertificate::DecryptClientIdentification( + encrypted_client_id, &decrypted_client_id)); + ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, + decrypted_client_id)); + + EncryptedClientIdentification corrupted; + corrupted = encrypted_client_id; + ++(*corrupted.mutable_encrypted_privacy_key())[20]; + EXPECT_EQ( + "Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: " + "privacy-key-decryption-failed", + DrmServiceCertificate::DecryptClientIdentification(corrupted, + &decrypted_client_id) + .ToString()); +} + +// TODO(user): Add more unit tests for various fail cases (bad keys having +// to do with bad keys and bad certs). + +} // namespace widevine diff --git a/common/ecb_util.cc b/common/ecb_util.cc new file mode 100644 index 0000000..e6de1a5 --- /dev/null +++ b/common/ecb_util.cc @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Implementation of ecb crypto routines used by Widevine services. + +#include "common/ecb_util.h" + +#include "glog/logging.h" +#include "absl/strings/string_view.h" +#include "openssl/aes.h" +#include "openssl/des.h" + +namespace widevine { +namespace crypto_util { + +const int kWvmDESKeySizeBytes = 16; + +static bool EncryptOrDecrypt3DesCbc(absl::string_view key, + absl::string_view src, std::string* dst, + bool encrypt) { + CHECK(dst); + if (key.size() != kWvmDESKeySizeBytes) { + LOG(WARNING) << "Invalid 3DES key size (" << key.size() << "!=16)."; + dst->clear(); + return false; + } + const int data_size = src.size(); + if (data_size % DES_KEY_SZ != 0) { + // Data must be a multiple of block size + LOG(WARNING) << "3DES data is not a multiple of 8 bytes."; + dst->clear(); + return false; + } + const int data_size_blocks = data_size / DES_KEY_SZ; + DES_key_schedule schedule[2]; + // const_DES_cblock (the type of the first argument to DES_ecb3_encrypt) isn't + // actually const, so we have to cast away const. + DES_cblock* keyblock = + const_cast(reinterpret_cast(key.data())); + DES_set_key(keyblock + 0, schedule + 1); + DES_set_key(keyblock + 1, schedule + 0); + DES_cblock* srcblock = + const_cast(reinterpret_cast(src.data())); + dst->resize(data_size); + DES_cblock* dstblock = reinterpret_cast(&*dst->begin()); + for (int i = 0; i < data_size_blocks; i++) { + DES_ecb3_encrypt(srcblock + i, dstblock + i, schedule + 0, schedule + 1, + schedule + 0, encrypt); + } + return true; +} + +bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) { + return EncryptOrDecrypt3DesCbc(key, src, dst, DES_ENCRYPT); +} + +bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) { + return EncryptOrDecrypt3DesCbc(key, src, dst, DES_DECRYPT); +} + +bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) { + CHECK(dst); + dst->clear(); + if (src.size() % 16 != 0) { + LOG(WARNING) << "AES-ECB data is not a multiple of 16 bytes."; + return false; + } + int num_bits = key.size() * 8; + AES_KEY aes_key; + int aes_result = AES_set_encrypt_key( + reinterpret_cast(key.data()), num_bits, &aes_key); + if (aes_result != 0) { + LOG(WARNING) << "AES result is not zero."; + return false; + } + dst->resize(src.size()); + for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) { + AES_encrypt(reinterpret_cast(src.data()+i), + reinterpret_cast(&(*dst)[i]), &aes_key); + } + return true; +} + +bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) { + CHECK(dst); + dst->clear(); + if (src.size() % 16 != 0) { + LOG(WARNING) << "AES-ECB data is not a multiple of 16 bytes."; + return false; + } + int num_bits = key.size() * 8; + AES_KEY aes_key; + int aes_result = AES_set_decrypt_key( + reinterpret_cast(key.data()), num_bits, &aes_key); + if (aes_result != 0) { + LOG(WARNING) << "AES result is not zero."; + return false; + } + dst->resize(src.size()); + for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) { + AES_decrypt(reinterpret_cast(src.data()+i), + reinterpret_cast(&(*dst)[i]), &aes_key); + } + return true; +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/ecb_util.h b/common/ecb_util.h new file mode 100644 index 0000000..0b5e82b --- /dev/null +++ b/common/ecb_util.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Contains ecb crypto routines for widevine protocols. These routines are used +// as part of licensing and provisioning request handling. + +#ifndef COMMON_ECB_UTIL_H_ +#define COMMON_ECB_UTIL_H_ + +#include + +#include "absl/strings/string_view.h" + +namespace widevine { +namespace crypto_util { + +// Encrypts |src| into |dst| using 3DES ECB2 mode with the given key. This is +// used for protecting content keys on some older Widevine keyboxes, and is not +// intended for any other purpose. Returns false and sets *|dst|="" if +// unsuccessful. Key should be 16 bytes. The first 8 are key2 of the 3DES key +// bundle, and the last 8 bytes are key1 and key3. |src| must be a multiple of +// 8 bytes. +bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst); + +// Decrypts |src| into |dst| using 3DES ECB2 mode with the given (16-byte) +// key. This is used for protecting content keys on some older Widevine +// keyboxes, and is not intended for any other purpose. +// Returns false and sets *|dst|="" if unsuccessful. Note that it can only +// fail if invalid key or data sizes are passed in. +// Key should be 16 bytes. The first 8 are key2 of the 3DES key bundle, +// and the last 8 bytes are key1 and key3. |src| must be a multiple of +// 8 bytes. +bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst); + +// Encrypts |src| into |dst| using AES ECB mode with the given +// key. This is used for protecting content keys on Widevine devices, +// and is not intended for any other purpose. +// Returns false and sets *|dst|="" if unsuccessful. Note that it can only +// fail if invalid key or data sizes are passed in. +// Key must be 16 bytes, and src must be a multiple of 16 bytes. +bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst); + +// Decrypts |src| into |dst| using AES ECB mode with the given +// key. This is used for protecting content keys on Widevine devices, +// and is not intended for any other purpose. +// Returns false and sets *|dst|="" if unsuccessful. Note that it can only +// fail if invalid key or data sizes are passed in. +// Key must be 16 bytes, and src must be a multiple of 16 bytes. +bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst); + +} // namespace crypto_util +} // namespace widevine + +#endif // COMMON_ECB_UTIL_H_ diff --git a/common/ecb_util_test.cc b/common/ecb_util_test.cc new file mode 100644 index 0000000..40285b8 --- /dev/null +++ b/common/ecb_util_test.cc @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Unit tests for the ecb_util functions. + +#include + +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "common/ecb_util.h" + +namespace widevine { +namespace crypto_util { + +TEST(CryptoUtilTest, TestEncrypt3DesEcb) { + // Test vector generated by (Python): + // c = M2Crypto.EVP.Cipher('des_ede3_ecb', '89abcdef0123456789abcdef', + // iv='', op=1, padding=False) + // (c.update('This is a test message. ')+c.final()).encode('hex') + // TODO(user): find some Widevine test vectors and use those + std::string key = "0123456789abcdef"; + std::string plain = "This is a test message. "; + std::string cipher; + std::string decrypted; + EXPECT_TRUE(Encrypt3DesEcb(key, plain, &cipher)); + EXPECT_EQ("ae7c7accaf99a973e7f89bb7f6dc61a9aa9e226a5ba17376", + absl::BytesToHexString(cipher)); + EXPECT_TRUE(Decrypt3DesEcb(key, cipher, &decrypted)); + EXPECT_EQ(plain, decrypted); +} + +TEST(CryptoUtilTest, TestEncrypt3DesEcbFail) { + // Verify that 3DES fails with invalid key or data size + std::string badkey = "0123456789abcde"; // 15 bytes + std::string badplain = "This is a test message."; // 23 bytes + std::string goodkey = "0123456789abcdef"; // 16 bytes + std::string goodplain = "This is a test message. "; // 24 bytes + + // Encryption failure should leave 'decrypted' as empty std::string + std::string out = "Not empty"; + EXPECT_FALSE(Encrypt3DesEcb(badkey, goodplain, &out)); + EXPECT_TRUE(out.empty()); + + out = "Not empty"; + EXPECT_FALSE(Decrypt3DesEcb(goodkey, badplain, &out)); + EXPECT_TRUE(out.empty()); +} + +TEST(CryptoUtilTest, TestEncryptAesEcb) { + // Test vector generated by (Python): + // c = M2Crypto.EVP.Cipher('aes_128_ecb', key, '', 1, padding=False) + // encrypted = c.update(data) + c.final(); + // TODO(user): find some Widevine test vectors and use those + std::string key = "0123456789abcdef"; + std::string plaintext = "This message has 32 bytes in it."; + std::string encrypted; + std::string decrypted; + EXPECT_TRUE(EncryptAesEcb(key, plaintext, &encrypted)); + EXPECT_EQ("1f6a7d63e0645de25c56c6b39ba7723d640129d65f41e96b87be812bc94ad8a9", + absl::BytesToHexString(encrypted)); + EXPECT_TRUE(DecryptAesEcb(key, encrypted, &decrypted)); + EXPECT_EQ(plaintext, decrypted); +} + +TEST(CryptoUtilTest, TestEncryptAesEcbFail) { + // Verify that EncryptAesEcb fails with invalid key or data size + std::string badkey = "0123456789abcde"; // 15 bytes + std::string badplain = "This message has 31 bytes in it"; + std::string goodkey = "0123456789abcdef"; // 16 bytes + std::string goodplain = "This message has 32 bytes in it."; + + // Encryption failure should leave 'decrypted' as empty std::string + std::string out = "Not empty"; + EXPECT_FALSE(EncryptAesEcb(badkey, goodplain, &out)); + EXPECT_TRUE(out.empty()); + + out = "Not empty"; + EXPECT_FALSE(EncryptAesEcb(goodkey, badplain, &out)); + EXPECT_TRUE(out.empty()); +} + +} // namespace crypto_util +} // namespace widevine diff --git a/common/error_space.cc b/common/error_space.cc new file mode 100644 index 0000000..2e9742b --- /dev/null +++ b/common/error_space.cc @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/error_space.h" + +#include "util/proto_status.h" +#include "protos/public/errors.pb.h" + +namespace widevine { + +const util::ErrorSpace* error_space = + util::ProtoEnumErrorSpace::Get(); + +} // namespace widevine diff --git a/common/error_space.h b/common/error_space.h new file mode 100644 index 0000000..1a4ab78 --- /dev/null +++ b/common/error_space.h @@ -0,0 +1,20 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_ERROR_SPACE_H_ +#define COMMON_ERROR_SPACE_H_ + +#include "util/error_space.h" + +namespace widevine { + +extern const util::ErrorSpace* error_space; + +} // namespace widevine + +#endif // COMMON_ERROR_SPACE_H_ diff --git a/common/file_util.cc b/common/file_util.cc new file mode 100644 index 0000000..eb961d3 --- /dev/null +++ b/common/file_util.cc @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/file_util.h" + +#include +#include + +#include "glog/logging.h" + +namespace widevine { + +bool GetContents(const std::string& file_name, std::string* contents) { + if (file_name.empty()) { + LOG(WARNING) << "File name is empty."; + return false; + } + FILE* file = fopen(file_name.c_str(), "r"); + if (!file) { + LOG(WARNING) << "Unable to open file " << file_name; + return false; + } + contents->clear(); + const size_t kReadSize = 0x1000; + char buffer[kReadSize]; + while (true) { + size_t size_read = fread(buffer, sizeof(char), kReadSize, file); + if (size_read == 0) break; + contents->append(buffer, size_read); + } + const bool eof = feof(file); + fclose(file); + if (!eof) { + LOG(WARNING) << "Failed to read all file contents."; + return false; + } + return true;; +} + +bool SetContents(const std::string& file_name, const std::string& contents) { + if (file_name.empty()) { + LOG(WARNING) << "File name is empty."; + return false; + } + FILE* file = fopen(file_name.c_str(), "w"); + if (!file) { + LOG(WARNING) << "Unable to open file " << file_name; + return false; + } + const size_t size_written = + fwrite(contents.data(), sizeof(char), contents.size(), file); + if (size_written != contents.size()) + LOG(WARNING) << "Failed to write to " << file_name; + fclose(file); + return size_written == contents.size(); +} + +} // namespace widevine diff --git a/common/file_util.h b/common/file_util.h new file mode 100644 index 0000000..42c03aa --- /dev/null +++ b/common/file_util.h @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// File util wrapper to be used in partner sdks. Implemented using generic file +// apis. + +#ifndef COMMON_FILE_UTIL_H_ +#define COMMON_FILE_UTIL_H_ + +#include + +namespace widevine { + +// Read file to string. +bool GetContents(const std::string& file_name, std::string* contents); + +// Write file. +bool SetContents(const std::string& file_name, const std::string& contents); + +} // namespace widevine + +#endif // COMMON_FILE_UTIL_H_ diff --git a/common/file_util_test.cc b/common/file_util_test.cc new file mode 100644 index 0000000..8d31cac --- /dev/null +++ b/common/file_util_test.cc @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/file_util.h" + +#include "testing/gunit.h" +#include "absl/strings/str_cat.h" + +namespace widevine { + +TEST(FileUtilTest, EmptyFileName) { + std::string contents; + EXPECT_FALSE(GetContents("", &contents)); + EXPECT_FALSE(SetContents("", "test content")); +} + +TEST(FileUtilTest, BasicTest) { + const std::string file_path = absl::StrCat("/tmp", "/file_util_test"); + EXPECT_TRUE(SetContents(file_path, "test content")); + std::string contents; + EXPECT_TRUE(GetContents(file_path, &contents)); + EXPECT_EQ("test content", contents); +} + +} // namespace widevine diff --git a/common/mock_rsa_key.h b/common/mock_rsa_key.h new file mode 100644 index 0000000..62ddead --- /dev/null +++ b/common/mock_rsa_key.h @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_MOCK_RSA_KEY_H_ +#define COMMON_MOCK_RSA_KEY_H_ + +#include +#include "testing/gmock.h" +#include "common/rsa_key.h" + +namespace widevine { + +class MockRsaPrivateKey : public RsaPrivateKey { + public: + MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {} + ~MockRsaPrivateKey() override {} + + MOCK_CONST_METHOD2(Decrypt, bool(const std::string& encrypted_message, + std::string* decrypted_message)); + MOCK_CONST_METHOD2(GenerateSignature, + bool(const std::string& message, std::string* signature)); + MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key)); + MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key)); + + private: + MockRsaPrivateKey(const MockRsaPrivateKey&) = delete; + MockRsaPrivateKey& operator=(const MockRsaPrivateKey&) = delete; +}; + +class MockRsaPublicKey : public RsaPublicKey { + public: + MockRsaPublicKey() : RsaPublicKey(RSA_new()) {} + ~MockRsaPublicKey() override {} + + MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message, + std::string* encrypted_message)); + MOCK_CONST_METHOD2(VerifySignature, bool(const std::string& message, + const std::string& signature)); + MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key)); + MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key)); + + private: + MockRsaPublicKey(const MockRsaPublicKey&) = delete; + MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete; +}; + +class MockRsaKeyFactory : public RsaKeyFactory{ + public: + MockRsaKeyFactory() {} + ~MockRsaKeyFactory() override {} + + MOCK_METHOD1(CreateFromPkcs1PrivateKey, + std::unique_ptr(const std::string& private_key)); + MOCK_METHOD2( + CreateFromPkcs8PrivateKey, + std::unique_ptr(const std::string& private_key, + const std::string& private_key_passphrase)); + MOCK_METHOD1(CreateFromPkcs1PublicKey, + std::unique_ptr(const std::string& public_key)); + + private: + MockRsaKeyFactory(const MockRsaKeyFactory&) = delete; + MockRsaKeyFactory& operator=(const MockRsaKeyFactory&) = delete; +}; + +} // namespace widevine + +#endif // COMMON_MOCK_RSA_KEY_H_ diff --git a/common/openssl_util.h b/common/openssl_util.h new file mode 100644 index 0000000..1400a58 --- /dev/null +++ b/common/openssl_util.h @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// RAII wrapper classes for cleaning up various OpenSSL dynamically allocated +// structures. + +#ifndef COMMON_OPENSSL_UTIL_H__ +#define COMMON_OPENSSL_UTIL_H__ + +#include "openssl/bio.h" +#include "openssl/evp.h" +#include "openssl/rsa.h" +#include "openssl/x509v3.h" + +template +struct OpenSSLDeleter { + void operator()(T *obj) { func(obj); } +}; + +template +struct OpenSSLStackDeleter { + void operator()(StackType *obj) { + sk_pop_free(reinterpret_cast<_STACK *>(obj), + reinterpret_cast(func)); + } +}; + +template +struct OpenSSLStackOnlyDeleter { + void operator()(StackType *obj) { sk_free(reinterpret_cast<_STACK *>(obj)); } +}; + +template +using ScopedOpenSSLType = std::unique_ptr>; +template +using ScopedOpenSSLStack = + std::unique_ptr>; +template +using ScopedOpenSSLStackOnly = + std::unique_ptr>; + +using ScopedBIGNUM = ScopedOpenSSLType; +using ScopedBIO = ScopedOpenSSLType; +using ScopedPKEY = ScopedOpenSSLType; +using ScopedRSA = ScopedOpenSSLType; +using ScopedX509 = ScopedOpenSSLType; +using ScopedX509Extension = + ScopedOpenSSLType; +using ScopedX509Name = ScopedOpenSSLType; +using ScopedX509NameEntry = + ScopedOpenSSLType; +using ScopedX509Store = ScopedOpenSSLType; +using ScopedX509StoreCtx = + ScopedOpenSSLType; +using ScopedX509Req = ScopedOpenSSLType; +using ScopedAsn1UtcTime = ScopedOpenSSLType; +using ScopedAsn1Utc8String = + ScopedOpenSSLType; +using ScopedAsn1Integer = ScopedOpenSSLType; +using ScopedAsn1Object = ScopedOpenSSLType; +using ScopedAsn1OctetString = + ScopedOpenSSLType; + +// XxxStack deallocates the stack and its members while XxxStackOnly deallocates +// the stack only. +using ScopedX509Stack = ScopedOpenSSLStack; +using ScopedX509StackOnly = ScopedOpenSSLStackOnly; +using ScopedX509InfoStack = + ScopedOpenSSLStack; +using ScopedX509InfoStackOnly = ScopedOpenSSLStackOnly; + +#endif // COMMON_OPENSSL_UTIL_H__ diff --git a/common/random_util.cc b/common/random_util.cc new file mode 100644 index 0000000..286ab3d --- /dev/null +++ b/common/random_util.cc @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/random_util.h" + +#include "glog/logging.h" +#include "openssl/rand.h" + +namespace widevine { + +bool RandomBytes(size_t num_bytes, std::string* output) { + DCHECK(output); + output->resize(num_bytes); + return RAND_bytes(reinterpret_cast(&(*output)[0]), num_bytes); +} + +std::string Random16Bytes() { + std::string output; + CHECK(RandomBytes(16u, &output)); + return output; +} +} // namespace widevine diff --git a/common/random_util.h b/common/random_util.h new file mode 100644 index 0000000..90eb37e --- /dev/null +++ b/common/random_util.h @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_RANDOM_UTIL_H_ +#define COMMON_RANDOM_UTIL_H_ + +#include + +namespace widevine { + +// Generates a random string. +// Returns true on success, false otherwise. +bool RandomBytes(size_t num_bytes, std::string* output); + +// Returns a 16-byte std::string suitable for use as an AES key +std::string Random16Bytes(); + +} // namespace widevine + +#endif // COMMON_RANDOM_UTIL_H_ diff --git a/common/random_util_test.cc b/common/random_util_test.cc new file mode 100644 index 0000000..a3cf2b3 --- /dev/null +++ b/common/random_util_test.cc @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/random_util.h" + +#include "testing/gunit.h" + +namespace widevine { + +TEST(RandomUtilTest, Test) { + std::string output; + ASSERT_TRUE(RandomBytes(16u, &output)); + EXPECT_EQ(16u, output.size()); + + std::string output2; + ASSERT_TRUE(RandomBytes(16u, &output2)); + EXPECT_EQ(16u, output2.size()); + EXPECT_NE(output, output2); + + ASSERT_TRUE(RandomBytes(10u, &output2)); + EXPECT_EQ(10u, output2.size()); +} + +} // namespace widevine diff --git a/common/remote_attestation_verifier.cc b/common/remote_attestation_verifier.cc new file mode 100644 index 0000000..b8cb252 --- /dev/null +++ b/common/remote_attestation_verifier.cc @@ -0,0 +1,261 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/remote_attestation_verifier.h" + +#include +#include + +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/synchronization/mutex.h" +#include "common/client_id_util.h" +#include "common/drm_service_certificate.h" +#include "common/error_space.h" +#include "common/rsa_key.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/errors.pb.h" + +namespace widevine { + +const char kTestRootCaDerCert[] = + "30820403308202eba003020102020900a24f94af7ae6831f300d06092a86" + "4886f70d0101050500308197310b30090603550406130255533113301106" + "035504080c0a57617368696e67746f6e3111300f06035504070c084b6972" + "6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f" + "060355040b0c085769646576696e653115301306035504030c0c54657374" + "20526f6f742043413121301f06092a864886f70d010901161274696e736b" + "697040676f6f676c652e636f6d301e170d3133303831363030353731305a" + "170d3333303831353030353731305a308197310b30090603550406130255" + "533113301106035504080c0a57617368696e67746f6e3111300f06035504" + "070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049" + "6e633111300f060355040b0c085769646576696e65311530130603550403" + "0c0c5465737420526f6f742043413121301f06092a864886f70d01090116" + "1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648" + "86f70d01010105000382010f003082010a0282010100c6eee629d99f7736" + "2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c" + "e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319" + "0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3" + "f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93" + "52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7" + "e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f" + "d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20" + "79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7" + "3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30" + "ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680" + "144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405" + "30030101ff300d06092a864886f70d01010505000382010100779e9b98d3" + "ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee" + "79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124" + "ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436" + "026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140" + "ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053" + "0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258" + "e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82" + "d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4" + "c270d87191c13aefdd177b"; + +const char kProdRootCaDerCert[] = + "30820408308202f0a003020102020101300d06092a864886f70d01010505" + "00307d311830160603550403130f5072697661637920434120526f6f7431" + "123010060355040b13094368726f6d65204f5331133011060355040a130a" + "476f6f676c6520496e63311630140603550407130d4d6f756e7461696e20" + "56696577311330110603550408130a43616c69666f726e6961310b300906" + "0355040613025553301e170d3133303231383130313334325a170d333330" + "3231333130313334325a307d311830160603550403130f50726976616379" + "20434120526f6f7431123010060355040b13094368726f6d65204f533113" + "3011060355040a130a476f6f676c6520496e63311630140603550407130d" + "4d6f756e7461696e2056696577311330110603550408130a43616c69666f" + "726e6961310b300906035504061302555330820122300d06092a864886f7" + "0d01010105000382010f003082010a0282010100e10ea6819d3d066b421d" + "d7612de3eef9599f5d9a2a24bfd09caab543511cf22f615e29f989425a65" + "7396bf33603747719cfb0b4240cd682c7c558fec0176b4793be440752246" + "83648f5b12d02a838a2a8e55a4b645ed0a4a52b19252a23d34bf64a17ac7" + "11fe93a889086d943211b17d670f96442c9f367d38026000da79664e600e" + "e9259348f4fd74108e973d561e624e9f5eda77a085a6eb15fadb2cc7787c" + "7f30ef3b196f2a416a76fa9eb30d65753f5039d97bea70e82431d2962396" + "a34864f33b74d60707fea794c03c82e547abc2407fa7bad67bd09cdab49b" + "26e68754994d12a3845dbeceffe18de0d51fc6fa78676d89ea1e0fcff931" + "59bfb809519b0203010001a3819230818f30290603551d0e042204204b1d" + "148aa5380938812ed6a763f5dc2c318610d5fa9604d609cb2e0d8cec3289" + "302b0603551d230424302280204b1d148aa5380938812ed6a763f5dc2c31" + "8610d5fa9604d609cb2e0d8cec3289300e0603551d0f0101ff0404030201" + "06300f0603551d130101ff040530030101ff30140603551d200101ff040a" + "300830060604551d2000300d06092a864886f70d01010505000382010100" + "c40d84bc8d609b1b68b3caa7e841021838d7e392557d40debab3e0685e72" + "80541092dc913b0aa6150228d8fe5ab08cceefbac56952fa00ba614294d1" + "ba4fa170c86b27f9bf58666c46940f740c4be2795501b25e40b9702af07c" + "884926bd8beed036c503e5e42a223ff36271404ca4360a93dec92a02fd8d" + "ae8f756fc68aaa647e2159f0a7a95d1446e92362bd512f59daec02c5d152" + "c301b9807db998ba70c616364762a0a497aaa92eb7d92f3635169d3f74c6" + "40c738941759a8ab43677b80329d015bdcf8922b779a80f85f1e4a677659" + "c60de80152e8c526a7de46cac143a75af58f0806de81e15c97f616e1bffa" + "1c1c6b0d2438543bdfb2a21bd9bc7ae4"; + +const char kServiceIdFieldName[] = "OU"; +const char kDeviceModeFieldName[] = "O"; +const char kExpectedDeviceMode[] = "Chrome Device Content Protection"; + +RemoteAttestationVerifier& RemoteAttestationVerifier::get() { + static RemoteAttestationVerifier instance; + return instance; +} + +void RemoteAttestationVerifier::EnableTestCertificates(bool enable) { + absl::WriterMutexLock lock(&ca_mutex_); + enable_test_certificates_ = enable; + ca_.reset(); +} + +util::Status RemoteAttestationVerifier::VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + std::string* remote_attestation_cert_sn) { + DCHECK(remote_attestation_cert_sn); + + // Sanity check RemoteAttestation. + if (!remote_attestation.has_certificate()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-certificate-missing")); + } + if (!remote_attestation.has_salt()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-salt-missing")); + } + if (!remote_attestation.has_signature()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-signature-missing")); + } + // Decrypt ClientIdentification containing remote attestation certificate. + // A service cert would be looked up first, then that cert will be used + // to decrypt the ClientIdentification. + ClientIdentification client_id; + util::Status status = DrmServiceCertificate::DecryptClientIdentification( + remote_attestation.certificate(), &client_id); + if (!status.ok()) return status; + + if (client_id.type() != + ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) { + return (util::Status(error_space, INVALID_MESSAGE, + std::string("remote-attestation-invalid-client-id-type (") + + absl::StrCat(client_id.type()) + ")")); + } + return VerifyRemoteAttestation(message, remote_attestation, client_id, + remote_attestation_cert_sn); +} + +util::Status RemoteAttestationVerifier::VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + const std::string& privacy_key) { + // Sanity check RemoteAttestation. + if (!remote_attestation.has_certificate()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-certificate-missing")); + } + if (!remote_attestation.has_salt()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-salt-missing")); + } + if (!remote_attestation.has_signature()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-signature-missing")); + } + // Decrypt ClientIdentification containing remote attestation certificate, + // directly using an explicitly provided key |privacy_key|. + ClientIdentification client_id; + util::Status status = DecryptEncryptedClientIdentification( + remote_attestation.certificate(), privacy_key, &client_id); + if (!status.ok()) return status; + + if (client_id.type() != + ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) { + return (util::Status(error_space, INVALID_MESSAGE, + std::string("remote-attestation-invalid-client-id-type (") + + absl::StrCat(client_id.type()) + ")")); + } + std::string remote_attestation_cert_sn; + return VerifyRemoteAttestation(message, remote_attestation, client_id, + &remote_attestation_cert_sn); +} + +util::Status RemoteAttestationVerifier::VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) { + if (!client_id.has_token()) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-token-missing")); + } + // Load and verify the certificate chain. + std::unique_ptr cert_chain(new X509CertChain); + util::Status status = cert_chain->LoadPem(client_id.token()); + if (!status.ok()) return status; + + if (cert_chain->GetNumCerts() < 1) { + return (util::Status(error_space, INVALID_MESSAGE, + "remote-attestation-empty-certificate-chain")); + } + std::string device_mode_string = + cert_chain->GetCert(0)->GetSubjectNameField(kDeviceModeFieldName); + if (device_mode_string != kExpectedDeviceMode) { + return (util::Status(error_space, REMOTE_ATTESTATION_FAILED, + std::string("remote-attestation-device-not-verified (") + + device_mode_string + " / " + kDeviceModeFieldName + + ")")); + } + ca_mutex_.ReaderLock(); + if (ca_ == NULL) { + ca_mutex_.ReaderUnlock(); + status = LoadCa(); + if (!status.ok()) return status; + ca_mutex_.ReaderLock(); + } + status = ca_->VerifyCertChain(*cert_chain); + ca_mutex_.ReaderUnlock(); + if (!status.ok()) { + return (util::Status( + error_space, REMOTE_ATTESTATION_FAILED, + std::string("remote-attestation-cert-chain-validation-failed: ") + + status.error_message())); + } + // Verify the remote attestation signature. + std::unique_ptr leaf_key; + std::string message_with_salt = message + remote_attestation.salt(); + for (size_t idx = 0; idx < cert_chain->GetNumCerts(); ++idx) { + if (!cert_chain->GetCert(idx)->IsCaCertificate()) { + leaf_key = cert_chain->GetCert(idx)->GetRsaPublicKey(); + break; + } + } + if (!leaf_key) { + return util::Status(error_space, REMOTE_ATTESTATION_FAILED, + "remote-attestation-cert-chain-no-leaf"); + } + + if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt, + remote_attestation.signature())) { + return (util::Status(error_space, REMOTE_ATTESTATION_FAILED, + "remote-attestation-signature-verification-failed: ")); + } + + *remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber(); + return util::OkStatus(); +} + +util::Status RemoteAttestationVerifier::LoadCa() { + absl::WriterMutexLock lock(&ca_mutex_); + std::unique_ptr ca_cert(new X509Cert); + util::Status status = ca_cert->LoadDer(absl::HexStringToBytes( + enable_test_certificates_ ? kTestRootCaDerCert : kProdRootCaDerCert)); + if (!status.ok()) { + return status; + } + ca_.reset(new X509CA(ca_cert.release())); + return util::OkStatus(); +} + +} // namespace widevine diff --git a/common/remote_attestation_verifier.h b/common/remote_attestation_verifier.h new file mode 100644 index 0000000..379a4d4 --- /dev/null +++ b/common/remote_attestation_verifier.h @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Functionality used to verifier ChromeOS remote attestation. + +#ifndef COMMON_REMOTE_ATTESTATION_VERIFIER_H__ +#define COMMON_REMOTE_ATTESTATION_VERIFIER_H__ + +#include +#include +#include + +#include "base/macros.h" +#include "base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "util/status.h" +#include "common/x509_cert.h" +#include "protos/public/client_identification.pb.h" +#include "protos/public/remote_attestation.pb.h" + +namespace widevine { + +// Singleton class used to do remote attestation. Access singleton instance via +// the get() method. +// TODO(user): This class is tested as part of the Session unit tests, but +// finer unit tests should be implemented for the failure cases. +class RemoteAttestationVerifier { + public: + RemoteAttestationVerifier() : enable_test_certificates_(false) {} + virtual ~RemoteAttestationVerifier() {} + + // Singleton accessor. + static RemoteAttestationVerifier& get(); + + // Call to use the test (non-production) remote attestation root certificate. + // This method is thread-safe. + void EnableTestCertificates(bool enable); + + // Call to verify a RemoteAttestation challenge response, used in licensing + // protocol. + // |message| is the challenge message, + // |remote_attestation| is the remote attestation response to verify, + // |remote_attestation_cert_sn| is a pointer to a std::string which on successful + // return will contain the serial number for the client's remote attestation + // certificate. + // This method is thread-safe. + util::Status VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + std::string* remote_attestation_cert_sn); + + // Call to verify a RemoteAttestation challenge response, used in certificate + // provisioning protocol. + // |message| is the challenge message, + // |remote_attestation| is the remote attestation response to verify, + // |privacy_key| is used to decrypt the EncryptedClientIdentification within + // the |remote_attestation| message. + // This method is thread-safe. + util::Status VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + const std::string& privacy_key); + + private: + // Common subroutine to perform the verification. + // |message| is the challenge message, + // |remote_attestation| is the remote attestation response to verify, + // |client_id| is the decrypted client identification carrying the token, + // |remote_attestation_cert_sn| is a pointer to a std::string which on successful + // return will contain the serial number for the client's remote attestation + // certificate. + util::Status VerifyRemoteAttestation( + const std::string& message, const RemoteAttestation& remote_attestation, + const ClientIdentification& client_id, + std::string* remote_attestation_cert_sn); + + util::Status LoadCa(); + + bool enable_test_certificates_; + absl::Mutex ca_mutex_; + std::unique_ptr ca_ GUARDED_BY(ca_mutex_); + + DISALLOW_COPY_AND_ASSIGN(RemoteAttestationVerifier); +}; + +} // namespace widevine + +#endif // COMMON_REMOTE_ATTESTATION_VERIFIER_H__ diff --git a/common/rsa_key.cc b/common/rsa_key.cc new file mode 100644 index 0000000..3f17744 --- /dev/null +++ b/common/rsa_key.cc @@ -0,0 +1,313 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// Definition of classes representing RSA private and public keys used +// for message signing, signature verification, encryption and decryption. +// +// RSA signature details: +// Algorithm: RSASSA-PSS +// Hash algorithm: SHA1 +// Mask generation function: mgf1SHA1 +// Salt length: 20 bytes +// Trailer field: 0xbc +// +// RSA encryption details: +// Algorithm: RSA-OAEP +// Mask generation function: mgf1SHA1 +// Label (encoding paramter): empty std::string + +#include "common/rsa_key.h" + +#include "glog/logging.h" +#include "openssl/bn.h" +#include "openssl/err.h" +#include "openssl/evp.h" +#include "openssl/rsa.h" +#include "openssl/sha.h" +#include "common/rsa_util.h" +#include "common/sha_util.h" + +static const int kPssSaltLength = 20; + +namespace { + +// Check if two RSA keys match. If matches, they are either a public-private key +// pair or the same public key or the same private key. +bool RsaKeyMatch(const RSA* key1, const RSA* key2) { + if (!key1 || !key2) return false; + return BN_cmp(key1->n, key2->n) == 0; +} + +std::string OpenSSLErrorString(uint32_t error) { + char buf[ERR_ERROR_STRING_BUF_LEN]; + ERR_error_string_n(error, buf, sizeof(buf)); + return buf; +} + +} // namespace + +namespace widevine { + +RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); } + +RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key) + : key_(RSAPrivateKey_dup(rsa_key.key_)) { + CHECK(key_ != nullptr); +} + +RsaPrivateKey::~RsaPrivateKey() { RSA_free(key_); } + +RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) { + RSA* key; + if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key)) return nullptr; + if (RSA_check_key(key) != 1) { + LOG(ERROR) << "Invalid private RSA key: " + << OpenSSLErrorString(ERR_get_error()); + RSA_free(key); + } + return new RsaPrivateKey(key); +} + +bool RsaPrivateKey::Decrypt(const std::string& encrypted_message, + std::string* decrypted_message) const { + DCHECK(decrypted_message); + + size_t rsa_size = RSA_size(key_); + if (encrypted_message.size() != rsa_size) { + LOG(ERROR) << "Encrypted RSA message has the wrong size (expected " + << rsa_size << ", actual " << encrypted_message.size() << ")"; + return false; + } + decrypted_message->assign(rsa_size, 0); + int decrypted_size = RSA_private_decrypt( + rsa_size, + const_cast( + reinterpret_cast(encrypted_message.data())), + reinterpret_cast(&(*decrypted_message)[0]), key_, + RSA_PKCS1_OAEP_PADDING); + if (decrypted_size == -1) { + LOG(ERROR) << "RSA private decrypt failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + decrypted_message->resize(decrypted_size); + return true; +} + +bool RsaPrivateKey::GenerateSignature(const std::string& message, + std::string* signature) const { + DCHECK(signature); + + if (message.empty()) { + LOG(ERROR) << "Message to be signed is empty"; + return false; + } + // Hash the message using SHA1. + std::string message_digest = Sha1_Hash(message); + + // Add PSS padding. + size_t rsa_size = RSA_size(key_); + std::string padded_digest(rsa_size, 0); + if (!RSA_padding_add_PKCS1_PSS_mgf1( + key_, reinterpret_cast(&padded_digest[0]), + reinterpret_cast(&message_digest[0]), EVP_sha1(), + EVP_sha1(), kPssSaltLength)) { + LOG(ERROR) << "RSA padding failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + // Encrypt PSS padded digest. + signature->assign(rsa_size, 0); + if (RSA_private_encrypt(padded_digest.size(), + reinterpret_cast(&padded_digest[0]), + reinterpret_cast(&(*signature)[0]), + key_, RSA_NO_PADDING) != + static_cast(signature->size())) { + LOG(ERROR) << "RSA private encrypt failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + return true; +} + +bool RsaPrivateKey::GenerateSignatureSha256Pkcs7(const std::string& message, + std::string* signature) const { + DCHECK(signature); + if (message.empty()) { + LOG(ERROR) << "Empty signature verification message"; + return false; + } + + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast(message.data()), message.size(), + digest); + unsigned int sig_len = RSA_size(key_); + signature->resize(sig_len); + return RSA_sign(NID_sha256, digest, sizeof(digest), + reinterpret_cast(&(*signature)[0]), &sig_len, + key_) == 1; +} + +bool RsaPrivateKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const { + return RsaKeyMatch(key(), private_key.key()); +} + +bool RsaPrivateKey::MatchesPublicKey(const RsaPublicKey& public_key) const { + return RsaKeyMatch(key(), public_key.key()); +} + +uint32_t RsaPrivateKey::KeySize() const { return RSA_size(key_); } + +RsaPublicKey::RsaPublicKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); } + +RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key) + : key_(RSAPublicKey_dup(rsa_key.key_)) { + CHECK(key_ != nullptr); +} + +RsaPublicKey::~RsaPublicKey() { RSA_free(key_); } + +RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) { + RSA* key; + if (!rsa_util::DeserializeRsaPublicKey(serialized_key, &key)) return nullptr; + if (RSA_size(key) == 0) { + LOG(ERROR) << "Invalid public RSA key: " + << OpenSSLErrorString(ERR_get_error()); + RSA_free(key); + } + return new RsaPublicKey(key); +} + +bool RsaPublicKey::Encrypt(const std::string& clear_message, + std::string* encrypted_message) const { + DCHECK(encrypted_message); + + if (clear_message.empty()) { + LOG(ERROR) << "Message to be encrypted is empty"; + return false; + } + size_t rsa_size = RSA_size(key_); + encrypted_message->assign(rsa_size, 0); + if (RSA_public_encrypt( + clear_message.size(), + const_cast( + reinterpret_cast(clear_message.data())), + reinterpret_cast(&(*encrypted_message)[0]), key_, + RSA_PKCS1_OAEP_PADDING) != static_cast(rsa_size)) { + LOG(ERROR) << "RSA public encrypt failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + return true; +} + +bool RsaPublicKey::VerifySignature(const std::string& message, + const std::string& signature) const { + if (message.empty()) { + LOG(ERROR) << "Signed message is empty"; + return false; + } + size_t rsa_size = RSA_size(key_); + if (signature.size() != rsa_size) { + LOG(ERROR) << "Message signature is of the wrong size (expected " + << rsa_size << ", actual " << signature.size() << ")"; + return false; + } + // Decrypt the signature. + std::string padded_digest(signature.size(), 0); + if (RSA_public_decrypt( + signature.size(), + const_cast( + reinterpret_cast(signature.data())), + reinterpret_cast(&padded_digest[0]), key_, + RSA_NO_PADDING) != static_cast(rsa_size)) { + LOG(ERROR) << "RSA public decrypt failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + // Hash the message using SHA1. + std::string message_digest = Sha1_Hash(message); + + // Verify PSS padding. + if (RSA_verify_PKCS1_PSS_mgf1( + key_, reinterpret_cast(&message_digest[0]), + EVP_sha1(), EVP_sha1(), + reinterpret_cast(&padded_digest[0]), + kPssSaltLength) == 0) { + LOG(ERROR) << "RSA Verify PSS padding failure: " + << OpenSSLErrorString(ERR_get_error()); + return false; + } + return true; +} + +bool RsaPublicKey::VerifySignatureSha256Pkcs7(const std::string& message, + const std::string& signature) const { + if (message.empty()) { + LOG(ERROR) << "Empty signature verification message"; + return false; + } + if (signature.empty()) { + LOG(ERROR) << "Empty signature"; + return false; + } + + if (signature.size() != RSA_size(key_)) { + LOG(ERROR) << "RSA signature has the wrong size"; + return false; + } + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast(message.data()), message.size(), + digest); + return RSA_verify(NID_sha256, digest, sizeof(digest), + reinterpret_cast(signature.data()), + signature.size(), key_) == 1; +} + +bool RsaPublicKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const { + return RsaKeyMatch(key(), private_key.key()); +} + +bool RsaPublicKey::MatchesPublicKey(const RsaPublicKey& public_key) const { + return RsaKeyMatch(key(), public_key.key()); +} + +uint32_t RsaPublicKey::KeySize() const { return RSA_size(key_); } + +RsaKeyFactory::RsaKeyFactory() {} + +RsaKeyFactory::~RsaKeyFactory() {} + +std::unique_ptr RsaKeyFactory::CreateFromPkcs1PrivateKey( + const std::string& private_key) { + return std::unique_ptr(RsaPrivateKey::Create(private_key)); +} + +std::unique_ptr RsaKeyFactory::CreateFromPkcs8PrivateKey( + const std::string& private_key, const std::string& private_key_passphrase) { + std::string pkcs1_key; + const bool result = + private_key_passphrase.empty() + ? rsa_util::PrivateKeyInfoToRsaPrivateKey(private_key, &pkcs1_key) + : rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey( + private_key, private_key_passphrase, &pkcs1_key); + if (!result) { + LOG(WARNING) << "Failed to get pkcs1_key."; + return std::unique_ptr(); + } + return std::unique_ptr(RsaPrivateKey::Create(pkcs1_key)); +} + +std::unique_ptr RsaKeyFactory::CreateFromPkcs1PublicKey( + const std::string& public_key) { + return std::unique_ptr(RsaPublicKey::Create(public_key)); +} + +} // namespace widevine diff --git a/common/rsa_key.h b/common/rsa_key.h new file mode 100644 index 0000000..af94c9a --- /dev/null +++ b/common/rsa_key.h @@ -0,0 +1,151 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// Declaration of classes representing RSA private and public keys used +// for message signing, signature verification, encryption and decryption. + +#ifndef COMMON_RSA_KEY_H_ +#define COMMON_RSA_KEY_H_ + +#include +#include + +#include +#include "base/macros.h" +#include "openssl/rsa.h" + +namespace widevine { + +class RsaPublicKey; + +class RsaPrivateKey { + public: + explicit RsaPrivateKey(RSA* key); + RsaPrivateKey(const RsaPrivateKey&); + virtual ~RsaPrivateKey(); + + // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. + // Returns NULL on failure. + static RsaPrivateKey* Create(const std::string& serialized_key); + + // Decrypt a message using RSA-OAEP. Caller retains ownership of all + // parameters. Returns true if successful, false otherwise. + virtual bool Decrypt(const std::string& encrypted_message, + std::string* decrypted_message) const; + + // Generate RSSASSA-PSS signature. Caller retains ownership of all parameters. + // Returns true if successful, false otherwise. + virtual bool GenerateSignature(const std::string& message, + std::string* signature) const; + + // Generate SHA256 digest, PKCS#7 padded signature. Caller retains ownership + // of all parameters. Returns true if successful, false otherwise. + virtual bool GenerateSignatureSha256Pkcs7(const std::string& message, + std::string* signature) const; + + // Return true if the underlying key matches with |private_key|. + virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const; + + // Return true if the underlying key is a public-private key pair with + // |public_key|. + virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const; + + // Returns the RSA key size (modulus) in bytes. + virtual uint32_t KeySize() const; + + const RSA* key() const { return key_; } + + private: + RSA* key_; + +// SWIG appears to think this declaration is a syntax error. Excluding it for +// python SWIG wrapping. +#ifndef SWIG + // Disallow assignment operator. + RsaPrivateKey& operator=(const RsaPrivateKey&) = delete; +#endif // SWIG +}; + +class RsaPublicKey { + public: + explicit RsaPublicKey(RSA* key); + RsaPublicKey(const RsaPublicKey&); + virtual ~RsaPublicKey(); + + // Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey. + // Returns NULL on failure. + static RsaPublicKey* Create(const std::string& serialized_key); + + // Encrypt a message using RSA-OAEP. Caller retains ownership of all + // parameters. Returns true if successful, false otherwise. + virtual bool Encrypt(const std::string& clear_message, + std::string* encrypted_message) const; + + // Verify RSSASSA-PSS signature. Caller retains ownership of all parameters. + // Returns true if validation succeeds, false otherwise. + virtual bool VerifySignature(const std::string& message, + const std::string& signature) const; + + + // Verify a signature. This method takes two parameters: |message| which is a + // std::string containing the data which was signed, and |signature| which is a + // std::string containing the message SHA256 digest signature with PKCS#7 + // padding. Returns true if verification succeeds, false otherwise. + virtual bool VerifySignatureSha256Pkcs7(const std::string& message, + const std::string& signature) const; + + // Return true if the underlying key is a public-private key pair with + // |private_key|. + virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const; + + // Return true if the underlying key matches with |public_key|. + virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const; + + // Returns the RSA key size (modulus) in bytes. + virtual uint32_t KeySize() const; + + const RSA* key() const { return key_; } + + private: + RSA* key_; + +// SWIG appears to think this declaration is a syntax error. Excluding it for +// python SWIG wrapping. +#ifndef SWIG + // Disallow assignment operator. + RsaPublicKey& operator=(const RsaPublicKey&) = delete; +#endif // SWIG +}; + +class RsaKeyFactory { + public: + RsaKeyFactory(); + virtual ~RsaKeyFactory(); + + // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. + virtual std::unique_ptr CreateFromPkcs1PrivateKey( + const std::string& private_key); + + // Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or + // EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty). + virtual std::unique_ptr CreateFromPkcs8PrivateKey( + const std::string& private_key, const std::string& private_key_passphrase); + + // Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey. + virtual std::unique_ptr CreateFromPkcs1PublicKey( + const std::string& public_key); + + private: + DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory); +}; + +} // namespace widevine + +#endif // COMMON_RSA_KEY_H_ diff --git a/common/rsa_key_test.cc b/common/rsa_key_test.cc new file mode 100644 index 0000000..f4b716a --- /dev/null +++ b/common/rsa_key_test.cc @@ -0,0 +1,255 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// Unit test for rsa_key RSA encryption and signing. + +#include + +#include "testing/gunit.h" +#include "common/rsa_key.h" +#include "common/rsa_test_keys.h" +#include "common/rsa_util.h" + +namespace widevine { + +static const char kTestMessage[] = + "A fool thinks himself to be wise, but a wise man knows himself to be a " + "fool."; + +class RsaKeyTest : public ::testing::Test { + protected: + void TestEncryption(std::unique_ptr private_key, + std::unique_ptr public_key); + + void TestSigning(std::unique_ptr private_key, + std::unique_ptr public_key); + + void TestSigningSha256Pkcs7(std::unique_ptr private_key, + std::unique_ptr public_key); + + RsaTestKeys test_keys_; + RsaKeyFactory factory_; +}; + +TEST_F(RsaKeyTest, CopyConstructor) { + std::unique_ptr private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + std::unique_ptr public_key( + RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); + + std::unique_ptr private_key_copy( + new RsaPrivateKey(*private_key)); + std::unique_ptr public_key_copy( + new RsaPublicKey(*public_key)); + + EXPECT_TRUE(public_key_copy->MatchesPublicKey(*public_key)); + EXPECT_TRUE(public_key_copy->MatchesPrivateKey(*private_key)); + + EXPECT_TRUE(private_key_copy->MatchesPublicKey(*public_key)); + EXPECT_TRUE(private_key_copy->MatchesPrivateKey(*private_key)); +} + +void RsaKeyTest::TestEncryption(std::unique_ptr private_key, + std::unique_ptr public_key) { + ASSERT_TRUE(private_key); + ASSERT_TRUE(public_key); + std::string encrypted_message; + EXPECT_TRUE(public_key->Encrypt(kTestMessage, &encrypted_message)); + std::string decrypted_message; + EXPECT_TRUE(private_key->Decrypt(encrypted_message, &decrypted_message)); + EXPECT_EQ(kTestMessage, decrypted_message); + // Add a byte to the encrypted message. + std::string bad_enc_message(encrypted_message); + bad_enc_message += '\0'; + EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message)); + // Remove a byte from the encrypted message. + bad_enc_message = encrypted_message.substr(0, encrypted_message.size() - 1); + EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message)); + // Change a byte in the encrypted message. + bad_enc_message = encrypted_message; + bad_enc_message[128] ^= 0x55; + EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message)); +} + +void RsaKeyTest::TestSigning(std::unique_ptr private_key, + std::unique_ptr public_key) { + ASSERT_TRUE(private_key); + ASSERT_TRUE(public_key); + std::string signature; + EXPECT_TRUE(private_key->GenerateSignature(kTestMessage, &signature)); + EXPECT_TRUE(public_key->VerifySignature(kTestMessage, signature)); + // Add a byte to the signature. + std::string bad_signature(signature); + bad_signature += '\0'; + EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature)); + // Remove a byte from the signature. + bad_signature = signature.substr(0, signature.size() - 1); + EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature)); + // Change a byte in the signature. + bad_signature = signature; + bad_signature[32] ^= 0x55; + EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature)); +} + +void RsaKeyTest::TestSigningSha256Pkcs7( + std::unique_ptr private_key, + std::unique_ptr public_key) { + ASSERT_TRUE(private_key); + ASSERT_TRUE(public_key); + std::string signature; + EXPECT_TRUE( + private_key->GenerateSignatureSha256Pkcs7(kTestMessage, &signature)); + EXPECT_TRUE(public_key->VerifySignatureSha256Pkcs7(kTestMessage, signature)); + // Add a byte to the signature. + std::string bad_signature(signature); + bad_signature += '\0'; + EXPECT_FALSE( + public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature)); + // Remove a byte from the signature. + bad_signature = signature.substr(0, signature.size() - 1); + EXPECT_FALSE( + public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature)); + // Change a byte in the signature. + bad_signature = signature; + bad_signature[32] ^= 0x55; + EXPECT_FALSE( + public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature)); +} + +TEST_F(RsaKeyTest, BadKey) { + std::unique_ptr private_key( + RsaPrivateKey::Create("bad_private_key")); + EXPECT_TRUE(!private_key); + std::unique_ptr public_key( + RsaPublicKey::Create("bad_public_key")); + EXPECT_TRUE(!public_key); +} + +TEST_F(RsaKeyTest, EncryptAndDecrypt_3072) { + const std::string& private_key = test_keys_.private_test_key_1_3072_bits(); + const std::string& public_key = test_keys_.public_test_key_1_3072_bits(); + TestEncryption( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + TestEncryption(factory_.CreateFromPkcs1PrivateKey(private_key), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, EncryptAndDecrypt_2048) { + const std::string& private_key = test_keys_.private_test_key_2_2048_bits(); + const std::string& public_key = test_keys_.public_test_key_2_2048_bits(); + TestEncryption( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + + std::string pkcs8_key; + std::string passphrase("passphrase"); + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + private_key, passphrase, &pkcs8_key)); + TestEncryption(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase), + factory_.CreateFromPkcs1PublicKey(public_key)); + + ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key)); + TestEncryption(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerify_3072) { + const std::string& private_key = test_keys_.private_test_key_1_3072_bits(); + const std::string& public_key = test_keys_.public_test_key_1_3072_bits(); + TestSigning( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + TestSigning(factory_.CreateFromPkcs1PrivateKey(private_key), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerify_2048) { + const std::string& private_key = test_keys_.private_test_key_2_2048_bits(); + const std::string& public_key = test_keys_.public_test_key_2_2048_bits(); + TestSigning( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + + std::string pkcs8_key; + std::string passphrase("passphrase"); + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + private_key, passphrase, &pkcs8_key)); + TestSigning(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase), + factory_.CreateFromPkcs1PublicKey(public_key)); + + ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key)); + TestSigning(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_3072) { + const std::string& private_key = test_keys_.private_test_key_1_3072_bits(); + const std::string& public_key = test_keys_.public_test_key_1_3072_bits(); + TestSigningSha256Pkcs7( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + TestSigningSha256Pkcs7(factory_.CreateFromPkcs1PrivateKey(private_key), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_2048) { + const std::string& private_key = test_keys_.private_test_key_2_2048_bits(); + const std::string& public_key = test_keys_.public_test_key_2_2048_bits(); + TestSigningSha256Pkcs7( + std::unique_ptr(RsaPrivateKey::Create(private_key)), + std::unique_ptr(RsaPublicKey::Create(public_key))); + + std::string pkcs8_key; + std::string passphrase("passphrase"); + ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( + private_key, passphrase, &pkcs8_key)); + TestSigningSha256Pkcs7( + factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase), + factory_.CreateFromPkcs1PublicKey(public_key)); + + ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key)); + TestSigningSha256Pkcs7( + factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()), + factory_.CreateFromPkcs1PublicKey(public_key)); +} + +TEST_F(RsaKeyTest, KeySize) { + std::unique_ptr private_key( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + std::unique_ptr public_key( + RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); + + EXPECT_EQ(256, private_key->KeySize()); + EXPECT_EQ(256, public_key->KeySize()); +} + +TEST_F(RsaKeyTest, RsaKeyMatch) { + std::unique_ptr private_key2( + RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); + std::unique_ptr private_key3( + RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits())); + std::unique_ptr public_key2( + RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits())); + std::unique_ptr public_key3( + RsaPublicKey::Create(test_keys_.public_test_key_3_2048_bits())); + + EXPECT_TRUE(public_key2->MatchesPublicKey(*public_key2)); + EXPECT_FALSE(public_key2->MatchesPublicKey(*public_key3)); + EXPECT_TRUE(public_key2->MatchesPrivateKey(*private_key2)); + EXPECT_FALSE(public_key2->MatchesPrivateKey(*private_key3)); + + EXPECT_TRUE(private_key2->MatchesPublicKey(*public_key2)); + EXPECT_FALSE(private_key2->MatchesPublicKey(*public_key3)); + EXPECT_TRUE(private_key2->MatchesPrivateKey(*private_key2)); + EXPECT_FALSE(private_key2->MatchesPrivateKey(*private_key3)); +} + +} // namespace widevine diff --git a/common/rsa_test_keys.cc b/common/rsa_test_keys.cc new file mode 100644 index 0000000..357bf5d --- /dev/null +++ b/common/rsa_test_keys.cc @@ -0,0 +1,1147 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// RSA keys generated using fake_prng for purposes of testing. + +#include "common/rsa_test_keys.h" + + +namespace widevine { + +static const unsigned char kTestRsaPrivateKey1_3072[] = { + 0x30, 0x82, 0x06, 0xe3, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x81, 0x00, 0xa5, 0x62, 0x07, 0xdf, + 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, + 0x78, 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, + 0x9d, 0x35, 0x3f, 0xf3, 0x0f, 0xe9, 0x61, 0x96, + 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, + 0xca, 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, + 0x08, 0x47, 0xe4, 0x55, 0x1b, 0x83, 0xbe, 0x10, + 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, + 0x46, 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, + 0x08, 0xc3, 0x04, 0x17, 0x01, 0xb5, 0x11, 0x53, + 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, + 0x5c, 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, + 0x82, 0xa0, 0x92, 0xf1, 0x14, 0xf1, 0x22, 0xff, + 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, + 0xcd, 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, + 0xfd, 0xa4, 0x01, 0xe3, 0xb6, 0x0e, 0x85, 0xe4, + 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, + 0xde, 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, + 0x24, 0xb9, 0xb0, 0xbb, 0x31, 0xa0, 0xee, 0x6a, + 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, + 0x3a, 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, + 0x51, 0xbb, 0xfa, 0xbb, 0x90, 0x97, 0x2c, 0x77, + 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, + 0x49, 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, + 0xda, 0xe2, 0xbd, 0xf0, 0x5f, 0x07, 0x53, 0x8a, + 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, + 0xda, 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, + 0x3a, 0xe1, 0xc8, 0xdb, 0x17, 0xe8, 0xc9, 0x3a, + 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, + 0x30, 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, + 0xf5, 0xca, 0x15, 0x26, 0xaf, 0x39, 0xf3, 0x5d, + 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, + 0x16, 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, + 0x82, 0xd0, 0x60, 0x0b, 0x42, 0x72, 0x85, 0xec, + 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, + 0xf9, 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, + 0x6b, 0xc9, 0x12, 0x64, 0xe4, 0x3a, 0x3b, 0xc9, + 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, + 0x4a, 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, + 0x82, 0xb2, 0xf0, 0xd1, 0x72, 0x71, 0x04, 0x35, + 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, + 0xde, 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, + 0xce, 0x6d, 0x87, 0x50, 0x04, 0xb5, 0xd7, 0x24, + 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, + 0x1b, 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, + 0x1e, 0x6f, 0xd7, 0xf0, 0x33, 0x61, 0x53, 0x7e, + 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, + 0x67, 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, + 0x83, 0x9c, 0xc2, 0x87, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x80, 0x5a, 0x09, 0x3f, + 0x9e, 0x2e, 0x4d, 0x26, 0x50, 0x7b, 0x70, 0x21, + 0xb0, 0x0c, 0x25, 0x21, 0x1f, 0xd9, 0x89, 0x5a, + 0xca, 0x35, 0x23, 0x0b, 0x58, 0xa9, 0x7d, 0xf6, + 0x19, 0xc4, 0x29, 0x87, 0xc7, 0xd4, 0x94, 0x85, + 0xb4, 0x2c, 0xaf, 0x62, 0xb1, 0xe8, 0x62, 0x5b, + 0xda, 0xdb, 0x70, 0x40, 0x37, 0xb1, 0x4e, 0x0c, + 0xc8, 0x62, 0xee, 0xa2, 0xfc, 0x3c, 0xd2, 0x39, + 0x90, 0x15, 0x2c, 0xba, 0x20, 0x50, 0xb7, 0x82, + 0x2a, 0xa0, 0x76, 0x83, 0x20, 0x7f, 0x56, 0x73, + 0x43, 0x8a, 0x9b, 0xa7, 0x6c, 0x63, 0xb6, 0xad, + 0x56, 0xb2, 0x8a, 0xb2, 0xbc, 0x8f, 0xe2, 0xef, + 0x83, 0x9d, 0x98, 0x0b, 0xc7, 0x62, 0x0e, 0x51, + 0x6e, 0x57, 0x1d, 0x1b, 0x0e, 0x3a, 0xea, 0x3b, + 0x76, 0x63, 0x35, 0xd0, 0xd1, 0xcf, 0xbe, 0xad, + 0xbb, 0x1d, 0xde, 0x0f, 0x05, 0x48, 0x55, 0x29, + 0xc1, 0xbc, 0x21, 0xc7, 0x87, 0xf2, 0x75, 0x12, + 0x7d, 0x92, 0x9e, 0xbf, 0xad, 0x04, 0x68, 0xc4, + 0xc9, 0x9d, 0x35, 0xd6, 0xa8, 0x62, 0xc1, 0x69, + 0x6a, 0xb6, 0x41, 0xb7, 0x37, 0x66, 0xdf, 0xb2, + 0xb9, 0x8c, 0x8b, 0x15, 0x08, 0x4c, 0x3d, 0xf1, + 0xed, 0x82, 0x0f, 0xe3, 0xd5, 0xff, 0x46, 0xbd, + 0xf7, 0x85, 0x43, 0xc0, 0x8b, 0xba, 0x47, 0xf1, + 0x41, 0x57, 0xc3, 0x7f, 0x8b, 0x0d, 0x48, 0xea, + 0xc2, 0xed, 0xc0, 0x69, 0x84, 0xb6, 0x32, 0x08, + 0x49, 0x74, 0x14, 0x84, 0xa4, 0x1b, 0x48, 0x5b, + 0xec, 0xd3, 0x0b, 0x12, 0x2b, 0x4c, 0x9e, 0x5c, + 0x01, 0x60, 0xad, 0xef, 0xcb, 0x2b, 0x56, 0x84, + 0x07, 0xfa, 0x62, 0xc6, 0x08, 0x92, 0x98, 0x70, + 0xc9, 0x5b, 0x18, 0xc8, 0xfa, 0x27, 0x0c, 0xe2, + 0xbd, 0xfb, 0x3e, 0x43, 0xa5, 0xb7, 0x06, 0x2c, + 0x4e, 0xf1, 0x07, 0x5d, 0x8d, 0xdd, 0x53, 0xc5, + 0x8c, 0x4a, 0xf2, 0x2f, 0x8e, 0x80, 0x96, 0x16, + 0xc0, 0xfc, 0xf9, 0x20, 0x4f, 0x35, 0xc7, 0x53, + 0x8b, 0x2d, 0x37, 0x43, 0x93, 0x3d, 0x74, 0x3f, + 0x63, 0xf7, 0x0b, 0xbd, 0x46, 0xe4, 0x51, 0x67, + 0x33, 0x57, 0x15, 0xf5, 0x59, 0x27, 0x66, 0xe8, + 0xe2, 0x4b, 0xa3, 0x93, 0x03, 0x8a, 0x9c, 0x05, + 0x13, 0xf2, 0xcb, 0xf7, 0x9c, 0x68, 0xe7, 0x16, + 0x4b, 0x8e, 0x59, 0x71, 0x2b, 0x73, 0x9b, 0xb9, + 0xae, 0x50, 0xfa, 0xd7, 0xd3, 0x34, 0x17, 0x1d, + 0x62, 0x88, 0xbd, 0x8c, 0xba, 0x5a, 0x6b, 0x6a, + 0x5e, 0xb3, 0xa5, 0x80, 0xca, 0xbb, 0xb9, 0xb5, + 0xa8, 0x2e, 0xb1, 0x61, 0x6e, 0xd5, 0xd6, 0x62, + 0x98, 0x4a, 0xb0, 0xb8, 0x76, 0xa9, 0x19, 0x5c, + 0xe2, 0xbe, 0xb3, 0x9b, 0x4a, 0x39, 0xf5, 0xe6, + 0xbb, 0x11, 0x6e, 0x13, 0x13, 0x38, 0xb8, 0x1f, + 0x21, 0x19, 0xf5, 0xa7, 0x76, 0x93, 0xb3, 0x56, + 0xfa, 0xcc, 0x74, 0xbc, 0x19, 0x02, 0x81, 0xc1, + 0x00, 0xd1, 0xd1, 0x72, 0x57, 0xe5, 0xb0, 0x1c, + 0x09, 0x05, 0xbb, 0x55, 0x89, 0x3c, 0x4a, 0x81, + 0x90, 0x9a, 0xf9, 0x32, 0x63, 0x41, 0xad, 0x6a, + 0x5f, 0x65, 0x94, 0x92, 0xcc, 0xf7, 0xc7, 0x53, + 0x93, 0xa0, 0xf7, 0xbe, 0x48, 0x82, 0x63, 0x31, + 0x7b, 0xd0, 0x82, 0x09, 0xbb, 0x0a, 0xbc, 0x60, + 0xc9, 0x4d, 0x83, 0xe4, 0x5d, 0x50, 0xe6, 0x5f, + 0x8b, 0x47, 0x07, 0xa3, 0x3a, 0x36, 0x97, 0xaa, + 0x21, 0x70, 0x7f, 0xd5, 0x6c, 0xb0, 0x56, 0xf5, + 0x5c, 0x48, 0x74, 0x2a, 0xdd, 0xfe, 0x94, 0x83, + 0x05, 0xe0, 0x3d, 0x5d, 0xdd, 0x5a, 0x05, 0xcb, + 0x47, 0xd7, 0xf9, 0x89, 0x55, 0xaa, 0x0b, 0x21, + 0xc0, 0x71, 0x5d, 0xe1, 0x4c, 0x6a, 0x45, 0x86, + 0x86, 0xf2, 0xb9, 0x38, 0x6a, 0x56, 0x51, 0x0d, + 0x7d, 0xac, 0x30, 0x31, 0xca, 0x2d, 0xaa, 0xaa, + 0xba, 0xcc, 0x12, 0x40, 0xc1, 0x0d, 0xa6, 0xc1, + 0x7d, 0x22, 0xec, 0xb6, 0x51, 0x45, 0xfe, 0x4e, + 0xbb, 0x4a, 0xd2, 0xba, 0x9b, 0xa2, 0xcc, 0x28, + 0x2b, 0x01, 0x53, 0x53, 0xf3, 0xa9, 0x5a, 0x8f, + 0xeb, 0xb7, 0xb8, 0x62, 0x6b, 0x8a, 0x79, 0x24, + 0xcc, 0x86, 0x34, 0x45, 0xe2, 0xad, 0x1d, 0xd0, + 0x4c, 0xc9, 0x77, 0x2a, 0xf9, 0x1a, 0xe8, 0x58, + 0x78, 0x51, 0x8a, 0xea, 0x3f, 0x90, 0x36, 0x46, + 0x2a, 0xc0, 0x71, 0x41, 0x83, 0x2c, 0x48, 0xee, + 0xc5, 0x02, 0x81, 0xc1, 0x00, 0xc9, 0xc8, 0xce, + 0xc4, 0x50, 0xb2, 0x26, 0xcb, 0x35, 0x78, 0x55, + 0x3c, 0xcc, 0xf0, 0x7e, 0xba, 0xad, 0xeb, 0x58, + 0xe9, 0xb5, 0x78, 0x2f, 0x43, 0x5f, 0x07, 0x47, + 0x56, 0x05, 0x41, 0x38, 0x71, 0xe1, 0x58, 0x62, + 0xb1, 0x8e, 0xbc, 0xf9, 0x80, 0x04, 0x22, 0x39, + 0x22, 0x24, 0x28, 0x86, 0x9c, 0x00, 0x44, 0x5f, + 0xc4, 0x97, 0xe6, 0x71, 0x5f, 0x1f, 0x58, 0xea, + 0x75, 0x18, 0x0c, 0x23, 0x63, 0x09, 0xc5, 0x98, + 0xc4, 0x6d, 0x23, 0xc2, 0x2c, 0x93, 0x6a, 0x26, + 0xe4, 0x3d, 0x8d, 0xa1, 0x39, 0x70, 0x34, 0x25, + 0xcd, 0xbc, 0x82, 0x78, 0x2b, 0xf3, 0x7e, 0x81, + 0xb6, 0x5f, 0xc5, 0x69, 0xd0, 0x81, 0x69, 0x50, + 0x2f, 0x17, 0x0c, 0x17, 0x3c, 0x0b, 0x45, 0x38, + 0xce, 0xe3, 0xbf, 0x8a, 0x50, 0x0a, 0x00, 0x74, + 0x7e, 0x7a, 0xd8, 0x55, 0x52, 0x6b, 0x82, 0xfb, + 0x34, 0x15, 0x73, 0x6a, 0xf4, 0x51, 0x9b, 0x9f, + 0xa0, 0x45, 0xb9, 0x76, 0xe5, 0xd3, 0xd5, 0xf4, + 0xa9, 0xa4, 0xcd, 0x42, 0x2f, 0x29, 0x89, 0xec, + 0x28, 0x5f, 0x03, 0x45, 0x27, 0xaf, 0x8c, 0x39, + 0x3e, 0x59, 0x9d, 0xaf, 0x27, 0x5d, 0x17, 0x53, + 0x17, 0xeb, 0x8d, 0x7f, 0x3d, 0xb8, 0x2a, 0x50, + 0x1e, 0xb5, 0xc5, 0x04, 0xab, 0x9c, 0xa7, 0xaa, + 0x86, 0x41, 0xb9, 0x36, 0x29, 0x9e, 0xd2, 0xd8, + 0xde, 0x5f, 0xde, 0x80, 0xdb, 0x02, 0x81, 0xc0, + 0x03, 0xf3, 0x5f, 0xa5, 0xcc, 0x0b, 0x5e, 0xdb, + 0xc4, 0xa1, 0xdc, 0x60, 0x73, 0x24, 0x2c, 0x00, + 0x5f, 0x0a, 0xa6, 0x2a, 0x3c, 0x48, 0x59, 0xa2, + 0x66, 0x35, 0x3f, 0xf6, 0x60, 0x0b, 0xfe, 0xc4, + 0xde, 0xd9, 0x0b, 0x5a, 0x2e, 0x2a, 0x53, 0xfa, + 0x32, 0xd8, 0xdf, 0xfa, 0x07, 0x9f, 0xb8, 0x6a, + 0xd1, 0xec, 0xd3, 0xd5, 0xf5, 0xfa, 0x00, 0x7e, + 0x8c, 0xdd, 0xd5, 0xf2, 0xf8, 0xa8, 0x2e, 0x69, + 0xe6, 0xc6, 0x61, 0x6c, 0x64, 0x7d, 0x9e, 0xad, + 0x18, 0x28, 0x27, 0xce, 0x7a, 0x46, 0xad, 0x98, + 0xe4, 0xba, 0x03, 0x14, 0x71, 0xe7, 0x7e, 0x06, + 0x62, 0x48, 0xae, 0x8f, 0x50, 0x5e, 0x59, 0x4a, + 0x58, 0x58, 0x1e, 0x2f, 0xe4, 0x28, 0x5e, 0xfa, + 0x17, 0x83, 0xe9, 0x4e, 0x07, 0x46, 0x0b, 0x6c, + 0xfc, 0x5b, 0x03, 0xf4, 0xfc, 0x9b, 0x24, 0x0f, + 0xd4, 0x5b, 0xdb, 0xa0, 0x46, 0xf3, 0x86, 0xdd, + 0x26, 0x55, 0x32, 0xb1, 0xa1, 0x11, 0xc2, 0xc5, + 0xc0, 0x08, 0xeb, 0xbe, 0x96, 0x78, 0x25, 0xa1, + 0x79, 0xaa, 0xe9, 0xff, 0xc2, 0x86, 0x94, 0x03, + 0x2a, 0x38, 0x6c, 0x91, 0xfd, 0xcf, 0x7e, 0x23, + 0xe3, 0xbb, 0x04, 0x3d, 0xda, 0x68, 0x9f, 0x4d, + 0x72, 0xd5, 0xad, 0x97, 0x77, 0x2c, 0x3c, 0xce, + 0x37, 0x2a, 0xd8, 0x72, 0x4d, 0xf2, 0xd7, 0xab, + 0x62, 0x68, 0x3f, 0x85, 0x8a, 0xc5, 0xec, 0xc9, + 0x02, 0x81, 0xc1, 0x00, 0x92, 0x43, 0x0c, 0x1d, + 0x20, 0xa1, 0x01, 0x9d, 0xaa, 0x54, 0x5e, 0xf4, + 0x83, 0x58, 0x8f, 0x83, 0xa1, 0x2d, 0x46, 0x75, + 0xa1, 0x24, 0x4c, 0x9d, 0xf8, 0xf3, 0xbd, 0xb1, + 0x8c, 0x7d, 0x89, 0xfc, 0x81, 0xeb, 0x1f, 0x1e, + 0xb4, 0xe8, 0x25, 0xb1, 0xb5, 0x4d, 0x59, 0x3c, + 0x76, 0x19, 0x29, 0xf9, 0x49, 0xf8, 0x45, 0xb2, + 0xaa, 0xa8, 0x4e, 0xe5, 0x34, 0x43, 0xaf, 0x2e, + 0xd1, 0x0f, 0x7b, 0x56, 0xfe, 0x6e, 0x4c, 0x1d, + 0x95, 0x3e, 0xa6, 0x30, 0xc9, 0x69, 0xd8, 0x66, + 0xf8, 0x77, 0x00, 0xb6, 0x31, 0xae, 0x9a, 0xf8, + 0x55, 0xfb, 0xfc, 0x3f, 0x5f, 0x70, 0x03, 0x75, + 0xbe, 0x55, 0xca, 0x2d, 0x68, 0xa0, 0x7d, 0x8e, + 0xa4, 0x96, 0x0f, 0x01, 0x66, 0xe9, 0xf6, 0x13, + 0x80, 0xe2, 0x05, 0xcf, 0x9e, 0x70, 0x56, 0x00, + 0x97, 0xea, 0xd7, 0x6d, 0xb6, 0xa0, 0x6a, 0x95, + 0x86, 0x36, 0xf2, 0xff, 0xc5, 0x67, 0x98, 0x7d, + 0x04, 0x0d, 0x3b, 0x31, 0xbc, 0x2b, 0x09, 0xfd, + 0x2d, 0x87, 0xda, 0xc1, 0x74, 0xca, 0x94, 0x73, + 0x6e, 0xeb, 0x5f, 0xe5, 0x34, 0x49, 0xdf, 0xf4, + 0x61, 0xe0, 0xfa, 0x64, 0xfe, 0x05, 0x3a, 0x25, + 0xcc, 0x87, 0xf4, 0x03, 0x38, 0xca, 0xf2, 0xe8, + 0x4f, 0xb9, 0x4f, 0x79, 0x55, 0x43, 0xf3, 0x46, + 0xfd, 0xbc, 0xd2, 0x95, 0xb8, 0x99, 0xfc, 0xb8, + 0xb3, 0xa5, 0x04, 0xa1, 0x02, 0x81, 0xc0, 0x47, + 0xc6, 0x9c, 0x18, 0x54, 0xe5, 0xbb, 0xf9, 0xf4, + 0x38, 0xd2, 0xc0, 0xd1, 0x1a, 0xcc, 0xdb, 0x06, + 0x87, 0x75, 0x1f, 0x13, 0xa2, 0x7f, 0x8b, 0x45, + 0x54, 0xcb, 0x43, 0xf8, 0xbb, 0x94, 0xd6, 0x2e, + 0x56, 0x5c, 0x69, 0x6d, 0x83, 0xb5, 0x45, 0x46, + 0x68, 0x5c, 0x76, 0x1e, 0x6c, 0x0c, 0x53, 0x59, + 0xcc, 0x19, 0xc7, 0x81, 0x62, 0x66, 0x92, 0x02, + 0x8f, 0xa6, 0xdb, 0x50, 0x1c, 0x67, 0xfc, 0x82, + 0x56, 0x2b, 0x4b, 0x1f, 0x97, 0x87, 0xc4, 0x7d, + 0x20, 0xda, 0xd3, 0x3f, 0x28, 0xf9, 0x55, 0xfe, + 0x84, 0x50, 0xc5, 0x3b, 0xd4, 0xaf, 0xf5, 0x3d, + 0x43, 0xce, 0xdc, 0x55, 0x11, 0x87, 0xdb, 0x72, + 0x66, 0xcc, 0x83, 0xc4, 0x8b, 0x20, 0xae, 0x59, + 0x4d, 0xeb, 0xac, 0xb5, 0x4a, 0xec, 0x66, 0x09, + 0x37, 0x55, 0x14, 0x21, 0x57, 0xff, 0x0a, 0xac, + 0xda, 0xb1, 0xae, 0x31, 0xab, 0x41, 0x30, 0x65, + 0x02, 0x83, 0xd1, 0xdb, 0x65, 0xb7, 0x52, 0xa7, + 0x21, 0x9f, 0x1f, 0x8f, 0x69, 0x23, 0x3b, 0xb8, + 0xf9, 0x6d, 0xe7, 0xc1, 0x53, 0x9f, 0x8f, 0x67, + 0xfc, 0x6e, 0x20, 0x18, 0x31, 0x89, 0xe7, 0xbb, + 0xd4, 0xc1, 0x03, 0x67, 0xd6, 0xa5, 0x76, 0xc9, + 0xea, 0x97, 0x93, 0x02, 0xca, 0x44, 0x52, 0x55, + 0x0f, 0xed, 0x55, 0xb5, 0x49, 0xd6, 0x94, 0x59, + 0xee, 0xcc, 0x1b, 0x5a, 0x00, 0x3d, 0xcd }; + +static const unsigned char kTestRsaPublicKey1_3072[] = { + 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, + 0x00, 0xa5, 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, + 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, 0x76, 0xbe, + 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, + 0xf3, 0x0f, 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, + 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, 0xe4, 0xdd, + 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, + 0x55, 0x1b, 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, + 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, 0xc2, 0x65, + 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, + 0x17, 0x01, 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, + 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, 0xb4, 0x7a, + 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, + 0xf1, 0x14, 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, + 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, 0xa0, 0x0a, + 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, + 0xe3, 0xb6, 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, + 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, 0xf2, 0x59, + 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, + 0xbb, 0x31, 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, + 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, 0xae, 0xb2, + 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, + 0xbb, 0x90, 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, + 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, 0x94, 0x53, + 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, + 0xf0, 0x5f, 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, + 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, 0xea, 0x1e, + 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, + 0xdb, 0x17, 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, + 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, 0xb7, 0xf5, + 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, + 0x26, 0xaf, 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, + 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, 0x9c, 0xee, + 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, + 0x0b, 0x42, 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, + 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, 0xa6, 0x82, + 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, + 0x64, 0xe4, 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, + 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, 0xe9, 0x4c, + 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, + 0xd1, 0x72, 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, + 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, 0x8f, 0xe1, + 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, + 0x50, 0x04, 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, + 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, 0x38, 0xff, + 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, + 0xf0, 0x33, 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, + 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, 0xd5, 0xf0, + 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, + 0x87, 0x02, 0x03, 0x01, 0x00, 0x01 }; + +static const unsigned char kTestRsaPrivateKey2_2048[] = { + 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, + 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4, + 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, + 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, + 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e, + 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, + 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, + 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7, + 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, + 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, + 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68, + 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, + 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, + 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, + 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, + 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, + 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e, + 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, + 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, + 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88, + 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, + 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, + 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8, + 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, + 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, + 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e, + 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, + 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, + 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29, + 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, + 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, + 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, + 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x5e, 0x79, 0x65, + 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, 0x45, 0x0f, + 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, 0xd5, 0xde, + 0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97, 0xeb, 0x3f, + 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, 0x3b, 0x5c, + 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, 0x46, 0xf5, + 0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc, 0x45, 0x38, + 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, 0x5e, 0x79, + 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, 0xc1, 0x6b, + 0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9, 0x0a, 0xfc, + 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, 0x68, 0x7b, + 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, 0xa7, 0x79, + 0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7, 0x00, 0x21, + 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, 0xbe, 0x09, + 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, 0x0e, 0x97, + 0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88, 0x7a, 0xd1, + 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, 0x25, 0x0b, + 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, 0xb6, 0xda, + 0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14, 0x46, 0xc7, + 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, 0xde, 0x00, + 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, 0x68, 0x71, + 0x17, 0x66, 0x12, 0x1a, 0x87, 0x27, 0xf7, 0xef, + 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, 0x6f, 0x35, + 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, 0x13, 0x86, + 0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64, 0xe8, 0x86, + 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, 0x1f, 0x51, + 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, 0x70, 0x98, + 0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f, 0x45, 0x6a, + 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, 0xf6, 0x06, + 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, 0x96, 0xa9, + 0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21, 0xe0, 0xac, + 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, 0xb2, 0x51, + 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, 0x81, 0x81, + 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, 0x45, 0x2d, + 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, 0x78, 0xcc, + 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, + 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, 0xb6, 0x7d, + 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, 0x84, 0x68, + 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, + 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, 0xe6, 0x4d, + 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, 0x3a, 0x83, + 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, + 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, 0x9e, 0xf7, + 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, 0x18, 0x6c, + 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, + 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, 0x06, 0x74, + 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, 0x3c, 0x2d, + 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, + 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, 0xf8, 0xcd, + 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, 0x15, 0x43, + 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, + 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, 0x23, 0x87, + 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, 0xcb, 0x43, + 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, + 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, 0x6d, 0x61, + 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, 0xa7, 0x5b, + 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, + 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, 0xd8, 0x3e, + 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, 0xc1, 0xee, + 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, + 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, 0x46, 0xcd, + 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, 0x0d, 0x6c, + 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, + 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, 0xab, 0x54, + 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, 0x53, 0xa8, + 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, + 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, 0xff, 0x73, + 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, 0x0a, 0x2d, + 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, + 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, 0x79, 0xa0, + 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, 0xeb, 0xdb, + 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, + 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, 0x33, 0xb2, + 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, 0x37, 0x43, + 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, + 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, 0x0f, 0x62, + 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, 0xf9, 0xca, + 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, + 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, 0x02, 0x71, + 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, 0xb1, 0x31, + 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, + 0x41, 0x29, 0x40, 0x19, 0x83, 0x35, 0x24, 0x69, + 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, 0xcc, 0x3b, + 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, + 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, 0xcb, 0x47, + 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, 0x6a, 0xab, + 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, + 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, 0xa8, 0xcd, + 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, 0x5f, 0xdf, + 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, + 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, 0x4b, 0x81, + 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, 0x77, 0xe6, + 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, + 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, 0x3b, 0x70, + 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, 0xd9, 0xbe, + 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, + 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, 0x6f, 0x44, + 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, 0xac, 0x21, + 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, + 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, 0xe7, 0xa6, + 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, 0x28, 0x66, + 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, + 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, 0x81, 0x7b, + 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, 0x70, 0xcf, + 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, + 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, 0xf0, 0xad, + 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, 0x50, 0xcb, + 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, + 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, 0x40, 0xda, + 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, 0xa2, 0x0d, + 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, + 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, 0x47, 0xb2, + 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, 0x72, 0x2c, + 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, + 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 }; + +static const unsigned char kTestRsaPublicKey2_2048[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, + 0x54, 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, + 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, + 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, + 0x57, 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, + 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, + 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, + 0x4e, 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, + 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, + 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, + 0xf9, 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, + 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, + 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, + 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, + 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, + 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, + 0x67, 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, + 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, + 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, + 0x8b, 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, + 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, + 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, + 0xf1, 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, + 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, + 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, + 0x40, 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, + 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, + 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, + 0x86, 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, + 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, + 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, + 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, + 0x05, 0x02, 0x03, 0x01, 0x00, 0x01 }; + +static const unsigned char kTestRsaPrivateKey3_2048[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, + 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40, + 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, + 0x3d, 0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, + 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44, + 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, + 0x32, 0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, + 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08, + 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, + 0xa3, 0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, + 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d, + 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, + 0xbc, 0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, + 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d, + 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, + 0x99, 0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, + 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55, + 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, + 0xb6, 0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, + 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, + 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, + 0x44, 0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, + 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06, + 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, + 0xea, 0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, + 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae, + 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, + 0x94, 0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, + 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84, + 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, + 0x16, 0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, + 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b, + 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x43, 0x8f, 0x19, + 0x83, 0xb1, 0x27, 0x4e, 0xee, 0x98, 0xba, 0xcb, + 0x54, 0xa0, 0x77, 0x11, 0x6d, 0xd4, 0x25, 0x31, + 0x8c, 0xb0, 0x01, 0xcf, 0xe6, 0x80, 0x83, 0x14, + 0x40, 0x67, 0x39, 0x33, 0x67, 0x03, 0x1e, 0xa0, + 0x8b, 0xd1, 0x1d, 0xfd, 0x80, 0xa4, 0xb9, 0xe7, + 0x57, 0x5e, 0xc8, 0x8e, 0x79, 0x71, 0xd5, 0x6b, + 0x09, 0xe9, 0x2b, 0x41, 0xa0, 0x33, 0x64, 0xc9, + 0x66, 0x33, 0xa1, 0xb1, 0x55, 0x07, 0x55, 0x98, + 0x53, 0x10, 0xe6, 0xc0, 0x39, 0x6d, 0x61, 0xd9, + 0xe8, 0x16, 0x52, 0x28, 0xe4, 0x2b, 0xda, 0x27, + 0x01, 0xaf, 0x21, 0x4a, 0xe8, 0x55, 0x1d, 0x0b, + 0xd1, 0x1c, 0xdc, 0xfd, 0xb3, 0x0b, 0xa6, 0x5c, + 0xcc, 0x6e, 0x77, 0xb8, 0xe0, 0xd1, 0x4e, 0x0a, + 0xd7, 0x7a, 0x5e, 0x18, 0xc3, 0xfb, 0xe9, 0xa1, + 0x9c, 0xc3, 0x9c, 0xd4, 0x4a, 0x7e, 0x70, 0x72, + 0x11, 0x18, 0x24, 0x56, 0x24, 0xdf, 0xf8, 0xba, + 0xac, 0x5b, 0x54, 0xd3, 0xc4, 0x65, 0x69, 0xc8, + 0x79, 0x94, 0x16, 0x88, 0x9a, 0x68, 0x1c, 0xbc, + 0xd4, 0xca, 0xec, 0x5e, 0x07, 0x4a, 0xc9, 0x54, + 0x7a, 0x4b, 0xdb, 0x19, 0x88, 0xf6, 0xbe, 0x50, + 0x9d, 0x9e, 0x9d, 0x88, 0x5b, 0x4a, 0x23, 0x86, + 0x2b, 0xa9, 0xa6, 0x6c, 0x70, 0x7d, 0xe1, 0x11, + 0xba, 0xbf, 0x03, 0x2e, 0xf1, 0x46, 0x7e, 0x1b, + 0xed, 0x06, 0x11, 0x57, 0xad, 0x4a, 0xcb, 0xe5, + 0xb1, 0x11, 0x05, 0x0a, 0x30, 0xb1, 0x73, 0x79, + 0xcd, 0x7a, 0x04, 0xcc, 0x70, 0xe9, 0x95, 0xe4, + 0x27, 0xc2, 0xd5, 0x2d, 0x92, 0x44, 0xdf, 0xb4, + 0x94, 0xa8, 0x73, 0xa1, 0x4a, 0xc3, 0xcc, 0xc4, + 0x0e, 0x8d, 0xa1, 0x6a, 0xc2, 0xd8, 0x03, 0x7f, + 0xfa, 0xa7, 0x76, 0x0d, 0xad, 0x87, 0x88, 0xa0, + 0x77, 0xaf, 0x3b, 0x23, 0xd1, 0x66, 0x0b, 0x31, + 0x2b, 0xaf, 0xef, 0xd5, 0x41, 0x02, 0x81, 0x81, + 0x00, 0xdb, 0xc1, 0xe7, 0xdd, 0xba, 0x3c, 0x1f, + 0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb, 0xd2, 0x47, + 0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5, 0xda, 0xc2, + 0x25, 0x64, 0x0a, 0x02, 0xbc, 0x7d, 0x7f, 0x50, + 0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d, 0x77, 0x6d, + 0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd, 0x18, 0xe9, + 0x53, 0x0b, 0x5c, 0xa2, 0x71, 0x14, 0x02, 0xfd, + 0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c, 0x2a, 0x4e, + 0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26, 0xd0, 0xd8, + 0x3d, 0xcf, 0x8c, 0x89, 0x65, 0x6c, 0x35, 0x19, + 0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49, 0xcd, 0x6d, + 0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9, 0xba, 0x43, + 0xe5, 0xdd, 0x39, 0x3a, 0x78, 0x8f, 0x07, 0xb8, + 0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8, 0x07, 0x02, + 0x9b, 0x79, 0x26, 0x32, 0x22, 0x38, 0x91, 0x01, + 0x90, 0x81, 0x29, 0x94, 0xad, 0x77, 0xeb, 0x86, + 0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x29, 0x88, + 0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77, 0x5d, 0x32, + 0x12, 0x5e, 0xdf, 0x28, 0x0c, 0x96, 0x0d, 0xa8, + 0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0, 0x41, 0xcb, + 0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2, 0xfb, 0xd2, + 0xf3, 0xf2, 0x95, 0xff, 0xd8, 0x33, 0x3f, 0x8c, + 0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe, 0x32, 0x66, + 0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe, 0x05, 0xb1, + 0xbc, 0xbe, 0x44, 0x09, 0xb4, 0xfe, 0x95, 0x06, + 0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d, 0x22, 0xa0, + 0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09, 0x2c, 0xb8, + 0xcc, 0xeb, 0x10, 0x4d, 0xa7, 0xd0, 0x4b, 0x46, + 0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47, 0x55, 0xd3, + 0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e, 0xf9, 0x5b, + 0x7b, 0xae, 0x6d, 0x08, 0x9f, 0x0c, 0x2a, 0xe1, + 0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55, 0xa3, 0x6d, + 0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02, 0x81, 0x80, + 0x61, 0x73, 0x3d, 0x64, 0xff, 0xdf, 0x05, 0x8d, + 0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d, 0x7d, 0x53, + 0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4, 0x72, 0x8f, + 0xae, 0x2b, 0x1a, 0x47, 0x87, 0xc7, 0x5b, 0x78, + 0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3, 0x8c, 0x0c, + 0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a, 0x88, 0x7d, + 0x40, 0xfe, 0x95, 0x32, 0x5b, 0xd3, 0x6f, 0x90, + 0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d, 0xc5, 0xc7, + 0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b, 0xcb, 0x78, + 0xad, 0x7e, 0xfe, 0xb6, 0xb1, 0x23, 0x63, 0x01, + 0x94, 0x9a, 0x99, 0x05, 0x63, 0xda, 0xea, 0xf1, + 0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b, 0x35, 0xec, + 0xcb, 0x0b, 0x43, 0xb8, 0x8e, 0x84, 0xb8, 0x09, + 0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c, 0x7c, 0x95, + 0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08, 0x09, 0x8f, + 0x91, 0x17, 0x65, 0x4c, 0xff, 0x6e, 0xbc, 0x51, + 0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d, 0x9d, 0xd8, + 0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f, 0x85, 0xd7, + 0xce, 0x72, 0x02, 0x38, 0xf2, 0x0f, 0x9c, 0x27, + 0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d, 0x02, 0x19, + 0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb, 0x61, 0x8e, + 0xe6, 0xb8, 0xfc, 0x07, 0x3c, 0xd1, 0x1b, 0x16, + 0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26, 0xb2, 0xbd, + 0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11, 0xc5, 0xdd, + 0xf9, 0xdb, 0x4e, 0x48, 0x52, 0xd8, 0xe6, 0x4b, + 0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1, 0x7c, 0xd3, + 0x51, 0x6b, 0x45, 0x46, 0x54, 0x11, 0x1a, 0x71, + 0xd3, 0xcd, 0x6b, 0x8f, 0x79, 0x22, 0x83, 0x02, + 0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed, 0x32, 0xd8, + 0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec, 0x2c, 0x7e, + 0xa4, 0x89, 0xdc, 0xbf, 0xf9, 0x0d, 0x32, 0xc8, + 0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc, 0x70, 0x4e, + 0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81, 0x81, 0x00, + 0xd3, 0x35, 0x1b, 0x19, 0x75, 0x3f, 0x61, 0xf2, + 0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf, 0x0c, 0x0a, + 0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b, 0x13, 0x4d, + 0x1f, 0x86, 0x58, 0x4f, 0xd8, 0xee, 0xfa, 0x76, + 0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2, 0xd2, 0x62, + 0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08, 0xa9, 0x83, + 0x07, 0xac, 0x8c, 0xa3, 0x7e, 0x18, 0xc0, 0x1c, + 0x57, 0x63, 0x8d, 0x05, 0x17, 0x47, 0x1b, 0xd3, + 0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8, 0x1a, 0x43, + 0x04, 0x36, 0xc8, 0x19, 0xbe, 0xdc, 0xa6, 0xe5, + 0x0f, 0x25, 0x62, 0x24, 0x96, 0x92, 0xb6, 0xb3, + 0x97, 0xad, 0x57, 0x9a, 0x90, 0x37, 0x4e, 0x31, + 0x44, 0x74, 0xfa, 0x7c, 0xb4, 0xea, 0xfc, 0x15, + 0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e, 0xed, 0x5b, + 0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b, + 0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8, 0xee, 0x24 }; + +static const unsigned char kTestRsaPublicKey3_2048[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, + 0x43, 0x51, 0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, + 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83, + 0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, + 0xa7, 0x5c, 0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, + 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a, + 0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, + 0x35, 0xb7, 0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, + 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74, + 0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, + 0xc2, 0xd7, 0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, + 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47, + 0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, + 0x56, 0x93, 0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, + 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03, + 0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, + 0x53, 0xfc, 0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, + 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c, + 0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, + 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, + 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0, + 0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, + 0x20, 0xec, 0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, + 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a, + 0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, + 0x59, 0x6e, 0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, + 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87, + 0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, + 0xbc, 0xe8, 0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, + 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f, + 0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, + 0xa7, 0xdd, 0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, + 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01 }; + +unsigned char kTestRsaPrivateKey2CarmichaelTotient_2048[] = { + 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, + 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4, + 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, + 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, + 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e, + 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, + 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, + 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7, + 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, + 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, + 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68, + 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, + 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, + 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, + 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, + 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, + 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e, + 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, + 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, + 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88, + 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, + 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, + 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8, + 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, + 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, + 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e, + 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, + 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, + 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29, + 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, + 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, + 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, + 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x0a, 0xf9, 0x4a, + 0x19, 0x72, 0x88, 0x1b, 0x4e, 0xd8, 0x2f, 0xef, + 0x99, 0x93, 0x32, 0xda, 0x51, 0x21, 0x2e, 0x14, + 0x06, 0xf4, 0xe9, 0x65, 0x1c, 0xf9, 0xd4, 0xcf, + 0x1a, 0x51, 0x53, 0xcd, 0x48, 0x33, 0x8c, 0x30, + 0xed, 0xdd, 0x53, 0x6f, 0x29, 0x82, 0xf9, 0xe0, + 0x74, 0xde, 0xb1, 0x13, 0x01, 0x88, 0x8f, 0xce, + 0x14, 0xc1, 0x3b, 0x90, 0xb7, 0xcc, 0x6c, 0xdf, + 0x35, 0xa1, 0xf2, 0x1a, 0x3d, 0xbe, 0x19, 0xd7, + 0x0a, 0xe4, 0x67, 0x75, 0xbb, 0xfa, 0x87, 0xf4, + 0x03, 0xb5, 0x7f, 0x69, 0xe4, 0x0b, 0x6a, 0xdc, + 0x92, 0x82, 0x54, 0x64, 0x1a, 0x94, 0x2d, 0xe4, + 0x63, 0x40, 0xb2, 0xb4, 0x85, 0x6b, 0xc8, 0x34, + 0xba, 0xa2, 0x14, 0x30, 0x47, 0x1a, 0xeb, 0x90, + 0x62, 0x30, 0x43, 0x44, 0x02, 0xc7, 0x0c, 0x30, + 0xc0, 0x7f, 0xa9, 0x47, 0xae, 0xde, 0x68, 0x27, + 0x92, 0xaa, 0x11, 0x95, 0xf5, 0x6f, 0xfc, 0x19, + 0x8b, 0x49, 0xa0, 0x77, 0x9d, 0xc6, 0x13, 0x5d, + 0x73, 0xff, 0x45, 0xa2, 0x4c, 0x3b, 0xf3, 0xe1, + 0x2d, 0xd7, 0xc4, 0x70, 0xe2, 0x6c, 0x37, 0x99, + 0x4c, 0x7a, 0xa9, 0x27, 0xf8, 0x3a, 0xd6, 0xfd, + 0xc5, 0xd8, 0xfa, 0x2d, 0x0e, 0x71, 0x4b, 0x85, + 0x7e, 0xce, 0xcb, 0x1c, 0x79, 0x71, 0xbd, 0xff, + 0x63, 0x03, 0x6b, 0x58, 0x68, 0xe0, 0x14, 0xca, + 0x5e, 0x85, 0xfd, 0xd0, 0xb7, 0xe0, 0x68, 0x14, + 0xff, 0x2c, 0x82, 0x22, 0x26, 0x8a, 0x3f, 0xbf, + 0xb0, 0x2a, 0x90, 0xff, 0xc7, 0x72, 0xfc, 0x66, + 0x51, 0x3e, 0x51, 0x9f, 0x82, 0x68, 0x0e, 0xf3, + 0x65, 0x74, 0x88, 0xab, 0xb7, 0xe5, 0x97, 0x5f, + 0x0f, 0x3e, 0xe5, 0x3a, 0xbc, 0xa4, 0xa1, 0x50, + 0xdd, 0x5c, 0x94, 0x4b, 0x0c, 0x70, 0x71, 0x48, + 0x4e, 0xd0, 0xec, 0x46, 0x8f, 0xdf, 0xa2, 0x9a, + 0xfe, 0xd8, 0x35, 0x1a, 0x2f, 0x02, 0x81, 0x81, + 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, 0x45, 0x2d, + 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, 0x78, 0xcc, + 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, + 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, 0xb6, 0x7d, + 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, 0x84, 0x68, + 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, + 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, 0xe6, 0x4d, + 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, 0x3a, 0x83, + 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, + 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, 0x9e, 0xf7, + 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, 0x18, 0x6c, + 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, + 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, 0x06, 0x74, + 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, 0x3c, 0x2d, + 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, + 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, 0xf8, 0xcd, + 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, 0x15, 0x43, + 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, + 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, 0x23, 0x87, + 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, 0xcb, 0x43, + 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, + 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, 0x6d, 0x61, + 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, 0xa7, 0x5b, + 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, + 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, 0xd8, 0x3e, + 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, 0xc1, 0xee, + 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, + 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, 0x46, 0xcd, + 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, 0x0d, 0x6c, + 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, + 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, 0xab, 0x54, + 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, 0x53, 0xa8, + 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, + 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, 0xff, 0x73, + 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, 0x0a, 0x2d, + 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, + 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, 0x79, 0xa0, + 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, 0xeb, 0xdb, + 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, + 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, 0x33, 0xb2, + 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, 0x37, 0x43, + 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, + 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, 0x0f, 0x62, + 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, 0xf9, 0xca, + 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, + 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, 0x02, 0x71, + 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, 0xb1, 0x31, + 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, + 0x41, 0x29, 0x40, 0x19, 0x83, 0x35, 0x24, 0x69, + 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, 0xcc, 0x3b, + 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, + 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, 0xcb, 0x47, + 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, 0x6a, 0xab, + 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, + 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, 0xa8, 0xcd, + 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, 0x5f, 0xdf, + 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, + 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, 0x4b, 0x81, + 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, 0x77, 0xe6, + 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, + 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, 0x3b, 0x70, + 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, 0xd9, 0xbe, + 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, + 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, 0x6f, 0x44, + 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, 0xac, 0x21, + 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, + 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, 0xe7, 0xa6, + 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, 0x28, 0x66, + 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, + 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, 0x81, 0x7b, + 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, 0x70, 0xcf, + 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, + 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, 0xf0, 0xad, + 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, 0x50, 0xcb, + 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, + 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, 0x40, 0xda, + 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, 0xa2, 0x0d, + 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, + 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, 0x47, 0xb2, + 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, 0x72, 0x2c, + 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, + 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 +}; + +static const unsigned char kTestRsaPrivateKey3CarmichaelTotient_2048[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, + 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40, + 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, + 0x3d, 0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, + 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44, + 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, + 0x32, 0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, + 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08, + 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, + 0xa3, 0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, + 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d, + 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, + 0xbc, 0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, + 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d, + 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, + 0x99, 0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, + 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55, + 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, + 0xb6, 0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, + 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, + 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, + 0x44, 0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, + 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06, + 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, + 0xea, 0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, + 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae, + 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, + 0x94, 0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, + 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84, + 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, + 0x16, 0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, + 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b, + 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x1a, 0x1a, 0xe3, + 0xb4, 0x2d, 0x9b, 0xd0, 0x1d, 0xc4, 0x54, 0x50, + 0xc4, 0x98, 0xeb, 0xae, 0xf4, 0xab, 0x95, 0x72, + 0x78, 0x60, 0xbe, 0x2e, 0xfc, 0x88, 0x59, 0xc3, + 0xff, 0x5f, 0xb4, 0x01, 0xfd, 0x2c, 0x06, 0x52, + 0xfa, 0xa4, 0x5b, 0xfc, 0x29, 0xdf, 0x82, 0x67, + 0x14, 0x52, 0x39, 0x67, 0xd5, 0x31, 0xc1, 0x41, + 0x02, 0x76, 0x03, 0x5d, 0xd2, 0xc5, 0x74, 0x2c, + 0x24, 0x26, 0xfe, 0xed, 0x46, 0x65, 0x97, 0x22, + 0x74, 0xe7, 0xff, 0x63, 0x35, 0x3b, 0xd8, 0xad, + 0x88, 0x2c, 0xe2, 0x50, 0xf3, 0x76, 0x14, 0xbe, + 0x3a, 0x37, 0x1b, 0xf0, 0x21, 0x91, 0x8e, 0xdd, + 0x43, 0xed, 0xb7, 0xab, 0xcd, 0xfb, 0x8a, 0x31, + 0xa6, 0x26, 0xb4, 0xd5, 0x4b, 0x2c, 0x80, 0x7f, + 0xfc, 0x39, 0x24, 0x33, 0x7d, 0x6d, 0xf3, 0x1c, + 0x06, 0xdd, 0x21, 0x53, 0x70, 0x78, 0xe4, 0x07, + 0x60, 0x2a, 0x3f, 0xb8, 0xd0, 0x47, 0xf6, 0x0e, + 0xbd, 0xde, 0x31, 0xf3, 0x66, 0xfe, 0x6e, 0x4b, + 0x50, 0x75, 0x0b, 0x49, 0x3a, 0x96, 0xeb, 0x63, + 0xb9, 0x24, 0xbb, 0xfc, 0xcd, 0xf5, 0x49, 0x12, + 0xa9, 0x6d, 0x39, 0xd4, 0x18, 0x15, 0x14, 0x50, + 0x82, 0xa9, 0x75, 0xeb, 0x9f, 0x1a, 0xaa, 0x52, + 0x1d, 0x0d, 0x55, 0x74, 0x30, 0x45, 0x3b, 0xd2, + 0xd3, 0xe1, 0xdb, 0x8d, 0xec, 0x38, 0x2b, 0xb0, + 0xdd, 0xda, 0x10, 0xe3, 0x40, 0x87, 0x27, 0xbe, + 0x0b, 0xbf, 0x08, 0x9e, 0x25, 0x95, 0x14, 0xf4, + 0xd7, 0xfe, 0x8c, 0x4f, 0x23, 0xfd, 0x1b, 0xad, + 0x83, 0x6b, 0x05, 0x3a, 0x83, 0xfa, 0x65, 0x1e, + 0x65, 0x12, 0xe3, 0x9f, 0xea, 0x52, 0xd7, 0xed, + 0x01, 0x7d, 0xc5, 0xf1, 0x96, 0x2d, 0xf5, 0x4a, + 0xa3, 0xcb, 0x69, 0x6c, 0x9a, 0x48, 0xe9, 0xf5, + 0x01, 0xef, 0x1d, 0x2e, 0x90, 0x64, 0x6c, 0x0b, + 0x79, 0xe0, 0xeb, 0x64, 0x29, 0x02, 0x81, 0x81, + 0x00, 0xdb, 0xc1, 0xe7, 0xdd, 0xba, 0x3c, 0x1f, + 0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb, 0xd2, 0x47, + 0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5, 0xda, 0xc2, + 0x25, 0x64, 0x0a, 0x02, 0xbc, 0x7d, 0x7f, 0x50, + 0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d, 0x77, 0x6d, + 0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd, 0x18, 0xe9, + 0x53, 0x0b, 0x5c, 0xa2, 0x71, 0x14, 0x02, 0xfd, + 0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c, 0x2a, 0x4e, + 0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26, 0xd0, 0xd8, + 0x3d, 0xcf, 0x8c, 0x89, 0x65, 0x6c, 0x35, 0x19, + 0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49, 0xcd, 0x6d, + 0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9, 0xba, 0x43, + 0xe5, 0xdd, 0x39, 0x3a, 0x78, 0x8f, 0x07, 0xb8, + 0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8, 0x07, 0x02, + 0x9b, 0x79, 0x26, 0x32, 0x22, 0x38, 0x91, 0x01, + 0x90, 0x81, 0x29, 0x94, 0xad, 0x77, 0xeb, 0x86, + 0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x29, 0x88, + 0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77, 0x5d, 0x32, + 0x12, 0x5e, 0xdf, 0x28, 0x0c, 0x96, 0x0d, 0xa8, + 0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0, 0x41, 0xcb, + 0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2, 0xfb, 0xd2, + 0xf3, 0xf2, 0x95, 0xff, 0xd8, 0x33, 0x3f, 0x8c, + 0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe, 0x32, 0x66, + 0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe, 0x05, 0xb1, + 0xbc, 0xbe, 0x44, 0x09, 0xb4, 0xfe, 0x95, 0x06, + 0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d, 0x22, 0xa0, + 0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09, 0x2c, 0xb8, + 0xcc, 0xeb, 0x10, 0x4d, 0xa7, 0xd0, 0x4b, 0x46, + 0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47, 0x55, 0xd3, + 0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e, 0xf9, 0x5b, + 0x7b, 0xae, 0x6d, 0x08, 0x9f, 0x0c, 0x2a, 0xe1, + 0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55, 0xa3, 0x6d, + 0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02, 0x81, 0x80, + 0x61, 0x73, 0x3d, 0x64, 0xff, 0xdf, 0x05, 0x8d, + 0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d, 0x7d, 0x53, + 0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4, 0x72, 0x8f, + 0xae, 0x2b, 0x1a, 0x47, 0x87, 0xc7, 0x5b, 0x78, + 0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3, 0x8c, 0x0c, + 0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a, 0x88, 0x7d, + 0x40, 0xfe, 0x95, 0x32, 0x5b, 0xd3, 0x6f, 0x90, + 0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d, 0xc5, 0xc7, + 0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b, 0xcb, 0x78, + 0xad, 0x7e, 0xfe, 0xb6, 0xb1, 0x23, 0x63, 0x01, + 0x94, 0x9a, 0x99, 0x05, 0x63, 0xda, 0xea, 0xf1, + 0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b, 0x35, 0xec, + 0xcb, 0x0b, 0x43, 0xb8, 0x8e, 0x84, 0xb8, 0x09, + 0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c, 0x7c, 0x95, + 0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08, 0x09, 0x8f, + 0x91, 0x17, 0x65, 0x4c, 0xff, 0x6e, 0xbc, 0x51, + 0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d, 0x9d, 0xd8, + 0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f, 0x85, 0xd7, + 0xce, 0x72, 0x02, 0x38, 0xf2, 0x0f, 0x9c, 0x27, + 0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d, 0x02, 0x19, + 0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb, 0x61, 0x8e, + 0xe6, 0xb8, 0xfc, 0x07, 0x3c, 0xd1, 0x1b, 0x16, + 0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26, 0xb2, 0xbd, + 0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11, 0xc5, 0xdd, + 0xf9, 0xdb, 0x4e, 0x48, 0x52, 0xd8, 0xe6, 0x4b, + 0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1, 0x7c, 0xd3, + 0x51, 0x6b, 0x45, 0x46, 0x54, 0x11, 0x1a, 0x71, + 0xd3, 0xcd, 0x6b, 0x8f, 0x79, 0x22, 0x83, 0x02, + 0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed, 0x32, 0xd8, + 0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec, 0x2c, 0x7e, + 0xa4, 0x89, 0xdc, 0xbf, 0xf9, 0x0d, 0x32, 0xc8, + 0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc, 0x70, 0x4e, + 0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81, 0x81, 0x00, + 0xd3, 0x35, 0x1b, 0x19, 0x75, 0x3f, 0x61, 0xf2, + 0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf, 0x0c, 0x0a, + 0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b, 0x13, 0x4d, + 0x1f, 0x86, 0x58, 0x4f, 0xd8, 0xee, 0xfa, 0x76, + 0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2, 0xd2, 0x62, + 0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08, 0xa9, 0x83, + 0x07, 0xac, 0x8c, 0xa3, 0x7e, 0x18, 0xc0, 0x1c, + 0x57, 0x63, 0x8d, 0x05, 0x17, 0x47, 0x1b, 0xd3, + 0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8, 0x1a, 0x43, + 0x04, 0x36, 0xc8, 0x19, 0xbe, 0xdc, 0xa6, 0xe5, + 0x0f, 0x25, 0x62, 0x24, 0x96, 0x92, 0xb6, 0xb3, + 0x97, 0xad, 0x57, 0x9a, 0x90, 0x37, 0x4e, 0x31, + 0x44, 0x74, 0xfa, 0x7c, 0xb4, 0xea, 0xfc, 0x15, + 0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e, 0xed, 0x5b, + 0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b, + 0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8, 0xee, 0x24 +}; + +static const unsigned char kTestRsaPrivateKey4CarmichaelTotient_2048[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xb5, 0xed, 0x90, 0x77, + 0xfe, 0xb4, 0xac, 0x2c, 0x7a, 0x53, 0x0a, 0xe0, + 0x94, 0x87, 0xfe, 0xc7, 0xc4, 0xa8, 0x3f, 0x09, + 0x5b, 0xdd, 0xa7, 0x6e, 0x70, 0xc9, 0xf0, 0xef, + 0x9e, 0x47, 0xad, 0xcf, 0xbe, 0xdc, 0x9b, 0x89, + 0xc7, 0xbd, 0xb5, 0x2f, 0xa9, 0x43, 0x97, 0xd5, + 0x5b, 0xae, 0x73, 0x46, 0xa9, 0x10, 0x47, 0x67, + 0x35, 0xa0, 0x6e, 0xfc, 0xfb, 0x45, 0xf8, 0x85, + 0xb0, 0xf1, 0xd3, 0x67, 0x84, 0x91, 0x03, 0x1b, + 0x90, 0xb1, 0x18, 0x12, 0xa4, 0x15, 0x7a, 0x65, + 0xfa, 0x74, 0x4f, 0x73, 0x34, 0x70, 0x9b, 0x64, + 0x91, 0x1f, 0x89, 0x4f, 0x40, 0x2d, 0x37, 0x2f, + 0xbf, 0x2b, 0x1e, 0x54, 0x95, 0x7c, 0x16, 0x14, + 0xd3, 0x60, 0xf4, 0xd7, 0x86, 0xf7, 0x0c, 0x2c, + 0xbb, 0x3f, 0xe7, 0x49, 0xdf, 0x74, 0x8b, 0xc9, + 0x8f, 0x7c, 0x22, 0x23, 0xd9, 0xbd, 0x2a, 0x57, + 0xfe, 0xfb, 0x7a, 0x77, 0x91, 0xce, 0xa9, 0xd8, + 0xa7, 0xfb, 0xf5, 0xe7, 0x39, 0xbe, 0xfb, 0xf5, + 0x47, 0x57, 0x48, 0xe7, 0x71, 0x37, 0xda, 0x53, + 0xe7, 0x89, 0xd3, 0x37, 0x31, 0x98, 0xba, 0xd8, + 0xd0, 0xd9, 0x74, 0xb2, 0xda, 0x06, 0x99, 0x3b, + 0x97, 0x87, 0xc4, 0x22, 0x3c, 0xf7, 0xa3, 0x13, + 0x84, 0x49, 0x27, 0x74, 0x50, 0x03, 0x51, 0xea, + 0xc9, 0x57, 0xeb, 0x16, 0xa9, 0x7a, 0xea, 0x3c, + 0x26, 0xda, 0xcb, 0xc6, 0x86, 0x85, 0xfc, 0xd0, + 0x73, 0x26, 0xce, 0xe8, 0xd9, 0x92, 0x09, 0xaf, + 0x68, 0x0c, 0x01, 0x8d, 0x95, 0xfd, 0x8d, 0x01, + 0xde, 0x26, 0x0f, 0x25, 0xb9, 0xd5, 0x80, 0x99, + 0x62, 0xb1, 0x71, 0xcd, 0xa1, 0x5f, 0x52, 0xab, + 0x97, 0x97, 0x3f, 0xa7, 0x93, 0x40, 0x89, 0x4f, + 0xbc, 0x7d, 0x99, 0x7a, 0x7e, 0x35, 0xa5, 0xfc, + 0x74, 0xfb, 0xe4, 0x5a, 0x0b, 0xed, 0xf0, 0xef, + 0x65, 0xed, 0x22, 0x95, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x35, 0x4a, 0x9d, + 0x36, 0xa1, 0x3c, 0x50, 0x71, 0x08, 0xf6, 0x19, + 0x6a, 0x16, 0xe8, 0x4d, 0x68, 0x3c, 0x41, 0xa2, + 0x91, 0x7d, 0xc1, 0x0c, 0xa2, 0x2b, 0xd4, 0xe3, + 0xc8, 0x75, 0x3f, 0x7e, 0xf9, 0x2b, 0x6a, 0x18, + 0xff, 0xbf, 0xac, 0x61, 0x0e, 0x50, 0x91, 0x55, + 0xc1, 0x30, 0x85, 0x86, 0x0c, 0x0d, 0x4b, 0x10, + 0xf7, 0x79, 0x3c, 0x81, 0x36, 0x86, 0xee, 0x84, + 0xb1, 0xb8, 0xd6, 0xe5, 0xbb, 0xdd, 0x97, 0xd2, + 0xe6, 0xb8, 0xb8, 0x3f, 0x9a, 0x7a, 0x49, 0x36, + 0x5c, 0xf8, 0x04, 0x29, 0x1f, 0xd0, 0x9d, 0x29, + 0xcf, 0xc8, 0x39, 0x0a, 0x32, 0x56, 0x54, 0xc8, + 0x65, 0x2a, 0xa5, 0x19, 0x51, 0xe2, 0xa6, 0x02, + 0x1b, 0xe0, 0x9d, 0x76, 0xab, 0x49, 0xc4, 0x45, + 0x63, 0x37, 0x08, 0xad, 0x9a, 0x34, 0xa4, 0x41, + 0xac, 0x6d, 0xe5, 0x09, 0x65, 0x22, 0x0b, 0xa9, + 0x03, 0x34, 0xd4, 0x7a, 0x97, 0x5c, 0x5d, 0xdc, + 0xa3, 0xae, 0xfd, 0xe8, 0xd6, 0xe8, 0xf8, 0x52, + 0x44, 0x9c, 0xf6, 0x00, 0xe3, 0x8e, 0xad, 0xee, + 0xa3, 0x82, 0x8a, 0x85, 0x07, 0x8b, 0x46, 0x12, + 0x0f, 0xa1, 0xca, 0x07, 0x0d, 0x50, 0x02, 0x96, + 0x71, 0x2f, 0x1b, 0x2b, 0x59, 0x17, 0x95, 0xec, + 0x90, 0xa6, 0xb9, 0x05, 0xff, 0xa2, 0x25, 0x75, + 0xc5, 0x57, 0x42, 0x37, 0x12, 0xa0, 0xb6, 0x09, + 0xa1, 0xe0, 0xef, 0x70, 0x85, 0xa7, 0x39, 0x47, + 0xb1, 0x72, 0x78, 0x0d, 0xf4, 0x4a, 0xa7, 0xca, + 0x92, 0x68, 0x2c, 0xe8, 0x53, 0x54, 0x52, 0xe2, + 0x21, 0xce, 0xaf, 0x7e, 0x56, 0x05, 0x74, 0xe1, + 0xb7, 0x18, 0xed, 0x7f, 0x83, 0xf6, 0x7d, 0x99, + 0x33, 0xf9, 0x60, 0x24, 0xae, 0x59, 0x09, 0xc0, + 0x95, 0x90, 0x5a, 0x19, 0x25, 0xb0, 0x79, 0xfd, + 0x0b, 0x2c, 0x73, 0x85, 0xc5, 0xa0, 0xe5, 0x7b, + 0x3a, 0x47, 0xce, 0x8d, 0x03, 0x02, 0x81, 0x81, + 0x00, 0xe2, 0xd7, 0xeb, 0x7f, 0xc1, 0x1f, 0x13, + 0x8f, 0x52, 0x49, 0xa2, 0x83, 0x22, 0xb7, 0x3d, + 0x93, 0xfe, 0xe7, 0x20, 0x03, 0xf6, 0x6c, 0xd8, + 0x95, 0x25, 0xef, 0x5f, 0xa4, 0xd4, 0x8f, 0xb1, + 0x38, 0x39, 0x7c, 0xe0, 0xdd, 0xff, 0x56, 0xa7, + 0xdf, 0x59, 0xe1, 0x26, 0x36, 0x72, 0x78, 0xdf, + 0xd4, 0x19, 0xc0, 0x37, 0x58, 0x75, 0x85, 0xdd, + 0x8b, 0x2e, 0x73, 0xfa, 0xf5, 0x65, 0x93, 0xc7, + 0x36, 0x3d, 0xb7, 0x8e, 0xa9, 0x5e, 0x41, 0x71, + 0x55, 0x1d, 0x22, 0x83, 0x13, 0x91, 0x76, 0xa4, + 0x80, 0xa0, 0xac, 0xf6, 0xd5, 0xbb, 0x6a, 0xf3, + 0x47, 0x01, 0x81, 0x3d, 0x34, 0xd6, 0x32, 0x49, + 0xb2, 0xc9, 0xa7, 0xb3, 0x93, 0x64, 0x47, 0x6d, + 0x30, 0x8b, 0x23, 0xbd, 0xf0, 0xe8, 0x3c, 0xb5, + 0x4e, 0x33, 0x74, 0x4b, 0x60, 0x38, 0x25, 0x89, + 0xaf, 0xf8, 0x86, 0x53, 0x73, 0x62, 0x74, 0x35, + 0xb7, 0x02, 0x81, 0x81, 0x00, 0xcd, 0x4f, 0xbd, + 0xe1, 0x43, 0xb4, 0xfe, 0x9a, 0x50, 0xb8, 0xeb, + 0xf3, 0xd6, 0x3f, 0x2a, 0xbe, 0x19, 0x7a, 0x2c, + 0xf4, 0x63, 0x35, 0x29, 0x47, 0x39, 0x17, 0xb4, + 0xc7, 0x58, 0xc0, 0x5a, 0xbf, 0x5c, 0xa4, 0xcf, + 0x9e, 0x4b, 0xad, 0x5a, 0x95, 0xfd, 0xfa, 0x50, + 0xbe, 0x54, 0x12, 0xbe, 0xd8, 0x8e, 0xaf, 0x8b, + 0xb2, 0x15, 0xa1, 0xb5, 0x78, 0x03, 0xa6, 0x3b, + 0x18, 0x13, 0x1d, 0xfe, 0x38, 0xb7, 0x65, 0x1e, + 0x20, 0x39, 0xa9, 0x25, 0x37, 0xed, 0xe8, 0x5b, + 0xc7, 0xd0, 0x60, 0xe2, 0x1f, 0xf2, 0x06, 0x5d, + 0x3c, 0x5c, 0x36, 0xe0, 0xc6, 0x83, 0x52, 0xd3, + 0xd3, 0x3f, 0x7c, 0x43, 0x82, 0x4a, 0x13, 0xf8, + 0x8d, 0x48, 0x03, 0x4f, 0xb8, 0xad, 0x64, 0x85, + 0xa9, 0xee, 0xde, 0x3d, 0x0c, 0x23, 0xb6, 0xe9, + 0x8d, 0xad, 0x02, 0x62, 0x32, 0x58, 0x39, 0xbf, + 0x5f, 0x3c, 0x6c, 0x0a, 0x13, 0x02, 0x81, 0x80, + 0x7f, 0x87, 0x3c, 0x12, 0x3a, 0x94, 0x29, 0xfe, + 0xed, 0x18, 0x10, 0x91, 0x00, 0xb7, 0x5b, 0x9b, + 0x14, 0x37, 0x03, 0xbc, 0xb6, 0x91, 0x42, 0xc1, + 0xb6, 0xed, 0xf8, 0x2b, 0x46, 0x84, 0xf1, 0xf0, + 0xd6, 0x00, 0xea, 0xba, 0x63, 0x8e, 0x68, 0x1a, + 0x1d, 0x01, 0x82, 0xe6, 0x21, 0x3c, 0xeb, 0x38, + 0xe6, 0xb1, 0x35, 0x6c, 0x39, 0xc5, 0xe4, 0x63, + 0x16, 0xde, 0x85, 0x3b, 0xe8, 0xbb, 0x47, 0xc7, + 0xaa, 0xb2, 0xc3, 0x35, 0x5c, 0x94, 0x16, 0x0e, + 0xef, 0xae, 0x33, 0x5a, 0x90, 0xf0, 0xce, 0x52, + 0xb2, 0x02, 0x0b, 0x52, 0xe5, 0x66, 0x9f, 0x16, + 0x50, 0x36, 0x44, 0x1e, 0xac, 0x3c, 0xe1, 0x49, + 0xee, 0x2c, 0xa5, 0xbc, 0x3b, 0x28, 0x1c, 0xae, + 0xa9, 0xca, 0x92, 0x42, 0x19, 0x8d, 0xe7, 0xaf, + 0xeb, 0x25, 0x7a, 0x2a, 0xc1, 0xe5, 0xf8, 0x9e, + 0x41, 0x6d, 0xe3, 0x04, 0x7f, 0x59, 0x2d, 0xc9, + 0x02, 0x81, 0x81, 0x00, 0xbe, 0xc3, 0x51, 0xdd, + 0x25, 0x48, 0xdd, 0xab, 0xca, 0x47, 0x17, 0xcd, + 0x57, 0x0b, 0x18, 0x0e, 0xcb, 0xa3, 0x4e, 0x73, + 0xc0, 0x5e, 0x1b, 0xbd, 0x76, 0x99, 0xc5, 0x39, + 0xd8, 0x07, 0xda, 0x09, 0xa5, 0xed, 0xe8, 0x8e, + 0xdf, 0x27, 0xf2, 0x5c, 0x1d, 0x40, 0xe0, 0x97, + 0x07, 0x8c, 0xe7, 0x50, 0x55, 0xbb, 0x5c, 0x24, + 0x1a, 0x9f, 0x46, 0xfa, 0x7d, 0x01, 0x8e, 0x34, + 0xbf, 0x46, 0x85, 0xf8, 0x72, 0xc6, 0x7c, 0x68, + 0x5a, 0xcb, 0x03, 0xae, 0xe4, 0xd9, 0x99, 0xb5, + 0x9d, 0xb2, 0xf7, 0x47, 0xd1, 0x5c, 0x02, 0x73, + 0x5c, 0x07, 0x0d, 0x70, 0xc5, 0x82, 0x47, 0x19, + 0x28, 0x0a, 0xb0, 0xbb, 0x35, 0x53, 0x3b, 0x05, + 0x22, 0x9d, 0x19, 0x0c, 0xb1, 0xe7, 0x0d, 0x9e, + 0xa8, 0x38, 0x4c, 0x26, 0xa4, 0x64, 0x86, 0xbb, + 0x41, 0xbe, 0x4e, 0x39, 0x12, 0xea, 0x8d, 0x1a, + 0xd3, 0x0c, 0x5b, 0x8b, 0x02, 0x81, 0x81, 0x00, + 0xa9, 0x2d, 0x4b, 0x43, 0x55, 0x7f, 0x90, 0xb4, + 0xea, 0xf9, 0xc2, 0x4c, 0x8c, 0x4d, 0xee, 0x62, + 0x85, 0xd2, 0x58, 0x71, 0xc7, 0xb3, 0xfb, 0x80, + 0x37, 0xd9, 0xc5, 0x20, 0x61, 0xeb, 0x39, 0x7f, + 0x4d, 0x09, 0x7c, 0x32, 0x46, 0x8b, 0x49, 0xa4, + 0xd6, 0x99, 0xb4, 0xdc, 0xb6, 0xe3, 0x2a, 0x77, + 0x53, 0xa3, 0xee, 0xdb, 0x4d, 0xf9, 0x59, 0x20, + 0x8a, 0x83, 0x94, 0x29, 0x72, 0x96, 0x76, 0x36, + 0x63, 0xbe, 0xa1, 0xe8, 0x94, 0xf9, 0x50, 0x71, + 0x56, 0xaa, 0xeb, 0x90, 0x06, 0xf2, 0x05, 0xb1, + 0x19, 0xf0, 0x13, 0x89, 0x2d, 0x7b, 0x94, 0xb7, + 0xf2, 0x18, 0xfa, 0x52, 0x09, 0x82, 0x99, 0x20, + 0xe9, 0x7a, 0x1c, 0x68, 0xfe, 0x44, 0xe5, 0xf8, + 0xc5, 0xd3, 0xc6, 0x66, 0x3d, 0xe3, 0x43, 0xc3, + 0x63, 0x81, 0xff, 0x78, 0x91, 0x5c, 0x48, 0x16, + 0xbe, 0x92, 0x90, 0x2f, 0x10, 0x9f, 0x2d, 0x17 +}; + +RsaTestKeys::RsaTestKeys() : + private_key_1_3072_bits_(std::begin(kTestRsaPrivateKey1_3072), + std::end(kTestRsaPrivateKey1_3072)), + public_key_1_3072_bits_(std::begin(kTestRsaPublicKey1_3072), + std::end(kTestRsaPublicKey1_3072)), + private_key_2_2048_bits_(std::begin(kTestRsaPrivateKey2_2048), + std::end(kTestRsaPrivateKey2_2048)), + public_key_2_2048_bits_(std::begin(kTestRsaPublicKey2_2048), + std::end(kTestRsaPublicKey2_2048)), + private_key_3_2048_bits_(std::begin(kTestRsaPrivateKey3_2048), + std::end(kTestRsaPrivateKey3_2048)), + public_key_3_2048_bits_(std::begin(kTestRsaPublicKey3_2048), + std::end(kTestRsaPublicKey3_2048)), + private_key_2_carmichael_totient_2048_bits_( + std::begin(kTestRsaPrivateKey2CarmichaelTotient_2048), + std::end(kTestRsaPrivateKey2CarmichaelTotient_2048)), + private_key_3_carmichael_totient_2048_bits_( + std::begin(kTestRsaPrivateKey3CarmichaelTotient_2048), + std::end(kTestRsaPrivateKey3CarmichaelTotient_2048)), + private_key_4_carmichael_totient_2048_bits_( + std::begin(kTestRsaPrivateKey4CarmichaelTotient_2048), + std::end(kTestRsaPrivateKey4CarmichaelTotient_2048)) { +} + +} // namespace widevine diff --git a/common/rsa_test_keys.h b/common/rsa_test_keys.h new file mode 100644 index 0000000..68d98a4 --- /dev/null +++ b/common/rsa_test_keys.h @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// RSA keys generated using fake_prng for purposes of testing. + +#ifndef COMMON_RSA_TEST_KEYS_H_ +#define COMMON_RSA_TEST_KEYS_H_ + +#include + +namespace widevine { + +// Container for test RSA keys +// +// Except for the keys noted below, these keys were generated using Euler's +// Totient. +// +class RsaTestKeys { + public: + RsaTestKeys(); + + // Returns 3072-bit private RSA test key 1 + const std::string& private_test_key_1_3072_bits() const { + return private_key_1_3072_bits_; + } + // Returns 3072-bit public RSA test key 1 + const std::string& public_test_key_1_3072_bits() const { + return public_key_1_3072_bits_; + } + // Returns 2048-bit private RSA test key 2 + const std::string& private_test_key_2_2048_bits() const { + return private_key_2_2048_bits_; + } + // Returns 2048-bit public RSA test key 2 + const std::string& public_test_key_2_2048_bits() const { + return public_key_2_2048_bits_; + } + // Returns 2048-bit private RSA test key 3 + const std::string& private_test_key_3_2048_bits() const { + return private_key_3_2048_bits_; + } + // Returns 2048-bit public RSA test key 3 + const std::string& public_test_key_3_2048_bits() const { + return public_key_3_2048_bits_; + } + + // This key was converted to a Carmichael totient version from the original + // private_key_2_2048_bits_. + const std::string& private_test_key_2_carmichael_totient_2048_bits() const { + return private_key_2_carmichael_totient_2048_bits_; + } + + // This key was converted to a Carmichael totient version from the original + // private_key_3_2048_bits_. + const std::string& private_test_key_3_carmichael_totient_2048_bits() const { + return private_key_3_carmichael_totient_2048_bits_; + } + + // This key has been created using the Carmichael totient. This is + // useful for testing with some RSA implementations that had challenges + // with keys generated this way. + const std::string& private_test_key_4_carmichael_totient_2048_bits() const { + return private_key_4_carmichael_totient_2048_bits_; + } + + private: + RsaTestKeys(const RsaTestKeys&) = delete; + RsaTestKeys& operator=(const RsaTestKeys&) = delete; + + std::string private_key_1_3072_bits_; + std::string public_key_1_3072_bits_; + std::string private_key_2_2048_bits_; + std::string public_key_2_2048_bits_; + std::string private_key_3_2048_bits_; + std::string public_key_3_2048_bits_; + + // Tests keys that use the Carmichael totient to calculate d. + std::string private_key_2_carmichael_totient_2048_bits_; + std::string private_key_3_carmichael_totient_2048_bits_; + std::string private_key_4_carmichael_totient_2048_bits_; +}; + +} // namespace widevine + +#endif // COMMON_RSA_TEST_KEYS_H_ diff --git a/common/rsa_util.cc b/common/rsa_util.cc new file mode 100644 index 0000000..f32219d --- /dev/null +++ b/common/rsa_util.cc @@ -0,0 +1,514 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// RSA utility functions for serializing and deserializing RSA keys, +// encryption, and signing. + +#include "common/rsa_util.h" + +#include +#include +#include + +#include "glog/logging.h" +#include "openssl/pem.h" +#include "openssl/x509.h" + +namespace { +int BigNumGreaterThanPow2(const BIGNUM* b, int n) { + if (BN_is_negative(b) || n == INT_MAX) { + return 0; + } + int b_bits = BN_num_bits(b); + return b_bits > n + 1 || (b_bits == n + 1 && !BN_is_pow2(b)); +} +} // anonymous namespace + +namespace widevine { +namespace rsa_util { + +static bool SerializeRsaKey(const RSA* key, std::string* serialized_key, + bool serialize_private_key) { + if (key == nullptr) { + LOG(ERROR) << (serialize_private_key ? "Private" : "Public") + << " RSA key is nullptr."; + return false; + } + if (serialized_key == nullptr) { + LOG(ERROR) << "Pointer to hold serialized RSA" << (serialize_private_key ? + "Private" : "Public") << "Key is nullptr."; + return false; + } + BIO* bio = BIO_new(BIO_s_mem()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new returned nullptr"; + return false; + } + bool success = false; + if ((serialize_private_key ? + i2d_RSAPrivateKey_bio(bio, const_cast(key)) : + i2d_RSAPublicKey_bio(bio, const_cast(key))) != 0) { + int serialized_size = BIO_pending(bio); + serialized_key->assign(serialized_size, 0); + if (BIO_read(bio, &(*serialized_key)[0], serialized_size) == + serialized_size) { + success = true; + } else { + LOG(ERROR) << "BIO_read failure"; + } + } else { + LOG(ERROR) << (serialize_private_key ? "Private" : "Public") << + " key serialization failure"; + } + BIO_free(bio); + return success; +} + +static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key, + bool deserialize_private_key) { + if (serialized_key.empty()) { + LOG(ERROR) << "Serialized RSA" << (deserialize_private_key ? + "Private" : "Public") << "Key is empty."; + return false; + } + if (key == nullptr) { + LOG(ERROR) << "Pointer to hold new RSA " << (deserialize_private_key ? + "private" : "public") << " key is nullptr."; + return false; + } + BIO* bio = BIO_new_mem_buf(const_cast(serialized_key.data()), + serialized_key.size()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new_mem_buf returned nullptr"; + return false; + } + *key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, nullptr) : + d2i_RSAPublicKey_bio(bio, nullptr); + BIO_free(bio); + if (*key == nullptr) { + LOG(ERROR) << (deserialize_private_key ? "Private" : "Public") << + " RSA key deserialization failure"; + } + return *key != nullptr; +} + +bool SerializeRsaPrivateKey(const RSA* private_key, + std::string* serialized_private_key) { + return SerializeRsaKey(private_key, serialized_private_key, true); +} + +bool DeserializeRsaPrivateKey(const std::string& serialized_private_key, + RSA** private_key) { + return DeserializeRsaKey(serialized_private_key, private_key, true); +} + +bool SerializeRsaPublicKey(const RSA* public_key, + std::string* serialized_public_key) { + return SerializeRsaKey(public_key, serialized_public_key, false); +} + +bool DeserializeRsaPublicKey(const std::string& serialized_public_key, + RSA** public_key) { + return DeserializeRsaKey(serialized_public_key, public_key, false); +} + +bool SerializePrivateKeyInfo(const RSA* private_key, + std::string* serialized_private_key) { + if (private_key == nullptr) { + LOG(ERROR) << "Private RSA key is nullptr."; + return false; + } + if (serialized_private_key == nullptr) { + LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is nullptr."; + return false; + } + // The following method of serializing a PKCS#8 PrivateKeyInfo object + // was obtained from analyzing the openssl utility code, as the official + // mechanism via i2d_PKCS8PrivateKey_bio is broken in the current openssl + // version (1.0.0c). Please refer to b/8560683. + EVP_PKEY* evp = EVP_PKEY_new(); + if (evp == nullptr) { + LOG(ERROR) << "EVP_PKEY_new returned nullptr."; + return false; + } + bool success = false; + PKCS8_PRIV_KEY_INFO *pkcs8_pki = nullptr; + BIO* bio = nullptr; + if (EVP_PKEY_set1_RSA(evp, const_cast(private_key)) == 0) { + LOG(ERROR) << "EVP_PKEY_set1_RSA failed."; + goto cleanup; + } + pkcs8_pki = EVP_PKEY2PKCS8(evp); + if (pkcs8_pki == nullptr) { + LOG(ERROR) << "EVP_PKEY2PKCS8 returned nullptr."; + goto cleanup; + } + bio = BIO_new(BIO_s_mem()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new returned nullptr."; + goto cleanup; + } + if (i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8_pki) == 0) { + LOG(ERROR) << "i2d_PKCS8_PRIV_KEY_INFO_bio failed."; + goto cleanup; + } + { + int serialized_size = BIO_pending(bio); + serialized_private_key->assign(serialized_size, 0); + if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) != + serialized_size) { + LOG(ERROR) << "BIO_read failed."; + goto cleanup; + } + } + success = true; + +cleanup: + if (bio != nullptr) { + BIO_free(bio); + } + if (pkcs8_pki != nullptr) { + PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); + } + EVP_PKEY_free(evp); + return success; +} + +bool DeserializePrivateKeyInfo(const std::string& serialized_private_key, + RSA** private_key) { + if (serialized_private_key.empty()) { + LOG(ERROR) << "Serialized PrivateKeyInfo is empty."; + return false; + } + if (private_key == nullptr) { + LOG(ERROR) << "Pointer to hold new RSA private key is nullptr."; + return false; + } + // The following method of deserializing a PKCS#8 PrivateKeyInfo object + // was obtained from analyzing the openssl utility code, as the official + // mechanism via d2i_PKCS8PrivateKey_bio is broken in the current openssl + // version (1.0.0c). Please refer to b/8560683. + BIO* bio = BIO_new_mem_buf(const_cast(serialized_private_key.data()), + serialized_private_key.size()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new_mem_buf returned nullptr"; + return false; + } + bool success = false; + EVP_PKEY* evp = nullptr; + PKCS8_PRIV_KEY_INFO *pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr); + if (pkcs8_pki == nullptr) { + LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr."; + goto cleanup; + } + evp = EVP_PKCS82PKEY(pkcs8_pki); + if (evp == nullptr) { + LOG(ERROR) << "EVP_PKCS82PKEY returned nullptr."; + goto cleanup; + } + *private_key = EVP_PKEY_get1_RSA(evp); + if (*private_key == nullptr) { + LOG(ERROR) << "PrivateKeyInfo did not contain an RSA key."; + goto cleanup; + } + success = true; + +cleanup: + if (evp != nullptr) { + EVP_PKEY_free(evp); + } + if (pkcs8_pki != nullptr) { + PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); + } + BIO_free(bio); + return success; +} + +bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key, + std::string* private_key_info) { + RSA* key = nullptr; + if (DeserializeRsaPrivateKey(rsa_private_key, &key)) { + bool success = SerializePrivateKeyInfo(key, private_key_info); + RSA_free(key); + return success; + } + return false; +} + +bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + std::string* rsa_private_key) { + RSA* key = nullptr; + if (DeserializePrivateKeyInfo(private_key_info, &key)) { + bool success = SerializeRsaPrivateKey(key, rsa_private_key); + RSA_free(key); + return success; + } + return false; +} + +bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key, + const std::string& passphrase, + std::string* serialized_private_key) { + if (private_key == nullptr) { + LOG(ERROR) << "Private RSA key is nullptr."; + return false; + } + if (passphrase.empty()) { + LOG(ERROR) << "Passphrase for RSA key encryption is empty."; + return false; + } + if (serialized_private_key == nullptr) { + LOG(ERROR) + << "Pointer to hold serialized EncryptedPrivateKeyInfo is nullptr."; + return false; + } + EVP_PKEY* evp = EVP_PKEY_new(); + if (evp == nullptr) { + LOG(ERROR) << "EVP_PKEY_new returned nullptr."; + return false; + } + bool success = false; + BIO* bio = nullptr; + if (EVP_PKEY_set1_RSA(evp, const_cast(private_key)) == 0) { + LOG(ERROR) << "EVP_PKEY_set1_RSA failed."; + goto cleanup; + } + bio = BIO_new(BIO_s_mem()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new returned nullptr."; + goto cleanup; + } + if (i2d_PKCS8PrivateKey_bio(bio, evp, EVP_aes_256_cbc(), + const_cast(passphrase.data()), + passphrase.size(), nullptr, nullptr) == 0) { + LOG(ERROR) << "i2d_PKCS8PrivateKey_bio failed."; + goto cleanup; + } + { + int serialized_size = BIO_pending(bio); + serialized_private_key->assign(serialized_size, 0); + if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) != + serialized_size) { + LOG(ERROR) << "BIO_read failed."; + goto cleanup; + } + } + success = true; + +cleanup: + if (bio != nullptr) { + BIO_free(bio); + } + EVP_PKEY_free(evp); + return success; +} + +namespace { +// Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below. +int get_password(char *buf, int size, int rwflag, void *u) { + CHECK(buf); + CHECK(u); + const std::string* pass(static_cast(u)); + if (!pass->empty() && size >= static_cast(pass->size())) { + memcpy(buf, pass->data(), pass->size()); + return pass->size(); + } + return 0; +} +} // namespace + +bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key, + const std::string& passphrase, + RSA** private_key) { + if (serialized_private_key.empty()) { + LOG(ERROR) << "Serialized RSAEncryptedPrivateKeyInfo is empty."; + return false; + } + if (passphrase.empty()) { + LOG(ERROR) << "Passphrase for RSA key decryption is empty."; + return false; + } + if (private_key == nullptr) { + LOG(ERROR) << "Pointer to hold new RSA private key is nullptr."; + return false; + } + BIO* bio = BIO_new_mem_buf(const_cast(serialized_private_key.data()), + serialized_private_key.size()); + if (bio == nullptr) { + LOG(ERROR) << "BIO_new_mem_buf returned nullptr"; + return false; + } + bool success = false; + EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, nullptr, get_password, + const_cast(&passphrase)); + if (evp == nullptr) { + LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned nullptr."; + goto cleanup; + } + *private_key = EVP_PKEY_get1_RSA(evp); + if (*private_key == nullptr) { + LOG(ERROR) << "EncryptedPrivateKeyInfo did not contain an RSA key."; + goto cleanup; + } + success = true; + +cleanup: + if (evp != nullptr) { + EVP_PKEY_free(evp); + } + BIO_free(bio); + return success; +} + +bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key, + const std::string& passphrase, + std::string* private_key_info) { + RSA* key = nullptr; + if (DeserializeRsaPrivateKey(rsa_private_key, &key)) { + bool success = SerializeEncryptedPrivateKeyInfo(key, + passphrase, + private_key_info); + RSA_free(key); + return success; + } + return false; +} + +bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + const std::string& passphrase, + std::string* rsa_private_key) { + RSA* key = nullptr; + if (DeserializeEncryptedPrivateKeyInfo(private_key_info, passphrase, &key)) { + bool success = SerializeRsaPrivateKey(key, rsa_private_key); + RSA_free(key); + return success; + } + return false; +} + +bool ConvertToEulerTotient(RSA* rsa) { + // RSA key generation requires computing e (public exponent) and d (private + // exponent), such that m^(e * d) = 1 (mod n) for all m coprime to n. + // BoringSSL previously computed this by taking the inverse of e modulo the + // Euler totient, (p - 1) * (q - 1). However, it now uses the Carmichael + // totient, lcm(p - 1, q - 1). These two methods produce equivalent RSA keys. + // + // This breaks some vendors' RSA code which use a custom RSA format (rather + // than the standard one in RFC 8017) which omits most of the required + // parameters. They then attempt to recover those parameters, but their + // recovery algorithm breaks when using the Carmichael totient. To work around + // this bug, re-compute the private exponent against the Euler totient. + const BIGNUM *e, *p, *q; + RSA_get0_key(rsa, nullptr /* n */, &e, nullptr /* d */); + RSA_get0_factors(rsa, &p, &q); + + bssl::UniquePtr ctx(BN_CTX_new()); + bssl::UniquePtr pm1(BN_new()); + bssl::UniquePtr qm1(BN_new()); + bssl::UniquePtr totient(BN_new()); + bssl::UniquePtr d(BN_new()); + if (!ctx || !pm1 || !qm1 || !totient || !d || + !BN_sub(pm1.get(), p, BN_value_one()) || + !BN_sub(qm1.get(), q, BN_value_one()) || + !BN_mul(totient.get(), pm1.get(), qm1.get(), ctx.get()) || + !BN_mod_inverse(d.get(), e, totient.get(), ctx.get())) { + return false; + } + + // Perform a sanity check that d is still valid after conversion. + // d > 2 ^ (nlen / 2) per Appendix B 3.1 in FIPS 186/4. + int prime_bits = (8 * RSA_size(rsa)) / 2; + if (!BigNumGreaterThanPow2(d.get(), prime_bits)) { + return false; + } + + if (!RSA_set0_key(rsa, nullptr /* n */, nullptr /* e */, d.get())) { + return false; + } + d.release(); // RSA_set0_key takes ownership on success. + + if (!RSA_check_key(rsa)) { + return false; + } + + return true; +} + +bool ConvertToEulerTotient(const std::string& private_key, + std::string* euler_private_key) { + CHECK(euler_private_key); + RSA* rsa_ptr; + if (!rsa_util::DeserializeRsaPrivateKey(private_key, &rsa_ptr)) { + return false; + } + bssl::UniquePtr rsa(rsa_ptr); + if (!rsa_util::ConvertToEulerTotient(rsa.get()) + || !rsa_util::SerializeRsaPrivateKey(rsa.get(), euler_private_key)) { + return false; + } + + return true; +} + +bool ConvertToCarmichaelTotient(RSA* rsa) { + bssl::UniquePtr ctx(BN_CTX_new()); + bssl::UniquePtr pm1(BN_new()); + bssl::UniquePtr qm1(BN_new()); + bssl::UniquePtr gcd(BN_new()); + bssl::UniquePtr totient(BN_new()); + bssl::UniquePtr d(BN_new()); + // This calculates d = e^-1 (mod lcm(p-1, q-1)). + // This is equivalent to what is used in RSA_generate_key in BoringSSL. + if (!BN_sub(pm1.get(), rsa->p, BN_value_one()) || + !BN_sub(qm1.get(), rsa->q, BN_value_one()) || + !BN_mul(totient.get(), pm1.get(), qm1.get(), ctx.get()) || + !BN_gcd(gcd.get(), pm1.get(), qm1.get(), ctx.get()) || + !BN_div(totient.get(), nullptr, totient.get(), gcd.get(), ctx.get()) || + !BN_mod_inverse(d.get(), rsa->e, totient.get(), ctx.get())) { + return false; + } + + // Perform a sanity check that d is still valid after conversion. + // d > 2 ^ (nlen / 2) per Appendix B 3.1 in FIPS 186/4. + int prime_bits = (8 * RSA_size(rsa)) / 2; + if (!BigNumGreaterThanPow2(d.get(), prime_bits)) { + return false; + } + + // TODO(user): Replace this with |RSA_set0_key| once BoringSSL has + // finished transitioning to the OpenSSL 1.1.0 API. + BN_free(rsa->d); + rsa->d = d.release(); + + if (!RSA_check_key(rsa)) { + return false; + } + + return true; +} + +bool ConvertToCarmichaelTotient(const std::string& private_key, + std::string* carmichael_private_key) { + CHECK(carmichael_private_key); + RSA* rsa_ptr; + if (!rsa_util::DeserializeRsaPrivateKey(private_key, &rsa_ptr)) { + return false; + } + bssl::UniquePtr rsa(rsa_ptr); + if (!rsa_util::ConvertToCarmichaelTotient(rsa.get()) + || !rsa_util::SerializeRsaPrivateKey(rsa.get(), carmichael_private_key)) { + return false; + } + + return true; +} + +} // namespace rsa_util +} // namespace widevine diff --git a/common/rsa_util.h b/common/rsa_util.h new file mode 100644 index 0000000..8e30a78 --- /dev/null +++ b/common/rsa_util.h @@ -0,0 +1,202 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// RSA utility functions for serializing and deserializing RSA keys, +// encryption, and signing. + +#ifndef COMMON_RSA_UTIL_H_ +#define COMMON_RSA_UTIL_H_ + +#include + +#include "openssl/rsa.h" + +namespace widevine { +namespace rsa_util { + +// Serialize RSA private key into DER encoded PKCS#1 RSAPrivateKey. +// - private_key is the RSA key to be serialized, which must not be NULL. +// - serialized_private_key is a pointer to the std::string to hold the serialized +// PKCS#1 RSAPrivateKey object. Caller retains ownership of the string. This +// parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializeRsaPrivateKey(const RSA* private_key, + std::string* serialized_private_key); + +// Deserialize RSA private key from DER encoded PKCS#1 RSAPrivateKey. +// - serialized_private_key is the DER-encoded PKCS#1 RSAPrivateKey to be +// deserialized. +// - private_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializeRsaPrivateKey(const std::string& serialized_private_key, + RSA** private_key); + +// Serialize RSA key into DER encoded PKCS#1 RSAPublicKey. +// - public_key is the RSA key to be serialized, which must not be NULL. +// - serialized_public_key is a pointer to the std::string to hold the serialized +// PKCS#1 RSAPublicKey object. Caller retains ownership of the string. This +// parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializeRsaPublicKey(const RSA* public_key, + std::string* serialized_public_key); + +// Deserialize RSA public key from DER encoded PKCS#1 RSAPublicKey. +// - serialized_public_key is the DER-encoded PKCS#1 RSAPublicKey to be +// deserialized. +// - public_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializeRsaPublicKey(const std::string& serialized_public_key, + RSA** public_key); + +// Serialize RSA private key into DER encoded PKCS#8 PrivateKeyInfo. +// - private_key is the RSA key to be serialized, which must not be NULL. +// - serialized_private_key is a pointer to the std::string to hold the serialized +// PKCS#8 PrivateKeyInfo object. Caller retains ownership of the string. This +// parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializePrivateKeyInfo(const RSA* private_key, + std::string* serialized_private_key); + +// Deserialize RSA private key from DER encoded PKCS#8 PrivateKeyInfo. +// - serialized_private_key is the DER-encoded PKCS#8 PrivateKeyInfo to be +// deserialized. +// - private_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializePrivateKeyInfo(const std::string& serialized_private_key, + RSA** private_key); + +// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8 +// PrivateKeyInfo. +// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted. +// - private_key_info is a pointer to std::string to hold the PKCS#8 PrivateKeyInfo. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key, + std::string* private_key_info); + +// Convert DER-encoded PKCS#8 PrivateKeyInfo to DER-encoded PKCS#1 +// RSAPrivateKey. +// - private_key_info is the PKCS#8 PrivateKeyInfo to be converted. +// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + std::string* rsa_private_key); + + +// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo. +// - private_key is the RSA key to be serialized, which must not be NULL. +// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption. +// - serialized_private_key is a pointer to the std::string to hold the serialized +// PKCS#8 EncryptedPrivateKeyInfo object. Caller retains ownership of the +// string. This parameter must not be NULL. +// Returns true if successful, false otherwise. +bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key, + const std::string& passphrase, + std::string* serialized_private_key); + +// Deserialize RSA private key from DER encoded PKCS#8 EncryptedPrivateKeyInfo. +// - serialized_private_key is the DER-encoded PKCS#8 EncryptedPrivateKeyInfo to +// be deserialized. +// - passphrase is the password to use for key decryption. +// - private_key is a pointer to an RSA structure pointer to point to a newly +// allocated RSA structure. Caller assumes ownership of the new RSA pointer, +// which is not allocated if the method fails. This parameter must not be +// NULL. +// Returns true if successful, false otherwise. +bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key, + const std::string& passphrase, + RSA** private_key); + +// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8 +// EncryptedPrivateKeyInfo. +// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted. +// - passphrase is the password to use for PKCS#5 v2.1 AES-256-CBC encryption. +// - private_key_info is a pointer to std::string to hold the PKCS#8 +// EncryptedPrivateKeyInfo. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key, + const std::string& passphrase, + std::string* private_key_info); + +// Convert DER-encoded PKCS#8 EncryptedPrivateKeyInfo to DER-encoded PKCS#1 +// RSAPrivateKey. +// - private_key_info is the PKCS#8 EncryptedPrivateKeyInfo to be converted. +// - passphrase is the password to use for key decryption. +// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey. +// The caller retains ownership of this parameter, which must not be NULL. +// Returns true if successful, false otherwise. +bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info, + const std::string& passphrase, + std::string* rsa_private_key); + +// This method changes |rsa| to use the more common, but not FIPS 186-4 +// compliant, computation for the RSA private exponent (d). This used to be the +// form produced by the BoringSSL method RSA_generate_key. This changed in this +// CL: https://boringssl-review.googlesource.com/c/boringssl/+/15944 +// +// This method is used to produce a "backward-compatible" version. Some vendor +// RSA implementations do not handle the new computation properly. +// +// - rsa is the openssl RSA structure. Must not be null. Caller retains +// ownership. +// +// Returns true if the key was successfully updated, false otherwise. +bool ConvertToEulerTotient(RSA* rsa); + +// This is wrapper to the other SwitchToEulerTotient that supports deserializing +// and serializing from and to a DER encoded PKCS#1 RSAPrivateKey. +// +// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be +// deserialized and converted. +// - euler_private_key is a pointer to the std::string to hold the converted and +// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the +// string. This parameter must not be NULL. +// Returns true if the key was successfully updated, false otherwise. +bool ConvertToEulerTotient(const std::string& private_key, + std::string* euler_private_key); + +// This method changes |rsa| to use the FIPS 186-4 compliant computation for d. +// This uses the Carmichael totient. This is equivalent to the way the key +// is generated in BoringSSL as of this change: +// https://boringssl-review.googlesource.com/c/boringssl/+/15944 +// +// This method is mostly used for testing. It allows tests to convert back and +// forth between forms and verify the output. +// +// Returns true if the key can be successfully updated, false otherwise. +bool ConvertToCarmichaelTotient(RSA* rsa); + +// This is wrapper to the other SwitchToCarmichaelTotient that supports +// deserializing and serializing from and to a DER encoded PKCS#1 RSAPrivateKey. +// +// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be +// deserialized and converted. +// - carmichael_private_key is a pointer to the std::string to hold the converted and +// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the +// string. This parameter must not be NULL. +// Returns true if the key was successfully updated, false otherwise. +bool ConvertToCarmichaelTotient(const std::string& private_key, + std::string* carmichael_private_key); + +} // namespace rsa_util +} // namespace widevine + +#endif // COMMON_RSA_UTIL_H_ diff --git a/common/rsa_util_test.cc b/common/rsa_util_test.cc new file mode 100644 index 0000000..5a0a087 --- /dev/null +++ b/common/rsa_util_test.cc @@ -0,0 +1,354 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Description: +// Unit test for rsa_util RSA utilties library. + +#include "common/rsa_util.h" + +#include +#include + +#include +#include "glog/logging.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "openssl/bn.h" +#include "common/rsa_test_keys.h" + +using ::testing::NotNull; + +namespace { +const uint32_t kRsaPublicExponent = 65537; +const int kTestRsaBits = 2048; +} // anonymous namespace + +namespace widevine { +namespace rsa_util { + +class RsaUtilTest : public ::testing::Test { + protected: + RsaTestKeys test_keys_; +}; + +TEST_F(RsaUtilTest, SerializeDeserializePrivateKey) { + RSA* private_key; + std::string serialized_private_key; + // Key 1 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_1_3072_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key)); + EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_1_3072_bits()); + EXPECT_EQ(RSA_check_key(private_key), 1); + RSA_free(private_key); + // Key 2 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key)); + EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_2_2048_bits()); + EXPECT_EQ(RSA_check_key(private_key), 1); + RSA_free(private_key); + // Key 3 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_3_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key)); + EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_3_2048_bits()); + EXPECT_EQ(RSA_check_key(private_key), 1); + RSA_free(private_key); + // Invalid key + EXPECT_FALSE(DeserializeRsaPrivateKey("this is a bad key", &private_key)); +} + +TEST_F(RsaUtilTest, SerializeDeserializePublicKey) { + RSA* public_key; + std::string serialized_public_key; + // Key 1 + EXPECT_TRUE(DeserializeRsaPublicKey( + test_keys_.public_test_key_1_3072_bits(), &public_key)); + ASSERT_TRUE(public_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits()); + RSA_free(public_key); + // Key 2 + EXPECT_TRUE(DeserializeRsaPublicKey( + test_keys_.public_test_key_2_2048_bits(), &public_key)); + ASSERT_TRUE(public_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits()); + RSA_free(public_key); + // Key 3 + EXPECT_TRUE(DeserializeRsaPublicKey( + test_keys_.public_test_key_3_2048_bits(), &public_key)); + ASSERT_TRUE(public_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits()); + RSA_free(public_key); + // Invalid key + EXPECT_FALSE(DeserializeRsaPublicKey("this is a bad key", &public_key)); +} + +TEST_F(RsaUtilTest, PublicKeyExtraction) { + RSA* private_key; + std::string serialized_public_key; + // Key 1 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_1_3072_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits()); + RSA_free(private_key); + // Key 2 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits()); + RSA_free(private_key); + // Key 3 + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_3_2048_bits(), &private_key)); + ASSERT_TRUE(private_key != NULL); + EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key)); + EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits()); + RSA_free(private_key); +} + +TEST_F(RsaUtilTest, Pkcs8PrivateKeyInfo) { + // The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8 + // serialization/deserialization functionality , so we test those. + std::string serialized_pkcs8; + std::string serialized_pkcs1; + // Key 1 + EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo( + test_keys_.private_test_key_1_3072_bits(), &serialized_pkcs8)); + EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1); + // Key 2 + EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), &serialized_pkcs8)); + EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1); + // Key 3 + EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo( + test_keys_.private_test_key_3_2048_bits(), &serialized_pkcs8)); + EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1); +} + +TEST_F(RsaUtilTest, Pkcs8EncryptedPrivateKeyInfo) { + // The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8 + // serialization/deserialization functionality , so we test those. + std::string serialized_pkcs8; + std::string serialized_pkcs1; + std::string passphrase("passphrase"); + // Key 1 + EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_1_3072_bits(), passphrase, + &serialized_pkcs8)); + EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + passphrase, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1); + // Key 2 + EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), passphrase, + &serialized_pkcs8)); + EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + passphrase, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1); + // Key 3 + EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo( + test_keys_.private_test_key_3_2048_bits(), passphrase, + &serialized_pkcs8)); + EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, + passphrase, + &serialized_pkcs1)); + EXPECT_NE(serialized_pkcs1, serialized_pkcs8); + EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1); +} + +TEST_F(RsaUtilTest, FailOnInvalidParams) { + RSA* test_input_key = NULL; + RSA* test_output_key = NULL; + std::string test_string; + std::string pass("password"); + ASSERT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &test_input_key)); + ASSERT_TRUE(test_input_key != NULL); + EXPECT_FALSE(SerializeRsaPrivateKey(NULL, &test_string)); + EXPECT_FALSE(SerializeRsaPrivateKey(test_input_key, NULL)); + EXPECT_FALSE(SerializeRsaPublicKey(NULL, &test_string)); + EXPECT_FALSE(SerializeRsaPublicKey(test_input_key, NULL)); + EXPECT_FALSE(SerializePrivateKeyInfo(NULL, &test_string)); + EXPECT_FALSE(SerializePrivateKeyInfo(test_input_key, NULL)); + EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(NULL, pass, &test_string)); + EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass, NULL)); + EXPECT_FALSE(DeserializeRsaPrivateKey("", &test_output_key)); + EXPECT_FALSE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), NULL)); + EXPECT_FALSE(DeserializeRsaPublicKey("", &test_output_key)); + EXPECT_FALSE(DeserializeRsaPublicKey( + test_keys_.public_test_key_2_2048_bits(), NULL)); + EXPECT_FALSE(DeserializePrivateKeyInfo("", &test_output_key)); + EXPECT_FALSE(DeserializePrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), NULL)); + EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo("", pass, &test_output_key)); + EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo( + test_keys_.private_test_key_2_2048_bits(), pass, NULL)); + RSA_free(test_input_key); +} + +TEST_F(RsaUtilTest, Pkcs8FailOnInvalidPassword) { + RSA* test_input_key = NULL; + RSA* test_output_key = NULL; + std::string serialized_pkcs8; + std::string pass("password"); + ASSERT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &test_input_key)); + EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(test_input_key, "", + &serialized_pkcs8)); + ASSERT_TRUE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass, + &serialized_pkcs8)); + EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass + "a", + &test_output_key)); + EXPECT_TRUE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass, + &test_output_key)); + RSA_free(test_input_key); + RSA_free(test_output_key); +} + +TEST_F(RsaUtilTest, ConvertToCarmichaelTotient_ExistingKey_Success) { + bssl::UniquePtr original_private_key; + RSA* original_key_ptr = nullptr; + + EXPECT_TRUE(DeserializeRsaPrivateKey( + test_keys_.private_test_key_2_2048_bits(), &original_key_ptr)); + original_private_key.reset(original_key_ptr); + ASSERT_THAT(original_private_key, NotNull()); + + bssl::UniquePtr private_key( + RSAPrivateKey_dup(original_private_key.get())); + ASSERT_THAT(private_key, NotNull()); + EXPECT_TRUE(ConvertToCarmichaelTotient(private_key.get())); + + // Confirm that the key is valid and has changed from the original. + EXPECT_EQ(1, RSA_check_key(private_key.get())); + std::string serialized_carmichael_private_key; + EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(), + &serialized_carmichael_private_key)); + EXPECT_NE(serialized_carmichael_private_key, + test_keys_.private_test_key_2_2048_bits()); + + // Convert back and make sure the serialized key matches the original. + EXPECT_TRUE(ConvertToEulerTotient(private_key.get())); + EXPECT_EQ(1, RSA_check_key(private_key.get())); + std::string serialized_euler_private_key; + EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(), + &serialized_euler_private_key)); + EXPECT_EQ(serialized_euler_private_key, + test_keys_.private_test_key_2_2048_bits()); +} + +TEST_F(RsaUtilTest, ConvertToEulerTotient_NewKey_Success) { + bssl::UniquePtr rsa; + bssl::UniquePtr private_key; + bssl::UniquePtr exponent(BN_new()); + ASSERT_TRUE(BN_set_word(exponent.get(), kRsaPublicExponent)); + + // It is possible that sometimes, the d value generated using carmichael + // and euler is the same. For this test, find a key where they are not the + // same (max 100 tries). + bool found_distinct_keys = false; + for (int i = 0; i < 100; i++) { + rsa.reset(RSA_new()); + ASSERT_TRUE(RSA_generate_key_ex(rsa.get(), kTestRsaBits, + exponent.get(), nullptr)); + + private_key.reset(RSAPrivateKey_dup(rsa.get())); + EXPECT_TRUE(ConvertToEulerTotient(private_key.get())); + EXPECT_EQ(1, RSA_check_key(private_key.get())); + + // If the values are different, break. + if (BN_cmp(private_key->d, rsa->d) != 0) { + found_distinct_keys = true; + break; + } + + LOG(INFO) << "Euler and Carmichael d values are the same. Count: " << i; + } + + ASSERT_TRUE(found_distinct_keys) + << "Reached maximum attempts, but did not generate distinct keys"; + EXPECT_EQ(1, RSA_check_key(private_key.get())); + + // Sanity check that the serialized keys are distinct. + std::string serialized_carmichael_private_key; + std::string serialized_private_key; + EXPECT_TRUE(SerializeRsaPrivateKey(rsa.get(), + &serialized_carmichael_private_key)); + + EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(), + &serialized_private_key)); + EXPECT_NE(serialized_carmichael_private_key, serialized_private_key); + + // Convert back to Carmichael, validate, and confirm that the keys are the + // same. + EXPECT_TRUE(ConvertToCarmichaelTotient(private_key.get())); + EXPECT_EQ(1, RSA_check_key(private_key.get())); + EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(), + &serialized_private_key)); + EXPECT_EQ(serialized_carmichael_private_key, serialized_private_key); +} + +TEST_F(RsaUtilTest, ConvertToSerializedCarmichaelTotient_Success) { + std::string private_key; + EXPECT_TRUE(ConvertToCarmichaelTotient( + test_keys_.private_test_key_2_2048_bits(), &private_key)); + EXPECT_EQ(test_keys_.private_test_key_2_carmichael_totient_2048_bits(), + private_key); +} + +TEST_F(RsaUtilTest, ConvertToSerializedEulerTotient_Success) { + std::string private_key; + EXPECT_TRUE(ConvertToEulerTotient( + test_keys_.private_test_key_2_carmichael_totient_2048_bits(), + &private_key)); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), private_key); +} + +TEST_F(RsaUtilTest, ConvertToEulerTotient_Idempotent_Success) { + std::string private_key; + EXPECT_TRUE(ConvertToEulerTotient(test_keys_.private_test_key_2_2048_bits(), + &private_key)); + EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), private_key); +} + +TEST_F(RsaUtilTest, ConvertToCarmichaelTotient_Idempotent_Success) { + std::string private_key; + EXPECT_TRUE(ConvertToCarmichaelTotient( + test_keys_.private_test_key_2_carmichael_totient_2048_bits(), + &private_key)); + EXPECT_EQ(test_keys_.private_test_key_2_carmichael_totient_2048_bits(), + private_key); +} + +} // namespace rsa_util +} // namespace widevine diff --git a/common/sha_util.cc b/common/sha_util.cc new file mode 100644 index 0000000..1ad68fc --- /dev/null +++ b/common/sha_util.cc @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/sha_util.h" + +#include +#include "openssl/sha.h" + +namespace widevine { + +std::string Sha1_Hash(const std::string& message) { + std::string digest; + digest.resize(SHA_DIGEST_LENGTH); + SHA1(reinterpret_cast(message.data()), message.size(), + reinterpret_cast(&digest[0])); + return digest; +} + +std::string Sha256_Hash(const std::string& message) { + std::string digest; + digest.resize(SHA256_DIGEST_LENGTH); + SHA256(reinterpret_cast(message.data()), message.size(), + reinterpret_cast(&digest[0])); + return digest; +} + +std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name) { + // X.667 14 Setting the fields of a name-based UUID. + // - Allocate a UUID to use as a "name space identifier" for all UUIDs + // generated from names in that name space. + // - Compute the 16-octet hash value of the name space identifier concatenated + // with the name. + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, name_space.data(), name_space.length()); + SHA1_Update(&ctx, name.data(), name.length()); + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA1_Final(hash, &ctx); + std::string hash_str = + std::string(reinterpret_cast(hash), SHA_DIGEST_LENGTH); + + // - For a SHA-1 hash function, the "hash value" referenced in 14.1 shall be + // octets zero to 15. + std::string uuid = hash_str.substr(0, 16); + + // - Overwrite the four most significant bits (bits 15 through 12) of the + // "VersionAndTimeHigh" field with the four-bit version number from Table 3 + // of 12.2 for the hash function that was used. [Name-based SHA-1 is 5] + (uuid[6] &= 0xF) |= 0x50; + + // - Overwrite the two most significant bits (bits 7 and 6) of the + // "VariantAndClockSeqHigh" field with 1 and 0, respectively. + (uuid[8] &= 0x3F) |= 0x80; + + return uuid; +} + +} // namespace widevine diff --git a/common/sha_util.h b/common/sha_util.h new file mode 100644 index 0000000..bc9390d --- /dev/null +++ b/common/sha_util.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_SHA_UTIL_H_ +#define COMMON_SHA_UTIL_H_ + +#include + +#include + +namespace widevine { + +// Calculates SHA1 hash. +std::string Sha1_Hash(const std::string& message); + +// Calculates SHA256 hash. +std::string Sha256_Hash(const std::string& message); + +// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based, +// 16-byte binary representation. Name_space is a GUID prefix; name is a unique +// name in the namespace. +std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name); + +} // namespace widevine + +#endif // COMMON_SHA_UTIL_H_ diff --git a/common/sha_util_test.cc b/common/sha_util_test.cc new file mode 100644 index 0000000..fcdcc3b --- /dev/null +++ b/common/sha_util_test.cc @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/sha_util.h" + +#include "testing/gunit.h" +#include "absl/strings/escaping.h" + +namespace widevine { + +TEST(ShaUtilTest, Sha1Empty) { + const uint8_t kExpected[] = { + 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, + 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha1_Hash("")); +} + +TEST(ShaUtilTest, Sha256Empty) { + const uint8_t kExpected[] = { + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, + 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, + 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha256_Hash("")); +} + +TEST(ShaUtilTest, Sha1) { + const uint8_t kExpected[] = { + 0x6b, 0x63, 0xa5, 0xe3, 0x6b, 0xfe, 0x54, 0x19, 0x4e, 0xfe, + 0x9f, 0xe0, 0x29, 0x7e, 0xd8, 0x11, 0x0f, 0x10, 0x0d, 0x1d, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), + Sha1_Hash("random data")); +} + +TEST(ShaUtilTest, Sha256) { + const uint8_t kExpected[] = { + 0x62, 0x4b, 0x56, 0xb3, 0x38, 0x1b, 0xbc, 0xe0, 0x4f, 0x58, 0x88, + 0x83, 0xb4, 0x2f, 0x4e, 0x27, 0xfe, 0xc0, 0x95, 0x56, 0xf8, 0x61, + 0xcf, 0x94, 0x49, 0xe6, 0x5f, 0x26, 0xea, 0x70, 0xad, 0x88, + }; + EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), + Sha256_Hash("random data")); +} + +TEST(ShaUtilTest, GenerateSha1Uuid) { + std::string name_space = + absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774"); + std::string name = "some seed value"; + EXPECT_EQ("730621a55c675c4086be38e72aefa03e", + absl::BytesToHexString(GenerateSha1Uuid(name_space, name))); +} + +} // namespace widevine diff --git a/common/signature_util.cc b/common/signature_util.cc new file mode 100644 index 0000000..b223233 --- /dev/null +++ b/common/signature_util.cc @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/signature_util.h" + +#include +#include + +#include "util/status.h" +#include "common/aes_cbc_util.h" +#include "common/rsa_key.h" +#include "common/sha_util.h" + +namespace widevine { +namespace signature_util { + +util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key, + const std::string& aes_iv, std::string* signature) { + if (signature == nullptr) { + return util::Status(util::error::INVALID_ARGUMENT, "signature is nullptr"); + } + std::string hash = Sha1_Hash(message); + if (hash.empty()) { + return util::Status(util::error::INTERNAL, "Computed hash is empty"); + } + std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash); + if (sig.empty()) { + return util::Status(util::error::INTERNAL, + "Computed AES signature is empty"); + } + *signature = sig; + return util::OkStatus(); +} + +util::Status GenerateRsaSignature(const std::string& message, + const std::string& private_key, + std::string* signature) { + if (signature == nullptr) { + return util::Status(util::error::INVALID_ARGUMENT, "signature is nullptr"); + } + std::unique_ptr rsa_private_key( + RsaPrivateKey::Create(private_key)); + if (rsa_private_key == nullptr) { + return util::Status(util::error::INTERNAL, + "Failed to construct a RsaPrivateKey"); + } + std::string sig; + if (!rsa_private_key->GenerateSignature(message, &sig)) { + return util::Status(util::error::INTERNAL, + "Failed to generate a RSA signature"); + } + if (sig.empty()) { + return util::Status(util::error::INTERNAL, + "Computed RSA signature is empty"); + } + *signature = sig; + return util::OkStatus(); +} + +} // namespace signature_util +} // namespace widevine diff --git a/common/signature_util.h b/common/signature_util.h new file mode 100644 index 0000000..f66693f --- /dev/null +++ b/common/signature_util.h @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_SIGNATURE_UTIL_H_ +#define COMMON_SIGNATURE_UTIL_H_ + +#include + +#include "util/status.h" + +namespace widevine { +namespace signature_util { + +// Generates an AES signature of |message| using |aes_key| and |aes_iv|. +// Signature is returned via |signature| if generation was successful. +// Returns a Status that carries the details of error if generation failed. +util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key, + const std::string& aes_iv, std::string* signature); + +// Generates a RSA signature of |message| using |private_key|. +// Signature is returned via |sigature| if generation was successful. +// Returns a Status that carries the details of error if generation failed. +util::Status GenerateRsaSignature(const std::string& message, + const std::string& private_key, std::string* signature); + +} // namespace signature_util +} // namespace widevine + +#endif // COMMON_SIGNATURE_UTIL_H_ diff --git a/common/signing_key_util.cc b/common/signing_key_util.cc new file mode 100644 index 0000000..959f437 --- /dev/null +++ b/common/signing_key_util.cc @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/signing_key_util.h" + +#include "glog/logging.h" +#include "common/crypto_util.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +using crypto_util::kSigningKeySizeBits; + +uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version) { + if (protocol_version <= VERSION_2_0) { + return kSigningKeySizeBits; + } else { + return kSigningKeySizeBits * 2; + } +} + +using crypto_util::kSigningKeySizeBytes; +std::string GetClientSigningKey(const std::string& derived_key, + ProtocolVersion protocol_version) { + if (protocol_version == VERSION_2_0) { + DCHECK(derived_key.size() >= kSigningKeySizeBytes); + return derived_key.substr(0, kSigningKeySizeBytes); + } else { + DCHECK(derived_key.size() >= kSigningKeySizeBytes * 2); + return derived_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes); + } +} + +std::string GetServerSigningKey(const std::string& derived_key) { + DCHECK(derived_key.size() >= kSigningKeySizeBytes); + return derived_key.substr(0, kSigningKeySizeBytes); +} + +} // namespace widevine diff --git a/common/signing_key_util.h b/common/signing_key_util.h new file mode 100644 index 0000000..eb71297 --- /dev/null +++ b/common/signing_key_util.h @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Contains functions that are used to create and parse derived keys created +// using the NIST 800-108 KDF recommendation, using AES-CMAC PRF. +// NIST 800-108: +// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf +// AES-CMAC: +// http://tools.ietf.org/html/rfc4493 +// +// Example usage: +// using video::widevine::common::crypto_util::DeriveKey; +// using widevine_server::sdk::VERSION_2_1; +// +// std::string derived_key = DeriveKey(key_str, +// label, +// context, +// SigningKeyMaterialSize(VERSION_2_1)); +// std::string server_derived_key = GetServerSigningKey(derived_key); +// std::string client_derived_key = GetClientSigninKey(derived_key); +#ifndef COMMON_SIGNING_KEY_UTIL_H_ +#define COMMON_SIGNING_KEY_UTIL_H_ + +#include + +#include "base/macros.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +// Returns the size of the signing key based on the License Protocol +// Version. Signing keys for version 2.0 have a length of 256. Signing +// keys for version 2.1 have a length of 512. +uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version); + +// Returns the client portion of the derived_key. The client portion +// depend on the size of the key. Keys that are 512 bits in length +// are assumed to be version 2.1 keys. The last 256 bits of those +// keys are returned. Keys that are 256 bits in length are returned +// in there entirety, version 2.0 keys. +std::string GetClientSigningKey(const std::string& derived_key, + ProtocolVersion protocol_version); + +// Returns the server portion of the derived_key. The server portion +// is the first 256 bits of the key. +std::string GetServerSigningKey(const std::string& derived_key); + +} // namespace widevine + +#endif // COMMON_SIGNING_KEY_UTIL_H_ diff --git a/common/signing_key_util_test.cc b/common/signing_key_util_test.cc new file mode 100644 index 0000000..25bea55 --- /dev/null +++ b/common/signing_key_util_test.cc @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/signing_key_util.h" + +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "common/crypto_util.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { +namespace signing_key_util { +namespace { +const char* kFrontKeyHex = + "0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b"; +const char* kBackKeyHex = + "0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d"; + +std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) { + if (protocol_version == widevine::VERSION_2_0) { + return absl::HexStringToBytes(kFrontKeyHex); + } else { + return absl::HexStringToBytes(kFrontKeyHex) + + absl::HexStringToBytes(kBackKeyHex); + } +} +} // namespace + +TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_0) { + ASSERT_EQ(crypto_util::kSigningKeySizeBits, + SigningKeyMaterialSize(VERSION_2_0)); +} + +TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_1) { + ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2, + SigningKeyMaterialSize(VERSION_2_1)); +} + +TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) { + ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey( + GenerateDerivedKey(VERSION_2_1)))); +} + +TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_1) { + ASSERT_EQ(kBackKeyHex, absl::BytesToHexString(GetClientSigningKey( + GenerateDerivedKey(VERSION_2_1), VERSION_2_1))); +} + +TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_0) { + ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey( + GenerateDerivedKey(VERSION_2_0)))); +} + +TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_0) { + ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetClientSigningKey( + GenerateDerivedKey(VERSION_2_0), VERSION_2_0))); +} + +} // namespace signing_key_util +} // namespace widevine diff --git a/common/string_util.cc b/common/string_util.cc new file mode 100644 index 0000000..e2d580b --- /dev/null +++ b/common/string_util.cc @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/string_util.h" + +#include +#include +#include +#include "util/status.h" + +namespace widevine { +namespace string_util { + +util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) { + if (output == nullptr) { + return util::Status(util::error::INTERNAL, "output is nullptr."); + } + + std::stringstream sstream(bitset); + for (size_t i = 0; i < bitset.size(); i += 8) { + std::bitset<8> bits; + sstream >> bits; + char c = static_cast(bits.to_ulong()); + *output += c; + } + + return util::OkStatus(); +} + +} // namespace string_util +} // namespace widevine diff --git a/common/string_util.h b/common/string_util.h new file mode 100644 index 0000000..57b3b6f --- /dev/null +++ b/common/string_util.h @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_STRING_UTIL_H_ +#define COMMON_STRING_UTIL_H_ + +#include +#include "util/status.h" + +namespace widevine { +namespace string_util { + +// Converts std::string representation of a bitset to its binary equivalent string. +// For example, converts "01110100011001010111001101110100" to "test". +util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output); + +} // namespace string_util +} // namespace widevine + +#endif // COMMON_STRING_UTIL_H_ diff --git a/common/string_util_test.cc b/common/string_util_test.cc new file mode 100644 index 0000000..a28e619 --- /dev/null +++ b/common/string_util_test.cc @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/string_util.h" + +#include "testing/gmock.h" +#include "testing/gunit.h" + +namespace widevine { +namespace string_util { + +TEST(StringUtilTest, BitsetStringToBinaryString) { + std::string input = "01110100011001010111001101110100"; + std::string output; + + EXPECT_OK(BitsetStringToBinaryString(input, &output)); + + EXPECT_EQ("test", output); +} + +} // namespace string_util +} // namespace widevine diff --git a/common/test_certificates.cc b/common/test_certificates.cc new file mode 100644 index 0000000..b9f53d8 --- /dev/null +++ b/common/test_certificates.cc @@ -0,0 +1,328 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// + +#include "common/test_certificates.h" + +namespace widevine { + +static const unsigned char kTestRootCertificate[] = { + 0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22, + 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5, + 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, + 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f, + 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, + 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b, + 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, + 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01, + 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, + 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14, + 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, + 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6, + 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, + 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31, + 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, + 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90, + 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, + 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f, + 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, + 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17, + 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, + 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf, + 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, + 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42, + 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, + 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4, + 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, + 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72, + 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, + 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04, + 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, + 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33, + 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, + 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a, + 0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35, + 0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe, + 0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49, + 0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6, + 0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58, + 0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce, + 0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27, + 0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5, + 0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c, + 0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e, + 0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12, + 0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54, + 0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8, + 0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1, + 0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1, + 0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8, + 0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4, + 0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9, + 0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd, + 0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9, + 0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f, + 0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92, + 0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15, + 0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0, + 0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9, + 0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1, + 0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2, + 0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c, + 0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36, + 0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35, + 0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7, + 0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44}; + +const unsigned char kTestIntermediateCertificate[] = { + 0x0a, 0xaf, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18, + 0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, + 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, + 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, + 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, + 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, + 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, + 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, + 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, + 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, + 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, + 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, + 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, + 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, + 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, + 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, + 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, + 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, + 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, + 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, + 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, + 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, + 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40, + 0xa8, 0xd0, 0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf, + 0xc9, 0x26, 0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4, + 0x33, 0x8e, 0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b, + 0xc3, 0x4f, 0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51, + 0xce, 0x45, 0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4, + 0x5a, 0x02, 0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9, + 0xee, 0xc9, 0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb, + 0x1f, 0x7a, 0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9, + 0x26, 0xbe, 0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25, + 0xdb, 0x85, 0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba, + 0xb9, 0x3d, 0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80, + 0x12, 0x72, 0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98, + 0xab, 0x73, 0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1, + 0x75, 0x79, 0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98, + 0xe8, 0x40, 0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f, + 0x6f, 0x23, 0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30, + 0xaa, 0xcf, 0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b, + 0x7f, 0x91, 0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a, + 0x98, 0xad, 0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e, + 0x6e, 0x9d, 0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04, + 0x4f, 0x32, 0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52, + 0xfc, 0xcc, 0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa, + 0xb3, 0x77, 0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d, + 0x3c, 0x74, 0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f, + 0x13, 0x97, 0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94, + 0x3b, 0x31, 0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b, + 0x65, 0xe9, 0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d, + 0x0a, 0xfd, 0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4, + 0x83, 0xcf, 0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16, + 0x7b, 0x66, 0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d, + 0x94, 0x5d, 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, + 0x55, 0xf8, 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a}; + +const unsigned char kTestUserDrmCertificate[] = { + 0x0a, 0xc1, 0x02, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42, + 0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18, + 0x91, 0xab, 0x4b, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51, + 0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, + 0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c, + 0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, + 0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7, + 0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, + 0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, + 0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, + 0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93, + 0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, + 0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc, + 0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, + 0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, + 0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, + 0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec, + 0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, + 0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e, + 0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, + 0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8, + 0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, + 0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd, + 0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x12, 0x80, 0x02, 0x62, 0xd5, 0x8b, 0xb6, 0x52, 0x94, 0xcb, 0x25, 0xba, + 0x68, 0x44, 0xdc, 0x6f, 0x03, 0xf6, 0x24, 0xc5, 0xba, 0x46, 0xd1, 0xa1, + 0x83, 0xe7, 0xaf, 0x94, 0x96, 0x8b, 0x57, 0x89, 0xd6, 0xa0, 0x99, 0x6f, + 0xed, 0xac, 0xfe, 0x6c, 0x9d, 0x80, 0x1c, 0xae, 0x34, 0xda, 0x49, 0x4f, + 0x10, 0x22, 0x3c, 0xdd, 0x77, 0xa0, 0x9a, 0x79, 0x73, 0x68, 0x66, 0xa4, + 0x6d, 0x1e, 0x82, 0xbf, 0xce, 0x06, 0x1a, 0x83, 0xcd, 0xa3, 0xed, 0x91, + 0xbe, 0xb1, 0xfe, 0xf3, 0xde, 0x63, 0x96, 0xd5, 0x24, 0x44, 0x46, 0x94, + 0x7f, 0xc2, 0x14, 0x19, 0x42, 0x08, 0x64, 0xef, 0x93, 0x81, 0x7a, 0x54, + 0x8b, 0x6e, 0xd9, 0xf5, 0x14, 0x88, 0x6c, 0x39, 0x6f, 0x0f, 0x70, 0x91, + 0x97, 0xd4, 0x24, 0x73, 0x9d, 0x12, 0x7a, 0xc8, 0x83, 0xd7, 0x2b, 0xc7, + 0xb7, 0xe1, 0x20, 0x6c, 0x28, 0x11, 0x6f, 0x56, 0x82, 0xf6, 0x1c, 0x4f, + 0x2d, 0x51, 0x0f, 0xd6, 0xd4, 0x14, 0xea, 0xac, 0x28, 0x66, 0xeb, 0x37, + 0xca, 0x00, 0x49, 0xff, 0xed, 0x8e, 0x8c, 0x3e, 0x4b, 0x9b, 0x12, 0x0e, + 0xbf, 0xcd, 0xb7, 0xe6, 0xed, 0xd6, 0x1f, 0x88, 0xe8, 0x99, 0x68, 0x1a, + 0xf8, 0xbb, 0xa2, 0x33, 0xfa, 0xb6, 0x21, 0xdf, 0xba, 0x24, 0x5c, 0x19, + 0xa2, 0xe7, 0x6f, 0x61, 0x90, 0x78, 0x21, 0xca, 0x2f, 0x84, 0xab, 0x9f, + 0xff, 0x37, 0x14, 0x33, 0x83, 0x43, 0x98, 0xeb, 0xa9, 0x88, 0xde, 0xad, + 0x3a, 0xd9, 0xe2, 0x5c, 0x26, 0xd3, 0x95, 0x72, 0xba, 0x8c, 0x77, 0xdf, + 0x90, 0x67, 0x4e, 0xbc, 0xda, 0x83, 0x09, 0x22, 0x70, 0x51, 0x84, 0x70, + 0x31, 0x25, 0x8b, 0xae, 0x5e, 0x19, 0xba, 0x97, 0xd7, 0x1f, 0x6a, 0xd7, + 0x95, 0xcf, 0xde, 0x8f, 0x93, 0x69, 0x88, 0x11, 0xbe, 0x8c, 0x6a, 0xfb, + 0x3c, 0x13, 0x87, 0x0e, 0x6c, 0xa5, 0xa0, 0x1a, 0xb5, 0x05, 0x0a, 0xaf, + 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18, 0xb2, 0x92, + 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, + 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, + 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, + 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, + 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, + 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, + 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, + 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, + 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, + 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, + 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, + 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, + 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, + 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, + 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, + 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, + 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, + 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, + 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, + 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, + 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, + 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x28, 0xd2, + 0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40, 0xa8, 0xd0, + 0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf, 0xc9, 0x26, + 0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4, 0x33, 0x8e, + 0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b, 0xc3, 0x4f, + 0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51, 0xce, 0x45, + 0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4, 0x5a, 0x02, + 0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9, 0xee, 0xc9, + 0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb, 0x1f, 0x7a, + 0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9, 0x26, 0xbe, + 0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25, 0xdb, 0x85, + 0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba, 0xb9, 0x3d, + 0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80, 0x12, 0x72, + 0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98, 0xab, 0x73, + 0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1, 0x75, 0x79, + 0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98, 0xe8, 0x40, + 0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f, 0x6f, 0x23, + 0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30, 0xaa, 0xcf, + 0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b, 0x7f, 0x91, + 0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a, 0x98, 0xad, + 0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e, 0x6e, 0x9d, + 0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04, 0x4f, 0x32, + 0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52, 0xfc, 0xcc, + 0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa, 0xb3, 0x77, + 0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d, 0x3c, 0x74, + 0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f, 0x13, 0x97, + 0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94, 0x3b, 0x31, + 0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b, 0x65, 0xe9, + 0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d, 0x0a, 0xfd, + 0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4, 0x83, 0xcf, + 0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16, 0x7b, 0x66, + 0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d, 0x94, 0x5d, + 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, 0x55, 0xf8, + 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a}; + +const unsigned char kTestDrmServiceCertificate[] = { + 0x0a, 0xbc, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32, + 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18, + 0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, + 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, + 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, + 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, + 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, + 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, + 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, + 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, + 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, + 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, + 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, + 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, + 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, + 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, + 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, + 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, + 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, + 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, + 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, + 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, + 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, + 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0x6e, 0xc3, + 0x5a, 0x17, 0xa8, 0xf9, 0xef, 0xee, 0x67, 0x4d, 0x0a, 0xef, 0x57, 0x5e, + 0xbc, 0x59, 0x3d, 0x22, 0x84, 0xa0, 0x0a, 0xf5, 0x84, 0x26, 0xb7, 0x8b, + 0xab, 0x91, 0x3e, 0x4b, 0xb9, 0x91, 0x3c, 0x50, 0xc9, 0x08, 0x2f, 0x97, + 0x0a, 0x91, 0xb5, 0x48, 0xe4, 0xba, 0xfd, 0x7b, 0xbd, 0xf0, 0xba, 0x08, + 0xb3, 0x29, 0xb4, 0x23, 0x74, 0xaf, 0x3f, 0xe9, 0x77, 0x78, 0x3f, 0xdc, + 0x3d, 0x8a, 0x37, 0xec, 0x1c, 0x3a, 0xff, 0x60, 0x8e, 0x10, 0x72, 0xaa, + 0x97, 0x98, 0x56, 0xa0, 0x35, 0xa9, 0xbf, 0x43, 0x21, 0x6a, 0x15, 0x88, + 0xba, 0xc0, 0x68, 0x01, 0x7b, 0xd7, 0x88, 0x2f, 0x1a, 0xc5, 0x1f, 0x54, + 0xf0, 0xea, 0x36, 0xb7, 0xed, 0x49, 0x78, 0x09, 0xb1, 0x07, 0x46, 0xfe, + 0xf4, 0xfa, 0x16, 0x0c, 0x46, 0x91, 0xe2, 0xa9, 0xe0, 0x8e, 0x97, 0xe5, + 0xea, 0x2f, 0xd9, 0x94, 0x1e, 0xe7, 0xba, 0x28, 0x98, 0x92, 0xae, 0xb8, + 0xb6, 0x6e, 0xf6, 0xd2, 0x50, 0xd3, 0x5b, 0x25, 0x12, 0x68, 0x5e, 0x07, + 0x82, 0x64, 0x27, 0xfe, 0x1a, 0xcd, 0x38, 0xa8, 0x00, 0x53, 0x8c, 0x69, + 0x51, 0x75, 0x71, 0xc2, 0x6a, 0x5f, 0x05, 0x13, 0x77, 0x2b, 0xc8, 0x6c, + 0xab, 0xd2, 0x64, 0x27, 0xbd, 0x21, 0xfc, 0x33, 0x0a, 0x3a, 0x53, 0xa6, + 0x28, 0x1c, 0x2a, 0xad, 0x23, 0x0a, 0x95, 0xe4, 0x38, 0x6b, 0x9b, 0x3e, + 0x77, 0x7d, 0x96, 0x20, 0x42, 0xf5, 0x18, 0xbe, 0xb0, 0x78, 0xe4, 0xf0, + 0x95, 0x6c, 0xd5, 0x30, 0xd6, 0xfc, 0x04, 0xe2, 0xf7, 0xff, 0x06, 0x6b, + 0xaf, 0xf1, 0x9c, 0x10, 0xa6, 0xdb, 0xed, 0x4a, 0x18, 0x68, 0x87, 0xda, + 0x43, 0x2c, 0x60, 0xc6, 0x0a, 0x72, 0x1e, 0x9f, 0x4b, 0x05, 0x80, 0x15, + 0x17, 0x84, 0xf1, 0xee, 0xcc, 0x80, 0x25, 0x33, 0x87, 0x74, 0x02, 0x8c, + 0xa1, 0xbb, 0xd9, 0x29, 0x33, 0x97, 0xbd, 0x5b, 0x1c, 0xed, 0xcc, 0x47, + 0xda, 0x73, 0xae, 0xb1, 0x75, 0xac, 0xf7, 0x39, 0xbe, 0x67, 0xc3, 0xaf, + 0x60, 0x07, 0xf5, 0xba, 0x81, 0xf4, 0x42, 0xad, 0x28, 0x8d, 0xe6, 0x63, + 0xea, 0x8a, 0x0e, 0x71, 0x53, 0x6e, 0x62, 0x8a, 0x23, 0x4f, 0xad, 0x2a, + 0x9a, 0xf6, 0xeb, 0xa8, 0x82, 0x83, 0xbb, 0x5f, 0xc9, 0x86, 0xd8, 0x76, + 0xb9, 0xf3, 0xe7, 0x32, 0xdd, 0xe0, 0x44, 0x6a, 0xab, 0x78, 0xa0, 0x8c, + 0xa4, 0x99, 0x6f, 0x71, 0x42, 0x8b, 0x31, 0x32, 0xbb, 0x80, 0x36, 0x61, + 0x1c, 0xe5, 0x6d, 0x87, 0xf2, 0x68, 0xca, 0xcd, 0xe0, 0x5f, 0xa2, 0x68, + 0x5b, 0xfc, 0x73, 0xc9, 0x26, 0x2b, 0x13, 0x05, 0x1c, 0xde, 0x19, 0xdf, + 0x34, 0xba, 0xf5, 0xec, 0xaf, 0x26, 0xfb, 0x64, 0xc4, 0x38, 0x7e, 0xdb, + 0x51, 0x28, 0x49, 0xa7, 0x12, 0x88, 0xa5, 0x6d, 0xa2, 0xfa}; + +TestCertificates::TestCertificates() + : test_root_certificate_( + kTestRootCertificate, + kTestRootCertificate + sizeof(kTestRootCertificate)), + test_intermediate_certificate_( + kTestIntermediateCertificate, + kTestIntermediateCertificate + sizeof(kTestIntermediateCertificate)), + test_user_device_certificate_( + kTestUserDrmCertificate, + kTestUserDrmCertificate + sizeof(kTestUserDrmCertificate)), + test_service_certificate_( + kTestDrmServiceCertificate, + kTestDrmServiceCertificate + sizeof(kTestDrmServiceCertificate)) {} + +} // namespace widevine diff --git a/common/test_certificates.h b/common/test_certificates.h new file mode 100644 index 0000000..4dae390 --- /dev/null +++ b/common/test_certificates.h @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// +// Class contains certificates that can be used for testing. Provides methods +// to retrieve a test root certificate, a test intermediate certificate and a +// test user device certificate. +#ifndef COMMON_TEST_CERTIFICATES_H_ +#define COMMON_TEST_CERTIFICATES_H_ + +#include +#include "base/macros.h" + +namespace widevine { + +class TestCertificates { + public: + TestCertificates(); + virtual ~TestCertificates() {} + + // returns a test root certificate + const std::string& test_root_certificate() const { + return test_root_certificate_; + } + + // returns a test intermediate certificate + const std::string& test_intermediate_certificate() const { + return test_intermediate_certificate_; + } + + // returns an user device certificate + const std::string& test_user_device_certificate() const { + return test_user_device_certificate_; + } + + // returns a service certificate + const std::string& test_service_certificate() const { + return test_service_certificate_; + } + + private: + const std::string test_root_certificate_; + const std::string test_intermediate_certificate_; + const std::string test_user_device_certificate_; + const std::string test_service_certificate_; + + DISALLOW_COPY_AND_ASSIGN(TestCertificates); +}; + +} // namespace widevine +#endif // COMMON_TEST_CERTIFICATES_H_ diff --git a/common/test_utils.cc b/common/test_utils.cc new file mode 100644 index 0000000..8628d64 --- /dev/null +++ b/common/test_utils.cc @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/test_utils.h" + +#include +#include + +#include "glog/logging.h" +#include "openssl/pem.h" +#include "openssl/rsa.h" +#include "openssl/sha.h" + +namespace widevine { + +util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key, + const std::string& message, + std::string* signature) { + CHECK(signature); + if (pem_private_key.empty()) { + return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM private key"); + } + if (message.empty()) { + return util::Status(util::error::INVALID_ARGUMENT, "Empty message"); + } + BIO* bio(NULL); + bio = BIO_new_mem_buf(const_cast(pem_private_key.data()), + pem_private_key.size()); + if (bio == NULL) { + return util::Status(util::error::INTERNAL, "BIO allocation failed"); + } + util::Status status; + RSA* key(NULL); + std::unique_ptr sig_buffer; + unsigned int sig_size; + unsigned char digest[SHA256_DIGEST_LENGTH]; + key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL); + if (key == NULL) { + status = util::Status(util::Status::canonical_space(), + util::error::INVALID_ARGUMENT, + "PEM RSA private key load failed"); + goto cleanup; + } + SHA256(reinterpret_cast(message.data()), message.size(), + digest); + sig_size = RSA_size(key); + sig_buffer.reset(new char[sig_size]); + if (RSA_sign(NID_sha256, digest, sizeof(digest), + reinterpret_cast(sig_buffer.get()), &sig_size, + key) != 1) { + status = + util::Status(util::Status::canonical_space(), util::error::INTERNAL, + "RSA private encrypt failed"); + goto cleanup; + } + signature->assign(sig_buffer.get(), sig_size); + +cleanup: + if (key != NULL) { + RSA_free(key); + } + if (bio != NULL) { + BIO_free(bio); + } + return status; +} + +} // namespace widevine diff --git a/common/test_utils.h b/common/test_utils.h new file mode 100644 index 0000000..0527cac --- /dev/null +++ b/common/test_utils.h @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2013 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Auxiliary functions for license server SDK testing. + +#ifndef COMMON_TEST_UTILS_H__ +#define COMMON_TEST_UTILS_H__ + +#include + +#include "util/status.h" + +namespace widevine { + +// Generate RSA signature using the specified RSA private key, SHA256 digest, +// and PKCS#1 1.5 padding. |pem_private_key| is a PEM-encoded private RSA key, +// |message| is the message to be signed, and |signature| is a pointer to a +// std::string where the signature will be stored. The caller returns ownership of +// all paramters. +util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key, + const std::string& message, + std::string* signature); + +} // namespace widevine + +#endif // COMMON_TEST_UTILS_H__ diff --git a/common/verified_media_pipeline.cc b/common/verified_media_pipeline.cc new file mode 100644 index 0000000..641ddaf --- /dev/null +++ b/common/verified_media_pipeline.cc @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Helper methods for verifying VMP (Verified Media Pipeline) data. + +#include "common/verified_media_pipeline.h" + +#include "common/vmp_checker.h" + +namespace widevine { +util::Status VerifyVmpData( + const std::string& vmp_data, + PlatformVerificationStatus* platform_verification_status) { + *platform_verification_status = PLATFORM_UNVERIFIED; + VmpChecker::Result vmp_result; + util::Status status = + VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result); + if (status.ok()) { + switch (vmp_result) { + case VmpChecker::kUnverified: + *platform_verification_status = PLATFORM_UNVERIFIED; + break; + case VmpChecker::kVerified: + *platform_verification_status = PLATFORM_SOFTWARE_VERIFIED; + break; + case VmpChecker::kSecureStorageVerified: + *platform_verification_status = + PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED; + break; + case VmpChecker::kTampered: + *platform_verification_status = PLATFORM_TAMPERED; + break; + } + } else { + *platform_verification_status = PLATFORM_TAMPERED; + } + return status; +} +} // namespace widevine diff --git a/common/verified_media_pipeline.h b/common/verified_media_pipeline.h new file mode 100644 index 0000000..64a9582 --- /dev/null +++ b/common/verified_media_pipeline.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Helper methods for verifying VMP (Verified Media Pipeline) data. + +#ifndef COMMON_VERIFIED_MEDIA_PIPELINE_H_ +#define COMMON_VERIFIED_MEDIA_PIPELINE_H_ + +#include +#include "util/status.h" +#include "protos/public/license_protocol.pb.h" + +namespace widevine { + +// Retrieve the PlatformVerificationStatus for |vmp_data|. The +// PlatformVerificationStatus is defined at +util::Status VerifyVmpData( + const std::string& vmp_data, + PlatformVerificationStatus* platform_verification_status); + +} // namespace widevine +#endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_ diff --git a/common/vmp_checker.cc b/common/vmp_checker.cc new file mode 100644 index 0000000..c40b45c --- /dev/null +++ b/common/vmp_checker.cc @@ -0,0 +1,358 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Singleton object which validates VMP (Verified Media Pipeline) data for +// purposes of platform software verification. Thread safe. + +#include "common/vmp_checker.h" + +#include +#include + +#include +#include "glog/logging.h" +#include "common/error_space.h" +#include "common/rsa_key.h" +#include "common/x509_cert.h" +#include "protos/public/errors.pb.h" +#include "protos/public/verified_media_pipeline.pb.h" + +namespace { + +const uint32_t kBlessedBinaryFlag = 0x00000001; + +const uint8_t kDevVmpCodeSigningDrmRootCertificate[] = { + 0x30, 0x82, 0x04, 0xb8, 0x30, 0x82, 0x03, 0x20, 0x02, 0x09, 0x00, 0xc5, + 0xf8, 0x2f, 0x03, 0x8f, 0xac, 0xf1, 0x58, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, + 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, + 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, + 0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, + 0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73, 0x6b, + 0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x30, 0x32, 0x38, 0x30, + 0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x31, 0x36, + 0x31, 0x30, 0x30, 0x34, 0x30, 0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x30, + 0x81, 0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, + 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, + 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, + 0x6e, 0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, + 0x69, 0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73, + 0x6b, 0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, + 0x6f, 0x6d, 0x30, 0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x8f, 0x00, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xd2, + 0x6f, 0x60, 0x7f, 0xff, 0x7c, 0xbd, 0xa4, 0xe5, 0x8c, 0xa6, 0xcf, 0xde, + 0x22, 0x6d, 0x3a, 0x5e, 0x83, 0xa9, 0x0e, 0x9b, 0xd4, 0x93, 0xb8, 0xb0, + 0xe0, 0x5d, 0x03, 0x3d, 0xc1, 0x00, 0xb8, 0x1a, 0xcc, 0x84, 0x31, 0xfb, + 0x9e, 0x97, 0x79, 0x17, 0x04, 0x48, 0xe7, 0x13, 0x98, 0x0a, 0x47, 0x41, + 0xac, 0x6c, 0x52, 0xb0, 0xca, 0x9e, 0xfb, 0xfd, 0x78, 0x65, 0xd0, 0xd6, + 0x12, 0x07, 0x7e, 0x24, 0x65, 0x46, 0x6c, 0xb9, 0x23, 0xbb, 0xdc, 0x02, + 0x03, 0xc7, 0xb0, 0x02, 0xc1, 0xd3, 0x10, 0x59, 0xe7, 0x0b, 0x45, 0x13, + 0x73, 0x5f, 0xae, 0x58, 0xcd, 0xbf, 0x42, 0x8a, 0xac, 0xf5, 0x6a, 0x1e, + 0x75, 0x26, 0xb1, 0x69, 0x07, 0xad, 0xf5, 0xfd, 0x4c, 0xaa, 0x66, 0x55, + 0x74, 0x56, 0x9a, 0x9e, 0x40, 0x78, 0x77, 0x9a, 0x39, 0x7a, 0x37, 0xe4, + 0x25, 0x6b, 0x07, 0x09, 0xbe, 0xe2, 0x0d, 0x23, 0x83, 0xfc, 0x94, 0x9f, + 0x26, 0x98, 0x0e, 0x49, 0x81, 0x7b, 0xf4, 0xe6, 0xd4, 0xda, 0x7a, 0xc9, + 0xa4, 0x14, 0x5a, 0xa9, 0xaf, 0x0c, 0xd9, 0xf1, 0xbc, 0xd8, 0x6c, 0xd2, + 0xd4, 0x1b, 0x82, 0x10, 0x3d, 0x87, 0xf1, 0x81, 0xe6, 0x1a, 0xb7, 0xfa, + 0xfa, 0x1f, 0x9c, 0xde, 0xa1, 0x3f, 0x11, 0xb1, 0xd8, 0x26, 0xd1, 0x86, + 0x21, 0xdc, 0x03, 0xcb, 0xd6, 0x40, 0xfb, 0x5f, 0xb0, 0x84, 0x7f, 0x69, + 0x9e, 0xa3, 0xbc, 0xfb, 0x03, 0xb6, 0xc1, 0xb9, 0x23, 0xb1, 0x20, 0x6f, + 0x71, 0xf5, 0x7a, 0x3b, 0x84, 0x30, 0xa8, 0x59, 0xc0, 0x8f, 0x75, 0xfd, + 0xcb, 0xe1, 0xc3, 0x5f, 0xe7, 0x8a, 0xb0, 0xe9, 0xf8, 0xef, 0x04, 0x4b, + 0x4a, 0xf6, 0xc3, 0x7d, 0x08, 0xfe, 0x08, 0x52, 0x2e, 0xbc, 0x1f, 0x65, + 0xf6, 0x51, 0xb7, 0xd8, 0x24, 0x21, 0x49, 0x1d, 0x7f, 0x16, 0x28, 0x14, + 0xd9, 0xc2, 0x19, 0xdb, 0xa2, 0xc6, 0xf0, 0x3a, 0x2d, 0x98, 0x70, 0x72, + 0x45, 0xf7, 0x80, 0x37, 0x56, 0x0b, 0x0c, 0x6f, 0x80, 0xf1, 0x8c, 0xe2, + 0xf3, 0x4d, 0x16, 0xc5, 0x74, 0x90, 0x34, 0x1e, 0x57, 0x3c, 0xde, 0xf1, + 0xc4, 0x8c, 0x17, 0x09, 0xd3, 0xc5, 0x92, 0x9d, 0xcf, 0xdc, 0x7b, 0x4f, + 0xae, 0x20, 0x10, 0xd7, 0x04, 0x56, 0x9d, 0x9a, 0xa9, 0xd8, 0x06, 0x4a, + 0xd6, 0x68, 0xd4, 0x83, 0xcb, 0x7d, 0xe2, 0x62, 0xd1, 0x99, 0xb8, 0x9d, + 0x81, 0xc7, 0xfc, 0x94, 0x69, 0x0d, 0x2a, 0x1c, 0x99, 0xcf, 0x40, 0xc3, + 0xfd, 0xe9, 0x64, 0x5b, 0xc3, 0x1d, 0xda, 0x1c, 0x89, 0xab, 0x34, 0x1b, + 0x53, 0x6e, 0xad, 0xf0, 0x6e, 0x97, 0x87, 0xe8, 0xfb, 0x0c, 0x96, 0x93, + 0x1b, 0x52, 0x82, 0x6a, 0xba, 0x0f, 0xe3, 0x5d, 0xc4, 0x17, 0xdc, 0xe4, + 0x31, 0x78, 0x12, 0x26, 0x10, 0x74, 0x14, 0x7c, 0x45, 0xb2, 0xb9, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00, + 0x8f, 0x5b, 0x2a, 0x40, 0xce, 0xae, 0xa2, 0x79, 0xfc, 0xc5, 0x5e, 0x94, + 0xc8, 0x10, 0x4b, 0xf1, 0x21, 0x7f, 0x4b, 0xeb, 0x81, 0x3d, 0xb8, 0x26, + 0x83, 0x84, 0x76, 0x79, 0xda, 0x35, 0xfc, 0xdf, 0xfe, 0x10, 0x7a, 0xd5, + 0x17, 0xc0, 0xad, 0x0d, 0xf9, 0x3f, 0xa6, 0xa1, 0xcd, 0x6c, 0x9c, 0x3b, + 0x52, 0xbd, 0x04, 0xf9, 0xe2, 0x9e, 0x86, 0x83, 0x98, 0x60, 0x01, 0x99, + 0xb7, 0xbd, 0x02, 0x87, 0xd8, 0xea, 0x65, 0xaa, 0x60, 0x6f, 0x33, 0x50, + 0x25, 0x84, 0xb7, 0x42, 0x63, 0x39, 0xfd, 0x17, 0x67, 0x74, 0x88, 0x66, + 0xe2, 0x38, 0x59, 0xf6, 0x9b, 0x98, 0x95, 0xdd, 0x54, 0x2c, 0x69, 0x6a, + 0x0a, 0x51, 0x66, 0x4d, 0x65, 0xc0, 0x58, 0x3e, 0xcf, 0x15, 0x63, 0x7a, + 0x32, 0xc8, 0xfb, 0xe7, 0x11, 0x2e, 0x25, 0x17, 0x52, 0x4d, 0x8e, 0x6b, + 0x6d, 0x58, 0x4e, 0xf6, 0xd6, 0xb0, 0xfa, 0x0d, 0x7a, 0xb1, 0x44, 0x52, + 0x9a, 0x6c, 0x90, 0x38, 0x68, 0xa5, 0xa9, 0x9b, 0xc5, 0x45, 0x09, 0xfa, + 0xaa, 0x8c, 0xfe, 0x91, 0x55, 0x93, 0x35, 0x52, 0x45, 0xbb, 0xaa, 0x5b, + 0xf0, 0x63, 0x53, 0x13, 0xcf, 0x48, 0x7b, 0xaa, 0x20, 0xa0, 0x07, 0x43, + 0x1d, 0xd7, 0xb3, 0x4a, 0x1b, 0x8c, 0x51, 0xb5, 0xf6, 0xb9, 0x5b, 0x13, + 0x02, 0x74, 0x3e, 0x48, 0xde, 0xec, 0xeb, 0x65, 0xfe, 0xf2, 0x61, 0xe5, + 0x68, 0xd5, 0xea, 0xd9, 0x79, 0xa6, 0x71, 0xb1, 0x57, 0x0f, 0xbc, 0xd0, + 0x31, 0x4f, 0xff, 0xc5, 0x95, 0xe8, 0xee, 0x70, 0x18, 0xb9, 0xbc, 0x19, + 0xcd, 0x3a, 0x06, 0x75, 0xe4, 0x57, 0xc1, 0x2e, 0x32, 0x19, 0xdd, 0x2e, + 0x45, 0xc0, 0x19, 0xe6, 0x72, 0x81, 0x2c, 0xb6, 0xed, 0x1c, 0xd4, 0xef, + 0x42, 0x18, 0x44, 0x44, 0x75, 0xd6, 0x29, 0x81, 0xe1, 0xf7, 0x5b, 0x48, + 0xa3, 0xf8, 0x92, 0x54, 0xd0, 0x79, 0xa1, 0xe1, 0x8e, 0xa8, 0x98, 0x2d, + 0x57, 0x5d, 0xb5, 0x5a, 0x01, 0x1b, 0xb3, 0xcf, 0x5f, 0x64, 0x2e, 0x70, + 0xba, 0xa0, 0x41, 0xbb, 0xd4, 0x82, 0x28, 0x3c, 0xb1, 0x81, 0x76, 0xd6, + 0x85, 0x2e, 0xc6, 0x01, 0x7f, 0xae, 0xc3, 0x17, 0x2f, 0xed, 0xbe, 0xad, + 0xa2, 0x7c, 0x53, 0xc6, 0x77, 0x73, 0x1d, 0x19, 0x90, 0x2a, 0xf8, 0xd5, + 0x50, 0x65, 0xc1, 0x22, 0x3c, 0x24, 0x96, 0xeb, 0x7b, 0x53, 0x8f, 0xbf, + 0xd9, 0xf7, 0x80, 0xa8, 0x76, 0x72, 0xea, 0xb7, 0x7f, 0xf7, 0xa2, 0x52, + 0xc7, 0xa7, 0xd6, 0x88, 0xa1, 0x38, 0x40, 0x5d, 0xcd, 0xdb, 0xe3, 0x8e, + 0xc7, 0xf9, 0x39, 0xbe, 0xfa, 0x27, 0x41, 0x73, 0x3a, 0x1c, 0xb3, 0x03, + 0xf1, 0x36, 0xea, 0xe8, 0xb3, 0xe1, 0x6e, 0x59, 0xcc, 0xe2, 0x75, 0x2b, + 0xf9, 0x55, 0xb9, 0xc2, 0xdf, 0x0a, 0x8d, 0x8d, 0xd1, 0x62, 0x3b, 0x86}; + +const uint8_t kProdVmpCodeSigningDrmRootCertificate[] = { + 0x30, 0x82, 0x04, 0xd7, 0x30, 0x82, 0x03, 0x3f, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x11, 0x00, 0xca, 0xa4, 0xbd, 0x6b, 0x93, 0x56, 0x4a, 0xf1, + 0x84, 0xef, 0x5e, 0xed, 0xe8, 0xf7, 0xe2, 0x0b, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, + 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, + 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, + 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, + 0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x72, + 0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x37, + 0x30, 0x31, 0x32, 0x34, 0x32, 0x33, 0x33, 0x38, 0x31, 0x34, 0x5a, 0x18, + 0x0f, 0x33, 0x30, 0x31, 0x36, 0x30, 0x35, 0x32, 0x37, 0x32, 0x33, 0x33, + 0x38, 0x31, 0x34, 0x5a, 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, + 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, + 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, + 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69, + 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, + 0x69, 0x67, 0x6e, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30, + 0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30, + 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xe0, 0xb5, 0xc5, 0x6d, + 0x04, 0xc1, 0x97, 0xaf, 0xe7, 0x62, 0xfb, 0x84, 0xdc, 0xd1, 0xf4, 0xb1, + 0xb5, 0xa2, 0x7c, 0xca, 0x31, 0xf8, 0xce, 0xa7, 0x7a, 0x92, 0xc2, 0xbe, + 0x14, 0xdc, 0x85, 0x9f, 0x18, 0x9e, 0x78, 0xba, 0x65, 0x05, 0x56, 0x88, + 0x88, 0xdc, 0x1f, 0x4f, 0x24, 0x7f, 0xf5, 0x26, 0x6f, 0x6e, 0xcc, 0x04, + 0x2f, 0x38, 0xb8, 0xcd, 0x27, 0xd7, 0x9e, 0x07, 0xd3, 0xa9, 0xd4, 0x6b, + 0x84, 0xfe, 0xf8, 0xac, 0x9c, 0x53, 0xdf, 0x7a, 0x45, 0x5e, 0x77, 0xf8, + 0x4e, 0x88, 0x00, 0x5c, 0x6f, 0xb6, 0xa7, 0x0b, 0x4b, 0x63, 0x57, 0x92, + 0x7a, 0x7b, 0x3d, 0x20, 0x88, 0x3e, 0x7b, 0xb4, 0x28, 0x2b, 0x63, 0x81, + 0xd4, 0x0c, 0x4c, 0xb7, 0x54, 0x68, 0x68, 0x2c, 0x0d, 0xf5, 0xa6, 0x9e, + 0x16, 0x93, 0x76, 0xdd, 0xc0, 0xd5, 0x93, 0x65, 0x99, 0x90, 0x17, 0x2d, + 0x2b, 0xdc, 0x6f, 0xaf, 0x58, 0xfd, 0x78, 0xe9, 0xf5, 0xde, 0x2e, 0x36, + 0x95, 0xf0, 0xcf, 0x25, 0x41, 0x3d, 0x4f, 0x37, 0xd1, 0x70, 0x5b, 0xb5, + 0xc0, 0xc8, 0xf3, 0x63, 0xa3, 0xda, 0x9f, 0x94, 0xdb, 0xf8, 0x51, 0xf2, + 0xa9, 0xe5, 0x67, 0x0e, 0x29, 0xb3, 0x45, 0x12, 0xc5, 0x42, 0xf9, 0x3b, + 0x38, 0xf9, 0xa5, 0x7b, 0x41, 0x88, 0x6f, 0x32, 0x62, 0x03, 0x5f, 0xfd, + 0x35, 0x97, 0xc2, 0x83, 0x15, 0xb6, 0x56, 0x4f, 0xbb, 0x81, 0x39, 0x37, + 0xf2, 0x9c, 0x2a, 0x61, 0xa8, 0x63, 0x5f, 0xa0, 0x27, 0x30, 0x06, 0xd4, + 0xcb, 0x9d, 0xb7, 0xe9, 0xf2, 0xae, 0xd6, 0xc9, 0xcd, 0x72, 0xa3, 0xe6, + 0xf8, 0x54, 0x03, 0x6e, 0xe1, 0x95, 0x03, 0xdd, 0x7a, 0x85, 0xb3, 0x5c, + 0xa7, 0xca, 0x99, 0xec, 0xa6, 0xe8, 0x1a, 0xb2, 0x72, 0xe1, 0x91, 0x2d, + 0x97, 0xe3, 0x2a, 0x9c, 0x42, 0xaa, 0x45, 0xf2, 0x8e, 0x51, 0xc0, 0xd8, + 0x21, 0x83, 0x66, 0x07, 0xb5, 0x20, 0xb8, 0x28, 0xa5, 0xde, 0xfb, 0x4e, + 0x2e, 0xc7, 0x70, 0x9b, 0x3d, 0x52, 0x66, 0x24, 0xc5, 0xa2, 0x2e, 0x49, + 0x54, 0x5c, 0xfd, 0xc0, 0xde, 0xf6, 0x9d, 0xb4, 0x70, 0x31, 0x2b, 0xac, + 0x14, 0xfb, 0x19, 0x9e, 0x89, 0xd5, 0x07, 0x87, 0xa0, 0xd8, 0x15, 0x96, + 0xe9, 0xf7, 0x91, 0x36, 0x52, 0x83, 0x3c, 0x2c, 0xfa, 0xb5, 0xc4, 0xc6, + 0x1a, 0x34, 0xf0, 0x53, 0x94, 0x15, 0x82, 0xa2, 0x2f, 0x98, 0xbb, 0x49, + 0xca, 0xf7, 0xe0, 0xcb, 0x9e, 0x3c, 0xa6, 0x64, 0x59, 0x77, 0x63, 0xd1, + 0x05, 0x03, 0x99, 0x6c, 0x50, 0x08, 0xec, 0x64, 0x86, 0xf7, 0x97, 0xaf, + 0xf8, 0xcc, 0xdf, 0x91, 0xc7, 0x2c, 0x15, 0x0f, 0xa7, 0x0e, 0x02, 0x33, + 0x63, 0x84, 0xb7, 0x7e, 0xd3, 0x10, 0x89, 0x05, 0x2d, 0xb4, 0x68, 0x38, + 0xe0, 0x00, 0x49, 0xda, 0xaa, 0xb9, 0xf9, 0xbf, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f, + 0xd0, 0x9a, 0xd9, 0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f, 0xd0, 0x9a, 0xd9, + 0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86, 0x30, 0x0c, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00, 0xc1, 0x81, 0x17, 0x08, 0x0a, + 0xcb, 0xae, 0x54, 0x92, 0xed, 0x0f, 0xc0, 0xf0, 0x83, 0xb8, 0xe9, 0x2a, + 0xd5, 0x65, 0x18, 0xb5, 0x92, 0xfc, 0x67, 0x9c, 0x39, 0x9e, 0x3a, 0x93, + 0x1f, 0x91, 0x7b, 0x35, 0x6d, 0x09, 0x6c, 0x3c, 0x02, 0x85, 0xe8, 0xc6, + 0x0c, 0x6b, 0x35, 0xeb, 0x8c, 0xe4, 0x44, 0x80, 0xc5, 0x4e, 0x65, 0xa6, + 0xbc, 0x25, 0x93, 0x5a, 0xed, 0x5a, 0xd1, 0x5a, 0xcf, 0xf1, 0xbe, 0xd5, + 0x46, 0x7c, 0x78, 0xfe, 0xb6, 0xa6, 0x9c, 0x85, 0xa9, 0xe2, 0x13, 0x70, + 0x99, 0x03, 0x5a, 0x3a, 0xa8, 0x7b, 0xdd, 0x50, 0x76, 0x83, 0xfe, 0x49, + 0xc0, 0x5e, 0xc7, 0xf1, 0x18, 0xc0, 0xe6, 0xb7, 0xc7, 0xe4, 0x9d, 0x54, + 0xaf, 0x25, 0xdf, 0xe4, 0x81, 0xc1, 0xe9, 0xaa, 0x7c, 0x05, 0x20, 0xfa, + 0x91, 0x47, 0xd1, 0x4a, 0xe2, 0x24, 0x6f, 0x72, 0x22, 0x69, 0xd0, 0x89, + 0x78, 0x2c, 0x9a, 0x16, 0x7d, 0x92, 0xdd, 0x64, 0x6c, 0xc8, 0xbf, 0x33, + 0xe3, 0x91, 0xb9, 0xb5, 0x16, 0xfe, 0xfd, 0xa2, 0xdb, 0x82, 0xf0, 0xec, + 0xac, 0xe7, 0xf9, 0x2e, 0xac, 0x50, 0xf0, 0xcf, 0xba, 0xe1, 0x55, 0xa9, + 0xb0, 0xd9, 0x66, 0x3f, 0xb6, 0xee, 0x57, 0xff, 0x8d, 0x43, 0xb7, 0xc3, + 0xb4, 0x44, 0xa9, 0xcc, 0x99, 0xa2, 0xbf, 0xac, 0x4f, 0xec, 0xed, 0xb5, + 0xd0, 0x3f, 0xa9, 0x50, 0x3b, 0xc1, 0x24, 0xf2, 0xd0, 0xef, 0x5c, 0xbf, + 0x5c, 0xc2, 0x41, 0x29, 0xbe, 0xb6, 0x76, 0x5a, 0x19, 0xde, 0x67, 0x1c, + 0x2a, 0x67, 0xae, 0x07, 0xe8, 0xfa, 0x49, 0xf1, 0x81, 0xbc, 0x22, 0xa1, + 0xe6, 0x5d, 0x27, 0x76, 0x0f, 0x4d, 0x41, 0x57, 0xcf, 0x0f, 0x12, 0x2f, + 0xdd, 0x20, 0x88, 0xcf, 0xc8, 0xa7, 0xc0, 0xe5, 0xec, 0xcd, 0xb9, 0xa7, + 0x1c, 0x29, 0x55, 0xed, 0x67, 0xf9, 0x38, 0x33, 0xea, 0x85, 0xe9, 0x69, + 0x5a, 0x7c, 0xfe, 0x37, 0x3b, 0xdd, 0x61, 0x5f, 0xaa, 0xc3, 0x18, 0xbc, + 0x58, 0x95, 0x39, 0x61, 0x79, 0xa1, 0x46, 0xcc, 0xc0, 0xe7, 0xd6, 0x52, + 0x3c, 0xc7, 0xfa, 0xed, 0x89, 0x06, 0xeb, 0xd4, 0x5e, 0x9c, 0xa5, 0x55, + 0x8c, 0xe3, 0x5f, 0xe6, 0xb4, 0x0a, 0xf4, 0xf6, 0x7d, 0xeb, 0x64, 0x74, + 0xa9, 0x1a, 0x8d, 0x6e, 0xf1, 0x41, 0xc7, 0x7e, 0xc6, 0x26, 0x3a, 0x47, + 0x70, 0x49, 0x07, 0x27, 0xa2, 0xb9, 0xc6, 0x79, 0x9d, 0x94, 0xf5, 0x51, + 0x69, 0xdf, 0xbd, 0x84, 0xee, 0xaa, 0x46, 0xea, 0x4b, 0x27, 0xb6, 0x5c, + 0xac, 0xcf, 0x4a, 0x48, 0x12, 0x40, 0x86, 0x80, 0xd4, 0xb8, 0x0a, 0xb1, + 0x9f, 0xdc, 0x68, 0x60, 0x14, 0x33, 0x1d, 0x88, 0xfb, 0xa2, 0xfc, 0x49, + 0x0c, 0xa9, 0x76, 0x2d, 0xd7, 0x32, 0x3f, 0x77, 0xdb, 0x62, 0x8c, 0x35, + 0x88, 0x5d, 0x66, 0xc9, 0x8d, 0x07, 0xe5}; + +const char kCodeSigningDevelopmentFlagOid[] = "1.3.6.1.4.1.11129.4.1.2"; +const char kSecureStorageFlagOid[] = "1.3.6.1.4.1.11129.4.1.3"; + +} // namespace + +namespace widevine { + +VmpChecker::VmpChecker() : allow_development_vmp_(false) {} + +VmpChecker::~VmpChecker() {} + +util::Status VmpChecker::SelectDrmCertificateType(CertificateType cert_type) { + std::unique_ptr ca_cert(new X509Cert); + util::Status status = ca_cert->LoadDer( + cert_type == kCertificateTypeProduction + ? std::string(reinterpret_cast( + kProdVmpCodeSigningDrmRootCertificate), + sizeof(kProdVmpCodeSigningDrmRootCertificate)) + : std::string(reinterpret_cast( + kDevVmpCodeSigningDrmRootCertificate), + sizeof(kDevVmpCodeSigningDrmRootCertificate))); + if (!status.ok()) return status; + + ca_.reset(new X509CA(ca_cert.release())); + + return util::OkStatus(); +} + +VmpChecker* VmpChecker::Instance() { + static VmpChecker instance; + return &instance; +} + +// Verify VMP data and return appropriate result. +util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) { + DCHECK(!vmp_data.empty()); + DCHECK(result); + + if (!ca_) return util::Status(error_space, CERT_CHAIN_NOT_SELECTED, ""); + + vmp::VmpData vmp_data_obj; + if (!vmp_data_obj.ParseFromString(vmp_data)) { + LOG(INFO) << "Error deserializing VmpData."; + return util::Status(error_space, INVALID_MESSAGE, + "vmp-data-deserialize-failed"); + } + + std::vector> code_signing_certs; + const std::string kDevelopmentFlagOid(kCodeSigningDevelopmentFlagOid); + bool secure_storage_verified(true); + for (int cert_idx = 0; cert_idx < vmp_data_obj.certificates_size(); + ++cert_idx) { + code_signing_certs.emplace_back(new X509Cert); + util::Status status(code_signing_certs.back()->LoadDer( + vmp_data_obj.certificates(cert_idx))); + if (!status.ok()) return status; + + if (!allow_development_vmp_) { + bool dev_flag; + if (code_signing_certs.back()->GetV3BooleanExtension(kDevelopmentFlagOid, + &dev_flag) && + dev_flag) { + return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, + "development-vmp-certificate-not-allowed"); + } + } + status = ca_->VerifyCert(*code_signing_certs.back()); + if (!status.ok()) return status; + + if (secure_storage_verified) { + bool secure_storage_flag; + if (!code_signing_certs.back()->GetV3BooleanExtension( + kSecureStorageFlagOid, &secure_storage_flag) || + !secure_storage_flag) { + secure_storage_verified = false; + } + } + } + + size_t num_blessed_binaries(0); + for (int binary_idx = 0; binary_idx < vmp_data_obj.signed_binary_info_size(); + ++binary_idx) { + const vmp::VmpData::SignedBinaryInfo& binary_info( + vmp_data_obj.signed_binary_info(binary_idx)); + if (binary_info.signature().empty()) { + LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\"."; + *result = kTampered; + return util::OkStatus(); + } + if (binary_info.certificate_index() >= code_signing_certs.size()) { + LOG(INFO) << "Invalid code signing certificate index."; + *result = kTampered; + return util::OkStatus(); + } + X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get(); + std::unique_ptr key(cert->GetRsaPublicKey()); + std::string message(binary_info.binary_hash()); + message += binary_info.flags() & 0xff; + if (!key->VerifySignature(message, binary_info.signature())) { + LOG(INFO) << "Code signature verification failed for file \"" + << binary_info.file_name() << "\"."; + *result = kTampered; + return util::OkStatus(); + } + if (binary_info.flags() & kBlessedBinaryFlag) ++num_blessed_binaries; + } + if (num_blessed_binaries != 1) { + LOG(INFO) << "Invalid number of blessed binaries (" << num_blessed_binaries + << ")."; + *result = kTampered; + return util::OkStatus(); + } + + VLOG(2) << "VMP verification success. Secure storage: " + << secure_storage_verified; + *result = secure_storage_verified ? kSecureStorageVerified : kVerified; + return util::OkStatus(); +} + +} // namespace widevine diff --git a/common/vmp_checker.h b/common/vmp_checker.h new file mode 100644 index 0000000..9122fa0 --- /dev/null +++ b/common/vmp_checker.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// Singleton object which validates VMP (Verified Media Pipeline) data for +// purposes of platform software verification. + +#ifndef COMMON_VMP_CHECKER_H_ +#define COMMON_VMP_CHECKER_H_ + +#include +#include + +#include "util/status.h" +#include "common/certificate_type.h" + +namespace widevine { +class X509CA; + +class VmpChecker { + public: + enum Result { + kUnverified = 0, + kVerified = 1, + kSecureStorageVerified = 2, + kTampered = 3 + }; + + // Singleton accessor. + static VmpChecker* Instance(); + + // Select the type of root to use. Not thread-safe. + virtual util::Status SelectDrmCertificateType(CertificateType root_type); + + // Verify VMP data and return appropriate result. + virtual util::Status VerifyVmpData(const std::string& vmp_data, Result* result); + + // Enable/disable development code signing certificates. + void set_allow_development_vmp(bool allow) { + allow_development_vmp_ = allow; + } + bool allow_development_vmp() const { return allow_development_vmp_; } + + private: + VmpChecker(); + ~VmpChecker(); + + std::unique_ptr ca_; + bool allow_development_vmp_ = false; +}; + +} // namespace widevine + +#endif // COMMON_VMP_CHECKER_H_ diff --git a/common/vmp_checker_test.cc b/common/vmp_checker_test.cc new file mode 100644 index 0000000..521b5a6 --- /dev/null +++ b/common/vmp_checker_test.cc @@ -0,0 +1,321 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include + +#include "glog/logging.h" +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "common/rsa_key.h" +#include "common/vmp_checker.h" +#include "protos/public/errors.pb.h" +#include "protos/public/verified_media_pipeline.pb.h" + +namespace widevine { + +namespace { + +const uint32_t kBlessedBinaryFlag = 0x00000001; + +const char kDevVmpCodeSigningKey[] = + "308204a50201000282010100b3a7da87309390688388d614a5a37e7ee73a" + "c3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd" + "8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6" + "d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b" + "b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51" + "957dc4608fd34c999a7b35245bce2795715d8a5957a0872398473eed4860" + "29286f51d4118927e88634ad040034e061fd3f58632961761b1fbb8faf45" + "391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706" + "424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203" + "01000102820101008befeb1ff28e7ea56a0f63f1a133c08c48c0553efe86" + "07cfd9d216d981aef81a81db226b47277a6d32d09207df88e03316247ae7" + "39325456a00644bbfacd6dc2990851f047ccdc1226bec21afac9eacfb957" + "1e51889cfb6dac853fcdd1bb1593bd5528cdd3cbbb32c69183ef018fd3f5" + "3153f097fd3de9aca7998a6f4522e60c5e76ecc717033a2be16c233fe3e2" + "94c0ae68179a1ae5d33d51f2c0bcda35cffa21e1814374af1c014c280cfb" + "09e294a099f9a6f003d3da026af0f53029cad3d401178b0db53ca97fd166" + "a5eddf1384ddd92c55ab31758e0651e41dd7b24f2c438d58de00f55343cc" + "a461b94ff6358fc37f404ecbda2365d3e75391566248d3e102818100e4c8" + "783343b43fc127f5077fb35b37145ab6ee5f9a802e8100161ce441cbdbdc" + "1eb15f50bbef1f5088ba83ff914977862dffc63bd364172c9044e1131dbb" + "ff98755daa49670a014163b3c121395d880ad874b9ebdf66ff72a245c2b5" + "4f8fe1e233e72fd9cc253cbcd144f52537e9b7ed16b8a6d328de61c41f86" + "ea1d9ef326fb02818100c90738b2cf61af785e53393d9e96bc2f4d79358b" + "7bd487a242d170b8372af0a26af4980534a099647a4f38efb1a47d9dcdcf" + "a4ea6036396ef333e4f1de62ec529c3122f0d3b6abc818918494a52b028b" + "2cdf06bf2b450d5b99da7f203ff011318c030a01659c4c56244c2c312528" + "e30dd3b7709022d8fd14a2dba23a855da02f0281804f8ebeede4cf5b9449" + "d6d582bcd62d73309088984a5be4d00b3da55262e7074fa684bbc69173f8" + "09c36248e0a89f49a7297bd66d9b7724efe4436f997c2f92146c4be4199e" + "71463a7cf75763bc552027d559d2058a2c810c560db845e0a30243ed14a9" + "f92d1a8de2834b5d8c51c33ea87dcc3c8715a12f9249fc5a916e62d3dd02" + "8181008f74c7d1528cb35b82748174a7a789c377d5f790025e382c62e273" + "3e02a071f875baf681407d1af9c90e9fe2ed322532679cb6634b2566f6f6" + "37223a3828ffdc33fa1ca51f704c460ec2498a8a13974d1a484dd83e5898" + "9fb5bb66dcecc3b4815719141acb182ea18a659163c0d0dcb7114ee6d4f5" + "09441165e6b66e6c9dd3a102818100c449003eee6d9a0b724b66027a791e" + "3b86a72b2098ab085c2e69c276627fd6beeee98918de14ce15d17e059906" + "a178015d04e205918203ffa6c1324868d5c0a019f6fe82335403743322ee" + "14b1159692194ac22d9056c0c39309d249b33abaddb0d08ee87cc053a12a" + "83f12d261525e1b37df643c3e55770ef247d5d094816a0"; + +const char kDevVmpCodeSigningCert[] = + "3082056a308203d2a003020102020f112233445566778899aabbccddeeff300d06092a86" + "4886f70d01010b050030819c310b30090603550406130255533113301106035504080c0a" + "57617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d060355" + "040a0c06476f6f676c653111300f060355040b0c085769646576696e65311e301c060355" + "04030c157769646576696e652d6465762d636f64657369676e3121301f06092a864886f7" + "0d010901161274696e736b697040676f6f676c652e636f6d301e170d3137313030393231" + "323835385a170d3237313030373231323835385a3081a0310b3009060355040613025553" + "3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b" + "6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465" + "76696e653122302006035504030c197769646576696e652d6465762d766d702d636f6465" + "7369676e3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e" + "636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100" + "b3a7da87309390688388d614a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5d" + "e2f7b409b1eac2dc578b2bfd8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c" + "62c71d894fe6d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b" + "b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd3" + "4c999a7b35245bce2795715d8a5957a0872398473eed486029286f51d4118927e88634ad" + "040034e061fd3f58632961761b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be" + "2fd258331e7a86baa7394706424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3b" + "b73008150203010001a38201213082011d301d0603551d0e041604147266b4ce84aafd02" + "b1159cd2fa04c2553c6c02463081bb0603551d230481b33081b0a181a2a4819f30819c31" + "0b30090603550406130255533113301106035504080c0a57617368696e67746f6e311130" + "0f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c65311130" + "0f060355040b0c085769646576696e65311e301c06035504030c157769646576696e652d" + "6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b6970" + "40676f6f676c652e636f6d820900c5f82f038facf15830090603551d1304023000300b06" + "03551d0f04040302078030130603551d25040c300a06082b060105050703033011060a2b" + "06010401d67904010204030101ff300d06092a864886f70d01010b05000382018100aa23" + "6a5c0e23d5bf67c9f80f893d8347ba489541cf7f4ab7dfffda0ca21a3372e8ee8cfea863" + "628b9e0795904bc0e7495517246143c7b8555884e82fe1c305f0f4c3575447d4e7ce3243" + "4e1e0cf11712d537cd434c11d1328b814c94dbd0bab802e8fed5390da5f0cd719ce0e366" + "47620bcf40ed3945c80ab19beb7728080a74d4ff5d62564f47b32c4915c1f14890d379c8" + "8060f0bac73301defda06275a2e1a2024f92f0b4700d4e50d2d6f8d033715a362d7ca5ab" + "6d2dae20d8cefa2d3fc4f61e1734802984e5078dd6d957719fa75ea10dd02d983f7e383b" + "10fa92be7add70238388e63ec2c7ec49a37aa0c8a2566c46e9755cd9ce654c6d42053d1e" + "1cd9555f8a3bc5e426857072a8e8b44a756543893b1d29dabf31a2301597df2666612f23" + "a442613526e19f2aa9b2ea49f16f14b16794e053967a21b821c7b2495b2e02b01344a339" + "9d7e31dd71982cf21a546b1947bbce236381d717070c27096b6a91413abc69c3c0759574" + "4b7e91daf24b2a0acfd85924669f00292a4bd0c57b4c"; + +const char kDevSecureStorageVmpCodeSigningCert[] = + "30820571308203d9a0030201020203112233300d06092a864886f70d01010b050030819c" + "310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3111" + "300f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c653111" + "300f060355040b0c085769646576696e65311e301c06035504030c157769646576696e65" + "2d6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b69" + "7040676f6f676c652e636f6d301e170d3138303232323231323834395a170d3238303232" + "303231323834395a3081a0310b30090603550406130255533113301106035504080c0a57" + "617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d06035504" + "0a0c06476f6f676c653111300f060355040b0c085769646576696e653122302006035504" + "030c197769646576696e652d6465762d766d702d636f64657369676e3121301f06092a86" + "4886f70d010901161274696e736b697040676f6f676c652e636f6d30820122300d06092a" + "864886f70d01010105000382010f003082010a0282010100b3a7da87309390688388d614" + "a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd" + "8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6d5524e1b28af" + "1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646bb50d3a13d8687ad304005b0b" + "0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd34c999a7b35245bce2795715d" + "8a5957a0872398473eed486029286f51d4118927e88634ad040034e061fd3f5863296176" + "1b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706" + "424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203010001a38201" + "3430820130301d0603551d0e041604147266b4ce84aafd02b1159cd2fa04c2553c6c0246" + "3081bb0603551d230481b33081b0a181a2a4819f30819c310b3009060355040613025553" + "3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b" + "6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465" + "76696e65311e301c06035504030c157769646576696e652d6465762d636f64657369676e" + "3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e636f6d82" + "0900c5f82f038facf15830090603551d1304023000300b0603551d0f0404030207803013" + "0603551d25040c300a06082b060105050703033011060a2b06010401d679040102040301" + "01ff3011060a2b06010401d67904010304030101ff300d06092a864886f70d01010b0500" + "038201810069482a9bb125b17c76c77f86004e36ca4fc36bbde9c86901481c70d165e3cf" + "1c8541264190b3e7106c33ff4920a99bc9f939298091dc72ff898d22f5017b04d213eb60" + "626382656fdd66b9cd1830d98ef89d8e405b2d7ce9445db8de2ca365438b848f85d5266b" + "c211c8934154bb88ddb9e6f9aaff761814c1c0da90dd499f174507b5f4e89339e8abd157" + "8b3238d8f5c9dcb1f76e4d810679c2eca3583144f1ac0ce955b7c6a2cc9fc9f3d6c87069" + "28e301ebc3844e53f4905ff60803110dfc3b4f74b50ae1baffd091daad3ea29925f8009e" + "adc471a9ae673d9a9a003901d962f58fede85b2f65d9a470725459b19a69b06ae49179a3" + "395286b7d7039b32985a6db5b30bcd5cbed975d9a68de44fbcd1dbc8b6fbf67746b87d30" + "122c1cd8ed303a8bec7e23d284c0de35cfdccf261c317efe192efa84d2600bba3f9af846" + "8f89953a98f92c97b13d320f484627790d5b7b407f29f343c41154efbcf06e98632a3f3d" + "7138184a6f40af2435b3d98054ed9f4c3aa9ecf95c5c3014d3aa4d12f2"; + +const char kSameAsPrevious[] = ""; + +} // namespace + +class VmpCheckerTest : public ::testing::Test { + public: + void SetUp() override { + ASSERT_OK(VmpChecker::Instance()->SelectDrmCertificateType( + kCertificateTypeTesting)); + vmp_data_.Clear(); + VmpChecker::Instance()->set_allow_development_vmp(true); + signing_key_.reset( + RsaPrivateKey::Create(absl::HexStringToBytes(kDevVmpCodeSigningKey))); + ASSERT_TRUE(signing_key_); + } + + // Adds a binary to the VMP data to be verified. If |signing_cert| is + // |kSameAsPrevious| (empty), then the binary is signed using the same + // certificate as the previously added binary. This means that the first + // call to this function should not use |kSameAsPrevious|. + void AddVmpBinary(const std::string& signing_cert, const std::string& file_name, + const std::string& binary_hash, uint32_t flags) { + DCHECK(!signing_cert.empty() || !vmp_data_.certificates().empty()); + + if (!signing_cert.empty()) { + *vmp_data_.add_certificates() = absl::HexStringToBytes(signing_cert); + } + vmp::VmpData::SignedBinaryInfo* new_binary = + vmp_data_.add_signed_binary_info(); + new_binary->set_file_name(file_name); + new_binary->set_certificate_index(vmp_data_.certificates_size() - 1); + new_binary->set_binary_hash(binary_hash); + new_binary->set_flags(flags); + std::string message(binary_hash); + message += flags & 0xff; + std::string signature; + ASSERT_TRUE(signing_key_->GenerateSignature(message, &signature)); + new_binary->set_signature(signature); + } + + const std::string GetVmpData() { + std::string result; + vmp_data_.SerializeToString(&result); + return result; + } + + protected: + vmp::VmpData vmp_data_; + std::unique_ptr signing_key_; +}; + +TEST_F(VmpCheckerTest, Success) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kVerified, result); +} + +TEST_F(VmpCheckerTest, SecureStorageSuccess) { + AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kSecureStorageVerified, result); +} + +TEST_F(VmpCheckerTest, FailDevelopmentCert) { + VmpChecker::Instance()->set_allow_development_vmp(false); + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + VmpChecker::Result result; + EXPECT_EQ(DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, + VmpChecker::Instance() + ->VerifyVmpData(GetVmpData(), &result) + .error_code()); +} + +TEST_F(VmpCheckerTest, FailTwoBlessedBinaries) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + vmp_data_.mutable_signed_binary_info(0)->set_flags(kBlessedBinaryFlag); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +TEST_F(VmpCheckerTest, FailNoBlessedBinaries) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + vmp_data_.mutable_signed_binary_info(3)->set_flags(0); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +TEST_F(VmpCheckerTest, FailBadSignature) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + ++((*vmp_data_.mutable_signed_binary_info(2)->mutable_signature())[10]); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +TEST_F(VmpCheckerTest, FailSignatureVerification) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + ++((*vmp_data_.mutable_signed_binary_info(3)->mutable_binary_hash())[16]); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +TEST_F(VmpCheckerTest, FailUnsignedBinary) { + AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe", + "0123456789abdef0123456789abdef", 0); + AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0); + AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0); + AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc", + kBlessedBinaryFlag); + vmp_data_.mutable_signed_binary_info(2)->clear_binary_hash(); + VmpChecker::Result result; + ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result)); + EXPECT_EQ(VmpChecker::kTampered, result); +} + +} // namespace widevine diff --git a/common/wvm_test_keys.cc b/common/wvm_test_keys.cc new file mode 100644 index 0000000..7c39ad9 --- /dev/null +++ b/common/wvm_test_keys.cc @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include + +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "common/wvm_test_keys.h" +#include "common/wvm_token_handler.h" + +namespace widevine { +namespace wvm_test_keys { + +// Preprov key and keyboxes in this header use system ID 0x112. +const char kTestPreprovKeyHex[] = "f7538b38acc78ec68c732ac665c55c65"; +const char kBadPreprovKey1Hex[] = "1badbadbadbadbadbadbadbadbadbad1"; +const char kBadPreprovKey2Hex[] = "2badbadbadbadbadbadbadbadbadbad2"; + +const char kTestToken1Hex[] = + "00000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"; + +// The device key corresponding to kTestToken1. +const char kTestDeviceKey1Hex[] = "4071197f1f8910d9bf10c6bc4c987638"; + +// Base64Encode(kTestToken1) +const char kTestToken1Base64[] = + "AAAAAgAAARKOHr/gN4KAlsplOLT29Ly1HCtxkc8Dfpi+qiSSSQfhKPn/SbVKFlzZ" + "wz5lR1N+tNKft+jfPCwc2SUXoS9JIpU+"; + +const char kTestToken2Hex[] = + "0000000200000112d906feebe1750c5886ff77c2dfa31bb40e002f3adbc0fa5b" + "eb2486cf5f419549cdaa23230e5165ac2ffab56d53b692b7ba0c1857400c6add" + "3af3ff3d5cb24985"; + +// The device key corresponding to kTestToken2. +const char kTestDeviceKey2Hex[] = "42cfb1765201042302a404d1e0fac8ed"; + +// Base64Encode(kTestToken2) +const char kTestToken2Base64[] = + "AAAAAgAAARLZBv7r4XUMWIb/d8Lfoxu0DgAvOtvA+lvrJIbPX0GVSc2qIyMOUWWs" + "L/q1bVO2kre6DBhXQAxq3Trz/z1cskmF"; + +const char kTestToken3DesHex[] = + "00000002100000138e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"; + +const char kTestDeviceKey3DesHex[] = "4071197f1f8910d9bf10c6bc4c987638"; + +std::vector GetPreprovKeyVector() { + return { + // Return multiple preprov keys for the same device. + WvmTokenHandler::PreprovKey(kTestSystemId, + absl::HexStringToBytes(kBadPreprovKey1Hex)), + WvmTokenHandler::PreprovKey(kTestSystemId, + absl::HexStringToBytes(kTestPreprovKeyHex)), + WvmTokenHandler::PreprovKey(kTestSystemId, + absl::HexStringToBytes(kBadPreprovKey2Hex)), + + // Add another device that uses 3DES encryption for asset keys. Tokens + // the same except for the system ID portion. + WvmTokenHandler::PreprovKey(kTestSystemId3Des, + absl::HexStringToBytes(kTestPreprovKeyHex), + WvmTokenHandler::DES3), + }; +} + +std::map GetPreprovKeyTable() { + return {{kTestSystemId, std::string(kTestPreprovKeyHex)}}; +} + +std::multimap GetPreprovKeyMultimap() { + return {{kTestSystemId, kBadPreprovKey1Hex}, + {kTestSystemId, kTestPreprovKeyHex}, + {kTestSystemId, kBadPreprovKey2Hex}}; +} + +} // namespace wvm_test_keys +} // namespace widevine diff --git a/common/wvm_test_keys.h b/common/wvm_test_keys.h new file mode 100644 index 0000000..b39d194 --- /dev/null +++ b/common/wvm_test_keys.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Sample keys for use in tests of Widevine WVM crypto code. + +#ifndef COMMON_WVM_TEST_KEYS_H_ +#define COMMON_WVM_TEST_KEYS_H_ + +#include +#include +#include + +#include +#include "common/wvm_token_handler.h" + +namespace widevine { +namespace wvm_test_keys { + +// Most preprov key and keyboxes in this header use system ID 0x112. +const uint32_t kTestSystemId = 0x112; + +// One oddball system that uses 3DES. (We'll re-use the system and device keys). +const uint32_t kTestSystemId3Des = 0x10000013; + +extern const char kTestPreprovKeyHex[]; + +extern const char kTestToken1Hex[]; +extern const char kTestDeviceKey1Hex[]; + +extern const char kTestToken2Hex[]; +extern const char kTestDeviceKey2Hex[]; + +extern const char kTestToken3DesHex[]; +extern const char kTestDeviceKey3DesHex[]; + +// Return a list of preprovisioning keys, suitable for initializing +// the preprov key table for tests. +std::vector GetPreprovKeyVector(); + +// Old version of GetPreprovKeyTable() which uses a simple int->std::string +// map. Doesn't support multiple devices per system ID. +// TODO(user): get rid of this once other code as been migrated off of it. +std::map GetPreprovKeyTable(); + +// Version of GetPreprovKeyTable() which uses a simple int->hex_string +// multimap, useful for modular DRM code which doesn't care about the asset +// key cipher. +std::multimap GetPreprovKeyMultimap(); + +} // namespace wvm_test_keys +} // namespace widevine + +#endif // COMMON_WVM_TEST_KEYS_H_ diff --git a/common/wvm_token_handler.cc b/common/wvm_token_handler.cc new file mode 100644 index 0000000..99986cf --- /dev/null +++ b/common/wvm_token_handler.cc @@ -0,0 +1,317 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/wvm_token_handler.h" + +#include + +#include "glog/logging.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "util/endian/endian.h" +#include "util/gtl/map_util.h" +#include "util/status.h" +#include "common/aes_cbc_util.h" +#include "common/ecb_util.h" +#include "common/sha_util.h" + +namespace widevine { + +namespace { +const int kKeyboxFlagInsecure = 1; +const int kSystemIdSamsungTVFactory = 8; +const int kPreProvisioningKeySizeBytes = 16; +const int kKeyboxSizeBytes = 72; +const std::string kZeroIV(16, '\0'); + +class PreprovKeysMap { + public: + void Update(const std::vector& key_vector); + std::vector GetPreprovKeys(uint32_t system_id); + bool IsSystemIdKnown(uint32_t system_id); + bool IsEmpty(); + + static PreprovKeysMap* GetSingleton(); + + private: + absl::Mutex mutex_; + std::multimap preprov_keys_; +}; + +void PreprovKeysMap::Update( + const std::vector& key_vector) { + absl::WriterMutexLock lock(&mutex_); + preprov_keys_.clear(); + for (const WvmTokenHandler::PreprovKey& ppk : key_vector) { + if (ppk.key_bytes.size() != kPreProvisioningKeySizeBytes) { + LOG(WARNING) << "Invalid preprov key for system id: " << ppk.system_id; + continue; + } + preprov_keys_.insert(std::make_pair(ppk.system_id, ppk)); + } +} + +std::vector PreprovKeysMap::GetPreprovKeys( + uint32_t system_id) { + absl::ReaderMutexLock lock(&mutex_); + std::vector key_vector; + auto range = preprov_keys_.equal_range(system_id); + for (auto it = range.first; it != range.second; ++it) + key_vector.push_back(it->second); + return key_vector; +} + +bool PreprovKeysMap::IsSystemIdKnown(uint32_t system_id) { + absl::ReaderMutexLock lock(&mutex_); + return gtl::ContainsKey(preprov_keys_, system_id); +} + +bool PreprovKeysMap::IsEmpty() { + absl::ReaderMutexLock lock(&mutex_); + return preprov_keys_.empty(); +} + +PreprovKeysMap* PreprovKeysMap::GetSingleton() { + static auto* const kInstance = new PreprovKeysMap(); + return kInstance; +} + +} // namespace + +WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id, + const std::string& key_bytes, Cipher cipher, + const std::string& model_filter) + : system_id(system_id), + key_bytes(key_bytes), + cipher(cipher), + model_filter(model_filter) {} + +WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id, + const std::string& key_bytes, Cipher cipher) + : system_id(system_id), key_bytes(key_bytes), cipher(cipher) {} + +WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id, + const std::string& key_bytes) + : system_id(system_id), key_bytes(key_bytes), cipher(AES) {} + +void WvmTokenHandler::SetPreprovKeys(const std::vector& keyvec) { + PreprovKeysMap::GetSingleton()->Update(keyvec); +} + +bool WvmTokenHandler::IsSystemIdKnown(uint32_t system_id) { + return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id); +} + +util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token, + std::string* device_key_out, + Cipher* cipher_out, + bool* insecure_out) { + const std::string default_make_model; + return DecryptDeviceKey(token, default_make_model, device_key_out, cipher_out, + insecure_out); +} + +util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token, + const std::string& make_model, + std::string* device_key_out, + Cipher* cipher_out, + bool* insecure_out) { + DCHECK(device_key_out); + // DCHECK below is commented out because preprov_keys_ being nullptr + // is a valid test in wvm_token_handler_test.cc. If we have + // DCHECK(preprov_keys_) here it would thrown an failure in Kokoro + // presubmit because evidently Kokoro does debug build. + // DCHECK(preprov_keys_); + if (token.size() < kKeyboxSizeBytes) { + return util::Status(util::error::INVALID_ARGUMENT, + "Keybox token is too short."); + } + if (PreprovKeysMap::GetSingleton()->IsEmpty()) { + return util::Status(util::error::INVALID_ARGUMENT, + "Pre-provisioning key map is nullptr."); + } + + uint32_t system_id = GetSystemId(token); + + // There may be multiple preprov keys for a system ID; try them all. + std::vector key_vector = + PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id); + + util::Status status; + // First pass through the matching system Ids is an attempt to find an + // alternate preprov key specific to this make/model. + const PreprovKey* preferred_ppk = NULL; + for (const PreprovKey& ppk : key_vector) { + if (!ppk.model_filter.empty() && ppk.model_filter == make_model) { + preferred_ppk = &ppk; + break; + } + } + for (const PreprovKey& ppk : key_vector) { + if (preferred_ppk && preferred_ppk != &ppk) { + continue; + } + uint32_t version; + status = DecryptDeviceKeyWithPreprovKey( + ppk.key_bytes, token, device_key_out, insecure_out, &version); + if (version != 2) { + // Only version 2 keyboxes supported. + return util::Status(util::error::PERMISSION_DENIED, + absl::StrCat("invalid-keybox-version ", version)); + } + if (status.ok()) { + if (cipher_out) { + *cipher_out = ppk.cipher; + } + return status; + } + } + if (!status.ok()) { + // Return error from last attempt. + return status; + } + return util::Status( + util::error::NOT_FOUND, + absl::StrCat("Unknown system id: ", system_id).c_str()); // NOLINT +} + +// Decrypt a token using the preprov key for its system ID, and use the +// decrypted device key to encrypt the given asset key. Returns the encrypted +// asset key in |result|. +// On failure, returns an error from the Widevine Server SDK error space. +util::Status WvmTokenHandler::GetEncryptedAssetKey( + absl::string_view token, absl::string_view raw_asset_key, + const std::string& make_model, std::string* result) { + std::string device_key; + Cipher cipher = AES; + util::Status status = + DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr); + if (!status.ok()) { + return status; + } + return EncryptAssetKey(device_key, raw_asset_key, cipher, result); +} + +uint32_t WvmTokenHandler::GetSystemId(absl::string_view token) { + uint32_t system_id = 0; + if (token.size() >= 8) { + // Bytes 4-8 contain the little-endian system ID. + system_id = BigEndian::Load32(token.data() + 4); + } + return system_id; +} + +std::string WvmTokenHandler::GetEncryptedUniqueId(absl::string_view token) { + std::string encrypted_unique_id(""); + if (token.size() >= 24) { + encrypted_unique_id = std::string(token.substr(8, 16)); + } + return encrypted_unique_id; +} + +util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + absl::string_view preprov_key, absl::string_view token, + std::string* device_key_out) { + return DecryptDeviceKeyWithPreprovKey(preprov_key, token, device_key_out, + nullptr, nullptr); +} + +util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + absl::string_view preprov_key, absl::string_view token, + std::string* device_key_out, bool* insecure_out, uint32_t* version) { + CHECK(device_key_out); + if (token.size() < kKeyboxSizeBytes) { + return util::Status(util::error::INVALID_ARGUMENT, + "Keybox token is too short."); + } + if (version) { + *version = BigEndian::Load32(token.data()); + } + // This was checked at initialization, so if it fails now something is wrong. + CHECK_EQ(preprov_key.size(), kPreProvisioningKeySizeBytes); + + absl::string_view encrypted_unique_id = token.substr(8, 16); + std::string unique_id = crypto_util::DecryptAesCbcNoPad( + std::string(preprov_key), kZeroIV, std::string(encrypted_unique_id)); + + if (unique_id.size() != 16) { + // Decrypting 16 bytes should result in 16 bytes. + LOG(WARNING) << "Internal error decrypting unique id from token."; + return util::Status(util::error::INTERNAL, "Wrong size after decrypt/16."); + } + + absl::string_view encrypted_bits = token.substr(24, 48); + std::string decrypted_bits = crypto_util::DecryptAesCbcNoPad( + unique_id, kZeroIV, std::string(encrypted_bits)); + if (decrypted_bits.size() != 48) { + // Decrypting 48 bytes should result in 48 bytes. + LOG(WARNING) << "Internal error decrypting device key from token."; + return util::Status(util::error::INTERNAL, "Wrong size after decrypt/48."); + } + uint8_t keybox_flags = decrypted_bits[36]; + absl::string_view device_key = + absl::string_view(decrypted_bits).substr(0, 16); + absl::string_view expected_hash = + absl::string_view(decrypted_bits).substr(16, 20); + std::string actual_hash = Sha1_Hash(std::string(device_key)); + + if (GetSystemId(token) == kSystemIdSamsungTVFactory) { + // Keyboxes with this system ID have corrupted bytes starting after the + // first 16 bytes of the hash, so we use only the uncorrupted part. + expected_hash = expected_hash.substr(0, 16); + actual_hash.resize(16); + keybox_flags = 0; + } + if (expected_hash != actual_hash) { + return util::Status(util::error::PERMISSION_DENIED, + "Keybox validation failed."); + } + *device_key_out = std::string(device_key); + if (insecure_out) { + *insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0; + } + return util::OkStatus(); +} + +util::Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key, + absl::string_view raw_asset_key, + Cipher cipher, std::string* result) { + CHECK(result); + if (device_key.size() != 16) { + return util::Status(util::error::INVALID_ARGUMENT, + "Invalid device key: size != 16"); + } + if (raw_asset_key.size() < 16) { + return util::Status(util::error::INVALID_ARGUMENT, + "Invalid asset key: size < 16"); + } + // Truncate extra characters in the key; wvm always uses 16. + absl::string_view asset_key = raw_asset_key.substr(0, 16); + switch (cipher) { + case DES3: + if (!crypto_util::Encrypt3DesEcb(device_key, asset_key, result)) { + return util::Status(util::error::INTERNAL, + "Error encrypting asset key with 3DES."); + } + return util::OkStatus(); + case AES: + if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) { + return util::Status(util::error::INTERNAL, + "Error encrypting asset key with AES."); + } + return util::OkStatus(); + case PASS_THRU: + result->assign(raw_asset_key.data(), raw_asset_key.size()); + return util::OkStatus(); + default: + return util::Status(util::error::INVALID_ARGUMENT, "Unknown cipher type"); + } +} + +} // namespace widevine diff --git a/common/wvm_token_handler.h b/common/wvm_token_handler.h new file mode 100644 index 0000000..0bd34d5 --- /dev/null +++ b/common/wvm_token_handler.h @@ -0,0 +1,126 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_WVM_TOKEN_HANDLER_H_ +#define COMMON_WVM_TOKEN_HANDLER_H_ + +#include +#include + +#include "base/macros.h" +#include "absl/strings/string_view.h" +#include "util/status.h" + +namespace widevine { + +// Class for decoding the encrypted token that comes from a WVM classic keybox. +// +// Internally, this class keeps a multimap from system ID to the preprov key(s) +// for that system, so construction is relatively expensive; don't create an +// instance of this class per-request. +// +// Errors in this file are returned in the canonical space, but are chosen so +// that it's possible to map different failure modes to the appropriate codes +// in the WVM or server SDK error spaces: +// OK - success. +// NOT_FOUND - system id from token wasn't in the preprov key table. +// PERMISSION_DENIED - hash of device key didn't match. +// INVALID_ARGUMENT - token or a key is wrong size or otherwise invalid. +// INTERNAL_ERROR - something went wrong that shouldn't have been able to. +class WvmTokenHandler { + public: + // Cipher type to use for encrypting asset keys. This matches the enum in + // video/widevine/lockbox/public/key.proto. + enum Cipher { + DES3 = 0, + AES = 1, + PASS_THRU = 2, + }; + + struct PreprovKey { + // Utility constructor. + PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher, + const std::string& model_filter); + PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher); + // Constructor if the cipher isn't needed (i.e. modular DRM). + PreprovKey(uint32_t system_id, const std::string& key_bytes); + uint32_t system_id; + std::string key_bytes; + Cipher cipher; + // If set, the make/model in the license request must match this value. + std::string model_filter; + }; + + // Set pre-provisioning keys from the given vector. This may be called + // concurrently with other methods. + static void SetPreprovKeys(const std::vector& preprov_keys); + + // Returns true if system_id is in the preprov key table. + static bool IsSystemIdKnown(uint32_t system_id); + + // Decrypt a token using the preprov key for its system ID, and + // return the decrypted device key in result. + // On failure, returns one of the errors listed above. + // cipher_out may be null; if not, *cipher_out will be set to the cipher type + // to use with the device key. + // insecure_out may be null; if not, *insecure_out will be set to the + // decrypted value of the 'insecure keybox' flag. + static util::Status DecryptDeviceKey(absl::string_view token, + std::string* device_key_out, + Cipher* cipher_out, bool* insecure_out); + // Same as above, except takes in the make/model from the license request. + // For legacy WVM license, we have some special cases where we need to inspect + // the make/model as we apply alternate keys. + static util::Status DecryptDeviceKey(absl::string_view token, + const std::string& make_model, + std::string* device_key_out, + Cipher* cipher_out, bool* insecure_out); + + // Decrypt a token using the preprov key for its system ID, and use the + // decrypted device key to encrypt the given asset key. Returns the encrypted + // asset key in result. + static util::Status GetEncryptedAssetKey(absl::string_view token, + absl::string_view raw_asset_key, + const std::string& make_model, + std::string* result); + + // Extract the system ID component of a token (bytes 4-8). + static uint32_t GetSystemId(absl::string_view token); + + // Extract the encrypted unique ID component of a token (bytes 8-24). + static std::string GetEncryptedUniqueId(absl::string_view token); + + // Try to decrypt a token using the provided preprov key, and return the + // decrypted device key in result. + // + // Note that the if the input std::string lengths are correct (16 and 72 bytes), + // the only possible cause of failure is the decrypted device key hash + // being incorrect. + static util::Status DecryptDeviceKeyWithPreprovKey( + absl::string_view preprov_key_bytes, absl::string_view token, + std::string* device_key_out); + + // Same as above, but allows extracting the 'insecure keybox' flag and keybox + // version. + static util::Status DecryptDeviceKeyWithPreprovKey( + absl::string_view preprov_key_bytes, absl::string_view token, + std::string* device_key_out, bool* insecure_out, uint32_t* version); + + // Given a decrypted device key as returned by DecryptToken(), use it to + // encrypt an asset key with the given cipher. + static util::Status EncryptAssetKey(absl::string_view device_key, + absl::string_view raw_asset_key, + Cipher cipher, std::string* result); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler); +}; + +} // namespace widevine + +#endif // COMMON_WVM_TOKEN_HANDLER_H_ diff --git a/common/wvm_token_handler_test.cc b/common/wvm_token_handler_test.cc new file mode 100644 index 0000000..6e8c36e --- /dev/null +++ b/common/wvm_token_handler_test.cc @@ -0,0 +1,236 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2016 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/wvm_token_handler.h" + +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "common/wvm_test_keys.h" + +using widevine::wvm_test_keys::kTestSystemId; +using widevine::wvm_test_keys::kTestSystemId3Des; +using widevine::wvm_test_keys::kTestPreprovKeyHex; +using widevine::wvm_test_keys::kTestDeviceKey1Hex; +using widevine::wvm_test_keys::kTestDeviceKey2Hex; +using widevine::wvm_test_keys::kTestDeviceKey3DesHex; +using widevine::wvm_test_keys::kTestToken1Hex; +using widevine::wvm_test_keys::kTestToken2Hex; +using widevine::wvm_test_keys::kTestToken3DesHex; +using widevine::wvm_test_keys::GetPreprovKeyVector; + +namespace widevine { + +using absl::BytesToHexString; +using absl::HexStringToBytes; + +// TODO(user): Add EXPECT_OK macro to testing/gmock.h. +// (b/37545268). +#define EXPECT_OK(expression) \ + EXPECT_EQ(util::error::OK, expression.error_code()) + + +TEST(WvmTokenHandlerTest, GetSystemId) { + EXPECT_EQ(kTestSystemId, + WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex))); + EXPECT_EQ(kTestSystemId, + WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken2Hex))); +} + +TEST(WvmTokenHandlerTest, GetEncryptedUniqueId) { + EXPECT_EQ( + HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"), + WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken1Hex))); + EXPECT_EQ( + HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"), + WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken2Hex))); +} + +TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) { + util::Status status; + std::string device_key; + + status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken1Hex), + &device_key); + EXPECT_OK(status) << status; + EXPECT_EQ(kTestDeviceKey1Hex, BytesToHexString(device_key)); + + status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken2Hex), + &device_key); + EXPECT_OK(status); + EXPECT_EQ(kTestDeviceKey2Hex, BytesToHexString(device_key)); + + // Test with invalid token. Hash failure should produce PERMISSION_DENIED. + device_key.clear(); + std::string token = HexStringToBytes( + "00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"); + status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + HexStringToBytes(kTestPreprovKeyHex), token, &device_key); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code()); + EXPECT_TRUE(device_key.empty()); +} + +// See b/68798704 for background of this test. +// It is important to keep this test *before* all the DecryptDeviceKey* tests +// below, in which WvmTokenHandler::SetPreprovKeys() would be called. +TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) { + // Not calling WvmTokenHandler::SetPreprovKeys() + // So preprov_keys_ would be nullptr. + util::Status status; + std::string device_key; + status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex), + &device_key, nullptr, nullptr); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(util::error::INVALID_ARGUMENT, status.error_code()); +} + +// Same tests as DecryptDeviceKeyWithPreprovKey(), but we use the handler's +// table of preprov keys instead of providing our own. +TEST(WvmTokenHandlerTest, DecryptDeviceKey) { + util::Status status; + std::string device_key; + WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector()); + + status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex), + &device_key, nullptr, nullptr); + EXPECT_OK(status); + EXPECT_EQ(HexStringToBytes(kTestDeviceKey1Hex), device_key); + + status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken2Hex), + &device_key, nullptr, nullptr); + EXPECT_OK(status); + EXPECT_EQ(HexStringToBytes(kTestDeviceKey2Hex), device_key); + + // Test with invalid token. Hash failure should produce PERMISSION_DENIED. + device_key.clear(); + std::string token = HexStringToBytes( + "00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"); + status = + WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code()); + EXPECT_TRUE(device_key.empty()); + + // Test with nonexistent system id. Should produce NOT_FOUND. + device_key.clear(); + token = HexStringToBytes( + "00000002555555559e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98" + "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" + "2517a12f4922953e"); + status = + WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(util::error::NOT_FOUND, status.error_code()); + EXPECT_TRUE(device_key.empty()); +} + +TEST(WvmTokenHandlerTest, GetEncryptedAssetKey) { + WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector()); + + std::string raw_asset_key = "asset-key-000000"; + std::string asset_key; + std::string make_model; + util::Status status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key); + EXPECT_OK(status); + EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key)); + + status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken2Hex), raw_asset_key, make_model, &asset_key); + EXPECT_OK(status); + EXPECT_EQ("091802159bf8da12aecfcdfb092075c8", BytesToHexString(asset_key)); + + // Check 3DES encryption of asset keys + status = WvmTokenHandler::EncryptAssetKey( + HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key, + WvmTokenHandler::DES3, &asset_key); + EXPECT_OK(status); + EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); + + asset_key.clear(); + status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model, + &asset_key); + EXPECT_OK(status); + EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); + + // Test with pass-thru (Cipher=PASS_THRU). + asset_key.clear(); + status = WvmTokenHandler::EncryptAssetKey( + HexStringToBytes(kTestDeviceKey1Hex), raw_asset_key, + WvmTokenHandler::PASS_THRU, &asset_key); + + EXPECT_OK(status); + EXPECT_EQ(BytesToHexString(raw_asset_key), BytesToHexString(asset_key)); +} + +TEST(WvmTokenHandlerTest, FilterOnMakeModel) { + // Use all good keys, but only the key for LG:BD572 should work. It works + // by setting only that one to DES3. All other AES ppks will encrypt the + // asset key incorrectly. + std::vector ppks; + ppks.push_back(WvmTokenHandler::PreprovKey( + kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex), + WvmTokenHandler::AES, "")); + ppks.push_back(WvmTokenHandler::PreprovKey( + kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex), + WvmTokenHandler::DES3, "LG:BD572")); + ppks.push_back(WvmTokenHandler::PreprovKey( + kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex), + WvmTokenHandler::AES, "")); + WvmTokenHandler::SetPreprovKeys(ppks); + std::string raw_asset_key = "asset-key-000000"; + std::string asset_key; + // Check 3DES encryption of asset keys + util::Status status = WvmTokenHandler::EncryptAssetKey( + HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key, + WvmTokenHandler::DES3, &asset_key); + EXPECT_OK(status); + EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); + + asset_key.clear(); + std::string make_model; + status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model, + &asset_key); + EXPECT_OK(status); + // Should fail because the asset key was encrypted with AES instead of DES3. + EXPECT_NE("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); + + // Set the make/model so we find and use the correct ppk. + make_model = "LG:BD572"; + status = WvmTokenHandler::GetEncryptedAssetKey( + HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model, + &asset_key); + EXPECT_OK(status); + // Should work because the asset key was encrypted with DES3. + EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key)); +} + +TEST(WvmTokenHandlerTest, AncientKeybox) { + util::Status status; + std::string device_key; + + std::string v1_token( + std::string(kTestPreprovKeyHex).replace(0, 16, "0000000100000001")); + status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( + HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex), + &device_key); + EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code()); + EXPECT_TRUE(device_key.empty()); +} + +} // namespace widevine diff --git a/common/x509_cert.cc b/common/x509_cert.cc new file mode 100644 index 0000000..5ba683f --- /dev/null +++ b/common/x509_cert.cc @@ -0,0 +1,361 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/x509_cert.h" + +#include + +#include +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" +#include "openssl/bio.h" +#include "openssl/evp.h" +#include "openssl/pem.h" +#include "openssl/x509.h" +#include "openssl/x509v3.h" +#include "common/openssl_util.h" +#include "common/rsa_key.h" + +namespace { +// Serializes the X509 |certificate| into a PEM-encoded string. Returns true +// on success, false otherwise. The caller retains ownership of +// |certificate| and |serialized_certificate|. |serialized_certificate| must +// not be NULL. +bool PemEncodeX509Certificate(const X509& certificate, + std::string* serialized_certificate) { + CHECK(serialized_certificate) << "serialized_certificate can not be null."; + + ScopedBIO bio(BIO_new(BIO_s_mem())); + if (bio == nullptr) { + return false; + } + + // The const_cast is necessary for the openssl call. + PEM_write_bio_X509(bio.get(), const_cast(&certificate)); + int serialized_size = BIO_pending(bio.get()); + serialized_certificate->resize(serialized_size); + if (BIO_read(bio.get(), &(*serialized_certificate)[0], serialized_size) != + serialized_size) { + return false; + } + + return true; +} + +} // anonymous namespace. + +namespace widevine { + +std::unique_ptr X509Cert::FromOpenSslCert( + ScopedX509 certificate) { + return std::unique_ptr(new X509Cert(certificate.release())); +} + +X509Cert::X509Cert() : openssl_cert_(NULL) {} + +X509Cert::~X509Cert() { + if (openssl_cert_ != NULL) { + X509_free(openssl_cert_); + } +} + +X509Cert::X509Cert(X509* openssl_cert) : openssl_cert_(openssl_cert) {} + +util::Status X509Cert::LoadPem(const std::string& pem_cert) { + if (pem_cert.empty()) { + return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM certificate"); + } + BIO* bio(NULL); + X509* new_cert(NULL); + bio = BIO_new_mem_buf(const_cast(pem_cert.data()), pem_cert.size()); + if (bio == NULL) { + return util::Status(util::error::INTERNAL, "BIO allocation failed"); + } + util::Status status; + new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); + if (new_cert == NULL) { + status = util::Status(util::Status::canonical_space(), + util::error::INVALID_ARGUMENT, + "PEM certificate load failed"); + goto cleanup; + } + if (openssl_cert_ != NULL) { + X509_free(openssl_cert_); + } + openssl_cert_ = new_cert; + +cleanup: + if (bio != NULL) { + BIO_free(bio); + } + return status; +} + +util::Status X509Cert::LoadDer(const std::string& der_cert) { + if (der_cert.empty()) { + return util::Status(util::error::INVALID_ARGUMENT, "Empty DER certificate"); + } + const unsigned char* cert_data = + reinterpret_cast(der_cert.data()); + X509* new_cert = d2i_X509(NULL, &cert_data, der_cert.size()); + if (new_cert == NULL) { + return util::Status(util::error::INVALID_ARGUMENT, + "DER certificate load failed"); + } + if (openssl_cert_ != NULL) { + X509_free(openssl_cert_); + } + openssl_cert_ = new_cert; + return util::OkStatus(); +} + +std::string X509Cert::GetPem() const { + std::string serialized_certificate; + if (!PemEncodeX509Certificate(*openssl_cert_, &serialized_certificate)) { + return ""; + } + return serialized_certificate; +} + +std::unique_ptr X509Cert::GetRsaPublicKey() const { + ScopedPKEY pkey(X509_get_pubkey(openssl_cert_)); + return std::unique_ptr( + new RsaPublicKey(EVP_PKEY_get1_RSA(pkey.get()))); +} + +const std::string& X509Cert::GetSubjectName() { + if (subject_name_.empty() && (openssl_cert_ != NULL)) { + X509_NAME* subject = X509_get_subject_name(openssl_cert_); + if (subject != NULL) { + BIO* bio = BIO_new(BIO_s_mem()); + if (bio != NULL) { + X509_NAME_print_ex(bio, subject, 0, 0); + int size = BIO_pending(bio); + std::unique_ptr buffer(new char[size]); + int bytes_read = BIO_read(bio, buffer.get(), size); + if (bytes_read == size) { + subject_name_.assign(buffer.get(), bytes_read); + } + BIO_free(bio); + } + } + } + return subject_name_; +} + +std::string X509Cert::GetSubjectNameField(const std::string& field) { + if (field.empty()) { + return std::string(); + } + const std::string& subject = GetSubjectName(); + size_t start_pos = subject.find(field + "="); + if (start_pos == std::string::npos) { + return std::string(); + } + start_pos += field.size() + 1; + size_t end_pos = subject.find(",", start_pos); + if (end_pos == std::string::npos) { + end_pos = subject.size(); + } + return subject.substr(start_pos, end_pos - start_pos); +} + +std::string X509Cert::GetSerialNumber() const { + if (openssl_cert_ == NULL) { + return std::string(); + } + BIGNUM* bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(openssl_cert_), NULL); + if (bn == NULL) { + return std::string(); + } + std::string result; + char* openssl_sn = BN_bn2hex(bn); + if (openssl_sn != NULL) { + result = absl::HexStringToBytes(openssl_sn); + OPENSSL_free(openssl_sn); + } + BN_free(bn); + return result; +} + +bool X509Cert::IsCaCertificate() const { + return X509_check_ca(openssl_cert_) != 0; +} + +bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const { + ScopedAsn1Object extension_name(OBJ_txt2obj(oid.c_str(), 1)); + int ext_pos = X509_get_ext_by_OBJ(openssl_cert_, extension_name.get(), -1); + if (ext_pos < 0) return false; + X509_EXTENSION* extension(X509_get_ext(openssl_cert_, ext_pos)); + if (!extension) return false; + ASN1_OCTET_STRING *extension_data(X509_EXTENSION_get_data(extension)); + if (!extension_data) return false; + if ((extension_data->length != 3) || (extension_data->data[0] != 1) || + (extension_data->data[1] != 1)) return false; + + *value = extension_data->data[2] != 0; + return true; +} + +X509CertChain::~X509CertChain() { Reset(); } + +void X509CertChain::Reset() { + for (auto certp : cert_chain_) { + delete certp; + } + cert_chain_.clear(); +} + +util::Status X509CertChain::LoadPem(const std::string& pem_cert_chain) { + static const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----"; + static const char kEndCertificate[] = "-----END CERTIFICATE-----"; + + Reset(); + size_t begin_pos = pem_cert_chain.find(kBeginCertificate); + while (begin_pos != std::string::npos) { + size_t end_pos = pem_cert_chain.find( + kEndCertificate, begin_pos + sizeof(kBeginCertificate) - 1); + if (end_pos != std::string::npos) { + end_pos += sizeof(kEndCertificate) - 1; + std::unique_ptr new_cert(new X509Cert); + util::Status status = new_cert->LoadPem( + pem_cert_chain.substr(begin_pos, end_pos - begin_pos)); + if (!status.ok()) { + return status; + } + cert_chain_.push_back(new_cert.release()); + begin_pos = pem_cert_chain.find(kBeginCertificate, end_pos); + } + } + return util::OkStatus(); +} + +util::Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) { + ScopedX509Stack cert_stack(sk_X509_new_null()); + CBS cbs; + CBS_init(&cbs, reinterpret_cast(pk7_cert_chain.data()), + pk7_cert_chain.size()); + if (!PKCS7_get_certificates(cert_stack.get(), &cbs)) { + return util::Status(util::error::INVALID_ARGUMENT, + "Unable to load PKCS#7 certificate chain"); + } + + while (sk_X509_num(cert_stack.get()) > 0) { + cert_chain_.push_back(new X509Cert(sk_X509_pop(cert_stack.get()))); + } + + return util::OkStatus(); +} + +X509Cert* X509CertChain::GetCert(size_t cert_index) const { + if (cert_index >= cert_chain_.size()) { + return NULL; + } + return cert_chain_[cert_index]; +} + +X509CA::X509CA(X509Cert* ca_cert) : ca_cert_(ca_cert), openssl_store_(NULL) {} + +X509CA::~X509CA() { + if (openssl_store_ != NULL) { + X509_STORE_free(openssl_store_); + } +} + +util::Status X509CA::InitializeStore() { + absl::WriterMutexLock lock(&openssl_store_mutex_); + if (openssl_store_ == NULL) { + if (ca_cert_ == NULL) { + return util::Status(util::error::INTERNAL, "CA X.509Cert is NULL"); + } + openssl_store_ = X509_STORE_new(); + if (openssl_store_ == NULL) { + return util::Status(util::error::INTERNAL, + "Failed to allocate X.509 store"); + } + if (X509_STORE_add_cert(openssl_store_, + const_cast(ca_cert_->openssl_cert())) == 0) { + X509_STORE_free(openssl_store_); + openssl_store_ = NULL; + + return util::Status(util::error::INTERNAL, + "Failed to add X.509 CA certificate to store"); + } + } + return util::OkStatus(); +} + +util::Status X509CA::VerifyCert(const X509Cert& cert) { + return OpenSslX509Verify(cert.openssl_cert(), nullptr); +} + +util::Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) { + if (cert_chain.GetNumCerts() < 1) { + return util::Status(util::error::INVALID_ARGUMENT, + "Cannot verify empty certificate chain"); + } + + ScopedX509StackOnly intermediates(sk_X509_new_null()); + if (!intermediates) { + return util::Status( + util::Status::canonical_space(), util::error::INTERNAL, + "Failed to allocate X.509 intermediate certificate stack"); + } + const X509Cert* leaf_cert(nullptr); + for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) { + if (cert_chain.GetCert(idx)->IsCaCertificate()) { + sk_X509_push(intermediates.get(), + const_cast(cert_chain.GetCert(idx)->openssl_cert())); + } else { + leaf_cert = cert_chain.GetCert(idx); + } + } + if (!leaf_cert) { + return util::Status(util::Status::canonical_space(), + util::error::INVALID_ARGUMENT, + "X.509 certificate chain without leaf certificate."); + } + return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get()); +} + +util::Status X509CA::OpenSslX509Verify(const X509* cert, + STACK_OF(X509) * intermediates) { + DCHECK(cert); + + absl::ReaderMutexLock lock(&openssl_store_mutex_); + if (openssl_store_ == NULL) { + openssl_store_mutex_.ReaderUnlock(); + util::Status status = InitializeStore(); + if (!status.ok()) { + return status; + } + openssl_store_mutex_.ReaderLock(); + } + ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new()); + if (!store_ctx) { + return util::Status(util::Status::canonical_space(), util::error::INTERNAL, + "Failed to allocate X.509 store context"); + } + if (X509_STORE_CTX_init(store_ctx.get(), openssl_store_, + const_cast(cert), intermediates) == 0) { + return util::Status(util::Status::canonical_space(), util::error::INTERNAL, + "Failed to initialize X.509 store context"); + } + int x509_status = X509_verify_cert(store_ctx.get()); + if (x509_status != 1) { + return util::Status(util::Status::canonical_space(), util::error::INTERNAL, + std::string("X.509 certificate chain validation failed: ") + + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get()))); + } + + return util::OkStatus(); +} + +} // namespace widevine diff --git a/common/x509_cert.h b/common/x509_cert.h new file mode 100644 index 0000000..deb093c --- /dev/null +++ b/common/x509_cert.h @@ -0,0 +1,155 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Description: +// X.509 certificate classes used by the license server SDK. + +#ifndef COMMON_X509_CERT_H__ +#define COMMON_X509_CERT_H__ + +#include +#include +#include +#include +#include + +#include "base/macros.h" +#include "base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "openssl/pem.h" +#include "openssl/x509.h" +#include "openssl/x509v3.h" +#include "util/status.h" +#include "common/openssl_util.h" + +namespace widevine { + +class RsaPublicKey; + +// NOTE: All util::Status codes are in the canonical error space. + +// Class which holds a single X.509 certificates. +class X509Cert { + public: + // Load the certificate from an openssl X509 certificate instance. + static std::unique_ptr FromOpenSslCert(ScopedX509 openssl_cert_); + + X509Cert(); + virtual ~X509Cert(); + + // Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is + // a PEM-encoded certificate. + util::Status LoadPem(const std::string& pem_cert); + + // Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is + // a DER-encoded certificate. + util::Status LoadDer(const std::string& der_cert); + + // Return a std::string containing the PEM-encoded certificate. + std::string GetPem() const; + + // Returns certificate RSA public key. nullptr if not found, or if key type + // is not RSA. + std::unique_ptr GetRsaPublicKey() const; + + // Returns the internal OpenSSL X509 certificate. + const X509* openssl_cert() const { return openssl_cert_; } + + // Returns the certificate subject name. + const std::string& GetSubjectName(); + + // Returns a field within the certificate subject name, or an empty std::string + // if the field is not found. + std::string GetSubjectNameField(const std::string& field); + + // Returns the certificate serial number, binary encoded, or an empty std::string + // if an error occurs. + std::string GetSerialNumber() const; + + // Returns true if the certificate is a CA (root or intermediate) certificate. + bool IsCaCertificate() const; + + // Gets the value of an X.509 V3 extension encoded as a boolean. |oid| is the + // object identifier sequence for the extension, and |value| is a pointer to + // an integer which will contain the extension value upon successful return. + // Returns true if successful, or false otherwise. + bool GetV3BooleanExtension(const std::string& oid, bool* value) const; + + private: + explicit X509Cert(X509* openssl_cert); + + X509* openssl_cert_; + std::string subject_name_; + + friend class X509CertChain; + + DISALLOW_COPY_AND_ASSIGN(X509Cert); +}; + +// Class which holds a chain of X.509 certificates. +class X509CertChain { + public: + X509CertChain() {} + virtual ~X509CertChain(); + + // Loads a chain of PEM-encoded X.509 certificates. Takes a single parameter, + // |pem_cert_chain|, which is the concatenation of a number of PEM X.509 + // certificates, beginning with the leaf certificate, and ending with the + // certificate signed by the root CA. + util::Status LoadPem(const std::string& pem_cert_chain); + + // Loads a chain of DER-encoded PKCS#7 certificates. Takes a single parameter, + // |pk7_cert_chain|, which is a DER-encoded PKCS#7 X.509 certificate + // container. + util::Status LoadPkcs7(const std::string& pk7_cert_chain); + + // Returns the number of certificates in the chain. + size_t GetNumCerts() const { return cert_chain_.size(); } + + // Returns the X509Cert at the specified chain index (0 start). Returns + // NULL if cert_index is out of range. The X509CertChain retains ownwership + // of the object returned. + X509Cert* GetCert(size_t cert_index) const; + + private: + void Reset(); + + std::vector cert_chain_; + + DISALLOW_COPY_AND_ASSIGN(X509CertChain); +}; + +// CA class which holds the root CA cert, and verifies certificate chains. +class X509CA { + public: + // New object assumes ownership of |ca_cert|. + explicit X509CA(X509Cert* ca_cert); + virtual ~X509CA(); + + // Does X.509 PKI validation of |cert| against the root CA certificate + // used when constructing X509CA. This method is thread-safe. + util::Status VerifyCert(const X509Cert& cert); + + // Does X.509 PKI validation of |cert_chain| against the root CA certificate + // used when constructing X509CA. This method is thread-safe. + util::Status VerifyCertChain(const X509CertChain& cert_chain); + + private: + util::Status InitializeStore(); + util::Status OpenSslX509Verify(const X509* cert, STACK_OF(X509)* stack); + + std::unique_ptr ca_cert_; + absl::Mutex openssl_store_mutex_; + X509_STORE* openssl_store_ GUARDED_BY(openssl_store_mutex_); + + DISALLOW_IMPLICIT_CONSTRUCTORS(X509CA); +}; + +} // namespace widevine + +#endif // COMMON_X509_CERT_H__ diff --git a/common/x509_cert_test.cc b/common/x509_cert_test.cc new file mode 100644 index 0000000..8a67001 --- /dev/null +++ b/common/x509_cert_test.cc @@ -0,0 +1,404 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "common/x509_cert.h" + +#include + +#include "testing/gunit.h" +#include "absl/strings/escaping.h" +#include "common/rsa_key.h" +#include "common/test_utils.h" + +namespace widevine { +const char kTestRootCaDerCert[] = + "30820403308202eba003020102020900a24f94af7ae6831f300d06092a86" + "4886f70d0101050500308197310b30090603550406130255533113301106" + "035504080c0a57617368696e67746f6e3111300f06035504070c084b6972" + "6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f" + "060355040b0c085769646576696e653115301306035504030c0c54657374" + "20526f6f742043413121301f06092a864886f70d010901161274696e736b" + "697040676f6f676c652e636f6d301e170d3133303831363030353731305a" + "170d3333303831353030353731305a308197310b30090603550406130255" + "533113301106035504080c0a57617368696e67746f6e3111300f06035504" + "070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049" + "6e633111300f060355040b0c085769646576696e65311530130603550403" + "0c0c5465737420526f6f742043413121301f06092a864886f70d01090116" + "1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648" + "86f70d01010105000382010f003082010a0282010100c6eee629d99f7736" + "2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c" + "e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319" + "0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3" + "f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93" + "52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7" + "e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f" + "d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20" + "79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7" + "3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30" + "ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680" + "144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405" + "30030101ff300d06092a864886f70d01010505000382010100779e9b98d3" + "ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee" + "79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124" + "ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436" + "026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140" + "ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053" + "0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258" + "e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82" + "d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4" + "c270d87191c13aefdd177b"; + +const char kTestPemCert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n" + "VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n" + "Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n" + "ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n" + "MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n" + "BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n" + "aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n" + "Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n" + "aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n" + "DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n" + "l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n" + "krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n" + "wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n" + "fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n" + "FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n" + "qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n" + "EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n" + "f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n" + "QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n" + "7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n" + "3rDeUI+Odg==\n" + "-----END CERTIFICATE-----\n"; +const char kTestPemCertSubjectField_C[] = "US"; +const char kTestPemCertSubjectField_CN[] = + "stable id/emailAddress=tinskip@google.com"; +const char kTestPemCertSerialNumber[] = "\002"; + +const char kTestPemCertChain[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n" + "VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n" + "Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n" + "ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n" + "MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n" + "BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n" + "aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n" + "Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n" + "aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n" + "DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n" + "l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n" + "krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n" + "wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n" + "fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n" + "FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n" + "qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n" + "EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n" + "f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n" + "QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n" + "7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n" + "3rDeUI+Odg==\n" + "-----END CERTIFICATE-----\n" + "-----BEGIN CERTIFICATE-----\n" + "MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n" + "EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n" + "DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n" + "b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n" + "ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n" + "BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n" + "bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n" + "ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n" + "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n" + "f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n" + "oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n" + "UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n" + "gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n" + "9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n" + "AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n" + "FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n" + "BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n" + "xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n" + "HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n" + "ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n" + "qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n" + "6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n" + "-----END CERTIFICATE-----\n"; + +const char kTestPk7CertChain[] = + "308207fb06092a864886f70d010702a08207ec308207e80201013100300b" + "06092a864886f70d010701a08207ce308203c3308202ab020102300d0609" + "2a864886f70d010105050030819f310b3009060355040613025553311330" + "1106035504080c0a57617368696e67746f6e3111300f06035504070c084b" + "69726b6c616e6431133011060355040a0c0a476f6f676c6520496e633111" + "300f060355040b0c085769646576696e65311d301b06035504030c145465" + "737420496e7465726d6564696174652043413121301f06092a864886f70d" + "010901161274696e736b697040676f6f676c652e636f6d301e170d313330" + "3831363231343430305a170d3333303831353231343430305a3081ae310b" + "30090603550406130255533113301106035504080c0a57617368696e6774" + "6f6e3111300f06035504070c084b69726b6c616e6431293027060355040a" + "0c204368726f6d652044657669636520436f6e74656e742050726f746563" + "74696f6e31153013060355040b0c0c74657374696e672e74657374311230" + "1006035504030c09737461626c652069643121301f06092a864886f70d01" + "0901161274696e736b697040676f6f676c652e636f6d30820122300d0609" + "2a864886f70d01010105000382010f003082010a0282010100a95b0eab2d" + "38ad1394bb4964e1b3cac8c3e3c657127a70b62d1c4002ba25c37d86dc29" + "1de98197301b22e3888de636b5fbe4976b8838e9cd3200222acfc3a6ef55" + "6de7570150a7bb113bff2ad6230feb835666a9dc505c5a93a3ee7612bb5e" + "32bbb892b39a36043add213ddf8c91d0c485cee49bbd381314b3fb847b9e" + "69e83cf8252f4115345a80b66a4317cd8d46e80915c3263f96e7c0fd7110" + "5e03db86f3faed8c51f9ec7009fcd01a8941e6192588abb1d2b9b30df708" + "88730cd080da13a9ee7db9bc04a3f19594bf42bcd3640afd43918cca372e" + "d174c8d5f927195ac4e2964c87c81efa89dd720210c5704e85f24a142a1f" + "6123fbac1c6d6ac2bee2770203010001300d06092a864886f70d01010505" + "0003820101004dc35f68ba5f2db5faab3d6a28c2d868d7b8388d17f2df19" + "357a8476ac8d7780bb91275a424c0dba7540ab0d4269d5332d18bc39fd7c" + "73095011704de1b3d378e0c2a1b3e34bbd587183e10ecbc641c4371900ba" + "04ec872824d8a6a81c2083c4185cc8f85ac009e75e7f8b7221ec6aeb7888" + "c75229993067a589e77e073cbf86a7a6134e0c728c4411f2f0125170aa53" + "145168e7bd5ce4e8c442384a10ea7d783ef61ae120b4ad1fee35d81f66d1" + "4f896648bc60d8bd636f0837a888e85f68237882f2cd417e153cceec6e49" + "5909f20e377f649ba577b151b099aecc080849ea957ab6985d4d69e2eb5b" + "aaeb404e698705c62191724a04a91fdeb0de508f8e7630820403308202eb" + "a003020102020101300d06092a864886f70d0101050500308197310b3009" + "0603550406130255533113301106035504080c0a57617368696e67746f6e" + "3111300f06035504070c084b69726b6c616e6431133011060355040a0c0a" + "476f6f676c6520496e633111300f060355040b0c085769646576696e6531" + "15301306035504030c0c5465737420526f6f742043413121301f06092a86" + "4886f70d010901161274696e736b697040676f6f676c652e636f6d301e17" + "0d3133303831363231343134365a170d3333303831353231343134365a30" + "819f310b30090603550406130255533113301106035504080c0a57617368" + "696e67746f6e3111300f06035504070c084b69726b6c616e643113301106" + "0355040a0c0a476f6f676c6520496e633111300f060355040b0c08576964" + "6576696e65311d301b06035504030c145465737420496e7465726d656469" + "6174652043413121301f06092a864886f70d010901161274696e736b6970" + "40676f6f676c652e636f6d30820122300d06092a864886f70d0101010500" + "0382010f003082010a0282010100da28062eb1dc8f489dcb3acad94f8c1b" + "bbc84cb97fa04b7f9351104e67426ef846decf000ed8543b711cffa98b5b" + "b2c6c2f77d4af1f74e2ce2d7573512bce7fdb39fbfa8a193d86fc7c7e9d8" + "25ee872661ec82d234a683bfbe662692e8eb233d25131366846c07ba43e6" + "6162c01b7642fb67674d50947d6a484b12a96a7d7e2ae5b580d0cdcfff6f" + "dfa8d60c4b4c34d74370a5df8ac9d6dc793d2b1d04e64c656ca8208b8266" + "8ff5af1a4113123f4767e4839c73f2b54279dfc358319e3185e1a04feca6" + "c2331341adb8013b0893c6010c21c299f481accc07297ed297e6b6b75de3" + "f315609c9f08a5a6969a13fd9759791c01e85cf1ddf1b64740619c952015" + "0203010001a350304e301d0603551d0e0416041439b7e60cccea5975dc78" + "5951b5b9b83c3b062793301f0603551d230418301680144d30ff181ac4f1" + "0da99e6a12c01e02accadf840a300c0603551d13040530030101ff300d06" + "092a864886f70d01010505000382010100b8fefd9f3281f595367715d0df" + "54b0073094651ee4c13e5685d417fd123a676b73c2ed3dcd8391cf1bc624" + "0dd701f113f385b1bb0e7686cfcf038187aecac98c955adc73cec2ced0c1" + "c6c6c6ce9b06b501eaeca0fe31f31a021da054f7472f898ab3e64eab6720" + "4894522b33996a1b5241dd4d244dc786ff5b2ac11000bbde0162a316d625" + "b2afd32a0124425413ab92c59d6b8a599022e9eb6be4ec8b4e72cd8fac77" + "fa5166c03530c7fbc43ea48c36c2e27995ffe29aa28eaab602266022e950" + "5ada3c87a9754f324ea52f5466764e1480eb11bec6e62b36f6bd87962308" + "6c5d8e52a68dd65532efea42241990853ffc2cedcb64f9f4328e3b6d9dc2" + "1d2f69a1003100"; + +const char kTestCertPrivateKey[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEowIBAAKCAQEAqVsOqy04rROUu0lk4bPKyMPjxlcSenC2LRxAArolw32G3Ckd\n" + "6YGXMBsi44iN5ja1++SXa4g46c0yACIqz8Om71Vt51cBUKe7ETv/KtYjD+uDVmap\n" + "3FBcWpOj7nYSu14yu7iSs5o2BDrdIT3fjJHQxIXO5Ju9OBMUs/uEe55p6Dz4JS9B\n" + "FTRagLZqQxfNjUboCRXDJj+W58D9cRBeA9uG8/rtjFH57HAJ/NAaiUHmGSWIq7HS\n" + "ubMN9wiIcwzQgNoTqe59ubwEo/GVlL9CvNNkCv1DkYzKNy7RdMjV+ScZWsTilkyH\n" + "yB76id1yAhDFcE6F8koUKh9hI/usHG1qwr7idwIDAQABAoIBADdwlZa30QvnkxLU\n" + "be/s+X9LkS8GpgfrCdgunU3HPkGGwDUmSKJ+R835tCwkMb+hPWXeaStMhsUS5UFh\n" + "7f3hoK5MmxPWSZnrrrNvnpKZUxUNFgucxBJZREJqfom7oVow9g6511xwKSqtUmJl\n" + "bN8JhPwwiZAQ45qNtINO3QnSy/y4IGrUPgjMpmJa26a+JhduTRq+LMPu2wz+HxS1\n" + "Vf2q0H1IOJr/kimMFMaBRYErNclFa8VIFjwjz5reH5lJyptajGhruor6EK1qqhNc\n" + "zPSRY4TZH5QcjM46zui6l3tL9e32j6oUd4mAp4HhH0fws/pwawFYECI+M+7OCjgK\n" + "y+qSJ1ECgYEA1g+L0yN4i+uScs7EpsYJfaRP1PMtGnUsof64Pg6i9IKcuf5mi5Kp\n" + "aIgZdXAZIzsACH5XbfuC5Srs4565k/9XrHehLcuBzodulrzwmOUDbJAxIDw4uTUX\n" + "95W0uK9UqyGLyM8wNYs/EzhveSFL8fnFWzOAL/+HshQpKCBzedSU+G0CgYEAyolH\n" + "xws2mim7rSrYyRz1Vj02rLZuBUR7cPaHDxjjuuSUbI2nsDRsm6ZUCNlJtReHBkpH\n" + "eW5iClBGkksVsJJYJBmyDw6a3mnj0mfxBnh9zGaHQi0RCuOwmYlu2L/XVQXiMFKT\n" + "gffazuvysg7N/bz7CJjm8PRRx/cAxxFfAozdf/MCgYEAtBagLCHLaOvnaW9LQoOZ\n" + "uHpkL2PmrjumMSN7HbpyngLEmDXPT90zaR4XTRXiECGzBXJFW+IdXW+fnGANANXx\n" + "jMeYck6kBn0qLOcIA5moJ82nhtcjYa2pXEI2qKnZMaAnWen1RRbBGgqAvgelPQ5F\n" + "W1UYo0j3gHo1peynOff+3IECgYAsP53M4KhHOgLkrE28cnUvKCR/y0NyJyoI3fNX\n" + "2wo11KaQqMoP9wQbZVVKsZ4m0EMRnrzKzNDii/M/FuRgNTjIekyqeXhgSyYY29iO\n" + "n1hshaHbVVk51dDJWns7I3559tUZ1ZCgfnPxbR8Sw6VBYD4//JfH4LjVRSOIWkU1\n" + "m2zw/QKBgGE55o0xrCywF3wDUtFa6vgpsOfZu9IblsWktSbD/lk1YOqGpU//B0O4\n" + "GqihOQT7E9kDNusspFUGpZrE0T0B+GW1T9iTR0zd+lC+qExv2ggDJoH063DnH5OU\n" + "Qz2M8LESeFxf6ZlBxkcyrk6G1RAy7lUs9fHhfmpEJLVv4DTCuWDl\n" + "-----END RSA PRIVATE KEY-----\n"; + +const char kTestMessage[] = + "c8635a17ccc672c941d0cc287715411a0a0222613a04d47693a53eb7f32c" + "1ebae1f5d916a815b880426362c42f5f18f694a380756e0452018c70b3e4" + "f72ebb5269cb7233a3b8a2a1840e33ca9d473224d17ff91bae6b8d4ff2d1" + "8e5c89b5fc8a52c4f791c2063ab1a29ffd3372db483e4975c1c9c7408bf6" + "dfe5696e256e86b75313c501ab781175971b9411a73c444592afb1ec1667" + "2bfb935715ef5302f3bef712d2296be4f64ef2dc861f0611b06c35d0a5c2" + "5ff9f4a2563f265f109d2fa8f8165d7891b8a83c84520eaa284d49a4f76e" + "ac158204a5bdf018edd9401ae6593092ba97970be9a58b10720a235c9158" + "b9f235f9dda3de05990cef8c2fd04920a2a434bd5b6aa75767762d89b964" + "90e42524855a7eab49a8f82ac593e4df01990206d3fa98329aa50e31db89" + "b46b82ee0073851826f77aabb3779738a6f311b79f54d036a98dca4881ef" + "88c3cbfc86ac358c7bd107dc234d3772fc707df01637354dcb9270c7aefa" + "852dd21818ede33ab7154c32f25268b82f89b344e6469b81b6699df68c56" + "a6e61f1dd8f140f3be4edce755ceee8ee7868f45a17f8b4b4b0988f45815" + "1b43d07dcb0cd80b1ffa37b824e0abc25897cb41c242a3db845bedd37adf" + "88a13c0b2f0b158464b02f9fd97ad6e87b92c13cbeee5e69d183cc898c4e" + "0cfa9c59abde74a437d030cb966137ffe9abe6be71ed21ef751cdea73625" + "7cff9e378718f7d7e9c4d567cbec8e0afdfab0585b8ed0d5f8de159b6524" + "22c90737b44c84603ba1131f557604fe4e6b4d91e45363903b8db179cee0" + "a50f2ae73394973c8671df7a7b2eeb8341a3417727cfe43290a67ac3ad02" + "a52c3d1698c2c28a46268518aea66cecb40f43f50bb9cea4ed1d49ceb51d" + "9967fabccccc7237a36b6cecda5916234730d7b3ca3295519d77b7516824" + "10e8a238b6345e8d28132f60423a13fdf4b6a6cf272cef9a0833abb4b86d" + "9828af45442a390e241b2b8c3290671da4a163d7e55fea7828098c0749ca" + "ff65145dd6b4a6e4c65d214801bb8302d8914864e99c4d0b390b8126d4bc" + "0353e376e69aba56cf71b9943a47dcffa07c6a24986a077f69b7bec6bd9c" + "357e211875453bdadd9bfc4526f96c458e0052d27a903611c09a9c7b5f51" + "83daad078aec0e79ef991d102d4af492773f1509a265c5644cbab3253e34" + "3015e4305fffd17ce0261bcb232cfa0e1dcc71f83dc1aac490e526f6269f" + "606d0e0e556bb30b774c2208ed3771474be23f39b7fc21dcbf304a923d9c"; + +const char kTestDevCodeSigningCert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIFDjCCA3agAwIBAgIPESIzRFVmd4iZqrvM3e7/MA0GCSqGSIb3DQEBCwUAMIGc\n" + "MQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2ly\n" + "a2xhbmQxDzANBgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNV\n" + "BAMMFXdpZGV2aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tp\n" + "cEBnb29nbGUuY29tMB4XDTE3MTAwOTIwMjUwNloXDTI3MTAwNzIwMjUwNlowRTEL\n" + "MAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy\n" + "bmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAObsg/w+dJedH3x5KEsXdA/5sunWc8G+iZl0wMcngh2DiwmOSkKf68uCK/iW\n" + "T0a2XGgk13zl1HuKrjatgc7n6E1j/sqDZBGkr0q1wQgsdzm3qvGZoDG/+Z2U23WU\n" + "kX6ZcyIYUbpO2VtQELEl6DgNwoUi/9Yp+vCb6lsItpSZ1WRD9NhbWh1MxZxj1s18\n" + "OYcEzpEYg4/vHTVhocUR/1Rp9M9yn0nH1MUdtjhgBM3BmlRH7TA/nF111A4+GzMN\n" + "qyqfb0/6yXE64Ca3+fGg1hstfUUXkpmjjNPhYJ6QTgA3Xfrz04a4uwB+pSliF3SD\n" + "gip7O3rDyK0ES55lGpZ7B3s3TakCAwEAAaOCASEwggEdMB0GA1UdDgQWBBQ2jJme\n" + "0BuaGrhgFGJR2i59HR+DizCBuwYDVR0jBIGzMIGwoYGipIGfMIGcMQswCQYDVQQG\n" + "EwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxDzAN\n" + "BgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNVBAMMFXdpZGV2\n" + "aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tpcEBnb29nbGUu\n" + "Y29tggkAxfgvA4+s8VgwCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAww\n" + "CgYIKwYBBQUHAwMwEQYKKwYBBAHWeQQBAgQDAQH/MA0GCSqGSIb3DQEBCwUAA4IB\n" + "gQAtan04ZGie7rRsKpb1F6t7xs48KE6cj6L99B5dgl37fZaZIQ3XE2vbmmmY5YTx\n" + "wofCkvOZMXHeQfJEK5GIK49TW/lAR+3kJUJzSh+N67f0X8O1pUl97IUFsbi6PTw/\n" + "mjhu197Kdy/OxPu/csOkEChuOfJLagRxXtIXeIyaeVOmn6fkFTOMOL2BusWOPuIs\n" + "9OmOQ+UHXpMuX4c2x9iO4NzZwwI/MgULLCrd/c73q199H+ttdPFoNs8+xGdodqA/\n" + "NFlHtMHMLMKVGpazAf+JW1/c3nb8L3S0nw4q7vPWi216RdZTfKfSIs/f/IW3CYJh\n" + "/IAuHOYvlD0GdSOFZHfhrnAvKhJ2iRu32psN87L9rL5EL22LT8csV/gLMc3SZ35n\n" + "/viuYcTDnMbe9S/Mge3mMJ9XHD5XBhN3hzmGDQEUdRS5MXrYdY32viPE7f+GAO9s\n" + "5MXS+h+FxQ6QUar2q1zHc/0Gr1hLzA6HYBmI0/AF8LsHs799XjrMKHkSBN6UQkC1\n" + "hRk=\n" + "-----END CERTIFICATE-----\n"; +const char kDevCertFlagOid[] = "1.3.6.1.4.1.11129.4.1.2"; +const bool kTestDevCodeSigningCertFlagValue = true; + + +TEST(X509CertTest, LoadCert) { + X509Cert test_cert; + EXPECT_EQ(util::OkStatus(), + test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); + EXPECT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); + // TODO(user): Add more specific status checks to failure tests. + EXPECT_NE(util::OkStatus(), test_cert.LoadDer("bad cert")); + EXPECT_NE(util::OkStatus(), test_cert.LoadPem("bad cert")); + EXPECT_NE(util::OkStatus(), test_cert.LoadDer("")); + EXPECT_NE(util::OkStatus(), test_cert.LoadPem("")); +} + +TEST(X509CertTest, VerifySignature) { + X509Cert test_cert; + ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); + std::string message(absl::HexStringToBytes(kTestMessage)); + std::string signature; + ASSERT_EQ(util::OkStatus(), GenerateRsaSignatureSha256Pkcs1( + kTestCertPrivateKey, message, &signature)); + std::unique_ptr pub_key(test_cert.GetRsaPublicKey()); + ASSERT_TRUE(pub_key); + EXPECT_TRUE(pub_key->VerifySignatureSha256Pkcs7(message, signature)); + + EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, "bad signature")); + EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("bad digest", signature)); + EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, "")); + EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("", signature)); +} + +TEST(X509CertTest, GetSubjectNameField) { + X509Cert test_cert; + ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); + EXPECT_EQ(kTestPemCertSubjectField_C, test_cert.GetSubjectNameField("C")); + EXPECT_EQ(kTestPemCertSubjectField_CN, test_cert.GetSubjectNameField("CN")); + EXPECT_EQ("", test_cert.GetSubjectNameField("invalid_field")); +} + +TEST(X509CertTest, GetSerialNumber) { + X509Cert test_cert; + ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); + EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber()); +} + +TEST(X509CertTest, CertChain) { + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + ASSERT_EQ(2, test_chain.GetNumCerts()); + EXPECT_FALSE(test_chain.GetCert(0) == NULL); + EXPECT_FALSE(test_chain.GetCert(1) == NULL); + EXPECT_TRUE(test_chain.GetCert(2) == NULL); +} + +TEST(X509CertTest, IsCaCertificate) { + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + ASSERT_EQ(2, test_chain.GetNumCerts()); + EXPECT_FALSE(test_chain.GetCert(0)->IsCaCertificate()); + EXPECT_TRUE(test_chain.GetCert(1)->IsCaCertificate()); +} + +TEST(X509CertTest, ChainVerificationPem) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(util::OkStatus(), + ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); + X509CA ca(ca_cert.release()); + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); +} + +TEST(X509CertTest, ChainVerificationPkcs7) { + std::unique_ptr ca_cert(new X509Cert); + ASSERT_EQ(util::OkStatus(), + ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); + X509CA ca(ca_cert.release()); + X509CertChain test_chain; + ASSERT_EQ(util::OkStatus(), + test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain))); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert)); + ASSERT_EQ(1, test_chain.GetNumCerts()); + EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain)); + ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); + EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); +} + +TEST(X509CertTest, BooleanExtension) { + std::unique_ptr cert1(new X509Cert); + ASSERT_EQ(util::OkStatus(), cert1->LoadPem(kTestPemCert)); + bool extension_value; + EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value)); + + std::unique_ptr cert2(new X509Cert); + ASSERT_EQ(util::OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert)); + ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value)); + EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value); +} + +} // namespace widevine diff --git a/glog.BUILD b/glog.BUILD new file mode 100644 index 0000000..1ddd6f7 --- /dev/null +++ b/glog.BUILD @@ -0,0 +1,183 @@ +################################################################################ +# Copyright 2016 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +# Bazel build file for glog. + +licenses(["notice"]) + +# Common options required by all library targets. +glog_copts = [ + "-Isrc", + "-D_START_GOOGLE_NAMESPACE_='namespace google {'", + "-D_END_GOOGLE_NAMESPACE_=}", + "-DGOOGLE_NAMESPACE=google", + "-DGOOGLE_GLOG_DLL_DECL=", + "-DHAVE_LIB_GFLAGS", + "-DHAVE_PTHREAD", + "-DHAVE_RWLOCK", + "-DHAVE_PREAD", + "-DHAVE_PWRITE", + "-DHAVE_SYS_TIME_H", + "-DHAVE_SYS_UTSNAME_H", + "-DHAVE_UNISTD_H", + "-DHAVE_GLOB_H", + "-DHAVE___ATTRIBUTE__", + "-D__NR_gettid", + "-Wno-sign-compare", +] + +cc_library( + name = "glog", + srcs = [ + "src/base/commandlineflags.h", + "src/base/googleinit.h", + "src/base/mutex.h", + "src/demangle.cc", + "src/demangle.h", + "src/glog/log_severity.h", + "src/logging.cc", + "src/raw_logging.cc", + "src/signalhandler.cc", + "src/stacktrace.h", + "src/stacktrace_generic-inl.h", + "src/stacktrace_libunwind-inl.h", + "src/stacktrace_powerpc-inl.h", + "src/stacktrace_x86-inl.h", + "src/stacktrace_x86_64-inl.h", + "src/symbolize.cc", + "src/symbolize.h", + "src/utilities.cc", + "src/utilities.h", + "src/vlog_is_on.cc", + ":config_h", + ":logging_h", + ":raw_logging_h", + ":stl_logging_h", + ":vlog_is_on_h", + ], + hdrs = glob(["glog/*.h"]), + copts = glog_copts, + includes = ["src/"], + linkopts = ["-pthread"], + visibility = ["//visibility:public"], + deps = ["//external:gflags"], +) + +genrule( + name = "config_h", + srcs = ["src/config.h.cmake.in"], + outs = ["config.h"], + cmd = "awk '{ gsub(/^#cmakedefine/, \"//cmakedefine\"); print; }' $(<) > $(@)", +) + +sub_cmd = ("awk '{ " + + "gsub(/@ac_google_start_namespace@/, \"namespace google {\"); " + + "gsub(/@ac_google_end_namespace@/, \"} // namespace google\"); " + + "gsub(/@ac_google_namespace@/, \"google\"); " + + ("gsub(/@(ac_cv_have_stdint_h|ac_cv_have_uint16_t|" + + "ac_cv_have_libgflags|ac_cv_have_unistd_h|" + + "ac_cv_have___builtin_expect|" + + "ac_cv_cxx_using_operator)@/, \"1\"); ") + + "gsub(/@ac_cv___attribute___noreturn@/, \"__attribute__ ((noreturn))\"); " + + "gsub(/@ac_cv___attribute___noinline@/, \"__attribute__ ((noinline))\"); " + + "gsub(/@(ac_cv___attribute___[a-z0-9_]+)@/, \"\"); " + + "gsub(/@([a-z0-9_]+)@/, \"0\"); " + + "print; }' $(<) > $(@)") + +genrule( + name = "logging_h", + srcs = ["src/glog/logging.h.in"], + outs = ["glog/logging.h"], + cmd = sub_cmd, +) + +genrule( + name = "raw_logging_h", + srcs = ["src/glog/raw_logging.h.in"], + outs = ["glog/raw_logging.h"], + cmd = sub_cmd, +) + +genrule( + name = "stl_logging_h", + srcs = ["src/glog/stl_logging.h.in"], + outs = ["glog/stl_logging.h"], + cmd = sub_cmd, +) + +genrule( + name = "vlog_is_on_h", + srcs = ["src/glog/vlog_is_on.h.in"], + outs = ["glog/vlog_is_on.h"], + cmd = sub_cmd, +) + +cc_library( + name = "test_hdrs", + hdrs = [ + "src/config_for_unittests.h", + "src/googletest.h", + ], +) + +cc_test( + name = "demangle_unittest", + size = "small", + srcs = ["src/demangle_unittest.cc"], + copts = glog_copts, + data = ["src/demangle_unittest.txt"], + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "logging_unittest", + size = "small", + srcs = ["src/logging_unittest.cc"], + copts = glog_copts, + data = ["src/logging_unittest.err"], + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "stacktrace_unittest", + size = "small", + srcs = ["src/stacktrace_unittest.cc"], + copts = glog_copts, + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "symbolize_unittest", + size = "small", + srcs = ["src/symbolize_unittest.cc"], + copts = glog_copts, + deps = [ + ":glog", + ":test_hdrs", + ], +) + +cc_test( + name = "utilities_unittest", + size = "small", + srcs = ["src/utilities_unittest.cc"], + copts = glog_copts, + deps = [ + ":glog", + ":test_hdrs", + ], +) diff --git a/media_cas_packager_sdk/internal/BUILD b/media_cas_packager_sdk/internal/BUILD new file mode 100644 index 0000000..218876e --- /dev/null +++ b/media_cas_packager_sdk/internal/BUILD @@ -0,0 +1,50 @@ +################################################################################ +# Copyright 2018 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +# Build file for ECM SDK internal library. + +# Only accessible by public media_cas_packager_sdk apis. +package_group( + name = "internal", + packages = [ + "//media_cas_packager_sdk/...", + ], +) + +package( + default_visibility = [":internal"], +) + +cc_library( + name = "ecm", + srcs = ["ecm.cc"], + hdrs = ["ecm.h"], + deps = [ + "//base", + "@abseil_repo//absl/base:core_headers", + "@abseil_repo//absl/strings", + "//util:status", + "//common:aes_cbc_util", + "//common:random_util", + "//common:string_util", + "//protos/public:media_cas_encryption_proto", + "//protos/public:media_cas_proto", + ], +) + +cc_test( + name = "ecm_test", + size = "small", + srcs = ["ecm_test.cc"], + deps = [ + ":ecm", + "//testing:gunit_main", + "//util:status", + "//protos/public:media_cas_encryption_proto", + ], +) diff --git a/media_cas_packager_sdk/internal/ecm.cc b/media_cas_packager_sdk/internal/ecm.cc new file mode 100644 index 0000000..a675d87 --- /dev/null +++ b/media_cas_packager_sdk/internal/ecm.cc @@ -0,0 +1,543 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "media_cas_packager_sdk/internal/ecm.h" + +#include +#include +#include + +#include "glog/logging.h" +#include "absl/strings/str_cat.h" +#include "util/status.h" +#include "common/aes_cbc_util.h" +#include "common/random_util.h" +#include "common/string_util.h" +#include "protos/public/media_cas.pb.h" +#include "protos/public/media_cas_encryption.pb.h" + +namespace widevine { +namespace cas { + +namespace { + +// ECM constants +static constexpr size_t kMaxEcmSizeBytes = 184; + +// Bitfield lengths for the mp2ts ecm payload. +// This ECM structure is specific to the Widevine CAS implementation. + +// Byte 0, 1 +static constexpr int kNumBitsCaSystemIdField = 16; + +// Byte 2 +static constexpr int kNumBitsEcmVersionField = 8; + +// Byte 3 +static constexpr int kNumBitsEcmGenerationCountField = 5; +// Values for Decrypt Mode are from enum CasCryptoMode +static constexpr int kNumBitsDecryptModeField = 2; +static constexpr int kNumBitsRotationEnabledField = 1; + +static constexpr int kMaxGeneration = + (1 << kNumBitsEcmGenerationCountField) - 1; + +// Byte 4 +// Size of IVs. +// Values for IV size fields are from enum EcmIvSize +// Note: Wrapped key IV size is always 16. The field is encoded, but it must +// always be set to 1. +static constexpr int kNumBitsWrappedKeyIvSizeField = 1; +static constexpr int kNumBitsContentIvSizeField = 1; +// Unused bits (mbz, must be zero) +static constexpr int kNumBitsUnusedField = 6; + +// Remaining bytes (starting from the 6th byte) hold entitled key info. +static constexpr size_t kKeyIdSizeBytes = 16; +static constexpr size_t kKeyDataSizeBytes = 16; +static constexpr size_t kWrappedKeyIvSizeBytes = 16; + +// BitField constants for the ECM payload + +// CA System ID for Widevine. From table in +// https://en.wikipedia.org/wiki/Conditional_access +// This should be the only file found. +static constexpr int kWvCasCaSystemId = 0x4AD4; + +// Version - this should be incremented if there are non-backwards compatible +// changes to the ECM. +static constexpr int kEcmVersion = 1; + +// Settings for RotationEnabled field. +static constexpr int kRotationDisabled = 0; +static constexpr int kRotationEnabled = 1; + +// Convert from boolean for rotation to rotation field value. +int RotationFieldValue(bool rotation_required) { + return rotation_required ? kRotationEnabled : kRotationDisabled; +} + +// Setting for Unused field. +static constexpr int kUnusedZero = 0; + +// Convert from IV size to IV size field value. +int IvSizeFieldValue(size_t iv_size) { + return (8 == iv_size) ? kIvSize8 : kIvSize16; +} + +void SerializeKeyInfo(const EntitledKeyInfo& key_info, std::string* ecm_buf) { + absl::StrAppend(ecm_buf, key_info.entitlement_key_id); + absl::StrAppend(ecm_buf, key_info.key_id); + absl::StrAppend(ecm_buf, key_info.wrapped_key_value); + absl::StrAppend(ecm_buf, key_info.wrapped_key_iv); + absl::StrAppend(ecm_buf, key_info.content_iv); +} + +bool ConvertIvSizeParam(EcmIvSize param, size_t* size) { + if (param == kIvSize8) { + *size = 8; + } else if (param == kIvSize16) { + *size = 16; + } else { + return false; + } + return true; +} + +} // namespace + +util::Status CasEcm::Initialize(const std::string& content_id, + const std::string& content_provider, + const EcmInitParameters& ecm_init_parameters, + std::string* key_request_message) { + if (initialized_) { + return {util::error::INTERNAL, "Already initialized."}; + } + if (content_id.empty()) { + return {util::error::INVALID_ARGUMENT, "Content ID is empty."}; + } + if (content_provider.empty()) { + return {util::error::INVALID_ARGUMENT, "Content Provider is empty."}; + } + if (key_request_message == nullptr) { + return {util::error::INVALID_ARGUMENT, "key_request_message is null."}; + } + + if (ecm_init_parameters.track_types.empty()) { + return {util::error::INVALID_ARGUMENT, + "Parameter track_ids must be set with one or more Track IDs."}; + } + + if (!ConvertIvSizeParam(ecm_init_parameters.content_iv_size, + &content_iv_size_)) { + return {util::error::INVALID_ARGUMENT, + "Parameter content_iv_size must be kIvSize8 or kIvSize16."}; + } + + if (ecm_init_parameters.crypto_mode != CasCryptoMode::CTR && + ecm_init_parameters.crypto_mode != CasCryptoMode::CBC) { + return {util::error::INVALID_ARGUMENT, + "Crypto mode setting is out of range."}; + } else { + crypto_mode_ = ecm_init_parameters.crypto_mode; + } + + content_id_ = content_id; + content_provider_ = content_provider; + paired_keys_required_ = ecm_init_parameters.key_rotation_enabled; + track_types_ = ecm_init_parameters.track_types; + // Generation is incremented before the ECM is generated. + // Initializing to kMaxGeneration ensures the first generated ECM has a gen + // count of zero. + generation_ = kMaxGeneration; + + // Construct and return CasEncryptionRequest message for caller to use. + util::Status status = CreateEntitlementRequest(key_request_message); + if (!status.ok()) { + LOG(ERROR) << "Entitlement request message could not be created."; + return status; + } + + // Everything is set up except entitlement keys. + ClearEntitlementKeys(); + initialized_ = true; + return util::OkStatus(); +} + +util::Status CasEcm::ProcessCasEncryptionResponse(const std::string& response) { + if (!initialized_) { + return {util::error::INTERNAL, "Not initialized."}; + } + if (response.empty()) { + return {util::error::INVALID_ARGUMENT, "Response std::string is empty."}; + } + return ParseEntitlementResponse(response); +} + +util::Status CasEcm::GenerateEcm(EntitledKeyInfo* even_key, + EntitledKeyInfo* odd_key, + const std::string& track_type, + std::string* serialized_ecm, uint32_t* generation) { + if (!initialized_) { + return {util::error::INTERNAL, "Not initialized."}; + } + if (!HaveEntitlementKeys()) { + return {util::error::INTERNAL, "Need entitlement key."}; + } + if (!paired_keys_required_) { + return {util::error::INVALID_ARGUMENT, + "Key rotation not enabled - use GenerateSingleKeyEcm()."}; + } + + std::vector keys; + keys.push_back(even_key); + keys.push_back(odd_key); + + return GenerateEcmCommon(keys, track_type, serialized_ecm, generation); +} + +util::Status CasEcm::GenerateSingleKeyEcm(EntitledKeyInfo* key, + const std::string& track_type, + std::string* serialized_ecm, + uint32_t* generation) { + if (!initialized_) { + return {util::error::INTERNAL, "Not initialized."}; + } + if (!HaveEntitlementKeys()) { + return {util::error::INTERNAL, "Need entitlement key."}; + } + if (paired_keys_required_) { + return {util::error::INVALID_ARGUMENT, + "Key rotation enabled - use GenerateEcm()."}; + } + + std::vector keys; + keys.push_back(key); + + return GenerateEcmCommon(keys, track_type, serialized_ecm, generation); +} + +util::Status CasEcm::GenerateEcmCommon( + const std::vector& keys, const std::string& track_type, + std::string* serialized_ecm, uint32_t* generation) { + if (serialized_ecm == nullptr) { + return {util::error::INVALID_ARGUMENT, "No return ecm std::string pointer."}; + } + if (generation == nullptr) { + return {util::error::INVALID_ARGUMENT, "No return generation pointer."}; + } + + util::Status status = ValidateKeys(keys); + if (!status.ok()) { + return status; + } + + status = WrapEntitledKeys(track_type, keys); + if (!status.ok()) { + return status; + } + + status = ValidateWrappedKeys(keys); + if (!status.ok()) { + return status; + } + + // TODO(user): validate inputs, compare against current values + // TODO(user): replace current values. + + // Updates complete, we are complete and consistent. + // Update generation before serializing. + uint32_t previous_generation = generation_; + IncrementGeneration(); + + // Generate TS packet payload for ECM, pass back to caller. + serialized_ecm->assign(SerializeEcm(keys)); + if (kMaxEcmSizeBytes < serialized_ecm->size()) { + generation_ = previous_generation; + serialized_ecm->clear(); + return util::Status(util::error::INTERNAL, + "Maximum size of ECM has been exceeded."); + } + *generation = generation_; + + return util::OkStatus(); +} + +void CasEcm::IncrementGeneration() { + generation_ = (generation_ >= kMaxGeneration) ? 0 : generation_ + 1; +} + +util::Status CasEcm::WrapEntitledKeys( + const std::string& track_type, const std::vector keys) { + if (!initialized_) { + return {util::error::INTERNAL, "Not initialized."}; + } + if (keys.empty()) { + return {util::error::INVALID_ARGUMENT, + "Vector of EntitledKeyInfo is empty."}; + } + + auto ekey_map_entry = entitlement_keys_.find(track_type); + if (ekey_map_entry == entitlement_keys_.end()) { + return {util::error::INTERNAL, + "No Entitlement Key found for given track_type."}; + } + + const auto& ekey_list = ekey_map_entry->second; + if (ekey_list.size() != keys.size()) { + return {util::error::INTERNAL, + "Number of Entitled keys and Entitlement keys must match."}; + } + + auto entitlement_key = ekey_list.begin(); + for (auto entitled_key : keys) { + entitled_key->entitlement_key_id = entitlement_key->key_id; + // Wrap key using entitlement key. First generate new IV. + CHECK(RandomBytes(kWrappedKeyIvSizeBytes, &entitled_key->wrapped_key_iv)); + entitled_key->wrapped_key_value = + WrapKey(entitlement_key->key_value, entitled_key->wrapped_key_iv, + entitled_key->key_value); + entitlement_key++; + } + return util::OkStatus(); +} + +std::string CasEcm::WrapKey(const std::string& wrapping_key, const std::string& iv, + const std::string& key_value) { + // Wrapped key IV is always 16 bytes. + return crypto_util::EncryptAesCbcNoPad(wrapping_key, iv, key_value); +} + +util::Status CasEcm::ValidateKeys(const std::vector& keys) { + for (const auto& key : keys) { + util::Status status; + status = ValidateKeyId(key->key_id); + if (!status.ok()) { + return status; + } + status = ValidateIv(key->content_iv, content_iv_size_); + if (!status.ok()) { + return status; + } + } + return util::OkStatus(); +} + +util::Status CasEcm::ValidateWrappedKeys( + const std::vector& keys) { + for (const auto& key : keys) { + util::Status status; + status = ValidateKeyId(key->key_id); + if (!status.ok()) { + return status; + } + status = ValidateKeyValue(key->wrapped_key_value); + if (!status.ok()) { + LOG(ERROR) << "Wrapped key is bad."; + return status; + } + status = ValidateIv(key->wrapped_key_iv, kWrappedKeyIvSizeBytes); + if (!status.ok()) { + return status; + } + status = ValidateIv(key->content_iv, content_iv_size_); + if (!status.ok()) { + return status; + } + } + return util::OkStatus(); +} + +util::Status CasEcm::ValidateKeyId(const std::string& key_id) { + if (key_id.size() != kKeyIdSizeBytes) { + return {util::error::INVALID_ARGUMENT, "Key ID must be 16 bytes."}; + } + return util::OkStatus(); +} + +util::Status CasEcm::ValidateKeyValue(const std::string& key_value) { + if (key_value.size() != kKeyDataSizeBytes) { + util::Status( + util::error::INVALID_ARGUMENT, + absl::StrCat("Key is wrong size (", key_value.size(), " bytes).")); + } + return util::OkStatus(); +} + +util::Status CasEcm::ValidateIv(const std::string& iv, size_t size) { + if (iv.size() != size) { + return {util::error::INVALID_ARGUMENT, "IV is wrong size."}; + } + return util::OkStatus(); +} + +std::string CasEcm::SerializeEcm(const std::vector& keys) { + // Five bytes (40 bits including padding) + std::bitset ca_system_id(kWvCasCaSystemId); + std::bitset ecm_version(kEcmVersion); + std::bitset ecm_generation_count( + generation()); + std::bitset decrypt_mode(crypto_mode()); + std::bitset rotation_enabled( + RotationFieldValue(paired_keys_required())); + std::bitset wrapped_key_iv_size( + IvSizeFieldValue(kWrappedKeyIvSizeBytes)); + std::bitset content_iv_size( + IvSizeFieldValue(this->content_iv_size())); + std::bitset padding(kUnusedZero); + + // Converts bitset to string. + std::string ecm_bitset = absl::StrCat( + ca_system_id.to_string(), ecm_version.to_string(), + ecm_generation_count.to_string(), decrypt_mode.to_string(), + rotation_enabled.to_string(), wrapped_key_iv_size.to_string(), + content_iv_size.to_string(), padding.to_string()); + if (ecm_bitset.size() != 40) { + LOG(FATAL) << "ECM bitset incorret size: " << ecm_bitset.size(); + } + std::string serialized_ecm; + util::Status status = + string_util::BitsetStringToBinaryString(ecm_bitset, &serialized_ecm); + if (!status.ok()) { + LOG(FATAL) << "Failed to convert ECM bitset to std::string"; + } + + // Appends entitled key info. + for (const auto& key : keys) { + SerializeKeyInfo(*key, &serialized_ecm); + } + + return serialized_ecm; +} + +util::Status CasEcm::CreateEntitlementRequest(std::string* request_string) { + CasEncryptionRequest request; + + request.set_content_id(content_id_); + request.set_provider(content_provider_); + request.set_key_rotation(paired_keys_required_); + // Add labels for tracks. + for (const auto& track_type : track_types_) { + request.add_track_types(track_type); + } + + if (!request.SerializeToString(request_string)) { + request_string->clear(); + return {util::error::INTERNAL, "Failure serializing request."}; + } + return util::OkStatus(); +} + +util::Status CasEcm::ParseEntitlementResponse(const std::string& response_string) { + // TODO(user): parse valid response. NOT Implemented. + ClearEntitlementKeys(); + + SignedCasEncryptionResponse signed_response; + if (!signed_response.ParseFromString(response_string)) { + return {util::error::INTERNAL, "Failure parsing signed response."}; + } + // TODO(user): Should verify signature. + CasEncryptionResponse response; + if (!response.ParseFromString(signed_response.response())) { + return {util::error::INTERNAL, "Failure parsing signed response."}; + } + if (response.status() != CasEncryptionResponse_Status_OK) { + return util::Status( + util::error::INTERNAL, + absl::StrCat("Failure reported by server: ", response.status(), " : ", + response.status_message())); + } + if (content_id_ != response.content_id()) { + return util::Status( + util::error::INTERNAL, + absl::StrCat("Content ID mismatch in Entitlement Response - expected: ", + content_id_, " received: ", response.content_id())); + } + if (response.entitlement_keys().empty()) { + return {util::error::INTERNAL, "Failure: no entitlement keys in response."}; + } + size_t keys_needed = (paired_keys_required_ ? 2 : 1) * track_types_.size(); + if (keys_needed > response.entitlement_keys().size()) { + return util::Status( + util::error::INTERNAL, + absl::StrCat( + "Wrong number of keys in Entitlement Response - expected: ", + keys_needed, " got: ", response.entitlement_keys().size())); + } + + // Scan available entitlement keys for the ones that are needed. + // For non-key-rotation, this is a key with a track type and not even or odd. + // For key rotation, this is a key with a track type and even or odd. + ClearEntitlementKeys(); + for (const auto& key : response.entitlement_keys()) { + if (!key.has_track_type()) { + LOG(WARNING) << "Entitlement key missing track type, skipped."; + // No track ID. Skip it. + continue; + } + EntitlementKeyInfo ekey; + ekey.key_id = key.key_id(); + ekey.key_value = key.key(); + + // Using only keys with correct KeySlot + if (!key.has_key_slot()) { + LOG(WARNING) << "Entitlement key missing key slot, skipped."; + continue; + } + if (paired_keys_required()) { + if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_EVEN) { + PushEntitlementKey(key.track_type(), true, ekey); + } else if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_ODD) { + PushEntitlementKey(key.track_type(), false, ekey); + } + } else { + if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_SINGLE) { + PushEntitlementKey(key.track_type(), false, ekey); + } + } + } + if (!CheckEntitlementKeys()) { + LOG(ERROR) << "Could not stage entitlement keys from response:"; + response.ShortDebugString(); + return util::Status(util::error::INTERNAL, + "No suitable entitlement key was found."); + } + return util::OkStatus(); +} + +size_t CasEcm::CountEntitlementKeys() const { + size_t count = 0; + for (const auto& track : entitlement_keys_) { + count += track.second.size(); + } + return count; +} + +bool CasEcm::CheckEntitlementKeys() const { + // TODO(user): Cross-check entitlement_keys_ track types with track_types_. + if (track_types_.size() > CountEntitlementTracks()) { + // One or more tracks are missing. + LOG(ERROR) + << "Entitlement keys for one or more tracks is missing - expected " + << track_types_.size() << " tracks, received " + << CountEntitlementTracks() << "tracks."; + return false; + } + for (const auto& track : entitlement_keys_) { + if (track.second.size() < (paired_keys_required() ? 2 : 1)) { + LOG(ERROR) << " Wrong number of entitlement keys for track " + << track.first << ": " << (paired_keys_required() ? 2 : 1) + << " expected, " << track.second.size() << " received."; + return false; + } + } + return true; +} + +} // namespace cas +} // namespace widevine diff --git a/media_cas_packager_sdk/internal/ecm.h b/media_cas_packager_sdk/internal/ecm.h new file mode 100644 index 0000000..3f8c2ff --- /dev/null +++ b/media_cas_packager_sdk/internal/ecm.h @@ -0,0 +1,244 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECM_H_ +#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECM_H_ + +#include +#include +#include +#include +#include +#include + +#include "util/status.h" +#include "protos/public/media_cas.pb.h" + +namespace widevine { +namespace cas { + +// Information needed to generate entitled key portion of ECM. This will be +// set up by the caller and passed into WrapEntitledKey() and GenerateEcm(). +struct EntitledKeyInfo { + // |key_id|, |key_value|, and |content_iv| are set up by the API client + // before calling GenerateEcm() or GenerateSingleKeyEcm(). + std::string key_id; + std::string key_value; + std::string content_iv; + // A key that has been wrapped has the + // following fields set. This is done by WrapEntitledKeys(). + std::string entitlement_key_id; + std::string wrapped_key_value; // always encrypted using AES CBC + std::string wrapped_key_iv; +}; + +// Declare Content IV size in an ECM stream. +// Content IVs may be encoded as 8 or 16 random bytes. The receiver is +// responsible for appending 8 zeros to an 8-byte IV. All content IVs, once the +// size is declared, must be the same size. Wrapped key IVs are always 16 bytes. +enum EcmIvSize { kIvSize8 = 0, kIvSize16 = 1 }; + +// Information needed to start a new ECM stream. +// Fields: +// |content_iv_size| size of all content key IVs in the ECM stream. +// A constant of type CasEcmSize specifying 8 or 16. +// |key_rotation_enabled| the encryption uses multiple keys in rotation. +// |crypto_mode| the encryption mode used for the content stream. +// A constant of type CasCryptoMode. +// |track_types| a vector of track ID (std::string) that specify the set of track +// types of interest; controls the entitlement keys returned by the server. +struct EcmInitParameters { + EcmIvSize content_iv_size = kIvSize8; + bool key_rotation_enabled = true; + CasCryptoMode crypto_mode = CasCryptoMode::CTR; + std::vector track_types; +}; + +// Generator for producing Widevine CAS-compliant ECMs. Used to construct the +// Transport Stream packet payload of an ECM containing key information for +// decrypting Widevine CAS encrypted content. The keys in the ECM are wrapped +// (encrypted) using the entitlement key associated with the content. +// The entitlement key ID is included in the ECM, and it is used to get and +// stage the entitlement key needed to unwrap the content keys. +// ECMs may hold one or two keys. If key rotation is enabled, the ECM holds a +// pair of keys (even and odd) to facilitate key rotation. +// +// TODO(user): Add usage example. +// +// Class CasEcm is not thread safe. +class CasEcm { + public: + CasEcm() = default; + virtual ~CasEcm() = default; + + // Perform initialization for a new ECM stream. + // Args: + // |content_id| uniquely identifies the content (with |content_provider|) + // |content_provider| unique std::string for provider of the content stream. + // |wv_ECM_parameters| encryption-related parameters for configuring + // the ECM stream. + // |key_request_message| pointer to a std::string to receive a CasEncryptionRequest + // message. + // Notes: + // The returned |key_request_message| must be sent to the server and + // the response correctly parsed before ECMs can be generated. + virtual util::Status Initialize(const std::string& content_id, + const std::string& content_provider, + const EcmInitParameters& ecm_init_parameters, + std::string* key_request_message); + + // Parse a CasEncryptionResponse message holding the entitlement keys for + // generating the ECM stream. The entitlement keys are used to encrypt the + // keys conveyed in the ECM. The entitlement key IDs are also part of the ECM. + // Args: + // |response| a serialized CasEncryptionRequest message from the server + // holding entitlement key information (or error information). + virtual util::Status ProcessCasEncryptionResponse(const std::string& response); + + // Accept keys and IVs and construct an ECM that will fit into a Transport + // Stream packet payload (184 bytes). + // Args: + // |even_key| information for even key to be encoded into ECM. + // |odd_key| information for odd key to be encoded into ECM. + // |track_type| the track that the keys are being used to encrypt. + // |serialized_ecm| caller-supplied std::string pointer to receive the ECM. + // |generation| pointer to uint32_t to receive the generation number of the ECM. + // The |even_key| and |odd_key| contents (specifically IV sizes) must be + // consistent with the initialized settings. + // The even_key and odd_key will be wrapped using the appropriate + // entitlement key. Wrapping modifies the original structure. + // Generation is a mod 32 counter. If the ECM has any changes from the + // previous ECM, the generation is increased by one. + virtual util::Status GenerateEcm(EntitledKeyInfo* even_key, + EntitledKeyInfo* odd_key, + const std::string& track_type, + std::string* serialized_ecm, uint32_t* generation); + + // Accept a key and IV and construct an ECM that will fit into a Transport + // Stream packet payload (184 bytes). This call is specifically for the case + // where key rotation is disabled. + // Args: + // |key| information for key to be encoded into ECM. + // |track_type| the track that the key is being used to encrypt. + // |serialized_ecm| caller-supplied std::string pointer to receive the ECM. + // |generation| pointer to uint32_t to receive the generation number of the ECM. + // The |key| contents (specifically IV sizes) must be consistent + // with the initialized settings. + // Generation is a mod 32 counter. If the ECM has any changes from the + // previous ECM, the generation is increased by one. + virtual util::Status GenerateSingleKeyEcm(EntitledKeyInfo* key, + const std::string& track_type, + std::string* serialized_ecm, + uint32_t* generation); + + protected: // For unit tests. + // Take the input entitled |keys| and our current state, and generate + // the ECM representing the keys. + virtual std::string SerializeEcm(const std::vector& keys); + + // Increment the ECM's generation count by one (mod kMaxGeneration). + virtual void IncrementGeneration(); + + // Apply the Entitlement key with the given track type and polarity + // (even vs. odd key) to wrap the given Entitled key. + // Args: + // |track_type| the track type for the keys. The type_track must match one + // of the |EcmInitParameters::track_types| strings passed into Initialize(). + // |key| the Entitled Key to be wrapped. + virtual util::Status WrapEntitledKeys( + const std::string& track_type, const std::vector keys); + + private: + // Entitlement key - |key_value| is used to wrap the content key, and |key_id| + // is added to the ECM. |track_type| allows the entitlement key to be + // associated with the original request. See Initialize() for details. + struct EntitlementKeyInfo { + std::string key_id; + std::string key_value; + }; + + // Check whether we've acquired entitlement keys. + bool HaveEntitlementKeys() const { return !entitlement_keys_.empty(); } + + // Count the different tracks for which we have entitlement keys. + size_t CountEntitlementTracks() const { return entitlement_keys_.size(); } + + // Count the total number of entitlement keys in our current state. + size_t CountEntitlementKeys() const; + + // Verify that each track has the right number of keys. Complain if not. + bool CheckEntitlementKeys() const; + + // Remove all current entitlement keys. + void ClearEntitlementKeys() { entitlement_keys_.clear(); } + + // Add an entitlement key to our current state. Even key is placed first. + void PushEntitlementKey(const std::string& track_type, bool is_even_key, + const EntitlementKeyInfo& key) { + auto emplaced = + entitlement_keys_.emplace(track_type, std::list{}); + if (is_even_key) { + emplaced.first->second.push_front(key); + } else { + emplaced.first->second.push_back(key); + } + } + + // Common helper for GenerateEcm() and GenerateSingleKeyEcm() + virtual util::Status GenerateEcmCommon( + const std::vector& keys, const std::string& track_type, + std::string* serialized_ecm, uint32_t* generation); + + // Wrap a |key_value| using |wrapping_key| (entitlement key) and |iv|. + // Returns the resulting wrapped key. + virtual std::string WrapKey(const std::string& wrapping_key, const std::string& iv, + const std::string& key_value); + + virtual util::Status ValidateKeys(const std::vector& keys); + virtual util::Status ValidateWrappedKeys( + const std::vector& keys); + + util::Status ValidateKeyId(const std::string& key_id); + util::Status ValidateKeyValue(const std::string& key_value); + util::Status ValidateIv(const std::string& iv, size_t size); + + // TODO(user): need unit tests for CreateEntitlementRequest. + virtual util::Status CreateEntitlementRequest(std::string* request_string); + // TODO(user): need unit tests for ParseEntitlementResponse. + virtual util::Status ParseEntitlementResponse(const std::string& response_string); + + virtual uint32_t generation() const { return generation_; } + virtual CasCryptoMode crypto_mode() const { return crypto_mode_; } + virtual bool paired_keys_required() const { return paired_keys_required_; } + virtual size_t content_iv_size() const { return content_iv_size_; } + + // Set to true when the object has been properly initialized. + bool initialized_ = false; + // Current generation. This will be incremented (mod 32) each time a new + // ECM has changes from the previous ECM. + uint32_t generation_ = 0; + // Content ID for this ECM stream. + std::string content_id_; + // Provider ID for this ECM stream. + std::string content_provider_; + // Content IV size may be 8 or 16. Size is set once during Initialize(). + size_t content_iv_size_ = 8; + // The set of tracks that are being encrypted and require ECM streams. + std::vector track_types_; + // Remember if a pair of keys is required (for key rotation). + bool paired_keys_required_ = false; + CasCryptoMode crypto_mode_ = CasCryptoMode::CTR; + // Entitlement keys needed for ECM generation. + // The keys are added when the CasEncryptionResponse is processed. + std::map> entitlement_keys_; +}; + +} // namespace cas +} // namespace widevine + +#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECM_H_ diff --git a/media_cas_packager_sdk/internal/ecm_test.cc b/media_cas_packager_sdk/internal/ecm_test.cc new file mode 100644 index 0000000..f4e7aa8 --- /dev/null +++ b/media_cas_packager_sdk/internal/ecm_test.cc @@ -0,0 +1,646 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "media_cas_packager_sdk/internal/ecm.h" + +#include +#include + +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "util/status.h" +#include "protos/public/media_cas_encryption.pb.h" + +using ::testing::Return; + +namespace widevine { +namespace cas { + +// TODO(user): Encryption options yield a small number of possible sizes +// for ECMS - Explain the distinct cases and add/use literals for each one. + +namespace { + +constexpr char kTrackTypeSD[] = "SD"; +constexpr char kTrackTypeHD[] = "HD"; +constexpr char kTrackTypeAUDIO[] = "AUDIO"; +constexpr char kTrackTypeBAD[] = "BAD"; + +constexpr size_t kEcmHeaderSize = 5; +constexpr size_t kEcmKeyIdSize = 16; +constexpr size_t kEcmKeyDataSize = 16; +constexpr size_t kEcmIvSize16 = 16; +constexpr size_t kEcmIvSize8 = 8; + +constexpr size_t kEcmKeyInfoSize = + kEcmKeyIdSize + kEcmKeyIdSize + kEcmKeyDataSize; + +size_t IvExpectedSize(EcmIvSize iv_size) { + return (iv_size == kIvSize8) ? kEcmIvSize8 : kEcmIvSize16; +} + +void InitEntitledKey(EntitledKeyInfo* key, const std::string& id, + const std::string& value, const std::string& iv) { + key->key_id = id; + key->key_value = value; + key->content_iv = iv; +} + +} // namespace + +class MockCasEcm : public CasEcm { + public: + MockCasEcm() = default; + ~MockCasEcm() override = default; + + MOCK_CONST_METHOD0(generation, uint32_t()); + MOCK_CONST_METHOD0(crypto_mode, CasCryptoMode()); + MOCK_CONST_METHOD0(paired_keys_required, bool()); + MOCK_CONST_METHOD0(content_iv_size, size_t()); + + std::string CallSerializeEcm(const std::vector& keys) { + return SerializeEcm(keys); + } + + virtual util::Status MockWrapEntitledKeys( + const std::string& track_type, const std::vector& keys) { + for (auto entitled_key : keys) { + entitled_key->entitlement_key_id = "entitlement_Mock"; + entitled_key->wrapped_key_value = "MockMockMockMock"; + entitled_key->wrapped_key_iv = "WRAPPED_KIV....x"; + } + return util::OkStatus(); + } + + void MockSetup(bool two_keys, const std::string& track_type, uint32_t generation, + CasCryptoMode crypto_mode, size_t civ_size) { + EXPECT_CALL(*this, generation()).WillRepeatedly(Return(generation)); + EXPECT_CALL(*this, crypto_mode()).WillRepeatedly(Return(crypto_mode)); + EXPECT_CALL(*this, paired_keys_required()).WillRepeatedly(Return(two_keys)); + EXPECT_CALL(*this, content_iv_size()).WillRepeatedly(Return(civ_size)); + } +}; + +class CasEcmTest : public testing::Test { + protected: + void SetUp() override { + InitEntitledKey(&valid1_iv_16_8_, "keyid_16b......1", "keyvalue_16b...1", + "ct_iv..1"); + InitEntitledKey(&valid2_iv_16_8_, "wkv_16b.2.4.6.81", "wiv_16b.a.b.c.d1", + "ct_iv..2"); + InitEntitledKey(&valid3_iv_16_16_, "keyid_16b......3", "keyvalue_16b...3", + "content_iv.....3"); + InitEntitledKey(&valid4_iv_16_16_, "keyid_16b....xx4", "keyvalue_16b...4", + "content_iv.....4"); + InitEntitledKey(&invalid1_key_id_iv_16_8_, "keyid_12b..1", + "keyvalue_16b...1", "ct_iv..5"); + + params_one_key_.key_rotation_enabled = false; + params_two_keys_.key_rotation_enabled = true; + params_simple_.track_types.push_back(kTrackTypeSD); + } + + virtual void InitParams(EcmInitParameters* params, const std::string& track_type) { + params->track_types.push_back(track_type); + } + + virtual void InitParams(EcmInitParameters* params, const std::string& track_type, + EcmIvSize content_iv) { + params->track_types.push_back(track_type); + params->content_iv_size = content_iv; + } + + virtual void ServerCall(const std::string& request_string, + std::string* signed_response_string, bool report_status_ok, + bool generate_valid_ecm) { + CasEncryptionRequest request; + ASSERT_TRUE(request.ParseFromString(request_string)); + + CasEncryptionResponse response; + if (!report_status_ok) { + response.set_status(CasEncryptionResponse_Status_INTERNAL_ERROR); + } else { + response.set_status(CasEncryptionResponse_Status_OK); + response.set_content_id(request.content_id()); + for (const auto& track_type : request.track_types()) { + if (request.key_rotation()) { + // Add the Even key. + auto key = response.add_entitlement_keys(); + key->set_key_id("fake_key_id....."); + key->set_key("fakefakefakefakefakefakefakefake"); + key->set_track_type(track_type); + key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_EVEN); + // Add the Odd key. + key = response.add_entitlement_keys(); + key->set_key_id("fake_key_id....."); + key->set_key("fakefakefakefakefakefakefakefake"); + key->set_track_type(track_type); + key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_ODD); + if (!generate_valid_ecm) { + auto key = response.add_entitlement_keys(); + key->set_key_id("fake_key_id....."); + key->set_key("fakefakefakefakefakefakefakefake"); + key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_EVEN); + key->set_track_type(track_type); + } + } else { + auto key = response.add_entitlement_keys(); + key->set_key_id("fake_key_id....."); + key->set_key("fakefakefakefakefakefakefakefake"); + key->set_track_type(track_type); + key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_SINGLE); + if (!generate_valid_ecm) { + auto key = response.add_entitlement_keys(); + key->set_key_id("fake_key_id....."); + key->set_key("fakefakefakefakefakefakefakefake"); + key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_SINGLE); + key->set_track_type(track_type); + } + } + } + } + std::string response_string; + ASSERT_TRUE(response.SerializeToString(&response_string)); + SignedCasEncryptionResponse signed_response; + signed_response.set_response(response_string); + ASSERT_TRUE(signed_response.SerializeToString(signed_response_string)); + } + + const std::string provider_ = "provider"; + const std::string content_id_ = "content-id"; + EcmInitParameters params_one_key_; + EcmInitParameters params_two_keys_; + EcmInitParameters params_simple_; + EcmInitParameters params_default_; + EntitledKeyInfo valid1_iv_16_8_; + EntitledKeyInfo valid2_iv_16_8_; + EntitledKeyInfo valid3_iv_16_16_; + EntitledKeyInfo valid4_iv_16_16_; + EntitledKeyInfo invalid1_key_id_iv_16_8_; +}; + +class CasEcmSerializeEcmTest : public CasEcmTest { + public: + void ValidateEcmHeaderFields(const std::string& ecm_string, bool rotation_enabled, + int gen, CasCryptoMode crypto_mode, + EcmIvSize content_iv) { + EXPECT_THAT('\x4A', ecm_string[0]); + EXPECT_THAT('\xD4', ecm_string[1]); + EXPECT_THAT('\x01', ecm_string[2]); // version + EXPECT_THAT(gen, ((ecm_string[3] >> 3) & 31)); + EXPECT_THAT(static_cast(crypto_mode), ((ecm_string[3] >> 1) & 3)); + EXPECT_THAT(rotation_enabled ? 1 : 0, ecm_string[3] & 1); + EXPECT_THAT(1, ((ecm_string[4] >> 7) & 1)); // wrapped key IV size, MB 1 + EXPECT_THAT(static_cast(content_iv), ((ecm_string[4] >> 6) & 1)); + EXPECT_THAT(0, ecm_string[4] & 63); // zero padding + } + + void ValidateEcmFieldsOneKey(const std::string& buf_string, int gen, + CasCryptoMode crypto_mode, + EcmIvSize content_iv) { + size_t expected_size = kEcmHeaderSize + kEcmKeyInfoSize + kEcmIvSize16 + + IvExpectedSize(content_iv); + size_t ecm_len = buf_string.size(); + EXPECT_THAT(expected_size, ecm_len); + ValidateEcmHeaderFields(buf_string, false, gen, crypto_mode, content_iv); + } + + void ValidateEcmFieldsTwoKeys(const std::string& buf_string, int gen, + CasCryptoMode crypto_mode, + EcmIvSize content_iv) { + size_t expected_size = + kEcmHeaderSize + + (2 * (kEcmKeyInfoSize + kEcmIvSize16 + IvExpectedSize(content_iv))); + size_t ecm_len = buf_string.size(); + EXPECT_THAT(expected_size, ecm_len); + ValidateEcmHeaderFields(buf_string, true, gen, crypto_mode, content_iv); + } +}; + +TEST_F(CasEcmTest, GenerateEcmNotInitialized) { + CasEcm ecm_gen; + EntitledKeyInfo key1; + std::string ecm_data; + uint32_t gen; + EXPECT_EQ(util::error::INTERNAL, + ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm_data, &gen) + .error_code()); +} + +TEST_F(CasEcmTest, GenerateEcm2NotInitialized) { + CasEcm ecm_gen; + EntitledKeyInfo key1; + EntitledKeyInfo key2; + std::string ecm_data; + uint32_t gen; + EXPECT_EQ(util::error::INTERNAL, + ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm_data, &gen) + .error_code()); +} + +TEST_F(CasEcmTest, SetResponseNotInitialized) { + CasEcm ecm_gen; + const std::string response("anything"); + EXPECT_EQ(util::error::INTERNAL, + ecm_gen.ProcessCasEncryptionResponse(response).error_code()); +} + +TEST_F(CasEcmTest, InitNoTracksFail) { + CasEcm ecm_gen; + std::string request; + EXPECT_EQ( + util::error::INVALID_ARGUMENT, + ecm_gen.Initialize(content_id_, provider_, params_default_, &request) + .error_code()); +} + +TEST_F(CasEcmTest, InitSucceed) { + CasEcm ecm_gen; + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_simple_, &request)); +} + +TEST_F(CasEcmTest, SecondInitFail) { + CasEcm ecm_gen; + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_simple_, &request)); + EXPECT_EQ(util::error::INTERNAL, + ecm_gen.Initialize(content_id_, provider_, params_simple_, &request) + .error_code()); +} + +TEST_F(CasEcmTest, InitEmptyContentIdFail) { + CasEcm ecm_gen; + const std::string empty_content_id; + std::string request; + EXPECT_EQ( + util::error::INVALID_ARGUMENT, + ecm_gen.Initialize(empty_content_id, provider_, params_simple_, &request) + .error_code()); +} + +TEST_F(CasEcmTest, InitEmptyProviderFail) { + CasEcm ecm_gen; + const std::string empty_provider; + std::string request; + EXPECT_EQ( + util::error::INVALID_ARGUMENT, + ecm_gen.Initialize(content_id_, empty_provider, params_simple_, &request) + .error_code()); +} + +TEST_F(CasEcmTest, InitIvSize16x16OK) { + CasEcm ecm_gen; + InitParams(¶ms_one_key_, kTrackTypeSD, kIvSize16); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_one_key_, &request)); +} + +TEST_F(CasEcmTest, GenerateWithNoEntitlementOneKeyFail) { + CasEcm ecm_gen; + EntitledKeyInfo key1; + InitParams(¶ms_one_key_, kTrackTypeSD); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_one_key_, &request)); + + std::string ecm; + uint32_t gen; + // This will fail because the entitlement key has not been acquired + // (via server call and ProcessCasEncryptionResponse()). + EXPECT_EQ(util::error::INTERNAL, + ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen) + .error_code()); +} + +TEST_F(CasEcmTest, GenerateWithNoEntitlementTwoKeysFail) { + CasEcm ecm_gen; + EntitledKeyInfo key1; + EntitledKeyInfo key2; + InitParams(¶ms_two_keys_, kTrackTypeSD); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_two_keys_, &request)); + + std::string ecm; + uint32_t gen; + // This will fail because the entitlement keys have not been acquired + // (via server call and ProcessCasEncryptionResponse()). + EXPECT_EQ( + util::error::INTERNAL, + ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code()); +} + +TEST_F(CasEcmTest, RequestNullFail) { + CasEcm ecm_gen; + EXPECT_EQ(util::error::INVALID_ARGUMENT, + ecm_gen.Initialize(content_id_, provider_, params_simple_, nullptr) + .error_code()); +} + +TEST_F(CasEcmTest, GenerateOneKeyOK) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = valid1_iv_16_8_; + InitParams(¶ms_one_key_, kTrackTypeSD, kIvSize8); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_one_key_, &request)); + + std::string response; + ServerCall(request, &response, true, true); + + ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); + + std::string ecm; + uint32_t gen; + ASSERT_OK(ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)); +} + +TEST_F(CasEcmTest, BadResponseFail) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = valid1_iv_16_8_; + InitParams(¶ms_one_key_, kTrackTypeSD, kIvSize8); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_one_key_, &request)); + + std::string response; + ServerCall(request, &response, false, true); + + EXPECT_EQ(util::error::INTERNAL, + ecm_gen.ProcessCasEncryptionResponse(response).error_code()); +} + +TEST_F(CasEcmTest, GenerateTwoKeysOK) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = valid1_iv_16_8_; + EntitledKeyInfo key2 = valid2_iv_16_8_; + InitParams(¶ms_two_keys_, kTrackTypeSD, kIvSize8); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_two_keys_, &request)); + + std::string response; + ServerCall(request, &response, true, true); + + ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); + + std::string ecm; + uint32_t gen; + ASSERT_OK(ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen)); +} + +TEST_F(CasEcmTest, GenerateOneKeyNoGenFail) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = valid1_iv_16_8_; + InitParams(¶ms_one_key_, kTrackTypeSD, kIvSize8); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_one_key_, &request)); + + std::string response; + ServerCall(request, &response, true, true); + + ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); + + std::string ecm; + EXPECT_EQ(util::error::INVALID_ARGUMENT, + ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, nullptr) + .error_code()); +} + +TEST_F(CasEcmTest, GenerateOneKeyBadKeyIdFail) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = invalid1_key_id_iv_16_8_; + InitParams(¶ms_one_key_, kTrackTypeSD, kIvSize8); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_one_key_, &request)); + + std::string response; + ServerCall(request, &response, true, true); + + ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); + + std::string ecm; + uint32_t gen; + EXPECT_EQ(util::error::INVALID_ARGUMENT, + ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen) + .error_code()); +} + +TEST_F(CasEcmTest, GenerateOneKeyWrong) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = valid1_iv_16_8_; + InitParams(¶ms_one_key_, kTrackTypeSD, kIvSize8); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_one_key_, &request)); + + std::string response; + ServerCall(request, &response, true, true); + + ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); + + std::string ecm; + uint32_t gen; + ASSERT_OK(ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)); + EXPECT_THAT(0, gen); + EXPECT_THAT(77, ecm.size()); + EXPECT_EQ( + util::error::INVALID_ARGUMENT, + ecm_gen.GenerateEcm(&key1, &key1, kTrackTypeSD, &ecm, &gen).error_code()); +} + +TEST_F(CasEcmTest, GenerateTwoKeysIvSizeFail) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = valid1_iv_16_8_; + EntitledKeyInfo key2 = valid2_iv_16_8_; + // IV size mismatch. + InitParams(¶ms_two_keys_, kTrackTypeSD, kIvSize16); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_two_keys_, &request)); + + std::string response; + ServerCall(request, &response, true, true); + + ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); + + std::string ecm; + uint32_t gen; + EXPECT_EQ( + util::error::INVALID_ARGUMENT, + ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code()); +} + +TEST_F(CasEcmTest, GenerateTwoKeysIvSize16x8OK) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = valid1_iv_16_8_; + EntitledKeyInfo key2 = valid2_iv_16_8_; + InitParams(¶ms_two_keys_, kTrackTypeSD, kIvSize8); + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_two_keys_, &request)); + + std::string response; + ServerCall(request, &response, true, true); + + ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); + + std::string ecm; + uint32_t gen; + ASSERT_OK(ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen)); + EXPECT_THAT(0, gen); + EXPECT_THAT(149, ecm.size()); + ASSERT_OK(ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen)); + EXPECT_THAT(1, gen); + EXPECT_THAT(149, ecm.size()); +} + +TEST_F(CasEcmTest, GenerateTwoKeysIvSize16x16OK) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = valid3_iv_16_16_; + EntitledKeyInfo key2 = valid4_iv_16_16_; + InitParams(¶ms_two_keys_, kTrackTypeSD, kIvSize16); + + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_two_keys_, &request)); + + std::string response; + ServerCall(request, &response, true, true); + + ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); + + std::string ecm; + uint32_t gen; + ASSERT_OK(ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen)); + EXPECT_THAT(0, gen); + EXPECT_THAT(165, ecm.size()); + ASSERT_OK(ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen)); + EXPECT_THAT(1, gen); + EXPECT_THAT(165, ecm.size()); +} + +TEST_F(CasEcmTest, GenerateThreeKeysIvSize16x16Fail) { + CasEcm ecm_gen; + EntitledKeyInfo key1 = valid3_iv_16_16_; + EntitledKeyInfo key2 = valid4_iv_16_16_; + InitParams(¶ms_two_keys_, kTrackTypeSD, kIvSize16); + + std::string request; + ASSERT_OK( + ecm_gen.Initialize(content_id_, provider_, params_two_keys_, &request)); + + std::string response; + ServerCall(request, &response, true, false); + + ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); + + std::string ecm; + uint32_t gen; + EXPECT_EQ( + util::error::INTERNAL, + ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code()); +} + +// TODO(user): Add more unit tests for error paths around SerializeEcm. +TEST_F(CasEcmSerializeEcmTest, SerializeEcmDoubleKey16ByteIvs) { + MockCasEcm ecm_gen; + EntitledKeyInfo key1 = valid3_iv_16_16_; + EntitledKeyInfo key2 = valid4_iv_16_16_; + + ecm_gen.MockSetup(true, kTrackTypeSD, 0, CasCryptoMode::CTR, 16); + + std::vector keys; + keys.push_back(&key1); + keys.push_back(&key2); + ASSERT_OK(ecm_gen.MockWrapEntitledKeys(kTrackTypeSD, keys)); + + std::string buf_string = ecm_gen.CallSerializeEcm(keys); + + ValidateEcmFieldsTwoKeys(buf_string, 0, CasCryptoMode::CTR, kIvSize16); + + EXPECT_CALL(ecm_gen, generation()).WillRepeatedly(Return(1)); + + buf_string = ecm_gen.CallSerializeEcm(keys); + + ValidateEcmFieldsTwoKeys(buf_string, 1, CasCryptoMode::CTR, kIvSize16); +} + +TEST_F(CasEcmSerializeEcmTest, SerializeEcmSingleKey16ByteIvs) { + MockCasEcm ecm_gen; + EntitledKeyInfo key1 = valid3_iv_16_16_; + + ecm_gen.MockSetup(false, kTrackTypeSD, 0, CasCryptoMode::CTR, 16); + + std::vector keys; + keys.push_back(&key1); + ASSERT_OK(ecm_gen.MockWrapEntitledKeys(kTrackTypeSD, keys)); + + std::string buf_string = ecm_gen.CallSerializeEcm(keys); + + ValidateEcmFieldsOneKey(buf_string, 0, CasCryptoMode::CTR, kIvSize16); + + EXPECT_CALL(ecm_gen, generation()).WillRepeatedly(Return(1)); + + buf_string = ecm_gen.CallSerializeEcm(keys); + + ValidateEcmFieldsOneKey(buf_string, 1, CasCryptoMode::CTR, kIvSize16); +} + +TEST_F(CasEcmSerializeEcmTest, SerializeEcmDoubleKey16x8ByteIvs) { + MockCasEcm ecm_gen; + EntitledKeyInfo key1 = valid1_iv_16_8_; + EntitledKeyInfo key2 = valid2_iv_16_8_; + + ecm_gen.MockSetup(true, kTrackTypeSD, 0, CasCryptoMode::CTR, 8); + + std::vector keys; + keys.push_back(&key1); + keys.push_back(&key2); + ASSERT_OK(ecm_gen.MockWrapEntitledKeys(kTrackTypeSD, keys)); + + std::string buf_string = ecm_gen.CallSerializeEcm(keys); + + ValidateEcmFieldsTwoKeys(buf_string, 0, CasCryptoMode::CTR, kIvSize8); + + EXPECT_CALL(ecm_gen, generation()).WillRepeatedly(Return(1)); + + buf_string = ecm_gen.CallSerializeEcm(keys); + + ValidateEcmFieldsTwoKeys(buf_string, 1, CasCryptoMode::CTR, kIvSize8); +} + +TEST_F(CasEcmSerializeEcmTest, SerializeEcmSingleKey16x8ByteIvs) { + MockCasEcm ecm_gen; + EntitledKeyInfo key1 = valid1_iv_16_8_; + + ecm_gen.MockSetup(false, kTrackTypeSD, 0, CasCryptoMode::CTR, 8); + + std::vector keys; + keys.push_back(&key1); + ASSERT_OK(ecm_gen.MockWrapEntitledKeys(kTrackTypeSD, keys)); + + std::string buf_string = ecm_gen.CallSerializeEcm(keys); + + ValidateEcmFieldsOneKey(buf_string, 0, CasCryptoMode::CTR, kIvSize8); + + EXPECT_CALL(ecm_gen, generation()).WillRepeatedly(Return(1)); + + buf_string = ecm_gen.CallSerializeEcm(keys); + + ValidateEcmFieldsOneKey(buf_string, 1, CasCryptoMode::CTR, kIvSize8); +} + +} // namespace cas +} // namespace widevine diff --git a/media_cas_packager_sdk/public/BUILD b/media_cas_packager_sdk/public/BUILD new file mode 100644 index 0000000..fe34d53 --- /dev/null +++ b/media_cas_packager_sdk/public/BUILD @@ -0,0 +1,68 @@ +################################################################################ +# Copyright 2018 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +# Build file for ECM 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"] + +filegroup( + name = "binary_release_files", + srcs = glob(["*.h"]), +) + +cc_binary( + name = "libmedia_cas_packager_sdk.so", + linkshared = 1, + deps = [":ecm_generator"], +) + +cc_library( + name = "libmedia_cas_packager_sdk", + srcs = [":libmedia_cas_packager_sdk.so"], + hdrs = glob(["*.h"]), + deps = [ + "//base", + "@abseil_repo//absl/base:core_headers", + "//util:status", + "//media_cas_packager_sdk/internal:ecm", + ], +) + +cc_library( + name = "ecm_generator", + srcs = ["ecm_generator.cc"], + hdrs = ["ecm_generator.h"], + copts = PUBLIC_COPTS, + deps = [ + "//base", + "@abseil_repo//absl/base:core_headers", + "//util:status", + "//media_cas_packager_sdk/internal:ecm", + ], +) + +cc_test( + name = "ecm_generator_test", + size = "small", + srcs = ["ecm_generator_test.cc"], + deps = [ + ":ecm_generator", + "//testing:gunit_main", + "@abseil_repo//absl/memory", + "//common:aes_cbc_util", + "//protos/public:media_cas_encryption_proto", + ], +) diff --git a/media_cas_packager_sdk/public/ecm_generator.cc b/media_cas_packager_sdk/public/ecm_generator.cc new file mode 100644 index 0000000..6b61d56 --- /dev/null +++ b/media_cas_packager_sdk/public/ecm_generator.cc @@ -0,0 +1,149 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "media_cas_packager_sdk/public/ecm_generator.h" + +#include "glog/logging.h" + +namespace widevine { +namespace cas { + +static constexpr int kKeyDataSize = 16; +static constexpr int kKeyIvSize8 = 8; +static constexpr int kKeyIvSize16 = 16; +static constexpr int kMaxBytesKeyIdField = 16; + +std::string CasEcmGenerator::GenerateEcm(const EcmParameters& params) { + std::vector keys; + util::Status status = ProcessEcmParameters(params, &keys); + if (!status.ok() || !initialized_) { + LOG(ERROR) << " EcmParameters is not set up properly: " << status; + return ""; + } + std::string serialized_ecm; + uint32_t generation; + // TODO(user): need track_type + std::string track_type = "SD"; + if (params.rotation_enabled) { + status = ecm_->GenerateEcm(&keys[0], &keys[1], track_type, &serialized_ecm, + &generation); + } else { + status = ecm_->GenerateSingleKeyEcm(&keys[0], track_type, &serialized_ecm, + &generation); + } + if (!status.ok()) { + LOG(ERROR) << " Call to CasEcm's ECM Generator failed: " << status; + return ""; + } + return serialized_ecm; +} + +util::Status CasEcmGenerator::ProcessEcmParameters( + const EcmParameters& ecm_params, std::vector* keys) { + initialized_ = false; + rotation_enabled_ = ecm_params.rotation_enabled; + + // Validate and add key data + keys->clear(); + uint32_t keys_needed = ecm_params.rotation_enabled ? 2 : 1; + if (ecm_params.key_params.size() < keys_needed) { + return {util::error::INVALID_ARGUMENT, + "Number of supplied keys is wrong (check rotation periods)."}; + } + for (int i = 0; i < keys_needed; i++) { + keys->emplace_back(EntitledKeyInfo()); + EntitledKeyInfo& key = keys->back(); + key.key_id = ecm_params.key_params[i].key_id; + key.key_value = ecm_params.key_params[i].key_data; + key.content_iv = ecm_params.key_params[i].content_ivs[0]; + } + current_key_index_ = 0; + current_key_even_ = true; + initialized_ = true; + return util::OkStatus(); +} + +util::Status CasEcmGenerator::ValidateKeyId(const std::string& id) { + if (id.empty()) { + return {util::error::INVALID_ARGUMENT, "Key id is empty."}; + } + if (id.size() > kMaxBytesKeyIdField) { + return {util::error::INVALID_ARGUMENT, "Key id is too long."}; + } + return util::OkStatus(); +} + +util::Status CasEcmGenerator::ValidateKeyData(const std::string& key_data) { + if (key_data.empty()) { + return {util::error::INVALID_ARGUMENT, "Key data is empty."}; + } + if (key_data.size() != kKeyDataSize) { + return {util::error::INVALID_ARGUMENT, "Key data is wrong size."}; + } + return util::OkStatus(); +} + +util::Status CasEcmGenerator::ValidateIv(const std::string& iv, + size_t required_size) { + if (iv.empty()) { + return {util::error::INVALID_ARGUMENT, "IV is empty."}; + } + if (required_size != 8 && required_size != 16) { + return {util::error::INTERNAL, "IV size has not been set up correctly."}; + } + + if (iv.size() != required_size) { + return {util::error::INVALID_ARGUMENT, + "IV has wrong or inconsistent size."}; + } + return util::OkStatus(); +} + +util::Status CasEcmGenerator::ValidateWrappedKeyIv(const std::string& iv) { + // All wrapped key IVs must be 16 bytes. + util::Status status = ValidateIv(iv, kIvSize16); + if (!status.ok()) { + LOG(ERROR) << " Wrapped key IV is not valid: " << status; + } + return status; +} + +util::Status CasEcmGenerator::ValidateContentIv(const std::string& iv) { + // If content_iv_size_ is zero, use this IV as the size for all future IVs in + // this stream. + if (content_iv_size_ == 0) { + content_iv_size_ = iv.size(); + } + util::Status status = ValidateIv(iv, content_iv_size_); + if (!status.ok()) { + LOG(ERROR) << " Content IV is not valid: " << status; + } + return status; +} + +util::Status CasEcmGenerator::ValidateKeyParameters( + const KeyParameters& key_params) { + util::Status status; + status = ValidateKeyId(key_params.key_id); + if (!status.ok()) return status; + status = ValidateKeyData(key_params.wrapped_key_data); + if (!status.ok()) return status; + status = ValidateWrappedKeyIv(key_params.wrapped_key_iv); + if (!status.ok()) return status; + if (key_params.content_ivs.empty()) { + return {util::error::INVALID_ARGUMENT, "Content IVs is empty."}; + } + for (int i = 0; i < key_params.content_ivs.size(); i++) { + status = ValidateContentIv(key_params.content_ivs[i]); + if (!status.ok()) return status; + } + return util::OkStatus(); +} + +} // namespace cas +} // namespace widevine diff --git a/media_cas_packager_sdk/public/ecm_generator.h b/media_cas_packager_sdk/public/ecm_generator.h new file mode 100644 index 0000000..a95733d --- /dev/null +++ b/media_cas_packager_sdk/public/ecm_generator.h @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_ +#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_ + +#include +#include +#include +#include +#include + +#include +#include "util/status.h" +#include "media_cas_packager_sdk/internal/ecm.h" + +namespace widevine { +namespace cas { + +// KeyParameters carries key information for a single encryption key. +// Instances of this struct are owned by an EcmParameters struct. +struct KeyParameters { + std::string key_id; + std::string key_data; + std::string wrapped_key_data; + std::string wrapped_key_iv; + std::vector content_ivs; +}; + +// EcmParameters holds information that is needed by the EcmGenerator. It is +// partially set up with data from a KeyGenerator (obtained via GenerateKey()). +// IVs are added later. +// TODO(user): may need a starting crypto period index. +struct EcmParameters { + static constexpr int kDefaultIVSize = 8; + int iv_size = kDefaultIVSize; + bool current_key_even = true; + uint32_t current_key_index = 0; + std::string entitlement_key_id; + bool rotation_enabled = true; + uint32_t rotation_periods; + std::vector key_params; + uint16_t program_id; + uint64_t rotation_period_microseconds; + // For video, this is zero. For audio, this is the size of the audio frame, + // which is a constant in the audio track. + uint16_t offset = 0; +}; + +// ECM Generator for Widevine/MediaCAS entitled keys. +class CasEcmGenerator { + public: + CasEcmGenerator() = default; + virtual ~CasEcmGenerator() = default; + + virtual std::string GenerateEcm(const EcmParameters& params); + + // Query the state of this ECM Generator + bool initialized() { return initialized_; } + bool rotation_enabled() { return rotation_enabled_; } + + void set_ecm(std::unique_ptr ecm) { ecm_ = std::move(ecm); } + + private: + friend class CasEcmGeneratorTest; + + util::Status ProcessEcmParameters(const EcmParameters& ecm_params, + std::vector* keys); + + util::Status ProcessEcmParameters(const EcmParameters& ecm_params); + util::Status ValidateKeyId(const std::string& id); + util::Status ValidateKeyData(const std::string& key_data); + util::Status ValidateWrappedKeyIv(const std::string& iv); + util::Status ValidateIv(const std::string& iv, size_t required_size); + util::Status ValidateContentIv(const std::string& iv); + util::Status ValidateKeyParameters(const KeyParameters& key_params); + + bool initialized_ = false; + uint32_t generation_ = 0; + bool rotation_enabled_ = false; + uint32_t current_key_index_ = 0; + bool current_key_even_ = true; + uint32_t content_iv_size_ = 0; + std::unique_ptr ecm_; +}; + +} // namespace cas +} // namespace widevine + +#endif // MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_ diff --git a/media_cas_packager_sdk/public/ecm_generator_test.cc b/media_cas_packager_sdk/public/ecm_generator_test.cc new file mode 100644 index 0000000..b30a761 --- /dev/null +++ b/media_cas_packager_sdk/public/ecm_generator_test.cc @@ -0,0 +1,311 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "media_cas_packager_sdk/public/ecm_generator.h" + +#include +#include + +#include "testing/gmock.h" +#include "testing/gunit.h" +#include "absl/memory/memory.h" +#include "common/aes_cbc_util.h" +#include "protos/public/media_cas_encryption.pb.h" + +namespace widevine { +namespace cas { + +namespace { + +constexpr char kCADescriptor[] = "TestCa"; +constexpr char kEcm[] = "TestEcm"; +constexpr char kProvider[] = "Gfiber"; +constexpr char kContentId[] = "TestContent"; +constexpr int kDefaultKeyRotationPeriodMilliseconds = 30 * 60 * 1000; +constexpr char kPsshData[] = "TestPsshData"; + +constexpr char kEntitlementKeySingle[] = "testEKId12345678"; +constexpr char kEntitlementKeyDouble[] = "testEKIdabcdefgh"; + +constexpr char kEcmKeyIdSingle[] = "key-id-One123456"; +constexpr char kEcmKeyDataSingle[] = "0123456701234567"; +constexpr char kEcmWrappedKeySingle[] = "1234567890123456"; +constexpr char kEcmWrappedKeyIvSingle[] = "abcdefghacbdefgh"; +constexpr char kEcmContentIvSingle[] = "ABCDEFGH"; + +constexpr char kEcmKeyIdEven[] = "key-Id-One123456"; +constexpr char kEcmKeyDataEven[] = "KKEEYYDDAATTAAee"; +constexpr char kEcmWrappedKeyEven[] = "1234567890123456"; +constexpr char kEcmWrappedKeyIvEven[] = "abcdefghacbdefgh"; +constexpr char kEcmContentIvEven[] = "ABCDEFGH"; + +constexpr char kEcmKeyIdOdd[] = "key-Id-Two456789"; +constexpr char kEcmKeyDataOdd[] = "kkeeyyddaattaaOO"; +constexpr char kEcmWrappedKeyOdd[] = "9876543210654321"; +constexpr char kEcmWrappedKeyIvOdd[] = "a1c2e3g4a1c2e3g4"; +constexpr char kEcmContentIvOdd[] = "AaCbEcGd"; + +constexpr char kFakeCasEncryptionResponseKeyId[] = "fake_key_id....."; +constexpr char kFakeCasEncryptionResponseKeyData[] = + "fakefakefakefakefakefakefakefake"; + +util::Status HandleCasEncryptionRequest(const std::string& request_string, + std::string* signed_response_string) { + CasEncryptionRequest request; + request.ParseFromString(request_string); + + CasEncryptionResponse response; + response.set_status(CasEncryptionResponse_Status_OK); + response.set_content_id(request.content_id()); + for (const auto& track_type : request.track_types()) { + if (request.key_rotation()) { + // Add the Even key. + auto key = response.add_entitlement_keys(); + key->set_key_id(kFakeCasEncryptionResponseKeyId); + key->set_key(kFakeCasEncryptionResponseKeyData); + key->set_track_type(track_type); + key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_EVEN); + // Add the Odd key. + key = response.add_entitlement_keys(); + key->set_key_id(kFakeCasEncryptionResponseKeyId); + key->set_key(kFakeCasEncryptionResponseKeyData); + key->set_track_type(track_type); + key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_ODD); + } else { + auto key = response.add_entitlement_keys(); + key->set_key_id(kFakeCasEncryptionResponseKeyId); + key->set_key(kFakeCasEncryptionResponseKeyData); + key->set_track_type(track_type); + key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_SINGLE); + } + } + std::string response_string; + response.SerializeToString(&response_string); + SignedCasEncryptionResponse signed_response; + signed_response.set_response(response_string); + signed_response.SerializeToString(signed_response_string); + return util::OkStatus(); +} + +} // namespace + +class CasEcmGeneratorTest : public testing::Test { + protected: + void SetUp() override { + } + + util::Status ProcessEcmParameters(const EcmParameters& params, + std::vector* keys) { + return ecm_gen_.ProcessEcmParameters(params, keys); + } + + int EntitlementKeySize(const EcmParameters& params) const { + return params.entitlement_key_id.size(); + } + + void SetTestConfig1(EcmParameters* params) { + params->entitlement_key_id = kEntitlementKeySingle; + params->rotation_enabled = false; + params->key_params.push_back(kKeyParamsSingle); + } + + void SetTestConfig2(EcmParameters* params) { + KeyParameters even_key_params; + KeyParameters odd_key_params; + params->entitlement_key_id = kEntitlementKeyDouble; + params->rotation_enabled = true; + params->rotation_periods = 2; + params->key_params.push_back(kKeyParamsEven); + params->key_params.push_back(kKeyParamsOdd); + } + + // Call this to setup the guts (CasEcm) of the ECM Generator. + void PrepareEcmGenerator(bool key_rotation_enabled) { + ecm_ = absl::make_unique(); + std::string entitlement_request; + std::string entitlement_response; + ecm_init_params_.key_rotation_enabled = key_rotation_enabled; + ecm_init_params_.track_types.push_back("SD"); + ASSERT_OK(ecm_->Initialize(kContentId, kProvider, ecm_init_params_, + &entitlement_request)); + ASSERT_OK( + HandleCasEncryptionRequest(entitlement_request, &entitlement_response)); + ASSERT_OK(ecm_->ProcessCasEncryptionResponse(entitlement_response)); + ecm_gen_.set_ecm(std::move(ecm_)); + } + + const KeyParameters kKeyParamsSingle{kEcmKeyIdSingle, + kEcmKeyDataSingle, + kEcmWrappedKeySingle, + kEcmWrappedKeyIvSingle, + {kEcmContentIvSingle}}; + const KeyParameters kKeyParamsEven{kEcmKeyIdEven, + kEcmKeyDataEven, + kEcmWrappedKeyEven, + kEcmWrappedKeyIvEven, + {kEcmContentIvEven}}; + const KeyParameters kKeyParamsOdd{kEcmKeyIdOdd, + kEcmKeyDataOdd, + kEcmWrappedKeyOdd, + kEcmWrappedKeyIvOdd, + {kEcmContentIvOdd}}; + + std::unique_ptr ecm_; + EcmInitParameters ecm_init_params_; + CasEcmGenerator ecm_gen_; +}; + +TEST_F(CasEcmGeneratorTest, InitializeNoRotation) { + EXPECT_FALSE(ecm_gen_.initialized()); + + PrepareEcmGenerator(false); + + EcmParameters ecm_params; + std::vector keys; + + SetTestConfig1(&ecm_params); + + util::Status status = ProcessEcmParameters(ecm_params, &keys); + ASSERT_OK(status); + + ASSERT_EQ(EntitlementKeySize(ecm_params), 16); + ASSERT_TRUE(ecm_gen_.initialized()); + EXPECT_FALSE(ecm_gen_.rotation_enabled()); +} + +TEST_F(CasEcmGeneratorTest, GenerateNoRotation) { + PrepareEcmGenerator(false); + EcmParameters ecm_params; + + SetTestConfig1(&ecm_params); + + std::string ecm_string = ecm_gen_.GenerateEcm(ecm_params); + + // Expected size (bytes): + // CA system ID: 2 bytes + // version: 1 byte + // generation + flags: 1 byte + // flags: 1 byte + // entitlement key ID: 16 bytes + // Single key: ID (16), Data (16), IV (16), IV (8) = 56 + // total = 77 + ASSERT_EQ(77, ecm_string.size()); + EXPECT_EQ('\x4A', ecm_string[0]); // CA System ID first byte. + EXPECT_EQ('\xD4', ecm_string[1]); // CA System ID second byte. + EXPECT_EQ('\x01', ecm_string[2]); // ECM version + EXPECT_EQ('\x02', ecm_string[3]); // generation + flags + EXPECT_EQ('\x80', ecm_string[4]); // flags + EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(5, 16)); + EXPECT_EQ(kEcmKeyIdSingle, ecm_string.substr(21, 16)); + EXPECT_NE(kEcmWrappedKeySingle, ecm_string.substr(37, 16)); + EXPECT_NE(kEcmWrappedKeyIvSingle, ecm_string.substr(53, 16)); + // Unwrap key and compare with original. + std::string wrapping_key = kFakeCasEncryptionResponseKeyData; + std::string wrapping_iv = ecm_string.substr(53, 16); + std::string wrapped_key = ecm_string.substr(37, 16); + std::string unwrapped_key = + crypto_util::DecryptAesCbcNoPad(wrapping_key, wrapping_iv, wrapped_key); + EXPECT_EQ(kEcmKeyDataSingle, unwrapped_key); + EXPECT_EQ(kEcmContentIvSingle, ecm_string.substr(69, 8)); +} + +TEST_F(CasEcmGeneratorTest, Generate2NoRotation) { + PrepareEcmGenerator(false); + EcmParameters ecm_params; + + SetTestConfig1(&ecm_params); + + std::string ecm_string = ecm_gen_.GenerateEcm(ecm_params); + ecm_string = ecm_gen_.GenerateEcm(ecm_params); + + // Second generate should have higher generation number. + ASSERT_EQ(77, ecm_string.size()); + EXPECT_EQ('\x4A', ecm_string[0]); // CA System ID first byte. + EXPECT_EQ('\xD4', ecm_string[1]); // CA System ID second byte. + EXPECT_EQ('\x01', ecm_string[2]); // ECM version + EXPECT_EQ('\x0A', ecm_string[3]); // generation + flags + EXPECT_EQ('\x80', ecm_string[4]); // flags + EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(5, 16)); + EXPECT_EQ(kEcmKeyIdSingle, ecm_string.substr(21, 16)); + EXPECT_NE(kEcmWrappedKeySingle, ecm_string.substr(37, 16)); + EXPECT_NE(kEcmWrappedKeyIvSingle, ecm_string.substr(53, 16)); + // Unwrap key and compare with original. + std::string wrapping_key = kFakeCasEncryptionResponseKeyData; + std::string wrapping_iv = ecm_string.substr(53, 16); + std::string wrapped_key = ecm_string.substr(37, 16); + std::string unwrapped_key = + crypto_util::DecryptAesCbcNoPad(wrapping_key, wrapping_iv, wrapped_key); + EXPECT_EQ(kEcmKeyDataSingle, unwrapped_key); + EXPECT_EQ(kEcmContentIvSingle, ecm_string.substr(69, 8)); +} + +TEST_F(CasEcmGeneratorTest, InitializeSimpleRotation) { + EXPECT_FALSE(ecm_gen_.initialized()); + EcmParameters ecm_params; + std::vector keys; + + PrepareEcmGenerator(true); + + SetTestConfig2(&ecm_params); + ecm_init_params_.key_rotation_enabled = true; + + util::Status status = ProcessEcmParameters(ecm_params, &keys); + + EXPECT_TRUE(status.ok()); + EXPECT_TRUE(ecm_gen_.initialized()); + EXPECT_TRUE(ecm_gen_.rotation_enabled()); +} + +TEST_F(CasEcmGeneratorTest, GenerateSimpleRotation) { + EcmParameters ecm_params; + + PrepareEcmGenerator(true); + + SetTestConfig2(&ecm_params); + + std::string ecm_string = ecm_gen_.GenerateEcm(ecm_params); + + // Expected size (bytes): + // same as no rotation case = 77 + // second entitlement key ID: 16 bytes + // Second key: ID (16), Data (16), IV (16), IV (8) = 56 + // total = 149 + ASSERT_EQ(149, ecm_string.size()); + EXPECT_EQ('\x4A', ecm_string[0]); // CA System ID first byte. + EXPECT_EQ('\xD4', ecm_string[1]); // CA System ID second byte. + EXPECT_EQ('\x01', ecm_string[2]); // ECM version + EXPECT_EQ('\x03', ecm_string[3]); // generation + flags + EXPECT_EQ('\x80', ecm_string[4]); // flags + EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(5, 16)); + EXPECT_EQ(kEcmKeyIdEven, ecm_string.substr(21, 16)); + EXPECT_NE(kEcmWrappedKeyEven, ecm_string.substr(37, 16)); + EXPECT_NE(kEcmWrappedKeyIvEven, ecm_string.substr(53, 16)); + EXPECT_EQ(kEcmContentIvEven, ecm_string.substr(69, 8)); + EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(77, 16)); + EXPECT_EQ(kEcmKeyIdOdd, ecm_string.substr(93, 16)); + EXPECT_NE(kEcmWrappedKeyOdd, ecm_string.substr(109, 16)); + EXPECT_NE(kEcmWrappedKeyIvOdd, ecm_string.substr(125, 16)); + EXPECT_EQ(kEcmContentIvOdd, ecm_string.substr(141, 8)); + // Unwrap even key and compare with original. + std::string wrapping_key_even = kFakeCasEncryptionResponseKeyData; + std::string wrapping_iv_even = ecm_string.substr(53, 16); + std::string wrapped_key_even = ecm_string.substr(37, 16); + std::string unwrapped_key_even = crypto_util::DecryptAesCbcNoPad( + wrapping_key_even, wrapping_iv_even, wrapped_key_even); + EXPECT_EQ(kEcmKeyDataEven, unwrapped_key_even); + // Unwrap odd key and compare with original. + std::string wrapping_key_odd = kFakeCasEncryptionResponseKeyData; + std::string wrapping_iv_odd = ecm_string.substr(125, 16); + std::string wrapped_key_odd = ecm_string.substr(109, 16); + std::string unwrapped_key_odd = crypto_util::DecryptAesCbcNoPad( + wrapping_key_odd, wrapping_iv_odd, wrapped_key_odd); + EXPECT_EQ(kEcmKeyDataOdd, unwrapped_key_odd); +} + +} // namespace cas +} // namespace widevine diff --git a/protos/public/BUILD b/protos/public/BUILD new file mode 100644 index 0000000..8f4205b --- /dev/null +++ b/protos/public/BUILD @@ -0,0 +1,27 @@ +################################################################################ +# Copyright 2018 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +# Protocol buffer definitions for Widevine media cas packager sdk. + +package(default_visibility = ["//visibility:public"]) + +load("@protobuf_repo//:protobuf.bzl", "cc_proto_library") + +cc_proto_library( + name = "media_cas_encryption_proto", + srcs = ["media_cas_encryption.proto"], + default_runtime = "@protobuf_repo//:protobuf", + protoc = "@protobuf_repo//:protoc", +) + +cc_proto_library( + name = "media_cas_proto", + srcs = ["media_cas.proto"], + default_runtime = "@protobuf_repo//:protobuf", + protoc = "@protobuf_repo//:protoc", +) diff --git a/protos/public/media_cas.proto b/protos/public/media_cas.proto new file mode 100644 index 0000000..60e4f2e --- /dev/null +++ b/protos/public/media_cas.proto @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + + +syntax = "proto2"; + +package widevine.cas; + +// Encrypt/decrypt mode. +enum CasCryptoMode { + CRYPTO_MODE_UNSPECIFIED = 0; + CTR = 1; + CBC = 2; +}; + +// Widevine private data in the CA descriptor. +message CaDescriptorPrivateData { + // Provider name. + optional string provider = 1; + + // Content ID. + optional bytes content_id = 2; +} diff --git a/protos/public/media_cas_encryption.proto b/protos/public/media_cas_encryption.proto new file mode 100644 index 0000000..81d0195 --- /dev/null +++ b/protos/public/media_cas_encryption.proto @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +// Protocol buffer definitions for Widevine CAS. + +syntax = "proto2"; + +option java_package = "com.google.video.widevine.mediacasencryption"; + +package widevine; + +message CasEncryptionRequest { + optional bytes content_id = 1; + optional string provider = 2; + // Optional track types such as "AUDIO", SD" or "HD". + repeated string track_types = 3; + // Indicates if the client is using key rotation. If true, the server will + // return one key for EVEN and one key for ODD, otherwise only a single key is + // returned. + optional bool key_rotation = 4; +} + +message CasEncryptionResponse { + enum Status { + STATUS_UNSPECIFIED = 0; + OK = 1; + SIGNATURE_FAILED = 2; + ACCESS_DENIED = 3; + INTERNAL_ERROR = 4; + INVALID_ARGUMENT = 5; + PROVIDER_ID_MISSING = 6; + CONTENT_ID_MISSING = 7; + TRACK_TYPE_MISSING = 8; + } + message KeyInfo { + enum KeySlot { + KEY_SLOT_UNSPECIFIED = 0; + SINGLE = 1; + EVEN = 2; + ODD = 3; + }; + optional bytes key_id = 1; + optional bytes key = 2; + // Optional label used for the key. + optional string track_type = 3; + optional KeySlot key_slot = 4; + } + optional Status status = 1; + optional string status_message = 2; + optional bytes content_id = 3; + repeated KeyInfo entitlement_keys = 4; +} + +message SignedCasEncryptionRequest { + optional bytes request = 1; + optional bytes signature = 2; + // Identifies the entity sending / signing the request. + optional string signer = 3; +} + +message SignedCasEncryptionResponse { + // Serialized CasEncryptionResponse message. + optional bytes response = 1; + optional bytes signature = 2; +} + +message HttpResponse { + optional bytes response = 1; +} diff --git a/strings/BUILD b/strings/BUILD new file mode 100644 index 0000000..5009d10 --- /dev/null +++ b/strings/BUILD @@ -0,0 +1,34 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "strings", + srcs = [ + "serialize.cc", + ], + hdrs = [ + "serialize.h", + ], + deps = [ + "//base", + ], +) + +cc_test( + name = "serialize_test", + srcs = ["serialize_test.cc"], + deps = [ + ":strings", + "//base", + "//testing:gunit_main", + ], +) diff --git a/strings/serialize.cc b/strings/serialize.cc new file mode 100644 index 0000000..1aaa836 --- /dev/null +++ b/strings/serialize.cc @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "strings/serialize.h" + +#include + +#include "glog/logging.h" + +namespace widevine { +namespace strings { + +uint32_t KeyToUint32(const std::string& key) { + CHECK_GE(key.size(), 4); + return static_cast(key[0]) << 24 | + static_cast(key[1]) << 16 | + static_cast(key[2]) << 8 | static_cast(key[3]); +} + +std::string EncodeUint64(uint64_t value) { + std::string s; + s.resize(sizeof(value)); + memcpy(&s[0], &value, sizeof(value)); + return s; +} + +} // namespace strings +} // namespace widevine diff --git a/strings/serialize.h b/strings/serialize.h new file mode 100644 index 0000000..b161cc5 --- /dev/null +++ b/strings/serialize.h @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef STRINGS_SERIALIZE_H_ +#define STRINGS_SERIALIZE_H_ + +#include + +namespace widevine { +namespace strings { + +// Convert a 4-byte key in network byte order to uint32_t +uint32_t KeyToUint32(const std::string& key); +// Encode a unit64 value to a string. +std::string EncodeUint64(uint64_t value); + +} // namespace strings +} // namespace widevine + +#endif // STRINGS_SERIALIZE_H_ diff --git a/strings/serialize_test.cc b/strings/serialize_test.cc new file mode 100644 index 0000000..38e5347 --- /dev/null +++ b/strings/serialize_test.cc @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// Contains the test cases for EncodeUint64 and KeyToUint32. + +#include "strings/serialize.h" + +#include "testing/gunit.h" + +namespace widevine { +namespace strings { + +TEST(SerializeTest, KeyToUint32) { + std::string val = "\x12\x34\x56\x78"; + EXPECT_EQ(0x12345678, KeyToUint32(val)); + val = "\x87\x65\x43\x21"; + EXPECT_EQ(0x87654321, KeyToUint32(val)); +} + +TEST(SerializeTest, EncodeUint64) { + uint64_t q1 = 0x4142434441424344; + std::string uint64_str = EncodeUint64(q1); + EXPECT_TRUE(uint64_str == "DCBADCBA" || uint64_str == "ABCDABCD"); +} + +} // namespace strings +} // namespace widevine diff --git a/testing/BUILD b/testing/BUILD new file mode 100644 index 0000000..bad30ce --- /dev/null +++ b/testing/BUILD @@ -0,0 +1,31 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "gunit", + hdrs = [ + "gmock.h", + "gunit.h", + ], + deps = [ + "//external:gtest", + "//util:status", + ], +) + +cc_library( + name = "gunit_main", + deps = [ + ":gunit", + "//external:gtest_main", + ], +) diff --git a/testing/gmock.h b/testing/gmock.h new file mode 100644 index 0000000..bdc95c0 --- /dev/null +++ b/testing/gmock.h @@ -0,0 +1,14 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TESTING_GMOCK_H_ +#define TESTING_GMOCK_H_ + +#include + +#endif // TESTING_GMOCK_H_ diff --git a/testing/gunit.h b/testing/gunit.h new file mode 100644 index 0000000..671dd49 --- /dev/null +++ b/testing/gunit.h @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TESTING_GUNIT_H_ +#define TESTING_GUNIT_H_ + +#include + +#define EXPECT_OK(expression) \ + EXPECT_EQ(util::error::OK, expression.error_code()) +#define ASSERT_OK(expression) \ + ASSERT_EQ(util::error::OK, expression.error_code()) + +#endif // TESTING_GUNIT_H_ diff --git a/util/BUILD b/util/BUILD new file mode 100644 index 0000000..714b6ef --- /dev/null +++ b/util/BUILD @@ -0,0 +1,68 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + + +filegroup( + name = "binary_release_files", + srcs = [ + "error_space.h", + "status.h", + ], +) + +cc_library( + name = "status", + srcs = [ + "status.cc", + ], + hdrs = [ + "status.h", + ], + deps = [ + ":error_space", + "//base", + "@abseil_repo//absl/strings", + ], +) + +cc_library( + name = "error_space", + hdrs = ["error_space.h"], +) + +cc_library( + name = "proto_status", + hdrs = ["proto_status.h"], + deps = [ + ":status", + "//external:protobuf", + ], +) + +cc_test( + name = "status_test", + srcs = ["status_test.cc"], + deps = [ + ":status", + "//testing:gunit_main", + ], +) + +cc_test( + name = "error_space_test", + srcs = ["error_space_test.cc"], + deps = [ + ":error_space", + "//testing:gunit_main", + ], +) + diff --git a/util/endian/BUILD b/util/endian/BUILD new file mode 100644 index 0000000..fbe70d4 --- /dev/null +++ b/util/endian/BUILD @@ -0,0 +1,19 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + + +cc_library( + name = "endian", + hdrs = [ + "endian.h", + ], +) diff --git a/util/endian/endian.h b/util/endian/endian.h new file mode 100644 index 0000000..df04c32 --- /dev/null +++ b/util/endian/endian.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_ENDIAN_ENDIAN_H_ +#define UTIL_ENDIAN_ENDIAN_H_ + +#include +#include + + +namespace widevine { + +// Utilities to convert numbers between the current hosts's native byte +// order and big-endian byte order (same as network byte order) +class BigEndian { + public: + static uint32_t Load32(const char* data) { + return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + } + + static uint64_t Load64(const char* data) { + return ((static_cast(Load32(data)) << 32) | Load32(data + 4)); + } + + static void Store32(void* p, uint32_t data) { + for (int i = 0; i < 4; ++i) + (reinterpret_cast(p))[i] = (data >> ((3 - i) * 8)) & 0xFF; + } +}; // BigEndian + +inline uint64_t gntohll(uint64_t n) { + uint64_t retval; +#if __BYTE_ORDER == __BIG_ENDIAN + retval = n; +#else + retval = ((uint64_t) ntohl(n & 0xFFFFFFFFLLU)) << 32; + retval |= ntohl((n & 0xFFFFFFFF00000000LLU) >> 32); +#endif + return(retval); +} + +} // namespace widevine + +#endif // UTIL_ENDIAN_ENDIAN_H_ diff --git a/util/error_space.h b/util/error_space.h new file mode 100644 index 0000000..68c093a --- /dev/null +++ b/util/error_space.h @@ -0,0 +1,83 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_ERROR_SPACE_H_ +#define UTIL_ERROR_SPACE_H_ + +#include + +namespace widevine { +namespace util { + +class ErrorSpace { + public: + std::string SpaceName() const { return space_name_func_(this); } + std::string String(int code) const { return code_to_string_func_(this, code); } + + protected: + // typedef instead of using statements for SWIG compatibility. + typedef std::string (*SpaceNameFunc)(const ErrorSpace* space); + typedef std::string (*CodeToStringFunc)(const ErrorSpace* space, int code); + constexpr ErrorSpace(SpaceNameFunc space_name_func, + CodeToStringFunc code_to_string_func) + : space_name_func_(space_name_func), + code_to_string_func_(code_to_string_func) {} + + private: + const SpaceNameFunc space_name_func_; + const CodeToStringFunc code_to_string_func_; +}; + +// Manages creation of error space subclasses. +template +class ErrorSpaceImpl : public ErrorSpace { + public: + constexpr ErrorSpaceImpl() + : ErrorSpace(&ErrorSpaceImpl::SpaceNameImpl, + &ErrorSpaceImpl::CodeToStringImpl) {} + + // Returns the canonical instance of the `T` error space. + static constexpr const T* Get(); + + private: + // These functions adapt the stateful implementation that takes a space + // pointer to stateless static methods, so that clients of ErrorSpaceImpl are + // safe to have constexpr global instances. + static std::string SpaceNameImpl(const ErrorSpace* /*space*/) { + return T::SpaceName(); + } + + static std::string CodeToStringImpl(const ErrorSpace* /*space*/, int code) { + return T::CodeToString(code); + } +}; + +namespace internal { + +// Provides a global constexpr instance of the error space `T`. +// We need the indirection because ErrorSpaceImpl can't declare constexpr +// instances of T since it is not yet fully declared. +template +struct ErrorSpaceInstance { + static constexpr T value = {}; +}; + +template +constexpr T ErrorSpaceInstance::value; + +} // namespace internal + +template +constexpr const T* ErrorSpaceImpl::Get() { + return &internal::ErrorSpaceInstance::value; +} + +} // namespace util +} // namespace widevine + +#endif // UTIL_ERROR_SPACE_H_ diff --git a/util/error_space_test.cc b/util/error_space_test.cc new file mode 100644 index 0000000..a32d339 --- /dev/null +++ b/util/error_space_test.cc @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "util/error_space.h" + +#include "testing/gunit.h" + +namespace widevine { +namespace util { +namespace { + +class Space1 : public util::ErrorSpaceImpl { + public: + static std::string SpaceName() { return "Space1"; } + static std::string CodeToString(int code) { return "Test" + std::to_string(code); } +}; + +TEST(ErrorSpaceTest, Basic) { + const ErrorSpace* space1 = Space1::Get(); + EXPECT_EQ("Space1", space1->SpaceName()); + EXPECT_EQ(Space1::CodeToString(23), space1->String(23)); +} + +} // namespace +} // namespace util +} // namespace widevine diff --git a/util/gtl/BUILD b/util/gtl/BUILD new file mode 100644 index 0000000..37a9667 --- /dev/null +++ b/util/gtl/BUILD @@ -0,0 +1,19 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + + +cc_library( + name = "map_util", + hdrs = [ + "map_util.h", + ], +) diff --git a/util/gtl/map_util.h b/util/gtl/map_util.h new file mode 100644 index 0000000..1fec8b7 --- /dev/null +++ b/util/gtl/map_util.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_GTL_MAP_UTIL_H_ +#define UTIL_GTL_MAP_UTIL_H_ + +namespace widevine { +namespace gtl { + +template +const typename Collection::value_type::second_type& FindWithDefault( + const Collection& collection, + const typename Collection::value_type::first_type& key, + const typename Collection::value_type::second_type& value) { + typename Collection::const_iterator it = collection.find(key); + if (it == collection.end()) { + return value; + } + return it->second; +} + +template +typename Collection::value_type::second_type* FindOrNull( + const Collection& collection, + const typename Collection::value_type::first_type& key) { + typename Collection::iterator it = collection.find(key); + if (it == collection.end()) { + return nullptr; + } + return &it->second; +} + +template +typename Collection::value_type::second_type* FindOrNull( + Collection& collection, // NOLINT + const typename Collection::value_type::first_type& key) { + typename Collection::iterator it = collection.find(key); + if (it == collection.end()) { + return nullptr; + } + return &it->second; +} + +template +bool ContainsKey(const C& collection, const K& key) { + typename C::const_iterator it = collection.find(key); + return it != collection.end(); +} + +} // namespace gtl +} // namespace widevine + +#endif // UTIL_GTL_MAP_UTIL_H_ diff --git a/util/proto_status.h b/util/proto_status.h new file mode 100644 index 0000000..2205db4 --- /dev/null +++ b/util/proto_status.h @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_PROTO_STATUS_H_ +#define UTIL_PROTO_STATUS_H_ + +#include "google/protobuf/descriptor.h" +#include "google/protobuf/generated_enum_reflection.h" +#include "util/status.h" + +namespace widevine { +namespace util { + +template +class ProtoEnumErrorSpace : public ErrorSpaceImpl> { + public: + static std::string SpaceName() { + return google::protobuf::GetEnumDescriptor()->full_name(); + } + + static std::string CodeToString(int code) { + const google::protobuf::EnumValueDescriptor* v = + google::protobuf::GetEnumDescriptor()->FindValueByNumber(code); + if (v) return v->name(); + return std::to_string(code); + } +}; + +} // namespace util +} // namespace widevine + +#endif // UTIL_PROTO_STATUS_H_ diff --git a/util/proto_status_test.cc b/util/proto_status_test.cc new file mode 100644 index 0000000..ea84e04 --- /dev/null +++ b/util/proto_status_test.cc @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2007 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "util/proto_status.h" + +#include "testing/gunit.h" +#include "protos/public/errors.pb.h" + +namespace widevine { +namespace util { + +TEST(StatusTest, PROVIDER_ID_MISMATCH_Status) { + Status status(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "provider_id_mismatch"); + EXPECT_EQ("Errors::PROVIDER_ID_MISMATCH: provider_id_mismatch", + status.ToString()); +} + +TEST(StatusTest, PROVIDER_ID_MISMATCH_Status2) { + Status status(static_cast(PROVIDER_ID_MISMATCH), + "provider_id_mismatch"); + EXPECT_EQ("Errors::157: provider_id_mismatch", status.ToString()); +} + +TEST(StatusTest, Same) { + Status status1(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "provider_id_mismatch"); + Status status2(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "this is a provider_id_mismatch error"); + EXPECT_EQ(status1, status2); +} + +TEST(StatusTest, NotTheSameStatus) { + Status status1(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "provider_id_mismatch"); + Status status2(ProtoEnumErrorSpace::Get(), MISSING_CLIENT_ID, + "missing client id"); + EXPECT_NE(status1, status2); +} + +TEST(StatusTest, NotTheSameErrorSpace) { + Status status1(ProtoEnumErrorSpace::Get(), PROVIDER_ID_MISMATCH, + "provider_id_mismatch"); + Status status2(static_cast(PROVIDER_ID_MISMATCH), + "this is a provider_id_mismatch error"); + EXPECT_NE(status1, status2); +} + +} // namespace util +} // namespace widevine diff --git a/util/random/BUILD b/util/random/BUILD new file mode 100644 index 0000000..c3c968f --- /dev/null +++ b/util/random/BUILD @@ -0,0 +1,31 @@ +################################################################################ +# Copyright 2017 Google LLC. +# +# This software is licensed under the terms defined in the Widevine Master +# License Agreement. For a copy of this agreement, please contact +# widevine-licensing@google.com. +################################################################################ + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "global_id", + hdrs = [ + "global_id.h", + ], + deps = [ + "//external:openssl", + ], +) + +cc_test( + name = "global_id_test", + srcs = ["global_id_test.cc"], + deps = [ + ":global_id", + "//base", + "//testing:gunit_main", + ], +) diff --git a/util/random/global_id.h b/util/random/global_id.h new file mode 100644 index 0000000..b91da81 --- /dev/null +++ b/util/random/global_id.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_RANDOM_GLOBAL_ID_H_ +#define UTIL_RANDOM_GLOBAL_ID_H_ + +#include "openssl/rand.h" + +namespace widevine { +namespace util { +namespace random { + +static uint64_t NewGlobalID() { + static __thread uint64_t val = 0; + if (val == 0) { + RAND_bytes(reinterpret_cast(&val), sizeof(val)); + } + return val++; +} + +} // namespace random +} // namespace util +} // namespace widevine + +#endif // UTIL_RANDOM_GLOBAL_ID_H_ diff --git a/util/random/global_id_test.cc b/util/random/global_id_test.cc new file mode 100644 index 0000000..6aea7b7 --- /dev/null +++ b/util/random/global_id_test.cc @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "util/random/global_id.h" + +#include "glog/logging.h" +#include "testing/gunit.h" + +namespace widevine { +namespace util { +namespace random { + +TEST(GlobalIDTest, NewGlobalID) { + uint64_t id = NewGlobalID(); + LOG(INFO) << "NewGlobalID " << id; + EXPECT_EQ(id + 1, NewGlobalID()); + EXPECT_EQ(id + 2, NewGlobalID()); +} + +} // namespace random +} // namespace util +} // namespace widevine diff --git a/util/status.cc b/util/status.cc new file mode 100644 index 0000000..2936549 --- /dev/null +++ b/util/status.cc @@ -0,0 +1,62 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include + +#include "base/macros.h" +#include "absl/strings/str_cat.h" +#include "util/status.h" + +namespace widevine { +namespace util { +namespace { + +const char* kLicenseServerStatusMessage[] = {"OK", + "UNKNOWN_ERROR", + "UNKNOWN_ERROR", + "INVALID_ARGUMENT", + "UNKNOWN_ERROR", + "NOT_FOUND", + "ALREADY_EXISTS", + "PERMISSION_DENIED", + "UNKNOWN_ERROR", + "UNKNOWN_ERROR", + "UNKNOWN_ERROR", + "UNKNOWN_ERROR", + "UNIMPLEMENTED", + "INTERNAL", + "UNAVAILABLE"}; + +} // namespace + +std::string GenericErrorSpace::SpaceName() { return "generic"; } + +std::string GenericErrorSpace::CodeToString(int code) { + static_assert( + arraysize(kLicenseServerStatusMessage) == error::NUM_ERRORS, + "mismatching license_server_sdk status message and license_server_sdk " + "status."); + + if (code >= 0 && code < error::NUM_ERRORS) + return kLicenseServerStatusMessage[code]; + return std::to_string(code); +} + +std::string Status::ToString() const { + if (status_code_ == error::OK) return "OK"; + return absl::StrCat("Errors::", error_space_->String(status_code_), ": ", + error_message_); +} + +std::ostream& operator<<(std::ostream& os, const Status& x) { + os << x.ToString(); + return os; +} + +} // namespace util +} // namespace widevine diff --git a/util/status.h b/util/status.h new file mode 100644 index 0000000..5e3b15b --- /dev/null +++ b/util/status.h @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef UTIL_STATUS_H_ +#define UTIL_STATUS_H_ + +#include + +#include "util/error_space.h" + +namespace widevine { +namespace util { +namespace error { + +enum StatusCode { + // Success. + OK = 0, + + // Client specified an invalid argument. + INVALID_ARGUMENT = 3, + + // Some requested entity (e.g., file or directory) was not found. + NOT_FOUND = 5, + + // Some entity that we attempted to create (e.g., file or directory) + // already exists. + ALREADY_EXISTS = 6, + + // The caller does not have permission to execute the specified + // operation. PERMISSION_DENIED must not be used for rejections + // caused by exhausting some resource (use RESOURCE_EXHAUSTED + // instead for those errors). + PERMISSION_DENIED = 7, + + // Operation is not implemented or not supported/enabled in this service. + UNIMPLEMENTED = 12, + + // Internal errors. Means some invariants expected by underlying + // system has been broken. If you see one of these errors, + // something is very broken. + INTERNAL = 13, + + // Operation is not implemented or not supported/enabled in this service. + UNAVAILABLE = 14, + + // Number of generic (non license related) errors. + NUM_ERRORS, +}; + +} // namespace error + +class GenericErrorSpace : public ErrorSpaceImpl { + public: + static std::string SpaceName(); + static std::string CodeToString(int code); +}; + +class Status { + public: + Status() : status_code_(error::OK) {} + ~Status() {} + explicit Status(error::StatusCode c) : status_code_(c) {} + Status(error::StatusCode c, const std::string& error_message) + : status_code_(c), error_message_(error_message) {} + Status(const ErrorSpace* e, error::StatusCode c, + const std::string& error_message) { + SetError(e, c, error_message); + } + Status(const ErrorSpace* e, int error, const std::string& error_message) { + SetError(e, error, error_message); + } + void SetError(const ErrorSpace* e, int c, const std::string& error_message) { + error_space_ = e; + status_code_ = c; + error_message_ = error_message; + } + + bool ok() const { return status_code_ == error::OK; } + const ErrorSpace* error_space() const { return error_space_; } + static const ErrorSpace* canonical_space() { + return GenericErrorSpace::Get(); + } + std::string ToString() const; + std::string error_message() const { return error_message_; } + int error_code() const { return status_code_; } + + private: + const ErrorSpace* error_space_ = GenericErrorSpace::Get(); + int status_code_; + std::string error_message_; +}; // class Status + +inline Status OkStatus() { return Status(); } + +// Here error_message_ is ignored during comparison. +inline bool operator==(const Status& s1, const Status& s2) { + return s1.error_space() == s2.error_space() && + s1.error_code() == s2.error_code(); +} +inline bool operator!=(const Status& s1, const Status& s2) { + return s1.error_space() != s2.error_space() || + s1.error_code() != s2.error_code(); +} + +// Prints a human-readable representation of 'x' to 'os'. +std::ostream& operator<<(std::ostream& os, const Status& x); + +#define CHECK_OK(expression) CHECK_EQ(util::error::OK, expression.error_code()) + + +} // namespace util +} // namespace widevine + +#endif // UTIL_STATUS_H_ diff --git a/util/status_test.cc b/util/status_test.cc new file mode 100644 index 0000000..a91259a --- /dev/null +++ b/util/status_test.cc @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2007 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// + +#include "util/status.h" + +#include "testing/gunit.h" + +namespace widevine { +namespace util { + +TEST(StatusTest, OK_Status) { + // test case for ok status. + Status status(error::OK); + EXPECT_EQ("OK", status.ToString()); +} + +TEST(StatusTest, OK_Status2) { + // test case for ok status. + Status status; + EXPECT_EQ("OK", status.ToString()); +} + +TEST(StatusTest, ALREADY_EXISTS_Status) { + Status status(error::ALREADY_EXISTS, "it is already exist"); + EXPECT_EQ("Errors::ALREADY_EXISTS: it is already exist", status.ToString()); +} + +// test case for status in boundary cases. +TEST(StatusTest, UNAVAILABLE_Status) { + Status status(error::UNAVAILABLE, "unavailable"); + EXPECT_EQ("Errors::UNAVAILABLE: unavailable", status.ToString()); +} + +TEST(StatusTest, NoNameCode) { + Status status(static_cast(101), "Unknown error"); + EXPECT_EQ("Errors::101: Unknown error", status.ToString()); +} + +TEST(StatusTest, EQUAL_OPERATOR) { + Status status1(error::ALREADY_EXISTS, "already exists 1"); + Status status2(error::ALREADY_EXISTS, "already exists 2"); + EXPECT_EQ(status1, status2); +} + +TEST(StatusTest, NOT_EQUAL_OPERATOR) { + Status status1(error::ALREADY_EXISTS, "already exists"); + Status status2(error::UNAVAILABLE, "unavailable"); + EXPECT_NE(status1, status2); +} + +TEST(StatusTest, NOT_EQUAL_OPERATOR_NONE_MSG) { + Status status1(error::ALREADY_EXISTS); + Status status2(error::UNAVAILABLE); + EXPECT_NE(status1, status2); +} + +} // namespace util +} // namespace widevine