Refactor and cleanup codes. No functional changes.

This commit is contained in:
KongQun Yang
2019-01-23 15:16:31 -08:00
parent 84f66d2320
commit 93265ab9d1
207 changed files with 14893 additions and 3332 deletions

2
BUILD
View File

@@ -1,5 +1,5 @@
################################################################################
# Copyright 2017 Google Inc.
# Copyright 2017 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact

View File

@@ -1,49 +1,62 @@
workspace(name = "provisioning_sdk")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository", "git_repository")
# CCTZ (Time-zone framework), needed by abseil.
git_repository(
name = "com_googlesource_code_cctz",
remote = "https://github.com/google/cctz.git",
tag = "v2.2",
)
git_repository(
name = "abseil_repo",
commit = "475d64f2de7403a01b1b36c487328ed41d29c20c", #2018-04-10
remote = "https://github.com/abseil/abseil-cpp.git",
)
git_repository(
name = "protobuf_repo",
remote = "https://github.com/google/protobuf.git",
tag = "v3.2.0",
tag = "v3.6.1.3",
)
git_repository(
name = "boringssl_repo",
commit = "bbcaa15b0647816b9a1a9b9e0d209cd6712f0105", # 2016-07-11
commit = "14164f6fef47b7ebd97cdb0cea1624eabd6fe6b8", # 2018-11-26
remote = "https://github.com/google/boringssl.git",
)
git_repository(
name = "gflags_repo",
commit = "fe57e5af4db74ab298523f06d2c43aa895ba9f98", # 2016-07-20
commit = "e171aa2d15ed9eb17054558e0b3a6a413bb01067", # 2018-11-11
remote = "https://github.com/gflags/gflags.git",
)
new_git_repository(
git_repository(
name = "googletest_repo",
build_file = "gtest.BUILD",
commit = "ec44c6c1675c25b9827aacd08c02433cccde7780", # 2016-07-26
commit = "b6cd405286ed8635ece71c72f118e659f4ade3fb", # 2019-01-04
remote = "https://github.com/google/googletest.git",
)
new_git_repository(
name = "glog_repo",
build_file = "glog.BUILD",
build_file = "@//:glog.BUILD",
commit = "0472b91c5defdf90cff7292e3bf7bd86770a9a0a", # 2016-07-13
remote = "https://github.com/google/glog.git",
)
# py_proto_library depends on six indirectly (py_proto_library uses
# protobuf_python as its default runtime which depends on six).
new_http_archive(
name = "six_archive",
build_file = "@protobuf_repo//:six.BUILD",
sha256 = "105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a",
url = "https://pypi.python.org/packages/source/s/six/six-1.10.0.tar.gz#md5=34eed507548117b2ab523ab14b2f8b55",
new_git_repository(
name = "six_repo",
build_file = "@//:six.BUILD",
remote = "https://github.com/benjaminp/six.git",
tag = "1.10.0",
)
bind(
name = "six",
actual = "@six_archive//:six",
actual = "@six_repo//:six",
)
bind(

View File

@@ -1,5 +1,5 @@
################################################################################
# Copyright 2016 Google Inc.
# Copyright 2016 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
@@ -14,10 +14,11 @@ cc_library(
name = "base",
hdrs = [
"macros.h",
"mutex.h",
"thread_annotations.h",
"timestamp.h",
],
deps = [
"@abseil_repo//absl/base",
"//external:gflags",
"//external:glog",
],

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -9,7 +9,7 @@
#ifndef BASE_MACROS_H_
#define BASE_MACROS_H_
#include <stddef.h> // For size_t
#include "absl/base/macros.h"
// DISALLOW_COPY_AND_ASSIGN disallows the copy constructor and copy assignment
// operator. DISALLOW_IMPLICIT_CONSTRUCTORS is like DISALLOW_COPY_AND_ASSIGN,
@@ -26,88 +26,6 @@
TypeName() = delete; \
DISALLOW_COPY_AND_ASSIGN(TypeName)
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
// used in defining new arrays, for example. If you use arraysize on
// a pointer by mistake, you will get a compile-time error.
//
// This template function declaration is used in defining arraysize.
// Note that the function doesn't need an implementation, as we only
// use its type.
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
// That gcc wants both of these prototypes seems mysterious. VC, for
// its part, can't decide which to use (another mystery). Matching of
// template overloads: the final frontier.
#ifndef COMPILER_MSVC
template <typename T, size_t N>
char (&ArraySizeHelper(const T (&array)[N]))[N];
#endif
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
// A macro to turn a symbol into a std::string
#define AS_STRING(x) AS_STRING_INTERNAL(x)
#define AS_STRING_INTERNAL(x) #x
// The following enum should be used only as a constructor argument to indicate
// that the variable has static storage class, and that the constructor should
// do nothing to its state. It indicates to the reader that it is legal to
// declare a static instance of the class, provided the constructor is given
// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
// static variable that has a constructor or a destructor because invocation
// order is undefined. However, IF the type can be initialized by filling with
// zeroes (which the loader does for static variables), AND the type's
// destructor does nothing to the storage, then a constructor for static
// initialization can be declared as
// explicit MyClass(base::LinkerInitialized x) {}
// and invoked as
// static MyClass my_variable_name(base::LINKER_INITIALIZED);
namespace base {
enum LinkerInitialized { LINKER_INITIALIZED };
}
// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
// between switch labels:
// switch (x) {
// case 40:
// case 41:
// if (truth_is_out_there) {
// ++x;
// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
// // comments.
// } else {
// return x;
// }
// case 42:
// ...
//
// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
// followed by a semicolon. It is designed to mimic control-flow statements
// like 'break;', so it can be placed in most places where 'break;' can, but
// only if there are no statements on the execution path between it and the
// next switch label.
//
// When compiled with clang in C++11 mode, the FALLTHROUGH_INTENDED macro is
// expanded to [[clang::fallthrough]] attribute, which is analysed when
// performing switch labels fall-through diagnostic ('-Wimplicit-fallthrough').
// See clang documentation on language extensions for details:
// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
//
// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
// effect on diagnostics.
//
// In either case this macro has no effect on runtime behavior and performance
// of code.
#if defined(__clang__) && defined(LANG_CXX11) && defined(__has_warning)
#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
#endif
#endif
#ifndef FALLTHROUGH_INTENDED
#define FALLTHROUGH_INTENDED do { } while (0)
#endif
#define arraysize(array) ABSL_ARRAYSIZE(array)
#endif // BASE_MACROS_H_

View File

@@ -1,91 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef BASE_MUTEX_H_
#define BASE_MUTEX_H_
#include <stdlib.h>
#include <pthread.h>
#include "glog/logging.h"
#include "base/macros.h"
#include "base/thread_annotations.h"
// Basic mutex wrapper around a pthread RW lock.
class LOCKABLE Mutex {
public:
inline Mutex() { CHECK_EQ(pthread_rwlock_init(&lock_, nullptr), 0); }
inline Mutex(base::LinkerInitialized) { // NOLINT
CHECK_EQ(pthread_rwlock_init(&lock_, nullptr), 0);
}
inline ~Mutex() { CHECK_EQ(pthread_rwlock_destroy(&lock_), 0); }
inline void Lock() EXCLUSIVE_LOCK_FUNCTION() { WriterLock(); }
inline void Unlock() UNLOCK_FUNCTION() { WriterUnlock(); }
inline void ReaderLock() SHARED_LOCK_FUNCTION() {
CHECK_EQ(pthread_rwlock_rdlock(&lock_), 0);
}
inline void ReaderUnlock() UNLOCK_FUNCTION() {
CHECK_EQ(pthread_rwlock_unlock(&lock_), 0);
}
inline void WriterLock() EXCLUSIVE_LOCK_FUNCTION() {
CHECK_EQ(pthread_rwlock_wrlock(&lock_), 0);
}
inline void WriterUnlock() UNLOCK_FUNCTION() {
CHECK_EQ(pthread_rwlock_unlock(&lock_), 0);
}
private:
pthread_rwlock_t lock_;
DISALLOW_COPY_AND_ASSIGN(Mutex);
};
// -----------------------------------------------------------------------------
// MutexLock(mu) acquires mu when constructed and releases it when destroyed.
class SCOPED_LOCKABLE MutexLock {
public:
explicit MutexLock(Mutex* mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) {
this->mu_->Lock();
}
~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); }
private:
Mutex* const mu_;
DISALLOW_COPY_AND_ASSIGN(MutexLock);
};
// The ReaderMutexLock and WriterMutexLock classes work like MutexLock
// to acquire/release read and write locks on reader/writer locks.
class SCOPED_LOCKABLE ReaderMutexLock {
public:
explicit ReaderMutexLock(Mutex* mu) SHARED_LOCK_FUNCTION(mu) : mu_(mu) {
mu->ReaderLock();
}
~ReaderMutexLock() UNLOCK_FUNCTION() { this->mu_->ReaderUnlock(); }
private:
Mutex* const mu_;
DISALLOW_COPY_AND_ASSIGN(ReaderMutexLock);
};
class SCOPED_LOCKABLE WriterMutexLock {
public:
explicit WriterMutexLock(Mutex* mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) {
mu->WriterLock();
}
~WriterMutexLock() UNLOCK_FUNCTION() { this->mu_->WriterUnlock(); }
private:
Mutex* const mu_;
DISALLOW_COPY_AND_ASSIGN(WriterMutexLock);
};
#endif // BASE_MUTEX_H_

View File

@@ -1,212 +1,14 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// This header file contains the macro definitions for thread safety
// annotations that allow the developers to document the locking policies
// of their multi-threaded code. The annotations can also help program
// analysis tools to identify potential thread safety issues.
//
//
// The annotations are implemented using GCC's "attributes" extension.
// Using the macros defined here instead of the raw GCC attributes allows
// for portability and future compatibility.
//
#ifndef BASE_THREAD_ANNOTATIONS_H_
#define BASE_THREAD_ANNOTATIONS_H_
#if defined(__GNUC__) && defined(__SUPPORT_TS_ANNOTATION__) && !defined(SWIG)
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
#if defined(__GNUC__) && !defined(__clang__)
// Document if a shared variable/field needs to be protected by a lock.
// GUARDED_BY allows the user to specify a particular lock that should be
// held when accessing the annotated variable, while GUARDED_VAR only
// indicates a shared variable should be guarded (by any lock). GUARDED_VAR
// is primarily used when the client cannot express the name of the lock.
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded)
// Document if the memory location pointed to by a pointer should be guarded
// by a lock when dereferencing the pointer. Similar to GUARDED_VAR,
// PT_GUARDED_VAR is primarily used when the client cannot express the name
// of the lock. Note that a pointer variable to a shared memory location
// could itself be a shared variable. For example, if a shared global pointer
// q, which is guarded by mu1, points to a shared memory location that is
// guarded by mu2, q should be annotated as follows:
// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
#define PT_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded_by(x))
#define PT_GUARDED_VAR \
THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded)
// Document the acquisition order between locks that can be held
// simultaneously by a thread. For any two locks that need to be annotated
// to establish an acquisition order, only one of them needs the annotation.
// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
// and ACQUIRED_BEFORE.)
#define ACQUIRED_AFTER(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
// The following three annotations document the lock requirements for
// functions/methods.
// Document if a function expects certain locks to be held before it is called
#define EXCLUSIVE_LOCKS_REQUIRED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
#define SHARED_LOCKS_REQUIRED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
// Document the locks acquired in the body of the function. These locks
// non-reentrant).
#define LOCKS_EXCLUDED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
// Document the lock the annotated function returns without acquiring it.
#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
// Document if a class/type is a lockable type (such as the Mutex class).
#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable)
// Document if a class is a scoped lockable type (such as the MutexLock class).
#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
// The following annotations specify lock and unlock primitives.
#define EXCLUSIVE_LOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock(__VA_ARGS__))
#define SHARED_LOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock(__VA_ARGS__))
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock(__VA_ARGS__))
#define SHARED_TRYLOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock(__VA_ARGS__))
#define UNLOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(unlock(__VA_ARGS__))
// An escape hatch for thread safety analysis to ignore the annotated function.
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
// Used to mark functions that need to be fixed, because they are producing
// thread safety warnings. This macro is intended primarily for use by the
// compiler team; it allows new thread safety warnings to be rolled out
// without breaking existing code. Code which triggers the new warnings are
// marked with a FIXME, and referred back to the code owners to fix.
#define NO_THREAD_SAFETY_ANALYSIS_FIXME \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
// NO_THREAD_SAFETY_ANALYSIS_OPT turns off thread-safety checking in the
// annotated function in opt (NDEBUG) mode. It is for use specifically when
// the thread-safety checker is failing in opt mode on an otherwise correct
// piece of code.
#ifdef NDEBUG
#define NO_THREAD_SAFETY_ANALYSIS_OPT NO_THREAD_SAFETY_ANALYSIS
#else
#define NO_THREAD_SAFETY_ANALYSIS_OPT
#endif
// TS_UNCHECKED should be placed around lock expressions that are not valid
// C++ syntax, but which are present for documentation purposes. The
// expressions are passed unchanged to gcc, which will usually treat them
// as the universal lock.
#define TS_UNCHECKED(x) x
// TS_FIXME is used to mark lock expressions that are not valid C++ syntax.
// This annotation should eventually be either fixed, or changed to
// TS_UNCHECKED.
#define TS_FIXME(x) x
// This is used to pass different annotations to gcc and clang, in cases where
// gcc would reject a lock expression (e.g. &MyClass::mu_) that is accepted
// by clang. This is seldom needed, since GCC usually ignores invalid lock
// expressions except in certain cases, such as LOCK_RETURNED.
#define TS_CLANG_ONLY(CLANG_EXPR, GCC_EXPR) GCC_EXPR
// Clang Attributes
// The names of attributes in the clang analysis are slightly different
#else
#define GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define GUARDED_VAR \
THREAD_ANNOTATION_ATTRIBUTE__(guarded)
#define PT_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
#define PT_GUARDED_VAR \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded)
#define ACQUIRED_AFTER(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
#define EXCLUSIVE_LOCKS_REQUIRED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
#define SHARED_LOCKS_REQUIRED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
#define LOCKS_EXCLUDED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
#define LOCK_RETURNED(x) \
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
#define LOCKABLE \
THREAD_ANNOTATION_ATTRIBUTE__(lockable)
#define SCOPED_LOCKABLE \
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
#define EXCLUSIVE_LOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
#define SHARED_LOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
#define SHARED_TRYLOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
#define UNLOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
#define NO_THREAD_SAFETY_ANALYSIS_OPT
#define NO_THREAD_SAFETY_ANALYSIS_FIXME \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
#define TS_UNCHECKED(x) ""
#define TS_FIXME(x) ""
#define TS_CLANG_ONLY(CLANG_EXPR, GCC_EXPR) CLANG_EXPR
#endif // defined(__clang__)
#include "absl/base/thread_annotations.h"
#endif // BASE_THREAD_ANNOTATIONS_H_

21
base/timestamp.h Normal file
View File

@@ -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_

View File

@@ -1,16 +1,209 @@
################################################################################
# Copyright 2016 Google Inc.
# Copyright 2017 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
#
# Description:
# Build file for code common to multiple Widevine services.
# Constants, data structures, util classes for Widevine libraries.
package(default_visibility = ["//visibility:public"])
package(
default_visibility = ["//visibility:public"],
)
filegroup(
name = "binary_release_files",
srcs = [
"certificate_type.h",
"status.h",
],
)
cc_library(
name = "widevine_system_id",
srcs = ["widevine_system_id.cc"],
hdrs = ["widevine_system_id.h"],
deps = ["//base"],
)
cc_library(
name = "certificate_type",
hdrs = ["certificate_type.h"],
)
cc_library(
name = "status",
srcs = ["status.cc"],
hdrs = ["status.h"],
deps = [
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"//util:error_space",
],
)
cc_test(
name = "status_test",
srcs = ["status_test.cc"],
deps = [
":status",
"//testing:gunit_main",
],
)
cc_library(
name = "client_cert",
srcs = ["client_cert.cc"],
hdrs = ["client_cert.h"],
deps = [
":crypto_util",
":drm_root_certificate",
":error_space",
":random_util",
":rsa_key",
":sha_util",
":signing_key_util",
":status",
":wvm_token_handler",
"//base",
"//strings",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"@abseil_repo//absl/time",
"//util/gtl:map_util",
"//protos/public:client_identification_proto",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:license_protocol_proto",
"//protos/public:signed_drm_certificate_proto",
],
)
cc_test(
name = "client_cert_test",
srcs = ["client_cert_test.cc"],
deps = [
":client_cert",
":drm_root_certificate",
":error_space",
":sha_util",
":test_drm_certificates",
":wvm_test_keys",
"//base",
"//strings",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"@abseil_repo//absl/time",
"//common:rsa_key",
"//common:rsa_test_keys",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto",
],
)
cc_library(
name = "device_status_list",
srcs = ["device_status_list.cc"],
hdrs = ["device_status_list.h"],
deps = [
":client_cert",
":crypto_util",
":drm_root_certificate",
":drm_service_certificate",
":error_space",
":random_util",
":rsa_key",
":signing_key_util",
":status",
"//base",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//util/gtl:map_util",
"//protos/public:client_identification_proto",
"//protos/public:device_certificate_status_proto",
"//protos/public:errors_proto",
"//protos/public:provisioned_device_info_proto",
],
)
cc_test(
name = "device_status_list_test",
timeout = "short",
srcs = ["device_status_list_test.cc"],
deps = [
":client_cert",
":device_status_list",
"//base",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//common:rsa_key",
"//common:rsa_test_keys",
"//protos/public:client_identification_proto",
"//protos/public:errors_proto",
"//protos/public:provisioned_device_info_proto",
"//protos/public:signed_drm_certificate_proto",
],
)
cc_library(
name = "drm_root_certificate",
srcs = ["drm_root_certificate.cc"],
hdrs = ["drm_root_certificate.h"],
deps = [
":certificate_type",
":error_space",
":rsa_key",
":sha_util",
":status",
"//base",
"@abseil_repo//absl/memory",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//external:openssl",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto",
],
)
cc_test(
name = "drm_root_certificate_test",
timeout = "short",
srcs = ["drm_root_certificate_test.cc"],
deps = [
":drm_root_certificate",
":error_space",
":rsa_key",
":rsa_test_keys",
":test_drm_certificates",
"//base",
"@protobuf_repo//:protobuf",
"//testing:gunit_main",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto",
],
)
cc_library(
name = "client_id_util",
srcs = ["client_id_util.cc"],
hdrs = ["client_id_util.h"],
deps = [
":aes_cbc_util",
":drm_service_certificate",
":error_space",
":status",
"//base",
"@abseil_repo//absl/strings",
"//protos/public:client_identification_proto",
"//protos/public:errors_proto",
],
)
cc_library(
name = "rsa_util",
@@ -30,8 +223,10 @@ cc_test(
deps = [
":rsa_test_keys",
":rsa_util",
"//external:gtest",
"//external:gtest_main",
"//base",
"//testing:gunit",
"//testing:gunit_main",
"//external:openssl",
],
)
@@ -63,8 +258,9 @@ cc_test(
deps = [
":rsa_key",
":rsa_test_keys",
"//external:gtest",
"//external:gtest_main",
":rsa_util",
"//testing:gunit",
"//testing:gunit_main",
],
)
@@ -84,7 +280,7 @@ cc_library(
hdrs = ["mock_rsa_key.h"],
deps = [
":rsa_key",
"//external:gtest",
"//testing:gunit",
],
)
@@ -104,7 +300,75 @@ cc_test(
srcs = ["aes_cbc_util_test.cc"],
deps = [
":aes_cbc_util",
"//external:gtest_main",
"//testing:gunit_main",
],
)
cc_library(
name = "crypto_util",
srcs = ["crypto_util.cc"],
hdrs = ["crypto_util.h"],
visibility = ["//visibility:public"],
deps = [
"//base",
"@abseil_repo//absl/strings",
"//external:openssl",
"//util/endian",
],
)
cc_test(
name = "crypto_util_test",
size = "medium",
srcs = ["crypto_util_test.cc"],
deps = [
":crypto_util",
"//testing:gunit",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "ecb_util",
srcs = ["ecb_util.cc"],
hdrs = ["ecb_util.h"],
visibility = ["//visibility:public"],
deps = [
"//base",
"@abseil_repo//absl/strings",
"//external:openssl",
],
)
cc_test(
name = "ecb_util_test",
size = "small",
srcs = ["ecb_util_test.cc"],
deps = [
":ecb_util",
"//testing:gunit",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "file_util",
srcs = ["file_util.cc"],
hdrs = ["file_util.h"],
deps = [
"//base",
],
)
cc_test(
name = "file_util_test",
srcs = ["file_util_test.cc"],
deps = [
":file_util",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
],
)
@@ -123,7 +387,7 @@ cc_test(
srcs = ["random_util_test.cc"],
deps = [
":random_util",
"//external:gtest_main",
"//testing:gunit_main",
],
)
@@ -142,24 +406,282 @@ cc_test(
srcs = ["sha_util_test.cc"],
deps = [
":sha_util",
"//external:gtest_main",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "file_util",
srcs = ["file_util.cc"],
hdrs = ["file_util.h"],
name = "signature_util",
srcs = ["signature_util.cc"],
hdrs = ["signature_util.h"],
deps = [
":aes_cbc_util",
":rsa_key",
":sha_util",
":status",
"//base",
],
)
cc_library(
name = "signing_key_util",
srcs = ["signing_key_util.cc"],
hdrs = ["signing_key_util.h"],
deps = [
":crypto_util",
"//base",
"//protos/public:license_protocol_proto",
],
)
cc_test(
name = "signing_key_util_test",
size = "small",
srcs = ["signing_key_util_test.cc"],
deps = [
":crypto_util",
":signing_key_util",
"//testing:gunit",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//protos/public:license_protocol_proto",
],
)
cc_library(
name = "test_drm_certificates",
testonly = 1,
srcs = ["test_drm_certificates.cc"],
hdrs = ["test_drm_certificates.h"],
deps = [
"//base",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "wvm_token_handler",
srcs = ["wvm_token_handler.cc"],
hdrs = ["wvm_token_handler.h"],
deps = [
":aes_cbc_util",
":ecb_util",
":sha_util",
":status",
"//base",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//util/endian",
"//util/gtl:map_util",
],
)
cc_test(
name = "wvm_token_handler_test",
size = "small",
srcs = ["wvm_token_handler_test.cc"],
deps = [
":wvm_test_keys",
":wvm_token_handler",
"//testing:gunit",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "wvm_test_keys",
testonly = 1,
srcs = ["wvm_test_keys.cc"],
hdrs = ["wvm_test_keys.h"],
deps = [
":wvm_token_handler",
"//base",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "error_space",
srcs = ["error_space.cc"],
hdrs = ["error_space.h"],
deps = [
"//util:error_space",
"//util:proto_status",
"//protos/public:errors_proto",
],
)
cc_library(
name = "remote_attestation_verifier",
srcs = ["remote_attestation_verifier.cc"],
hdrs = ["remote_attestation_verifier.h"],
deps = [
":client_id_util",
":drm_service_certificate",
":error_space",
":rsa_key",
":status",
":x509_cert",
"//base",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//protos/public:client_identification_proto",
"//protos/public:errors_proto",
"//protos/public:remote_attestation_proto",
],
)
cc_library(
name = "drm_service_certificate",
srcs = ["drm_service_certificate.cc"],
hdrs = ["drm_service_certificate.h"],
deps = [
":aes_cbc_util",
":certificate_type",
":drm_root_certificate",
":error_space",
":rsa_key",
":rsa_util",
":status",
"//base",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//util/gtl:map_util",
"//protos/public:client_identification_proto",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto",
],
)
cc_test(
name = "drm_service_certificate_test",
timeout = "short",
srcs = ["drm_service_certificate_test.cc"],
deps = [
":aes_cbc_util",
":drm_root_certificate",
":drm_service_certificate",
":rsa_key",
":rsa_test_keys",
":rsa_util",
":test_drm_certificates",
"//base",
"@protobuf_repo//:protobuf",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//protos/public:client_identification_proto",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:license_server_sdk_proto",
"//protos/public:signed_drm_certificate_proto",
],
)
cc_library(
name = "verified_media_pipeline",
srcs = ["verified_media_pipeline.cc"],
hdrs = ["verified_media_pipeline.h"],
deps = [
":status",
":vmp_checker",
"//base",
"@abseil_repo//absl/strings",
"//protos/public:license_protocol_proto",
],
)
cc_library(
name = "x509_cert",
srcs = ["x509_cert.cc"],
hdrs = ["x509_cert.h"],
deps = [
":error_space",
":openssl_util",
":rsa_key",
":status",
"//base",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//external:openssl",
],
)
cc_library(
name = "test_utils",
testonly = 1,
srcs = ["test_utils.cc"],
hdrs = ["test_utils.h"],
deps = [
":status",
"//base",
"//external:openssl",
],
)
cc_test(
name = "x509_cert_test",
timeout = "short",
srcs = ["x509_cert_test.cc"],
deps = [
":rsa_key",
":test_utils",
":x509_cert",
"//base",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "vmp_checker",
srcs = ["vmp_checker.cc"],
hdrs = ["vmp_checker.h"],
deps = [
":certificate_type",
":error_space",
":rsa_key",
":status",
":x509_cert",
"//base",
"//protos/public:errors_proto",
"//protos/public:verified_media_pipeline_proto",
],
)
cc_test(
name = "vmp_checker_test",
timeout = "short",
srcs = ["vmp_checker_test.cc"],
deps = [
":rsa_key",
":vmp_checker",
"//base",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//protos/public:errors_proto",
"//protos/public:verified_media_pipeline_proto",
],
)
cc_library(
name = "string_util",
srcs = ["string_util.cc"],
hdrs = ["string_util.h"],
deps = [
":status",
"//base",
],
)
cc_test(
name = "file_util_test",
srcs = ["file_util_test.cc"],
name = "string_util_test",
srcs = ["string_util_test.cc"],
deps = [
":file_util",
"//external:gtest_main",
":string_util",
"//base",
"//testing:gunit_main",
],
)

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -20,23 +20,35 @@ namespace crypto_util {
// Encrypts the provided plantext std::string using AES-CBC encryption.
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
const std::string& plaintext) {
if (iv.size() != AES_BLOCK_SIZE) return "";
const size_t num_padding_bytes =
AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE);
std::string padded_text = plaintext;
padded_text.append(num_padding_bytes, static_cast<char>(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<const uint8_t*>(&key[0]),
key.size() * 8, &aes_key) != 0) {
return "";
LOG(WARNING) << "Invalid AES key.";
return std::string();
}
std::string encrypted(padded_text);
std::string encrypted(plaintext.size(), 0);
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(padded_text.data()),
reinterpret_cast<uint8_t*>(&encrypted[0]), padded_text.size(),
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(plaintext.data()),
reinterpret_cast<uint8_t*>(&encrypted[0]), plaintext.size(),
&aes_key, &local_iv[0], AES_ENCRYPT);
return encrypted;
}
@@ -45,26 +57,43 @@ std::string EncryptAesCbc(const std::string& key, const std::string& iv,
// the plaintext on success.
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
const std::string& ciphertext) {
if (ciphertext.empty()) return "";
if (iv.size() != AES_BLOCK_SIZE) return "";
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) return "";
if (ciphertext.empty()) {
LOG(WARNING) << "Empty ciphertext.";
return std::string();
}
if (iv.size() != AES_BLOCK_SIZE) {
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
return std::string();
}
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) {
LOG(WARNING) << "Ciphertext not a multiple of AES block size: "
<< ciphertext.size();
return std::string();
}
AES_KEY aes_key;
if (AES_set_decrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
key.size() * 8, &aes_key) != 0) {
return "";
LOG(WARNING) << "Invalid AES key.";
return std::string();
}
std::string cleartext(ciphertext);
std::string cleartext(ciphertext.size(), 0);
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(ciphertext.data()),
reinterpret_cast<uint8_t*>(&cleartext[0]), ciphertext.size(),
&aes_key, &local_iv[0], AES_DECRYPT);
const uint8_t num_padding_bytes = cleartext[cleartext.size() - 1];
if (num_padding_bytes > AES_BLOCK_SIZE) return "";
if (num_padding_bytes > AES_BLOCK_SIZE) {
LOG(WARNING) << "AES padding too long: " << num_padding_bytes;
return std::string();
}
for (uint8_t i = 0; i < num_padding_bytes; ++i) {
if (cleartext[cleartext.size() - 1 - i] != num_padding_bytes) return "";
if (cleartext[cleartext.size() - 1 - i] != num_padding_bytes) {
LOG(WARNING) << "Padding verification failure.";
return std::string();
}
}
cleartext.resize(cleartext.size() - num_padding_bytes);
return cleartext;
@@ -73,17 +102,26 @@ std::string DecryptAesCbc(const std::string& key, const std::string& iv,
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
const std::string& ciphertext) {
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
if (local_iv.empty()) local_iv.resize(AES_BLOCK_SIZE, '\0');
else if (local_iv.size() != AES_BLOCK_SIZE) return "";
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) return "";
if (local_iv.empty()) {
local_iv.resize(AES_BLOCK_SIZE, '\0');
} else if (local_iv.size() != AES_BLOCK_SIZE) {
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
return std::string();
}
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) {
LOG(WARNING) << "Ciphertext not a multiple of AES block size: "
<< ciphertext.size();
return std::string();
}
AES_KEY aes_key;
if (AES_set_decrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
key.size() * 8, &aes_key) != 0) {
return "";
LOG(WARNING) << "Invalid AES key.";
return std::string();
}
std::string cleartext(ciphertext);
std::string cleartext(ciphertext.size(), 0);
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(ciphertext.data()),
reinterpret_cast<uint8_t*>(&cleartext[0]), ciphertext.size(),
&aes_key, &local_iv[0], AES_DECRYPT);

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -14,12 +14,18 @@
namespace widevine {
namespace crypto_util {
// Helper for wrapping AES CBC encryption. Uses PKCS5 padding.
// Helper for wrapping AES CBC encryption. Uses PKCS7 padding.
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
const std::string& plaintext);
// Helper for wrapping AES CBC encryption. Adds no padding, so the input
// must be an multiple of the 16-byte AES block size. Returns empty std::string
// on error.
std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
const std::string& plaintext);
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
// empty std::string on error or the plaintext on success. Uses PKCS5 padding.
// empty std::string on error or the plaintext on success. Expects PKCS7 padding.
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
const std::string& ciphertext);
@@ -35,4 +41,3 @@ std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
} // namespace widevine
#endif // COMMON_AES_CBC_UTIL_H_

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -7,9 +7,8 @@
////////////////////////////////////////////////////////////////////////////////
#include "common/aes_cbc_util.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
namespace {
@@ -97,6 +96,31 @@ TEST(CryptoUtilTest, TestFailedEncrypt) {
ASSERT_EQ(ciphertext.size(), 0);
}
TEST(CryptoUtilTest, TestFailedEncryptNoPad) {
std::string plaintext("0123456789abcdef");
std::string key(kKey, kKey + sizeof(kKey));
std::string iv(kIv, kIv + sizeof(kIv));
// Control.
std::string ciphertext = EncryptAesCbcNoPad(key, iv, plaintext);
ASSERT_EQ(plaintext.size(), ciphertext.size());
// Bogus key.
std::string bogus_key("bogus");
ciphertext = EncryptAesCbcNoPad(bogus_key, iv, plaintext);
EXPECT_EQ(ciphertext.size(), 0);
// Bogus IV.
std::string bogus_iv("bogus");
ciphertext = EncryptAesCbcNoPad(key, bogus_iv, plaintext);
EXPECT_EQ(ciphertext.size(), 0);
// Incorrectly-sized plaintext.
std::string bad_plaintext("Foo");
ciphertext = EncryptAesCbcNoPad(key, iv, bad_plaintext);
EXPECT_EQ(ciphertext.size(), 0);
}
TEST(CryptoUtilTest, TestFailedDecrypt) {
// First, encrypt the data.
std::string plain_text("Foo");

View File

@@ -1,22 +1,22 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_
#define PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_
#ifndef COMMON_CERTIFICATE_TYPE_H_
#define COMMON_CERTIFICATE_TYPE_H_
namespace widevine {
enum CertificateType {
kCertTesting = 0,
kCertDevelopment,
kCertProduction,
kCertificateTypeTesting,
kCertificateTypeDevelopment,
kCertificateTypeProduction,
};
} // namespace widevine
#endif // PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_
#endif // COMMON_CERTIFICATE_TYPE_H_

262
common/client_cert.cc Normal file
View File

@@ -0,0 +1,262 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/client_cert.h"
#include <memory>
#include <utility>
#include <vector>
#include "glog/logging.h"
#include "strings/serialize.h"
#include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h"
#include "util/gtl/map_util.h"
#include "common/crypto_util.h"
#include "common/drm_root_certificate.h"
#include "common/error_space.h"
#include "common/random_util.h"
#include "common/sha_util.h"
#include "common/signing_key_util.h"
#include "common/status.h"
#include "common/wvm_token_handler.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
namespace widevine {
namespace {
const int kKeyboxSizeBytes = 72;
} // namespace
// instead of ClientCert** to explicitly assigning ownership of the created
// object to the caller.
Status ClientCert::Create(const DrmRootCertificate* root_certificate,
ClientIdentification::TokenType token_type,
const std::string& token, ClientCert** client_cert) {
DCHECK(client_cert);
if (token_type == ClientIdentification::KEYBOX) {
*client_cert = nullptr;
if (token.size() < kKeyboxSizeBytes) {
return Status(error_space, INVALID_KEYBOX_TOKEN,
"keybox-token-is-too-short");
}
return ClientCert::CreateWithKeybox(token, client_cert);
} else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) {
return CreateWithDrmCertificate(root_certificate, token, client_cert);
} else {
return Status(error_space, error::UNIMPLEMENTED,
"client-type-not-implemented");
}
}
Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
ClientCert** client_cert) {
CHECK(client_cert);
*client_cert = nullptr;
std::unique_ptr<KeyboxClientCert> new_client_cert(new KeyboxClientCert);
Status status = new_client_cert->Initialize(keybox_token);
if (!status.ok()) {
return status;
}
*client_cert = new_client_cert.release();
return OkStatus();
}
Status ClientCert::CreateWithDrmCertificate(
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
ClientCert** client_cert) {
CHECK(client_cert);
*client_cert = nullptr;
std::unique_ptr<CertificateClientCert> new_client_cert(
new CertificateClientCert);
Status status =
new_client_cert->Initialize(root_certificate, drm_certificate);
if (!status.ok()) {
return status;
}
*client_cert = new_client_cert.release();
return OkStatus();
}
void ClientCert::CreateSignature(const std::string& message, std::string* signature) {
DCHECK(signature);
DCHECK(!signing_key().empty());
if (signature == nullptr) {
return;
}
using crypto_util::CreateSignatureHmacSha256;
*signature =
CreateSignatureHmacSha256(GetServerSigningKey(signing_key()), message);
}
void ClientCert::GenerateSigningKey(const std::string& message,
ProtocolVersion protocol_version) {
if (!signing_key_.empty()) return;
DCHECK(!key().empty());
using crypto_util::DeriveKey;
using crypto_util::kSigningKeyLabel;
set_signing_key(
DeriveKey(key(), kSigningKeyLabel,
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
SigningKeyMaterialSizeBits(protocol_version)));
}
KeyboxClientCert::KeyboxClientCert() {}
KeyboxClientCert::~KeyboxClientCert() {}
void KeyboxClientCert::SetPreProvisioningKeys(
const std::multimap<uint32_t, std::string>& keymap) {
std::vector<WvmTokenHandler::PreprovKey> keyvector;
keyvector.reserve(keymap.size());
for (std::multimap<uint32_t, std::string>::const_iterator it = keymap.begin();
it != keymap.end(); ++it) {
std::string key = absl::HexStringToBytes(it->second);
DCHECK_EQ(key.size(), 16);
keyvector.push_back(WvmTokenHandler::PreprovKey(it->first, key));
}
WvmTokenHandler::SetPreprovKeys(keyvector);
}
bool KeyboxClientCert::IsSystemIdKnown(const uint32_t system_id) {
return WvmTokenHandler::IsSystemIdKnown(system_id);
}
uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
return WvmTokenHandler::GetSystemId(keybox_bytes);
}
Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) {
if (keybox_bytes.size() < kKeyboxSizeBytes) {
return Status(error_space, INVALID_KEYBOX_TOKEN,
"keybox-token-is-too-short");
}
set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes));
set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes));
bool insecure_keybox = false;
Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &device_key_,
nullptr, &insecure_keybox);
if (!status.ok()) {
Errors new_code = status.error_code() == error::NOT_FOUND
? MISSING_PRE_PROV_KEY
: KEYBOX_DECRYPT_ERROR;
return Status(error_space, new_code, status.error_message());
}
return OkStatus();
}
Status KeyboxClientCert::VerifySignature(const std::string& message,
const std::string& signature,
ProtocolVersion protocol_version) {
DCHECK(!signing_key().empty());
using crypto_util::VerifySignatureHmacSha256;
if (!VerifySignatureHmacSha256(
GetClientSigningKey(signing_key(), protocol_version), signature,
message)) {
return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
}
return OkStatus();
}
CertificateClientCert::CertificateClientCert() {}
CertificateClientCert::~CertificateClientCert() {}
Status CertificateClientCert::Initialize(
const DrmRootCertificate* drm_root_certificate,
const std::string& serialized_certificate) {
CHECK(drm_root_certificate);
SignedDrmCertificate signed_device_cert;
DrmCertificate device_cert;
Status status = drm_root_certificate->VerifyCertificate(
serialized_certificate, &signed_device_cert, &device_cert);
if (!status.ok()) {
return status;
}
const SignedDrmCertificate& signer = signed_device_cert.signer();
DrmCertificate model_certificate;
if (!model_certificate.ParseFromString(signer.drm_certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-invalid-signer");
}
if (!model_certificate.has_serial_number()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-signer-serial-number");
}
// Check to see if this model certificate is signed by a
// provisioner (entity using Widevine Provisioning Server SDK).
if (signer.has_signer()) {
DrmCertificate provisioner_certificate;
if (!provisioner_certificate.ParseFromString(
signer.signer().drm_certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"model-certificate-invalid-signer");
}
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
set_signed_by_provisioner(true);
} else {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"expected-provisioning-provider-certificate-type");
}
if (!provisioner_certificate.has_provider_id() ||
provisioner_certificate.provider_id().empty()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-provisioning-service-id");
}
set_service_id(provisioner_certificate.provider_id());
}
set_signer_serial_number(model_certificate.serial_number());
set_signer_creation_time_seconds(model_certificate.creation_time_seconds());
if (!model_certificate.has_system_id()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"model-certificate-missing-system-id");
}
set_system_id(model_certificate.system_id());
set_serial_number(device_cert.serial_number());
set_public_key(device_cert.public_key());
rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
if (rsa_public_key_ == nullptr) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-public-key-failed");
}
// TODO(user): Move this somewhere else. It is license protocol.
set_key(Random16Bytes());
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
return Status(error_space, ENCRYPT_ERROR,
"drm-certificate-failed-encrypt-session-key");
}
return OkStatus();
}
Status CertificateClientCert::VerifySignature(
const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) {
CHECK(rsa_public_key_);
if (!rsa_public_key_->VerifySignature(
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
signature)) {
return Status(error_space, INVALID_SIGNATURE, "");
}
return OkStatus();
}
} // namespace widevine

187
common/client_cert.h Normal file
View File

@@ -0,0 +1,187 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef COMMON_CLIENT_CERT_H__
#define COMMON_CLIENT_CERT_H__
#include <map>
#include <memory>
#include <string>
#include "common/rsa_key.h"
#include "common/status.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
class DrmRootCertificate;
class SignedDrmCertificate;
// Handler class for LicenseRequests; validates requests and encrypts licenses.
// TODO(user): Remove extra accessors after Keybox parsing is moved
// to a separate class in KeyboxClientCert class.
class ClientCert {
public:
virtual ~ClientCert() {}
static Status Create(
const DrmRootCertificate* root_certificate,
widevine::ClientIdentification::TokenType token_type,
const std::string& token, ClientCert** client_cert);
// Creates a Keybox based ClientCert.
static Status CreateWithKeybox(const std::string& keybox_token,
ClientCert** client_cert);
// Creates a Device Certificate based ClientCert.
static Status CreateWithDrmCertificate(
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
ClientCert** client_cert);
// Creates a HMAC SHA256 signature based on the message and the key().
// signature is owned by the caller and can not be NULL.
virtual void CreateSignature(const std::string& message, std::string* signature);
// Checks the passed in signature against a signature created used the
// classes information and the passed in message. Returns OK if signature
// is valid.
virtual Status VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) = 0;
// Creates a signing_key that is accessible using signing_key(). Signing_key
// is constructed by doing a key derivation using the key() and message.
virtual void GenerateSigningKey(const std::string& message,
ProtocolVersion protocol_version);
// Used to create signing keys. For Keybox token types this is the device key.
// For Device Certificate token types this the session key.
virtual const std::string& key() const = 0;
virtual void set_key(const std::string& key) = 0;
virtual const std::string& encrypted_key() const = 0;
virtual uint32_t system_id() const { return system_id_; }
virtual const std::string& signing_key() const { return signing_key_; }
virtual const std::string& public_key() const { return public_key_; }
virtual const std::string& serial_number() const { return serial_number_; }
virtual void set_serial_number(const std::string& serial_number) {
serial_number_ = serial_number;
}
virtual const std::string& signer_serial_number() const {
return signer_serial_number_;
}
virtual uint32_t signer_creation_time_seconds() const {
return signer_creation_time_seconds_;
}
virtual widevine::ClientIdentification::TokenType type() const = 0;
virtual std::string service_id() const { return service_id_; }
virtual bool signed_by_provisioner() const { return signed_by_provisioner_; }
protected:
ClientCert() {}
virtual void set_system_id(uint32_t system_id) { system_id_ = system_id; }
virtual void set_signing_key(const std::string& signing_key) {
signing_key_ = signing_key;
}
virtual void set_service_id(const std::string& service_id) {
service_id_ = service_id;
}
virtual void set_signed_by_provisioner(bool provisioner_signed_flag) {
signed_by_provisioner_ = provisioner_signed_flag;
}
std::string public_key_;
std::string serial_number_;
std::string signer_serial_number_;
uint32_t signer_creation_time_seconds_ = 0;
bool signed_by_provisioner_ = false;
private:
uint32_t system_id_ = 0;
std::string signing_key_;
std::string service_id_;
DISALLOW_COPY_AND_ASSIGN(ClientCert);
};
// This class implements the crypto operations based on the Widevine keybox.
// It will unpack token and perform all the crypto operations for securing
// the key material in the license response.
class KeyboxClientCert : public ClientCert {
public:
~KeyboxClientCert() override;
// Set the system-wide pre-provisioning keys; argument must be human-readable
// hex digits.
// Must be called before any other method of this class is called, unless
// created by ClientCert::CreateWithPreProvisioningKey(...).
static void SetPreProvisioningKeys(const std::multimap<uint32_t, std::string>& keys);
static bool IsSystemIdKnown(const uint32_t system_id);
static uint32_t GetSystemId(const std::string& keybox_bytes);
Status Initialize(const std::string& keybox_bytes);
Status VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) override;
const std::string& key() const override { return device_key_; }
void set_key(const std::string& key) override { device_key_ = key; }
const std::string& encrypted_key() const override { return encrypted_device_key_; }
widevine::ClientIdentification::TokenType type() const override {
return widevine::ClientIdentification::KEYBOX;
}
private:
KeyboxClientCert();
friend class ClientCert;
friend class MockKeyboxClientCert;
std::string device_key_;
std::string encrypted_device_key_;
DISALLOW_COPY_AND_ASSIGN(KeyboxClientCert);
};
// This class implements the device certificate operations based on RSA keys.
// It will unpack token and perform all the crypto operations for securing
// the key material in the license response.
using widevine::RsaPublicKey;
class CertificateClientCert : public ClientCert {
public:
~CertificateClientCert() override;
Status VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) override;
const std::string& key() const override { return session_key_; }
void set_key(const std::string& key) override { session_key_ = key; }
const std::string& encrypted_key() const override {
return encrypted_session_key_;
}
widevine::ClientIdentification::TokenType type() const override {
return widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE;
}
protected:
friend class ClientCert;
friend class MockCertificateClientCert;
Status Initialize(const DrmRootCertificate* drm_root_certificate,
const std::string& serialized_certificate);
virtual void set_public_key(const std::string& public_key) {
public_key_ = public_key;
}
virtual void set_signer_serial_number(const std::string& signer_serial_number) {
signer_serial_number_ = signer_serial_number;
}
virtual void set_signer_creation_time_seconds(uint32_t creation_time_seconds) {
signer_creation_time_seconds_ = creation_time_seconds;
}
std::string session_key_;
std::string encrypted_session_key_;
std::unique_ptr<RsaPublicKey> rsa_public_key_;
private:
CertificateClientCert();
DISALLOW_COPY_AND_ASSIGN(CertificateClientCert);
};
} // namespace widevine
#endif // COMMON_CLIENT_CERT_H__

617
common/client_cert_test.cc Normal file
View File

@@ -0,0 +1,617 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/client_cert.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include "glog/logging.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "common/drm_root_certificate.h"
#include "common/error_space.h"
#include "common/rsa_test_keys.h"
#include "common/sha_util.h"
#include "common/test_drm_certificates.h"
#include "common/wvm_test_keys.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
// TODO(user): Change these tests to use on-the-fly generated intermediate
// and device certificates based on RsaTestKeys.
// TODO(user): Add testcase(s) CreateSignature,
// and GenerateSigningKey.
namespace widevine {
using ::testing::_;
using ::testing::Return;
class ClientCertTest : public ::testing::Test {
public:
void SetUp() override {
if (!setup_preprov_keys_) {
KeyboxClientCert::SetPreProvisioningKeys(
wvm_test_keys::GetPreprovKeyMultimap());
setup_preprov_keys_ = true;
}
ASSERT_OK(
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
}
protected:
// Simple container struct for test value and expected keys.
class TestTokenAndKeys {
public:
const std::string token_;
uint32_t expected_system_id_;
const std::string expected_serial_number_;
const std::string expected_device_key_;
TestTokenAndKeys(const std::string& token, uint32_t expected_system_id,
const std::string& expected_serial_number,
const std::string& expected_device_key)
: token_(token),
expected_system_id_(expected_system_id),
expected_serial_number_(expected_serial_number),
expected_device_key_(expected_device_key) {}
};
class TestCertificateAndData {
public:
const std::string certificate_;
const std::string expected_serial_number_;
uint32_t expected_system_id_;
Status expected_status_;
TestCertificateAndData(const std::string& certificate,
const std::string& expected_serial_number,
uint32_t expected_system_id, Status expected_status)
: certificate_(certificate),
expected_serial_number_(expected_serial_number),
expected_system_id_(expected_system_id),
expected_status_(std::move(expected_status)) {}
};
void TestBasicValidation(const TestTokenAndKeys& expectation,
const bool expect_success,
const bool compare_device_key);
void TestBasicValidationDrmCertificate(
const TestCertificateAndData& expectation, const bool compare_data);
void GenerateSignature(const std::string& message, const std::string& private_key,
std::string* signature);
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate,
SignedDrmCertificate* signer,
const std::string& private_key);
DrmCertificate* GenerateProvisionerCertificate(uint32_t system_id,
const std::string& serial_number,
const std::string& provider_id);
SignedDrmCertificate* GenerateSignedProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, const std::string& service_id);
DrmCertificate* GenerateIntermediateCertificate(uint32_t system_id,
const std::string& serial_number);
SignedDrmCertificate* GenerateSignedIntermediateCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number);
DrmCertificate* GenerateDrmCertificate(uint32_t system_id,
const std::string& serial_number);
SignedDrmCertificate* GenerateSignedDrmCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number);
RsaTestKeys test_rsa_keys_;
TestDrmCertificates test_drm_certs_;
std::unique_ptr<DrmRootCertificate> root_cert_;
static bool setup_preprov_keys_;
};
bool ClientCertTest::setup_preprov_keys_(false);
void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
const bool expect_success,
const bool compare_device_key) {
// Test validation of a valid request.
Status status;
ClientCert* client_cert_ptr = nullptr;
// Two ways to create a client cert object, test both.
for (int i = 0; i < 2; i++) {
if (i == 0) {
status =
ClientCert::Create(root_cert_.get(), ClientIdentification::KEYBOX,
expectation.token_, &client_cert_ptr);
} else {
status =
ClientCert::CreateWithKeybox(expectation.token_, &client_cert_ptr);
}
std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
if (expect_success) {
ASSERT_EQ(OkStatus(), status);
ASSERT_TRUE(keybox_cert.get());
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
EXPECT_EQ(expectation.expected_serial_number_,
keybox_cert->serial_number());
if (compare_device_key) {
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
}
} else {
EXPECT_NE(OkStatus(), status);
EXPECT_FALSE(keybox_cert);
}
}
}
void ClientCertTest::TestBasicValidationDrmCertificate(
const TestCertificateAndData& expectation, const bool compare_data) {
// Reset DRM certificate signature cache since some certificates get
// re-generated.
ASSERT_OK(
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
// Test validation of a valid request.
Status status;
ClientCert* client_cert_ptr = nullptr;
status = ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
expectation.certificate_, &client_cert_ptr);
std::unique_ptr<ClientCert> drm_certificate_cert(client_cert_ptr);
ASSERT_EQ(expectation.expected_status_, status);
if (expectation.expected_status_.ok()) {
ASSERT_TRUE(drm_certificate_cert.get());
if (compare_data) {
ASSERT_EQ(expectation.expected_serial_number_,
drm_certificate_cert->signer_serial_number());
ASSERT_EQ(expectation.expected_system_id_,
drm_certificate_cert->system_id());
}
} else {
ASSERT_FALSE(drm_certificate_cert.get());
}
}
void ClientCertTest::GenerateSignature(const std::string& message,
const std::string& private_key,
std::string* signature) {
std::unique_ptr<RsaPrivateKey> rsa_private_key(
RsaPrivateKey::Create(private_key));
ASSERT_TRUE(rsa_private_key != nullptr);
rsa_private_key->GenerateSignature(message, signature);
}
// The caller relinquishes ownership of |signer|, which may also be nullptr.
SignedDrmCertificate* ClientCertTest::SignCertificate(
const DrmCertificate& certificate, SignedDrmCertificate* signer,
const std::string& private_key) {
std::unique_ptr<SignedDrmCertificate> signed_certificate(
new SignedDrmCertificate);
signed_certificate->set_drm_certificate(certificate.SerializeAsString());
GenerateSignature(signed_certificate->drm_certificate(), private_key,
signed_certificate->mutable_signature());
if (signer != nullptr) {
signed_certificate->set_allocated_signer(signer);
}
return signed_certificate.release();
}
DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
uint32_t system_id, const std::string& serial_number) {
std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate);
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
intermediate_certificate->set_serial_number(serial_number);
intermediate_certificate->set_public_key(
test_rsa_keys_.public_test_key_2_2048_bits());
intermediate_certificate->set_system_id(system_id);
intermediate_certificate->set_creation_time_seconds(1234);
return intermediate_certificate.release();
}
SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number) {
std::unique_ptr<DrmCertificate> intermediate_certificate(
GenerateIntermediateCertificate(system_id, serial_number));
return SignCertificate(*intermediate_certificate, signer,
test_rsa_keys_.private_test_key_1_3072_bits());
}
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
uint32_t system_id, const std::string& serial_number) {
std::unique_ptr<DrmCertificate> drm_certificate(new DrmCertificate);
drm_certificate->set_type(DrmCertificate::DEVICE);
drm_certificate->set_serial_number(serial_number);
drm_certificate->set_system_id(system_id);
drm_certificate->set_public_key(test_rsa_keys_.public_test_key_3_2048_bits());
drm_certificate->set_creation_time_seconds(4321);
return drm_certificate.release();
}
SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number) {
std::unique_ptr<DrmCertificate> drm_certificate(
GenerateDrmCertificate(system_id, serial_number));
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate(
*drm_certificate, signer, test_rsa_keys_.private_test_key_2_2048_bits()));
return signed_drm_certificate.release();
}
DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, const std::string& provider_id) {
std::unique_ptr<DrmCertificate> provisioner_certificate(new DrmCertificate);
provisioner_certificate->set_type(DrmCertificate::PROVISIONER);
provisioner_certificate->set_serial_number(serial_number);
// TODO(user): Need to generate 3072 bit test for provisioner certificates.
provisioner_certificate->set_public_key(
test_rsa_keys_.public_test_key_1_3072_bits());
provisioner_certificate->set_system_id(system_id);
provisioner_certificate->set_provider_id(provider_id);
provisioner_certificate->set_creation_time_seconds(1234);
return provisioner_certificate.release();
}
SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, const std::string& service_id) {
std::unique_ptr<DrmCertificate> provisioner_certificate(
GenerateProvisionerCertificate(system_id, serial_number, service_id));
return SignCertificate(*provisioner_certificate, nullptr,
test_rsa_keys_.private_test_key_1_3072_bits());
}
TEST_F(ClientCertTest, BasicValidation) {
const TestTokenAndKeys kValidTokenAndExpectedKeys[] = {
TestTokenAndKeys(
absl::HexStringToBytes(
"00000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e"),
274, absl::HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"),
absl::HexStringToBytes("4071197f1f8910d9bf10c6bc4c987638")),
TestTokenAndKeys(
absl::HexStringToBytes(
"0000000200000112d906feebe1750c5886ff77c2dfa31bb40e002f3adbc0fa5b"
"eb2486cf5f419549cdaa23230e5165ac2ffab56d53b692b7ba0c1857400c6add"
"3af3ff3d5cb24985"),
274, absl::HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"),
absl::HexStringToBytes("42cfb1765201042302a404d1e0fac8ed"))};
for (size_t i = 0; i < ABSL_ARRAYSIZE(kValidTokenAndExpectedKeys); ++i) {
SCOPED_TRACE("Test data: " + absl::StrCat(i));
TestBasicValidation(kValidTokenAndExpectedKeys[i], true, true);
}
EXPECT_EQ(
wvm_test_keys::kTestSystemId,
KeyboxClientCert::GetSystemId(kValidTokenAndExpectedKeys[0].token_));
}
TEST_F(ClientCertTest, BasicCertValidation) {
const uint32_t system_id = 1234;
const std::string serial_number("serial_number");
std::unique_ptr<SignedDrmCertificate> signed_cert(
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
nullptr, system_id, serial_number),
system_id, serial_number + "-device"));
const TestCertificateAndData kValidCertificateAndExpectedData(
signed_cert->SerializeAsString(), serial_number, system_id, OkStatus());
const bool compare_data = true;
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
compare_data);
}
TEST_F(ClientCertTest, InvalidKeybox) {
const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = {
// This tests a malformed, but appropriately sized keybox.
TestTokenAndKeys(
absl::HexStringToBytes(
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e"),
0, absl::HexStringToBytes(""), absl::HexStringToBytes("")),
// This has a length and system_id, but nothing else.
TestTokenAndKeys(absl::HexStringToBytes("0000000200000112"), 0,
absl::HexStringToBytes(""), absl::HexStringToBytes("")),
// This has only a byte.
TestTokenAndKeys(absl::HexStringToBytes(""), 0,
absl::HexStringToBytes(""), absl::HexStringToBytes("")),
// This has an emptry std::string for the keybox.
TestTokenAndKeys(absl::HexStringToBytes(""), 0,
absl::HexStringToBytes(""), absl::HexStringToBytes(""))};
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidTokenAndExpectedKeys); ++i) {
SCOPED_TRACE("Test data: " + absl::StrCat(i));
TestBasicValidation(kInvalidTokenAndExpectedKeys[i], false, false);
}
}
TEST_F(ClientCertTest, InvalidCertificate) {
const uint32_t system_id(1234);
const std::string device_sn("device-serial-number");
const std::string signer_sn("signer-serial-number");
std::unique_ptr<DrmCertificate> dev_cert;
std::unique_ptr<DrmCertificate> signer_cert;
std::unique_ptr<SignedDrmCertificate> signed_signer;
// Invalid serialized device certificate.
std::unique_ptr<SignedDrmCertificate> invalid_drm_cert(
new SignedDrmCertificate);
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
GenerateSignature(invalid_drm_cert->drm_certificate(),
test_rsa_keys_.private_test_key_2_2048_bits(),
invalid_drm_cert->mutable_signature());
invalid_drm_cert->set_allocated_signer(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
// Invalid device public key.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
dev_cert->set_public_key("bad-device-public-key");
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
*dev_cert,
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid serialized intermediate certificate.
signed_signer.reset(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
signed_signer->set_drm_certificate("bad-serialized-cert");
GenerateSignature(signed_signer->drm_certificate(),
test_rsa_keys_.private_test_key_1_3072_bits(),
signed_signer->mutable_signature());
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
std::unique_ptr<SignedDrmCertificate> invalid_signer(
SignCertificate(*dev_cert, signed_signer.release(),
test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid signer public key.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
signer_cert->set_public_key("bad-signer-public-key");
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
*dev_cert,
SignCertificate(*signer_cert, nullptr,
test_rsa_keys_.private_test_key_1_3072_bits()),
test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid device certificate signature.
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
GenerateSignedDrmCertificate(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
system_id, device_sn));
bad_device_signature->set_signature("bad-signature");
// Missing model system ID.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
signer_cert->clear_system_id();
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
*dev_cert,
SignCertificate(*signer_cert, nullptr,
test_rsa_keys_.private_test_key_1_3072_bits()),
test_rsa_keys_.private_test_key_2_2048_bits()));
// Missing signer serial number.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
signer_cert->clear_serial_number();
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
*dev_cert,
SignCertificate(*signer_cert, nullptr,
test_rsa_keys_.private_test_key_1_3072_bits()),
test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid serialized intermediate certificate.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signed_signer.reset(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
signed_signer->set_signature("bad-signature");
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
SignCertificate(*dev_cert, signed_signer.release(),
test_rsa_keys_.private_test_key_2_2048_bits()));
const TestCertificateAndData kInvalidCertificate[] = {
TestCertificateAndData("f", "", 0,
Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signed-drm-certificate")),
TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0,
Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-drm-certificate")),
TestCertificateAndData(bad_device_public_key->SerializeAsString(), "", 0,
Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-public-key-failed")),
TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0,
Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-certificate")),
TestCertificateAndData(bad_signer_public_key->SerializeAsString(), "", 0,
Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-leaf-signer-public-key")),
TestCertificateAndData(bad_device_signature->SerializeAsString(), "", 0,
Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature")),
TestCertificateAndData(missing_model_sn->SerializeAsString(), "", 0,
Status(error_space, INVALID_DRM_CERTIFICATE,
"model-certificate-missing-system-id")),
TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0,
Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-signer-serial-number")),
TestCertificateAndData(bad_signer_signature->SerializeAsString(), "", 0,
Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature")),
};
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) {
TestBasicValidationDrmCertificate(kInvalidCertificate[i], false);
}
}
TEST_F(ClientCertTest, MissingPreProvKey) {
// system ID in token is 0x01234567
const std::string token(absl::HexStringToBytes(
"00000002012345678e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e"));
ClientCert* client_cert_ptr = nullptr;
Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr);
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
}
TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
const uint32_t system_id = 5000;
const std::string service_id("widevine_test.com");
const std::string device_serial_number("device-serial-number");
const std::string intermediate_serial_number("intermediate-serial-number");
const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id));
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
system_id,
intermediate_serial_number));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
system_id, device_serial_number));
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
ClientCert* client_cert_ptr = nullptr;
EXPECT_OK(ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr));
ASSERT_TRUE(client_cert_ptr != nullptr);
std::unique_ptr<ClientCert> drm_cert(client_cert_ptr);
EXPECT_EQ(service_id, drm_cert->service_id());
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
EXPECT_EQ(intermediate_serial_number, drm_cert->signer_serial_number());
EXPECT_EQ(system_id, drm_cert->system_id());
}
TEST_F(ClientCertTest, InvalidProvisionerDeviceCertEmptyServiceId) {
const uint32_t system_id = 4890;
const std::string service_id("");
const std::string device_serial_number("device-serial-number");
const std::string intermediate_serial_number("intermediate-serial-number");
const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id));
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
system_id,
intermediate_serial_number));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
system_id, device_serial_number));
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
ClientCert* client_cert_ptr = nullptr;
EXPECT_EQ("missing-provisioning-service-id",
ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message());
EXPECT_FALSE(client_cert_ptr);
}
TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
const uint32_t system_id = 4890;
const uint32_t system_id2 = 4892;
const std::string service_id("widevine_test.com");
const std::string device_serial_number("device-serial-number");
const std::string intermediate_serial_number("intermediate-serial-number");
const std::string intermediate_serial_number2("intermediate-serial-number-2");
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
GenerateSignedIntermediateCertificate(nullptr, system_id2,
intermediate_serial_number2));
// Instead of using a provisioner certificate to sign this intermediate
// certificate, use another intermediate certificate. This is an invalid
// chain and should generate an error when trying to create a client
// certificate.
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(signed_intermediate_cert2.release(),
system_id,
intermediate_serial_number));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
system_id, device_serial_number));
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
ClientCert* client_cert_ptr = nullptr;
// TODO(user): Fix this test. It is failing for the right reasons, but the
// certificate chain is broken (intermediate signature does not match signer).
ASSERT_EQ("cache-miss-invalid-signature",
ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message());
EXPECT_FALSE(client_cert_ptr);
}
TEST_F(ClientCertTest, Protocol21WithDrmCert) {
const char message[] = "A weekend wasted is a weekend well spent.";
ClientCert* client_cert_ptr = nullptr;
ASSERT_OK(ClientCert::Create(
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
ASSERT_TRUE(private_key);
// Success
std::string signature;
ASSERT_TRUE(private_key->GenerateSignature(message, &signature));
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_1));
// Failure
ASSERT_EQ(256, signature.size());
++signature[127];
EXPECT_FALSE(
client_cert->VerifySignature(message, signature, VERSION_2_1).ok());
}
TEST_F(ClientCertTest, Protocol22WithDrmCert) {
const char message[] = "There is nothing permanent except change.";
const std::string message_hash(Sha512_Hash(message));
ClientCert* client_cert_ptr = nullptr;
ASSERT_OK(ClientCert::Create(
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
ASSERT_TRUE(private_key);
// Success
std::string signature;
ASSERT_TRUE(private_key->GenerateSignature(message_hash, &signature));
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_2));
// Failure
ASSERT_EQ(256, signature.size());
++signature[127];
EXPECT_FALSE(
client_cert->VerifySignature(message, signature, VERSION_2_2).ok());
}
} // namespace widevine

89
common/client_id_util.cc Normal file
View File

@@ -0,0 +1,89 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/client_id_util.h"
#include "glog/logging.h"
#include "common/aes_cbc_util.h"
#include "common/drm_service_certificate.h"
#include "common/error_space.h"
#include "protos/public/errors.pb.h"
namespace widevine {
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
absl::string_view value) {
ClientIdentification_NameValue* nv = client_id->add_client_info();
nv->set_name(std::string(name));
nv->set_value(std::string(value));
}
bool SetClientInfo(ClientIdentification* client_id, absl::string_view name,
absl::string_view value) {
int n = client_id->client_info_size();
for (int i = 0; i < n; i++) {
if (client_id->client_info(i).name() == name) {
client_id->mutable_client_info(i)->set_value(std::string(value));
return true;
}
}
AddClientInfo(client_id, name, value);
return false;
}
std::string GetClientInfo(const ClientIdentification& client_id,
absl::string_view name) {
return GetClientInfo(client_id, name, std::string());
}
std::string GetClientInfo(const ClientIdentification& client_id,
absl::string_view name, const std::string& default_value) {
for (const auto& nv : client_id.client_info()) {
if (nv.name() == name) {
return nv.value();
}
}
return default_value;
}
Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id) {
return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id,
client_id);
}
Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
const std::string& privacy_key, ClientIdentification* client_id) {
DCHECK(client_id);
if (!encrypted_client_id.has_encrypted_client_id() ||
encrypted_client_id.encrypted_client_id().empty()) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-client-id");
}
if (!encrypted_client_id.has_encrypted_client_id_iv() ||
encrypted_client_id.encrypted_client_id_iv().empty()) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-client-id-iv");
}
std::string serialized_client_id(crypto_util::DecryptAesCbc(
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
encrypted_client_id.encrypted_client_id()));
if (serialized_client_id.empty()) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"client-id-decryption-failed");
}
if (!client_id->ParseFromString(serialized_client_id)) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"client-id-parse-failed");
}
return OkStatus();
}
} // namespace widevine

61
common/client_id_util.h Normal file
View File

@@ -0,0 +1,61 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Utilities for manipulating the ClientIdentification proto.
// ClientIdentification.client_info() contains a sequence of
// arbitrary name-value pairs; this code consolidates the
// accessors for them in one place.
#ifndef COMMON_CLIENT_ID_UTIL_H_
#define COMMON_CLIENT_ID_UTIL_H_
#include "absl/strings/string_view.h"
#include "common/status.h"
#include "protos/public/client_identification.pb.h"
namespace widevine {
// Append the given name/value pair to client_id->client_info(). Does not
// check for duplicates.
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
absl::string_view value);
// Append the given name/value pair to client_id->client_info(). If the
// given name already had a value, replaces it and returns true.
bool SetClientInfo(ClientIdentification* client_id, absl::string_view name,
absl::string_view value);
// Return the value from client_id.client_info() matching the given name,
// or the empty std::string if not found.
std::string GetClientInfo(const ClientIdentification& client_id,
absl::string_view name);
// Return the value from client_id.client_info() matching the given name,
// or the given default value if not found.
std::string GetClientInfo(const ClientIdentification& client_id,
absl::string_view name, const std::string& default_value);
// Decrypts the encrypted client identification in |encrypted_client_id| into
// |client_id| using the private key for the service certificate which was
// used to encrypt the information.
// |client_id| is owned by caller.
// Returns Status::OK, if successful, else an error.
Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id);
// Decrypts the encrypted client identification in |encrypted_client_id| into
// |client_id| using |privacy_key|.
// |client_id| is owned by caller.
// Returns Status::OK, if successful, else an error.
Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
const std::string& privacy_key, ClientIdentification* client_id);
} // namespace widevine
#endif // COMMON_CLIENT_ID_UTIL_H_

181
common/crypto_util.cc Normal file
View File

@@ -0,0 +1,181 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Implementation of Common crypto utilities used by Widevine services.
#include "common/crypto_util.h"
#include "glog/logging.h"
#include "absl/strings/string_view.h"
#include "openssl/aes.h"
#include "openssl/cmac.h"
#include "openssl/evp.h"
#include "openssl/hmac.h"
#include "openssl/sha.h"
#include "util/endian/endian.h"
namespace widevine {
namespace crypto_util {
const char kWrappingKeyLabel[] = "ENCRYPTION";
const int kWrappingKeySizeBits = 128;
const char kSigningKeyLabel[] = "AUTHENTICATION";
const int kSigningKeySizeBits = 256;
const size_t kSigningKeySizeBytes = 32;
const char kIvMasterKey[] = "1234567890123456";
const char kIvLabel[] = "IV_ENCRYPTION";
const int kIvSizeBits = 128;
const char kKeyIdMasterKey[] = "0123456789abcdef";
const char kKeyIdLabel[] = "KEY_ID_ENCRYPTION";
const int kKeyIdSizeBits = 128;
const char kGroupKeyLabel[] = "GROUP_ENCRYPTION";
// TODO(user): This is a temporary key for development. Replace this with
// a real group master key in keystore.
// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy
// like VerifySignatureHmacSha1.
const char kPhonyGroupMasterKey[] = "fedcba9876543210";
const int kAes128KeySizeBits = 128;
const int kAes128KeySizeBytes = 16;
const uint32_t kCENCSchemeID = 0x63656E63; // 'cenc' (AES-CTR): 0x63656E63
const uint32_t kCBC1SchemeID = 0x63626331; // 'cbc1' (AES-CBC): 0x63626331
const uint32_t kCENSSchemeID =
0x63656E73; // 'cens' (AES-CTR subsample): 0x63656E73
const uint32_t kCBCSSchemeID =
0x63626373; // 'cbcs' (AES-CBC subsample): 0x63626373
// Creates a SHA-256 HMAC signature for the given message.
std::string CreateSignatureHmacSha256(absl::string_view key,
absl::string_view message) {
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha256());
HMAC_Update(&ctx, reinterpret_cast<const unsigned char*>(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<char*>(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<const unsigned char*>(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<char*>(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<const uint8_t*>(message.data()),
message.size())) {
size_t reslen;
unsigned char res[AES_BLOCK_SIZE];
if (CMAC_Final(cmac_ctx, res, &reslen)) {
result.append(reinterpret_cast<const char*>(res), reslen);
}
DCHECK(reslen == AES_BLOCK_SIZE);
}
}
}
CMAC_CTX_free(cmac_ctx);
return result;
}
// Derives an IV from the provided info.
std::string DeriveIv(absl::string_view context) {
return DeriveKey(kIvMasterKey, kIvLabel, context, kIvSizeBits);
}
// Derives a key ID from the provided info.
std::string DeriveKeyId(absl::string_view context) {
return DeriveKey(kKeyIdMasterKey, kKeyIdLabel, context, kKeyIdSizeBits);
}
std::string DeriveGroupSessionKey(absl::string_view context,
const uint32_t size_bits) {
return DeriveKey(kPhonyGroupMasterKey, kGroupKeyLabel, context, size_bits);
}
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
const uint32_t size_bits) {
return DeriveKey(key, kSigningKeyLabel, context, size_bits);
}
bool FourCCEncryptionSchemeIDFromString(const std::string& requested,
uint32_t* four_cc_code) {
if (requested.size() != 4 || four_cc_code == nullptr) return false;
uint32_t result = 0;
for (auto i = 0; i < 4; ++i) {
result <<= 8;
result |= requested[i];
}
switch (result) {
case kCENCSchemeID:
case kCBC1SchemeID:
case kCENSSchemeID:
case kCBCSSchemeID:
*four_cc_code = result;
return true;
default:
return false;
}
}
} // namespace crypto_util
} // namespace widevine

89
common/crypto_util.h Normal file
View File

@@ -0,0 +1,89 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Contains common crypto routines for widevine protocols. These routines are
// used as part of licensing and provisioning request handling.
#ifndef COMMON_CRYPTO_UTIL_H_
#define COMMON_CRYPTO_UTIL_H_
#include <string>
#include "base/macros.h"
#include "absl/strings/string_view.h"
namespace widevine {
namespace crypto_util {
// Default constants used for key derivation for encryption and signing.
// TODO(user): These are duplicated in session.cc in the sdk. de-dup.
extern const char kWrappingKeyLabel[];
extern const int kWrappingKeySizeBits;
extern const char kSigningKeyLabel[];
extern const int kSigningKeySizeBits;
extern const size_t kSigningKeySizeBytes;
extern const char kIvMasterKey[];
extern const char kIvLabel[];
extern const int kIvSizeBits;
extern const int kAes128KeySizeBits;
extern const int kAes128KeySizeBytes;
extern const uint32_t kCENCSchemeID; // 'cenc' (AES-CTR): 0x63656E63
extern const uint32_t kCBC1SchemeID; // 'cbc1' (AES-CBC): 0x63626331
extern const uint32_t kCENSSchemeID; // 'cens' (AES-CTR subsample): 0x63656E73
extern const uint32_t kCBCSSchemeID; // 'cbcs' (AES-CBC subsample): 0x63626373
// DeriveKey uses the NIST 800-108 KDF recommendation, using AES-CMAC PRF.
// NIST 800-108:
// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf
// AES-CMAC:
// http://tools.ietf.org/html/rfc4493
std::string DeriveKey(absl::string_view key, absl::string_view label,
absl::string_view context, const uint32_t size_bits);
// Derives an IV from the provided |context|.
std::string DeriveIv(absl::string_view context);
// Derives a key ID from the provided |context|.
std::string DeriveKeyId(absl::string_view context);
// Helper function to derive a key using the group master key and context.
std::string DeriveGroupSessionKey(absl::string_view context, const uint32_t size_bits);
// Helper function to derive a signing key for from the signing context.
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
const uint32_t size_bits);
// Helper function to create a SHA-256 HMAC signature for the given message.
std::string CreateSignatureHmacSha256(absl::string_view key,
absl::string_view message);
// Helper function which compares the SHA-256 HMAC against the provided
// signature.
bool VerifySignatureHmacSha256(absl::string_view key,
absl::string_view signature,
absl::string_view message);
// Helper function to create a SHA-1 HMAC signature for the given message.
std::string CreateSignatureHmacSha1(absl::string_view key,
absl::string_view message);
// Helper function which compares the SHA-1 HMAC against the provided
// signature.
bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature,
absl::string_view message);
// Converts a requested 4CC encryption scheme ID from a std::string to a uint32_t and
// verifies it is a correct value.
bool FourCCEncryptionSchemeIDFromString(const std::string& requested,
uint32_t* four_cc_code);
} // namespace crypto_util
} // namespace widevine
#endif // COMMON_CRYPTO_UTIL_H_

236
common/crypto_util_test.cc Normal file
View File

@@ -0,0 +1,236 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Unit tests for the crypto_util helper functions.
#include <string>
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "common/crypto_util.h"
namespace widevine {
namespace crypto_util {
const char kCENCStr[] = "cenc";
const char kCBC1Str[] = "cbc1";
const char kCENSStr[] = "cens";
const char kCBCSStr[] = "cbcs";
static unsigned char key_data[] =
{ 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e,
0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b };
static std::string key_str(key_data, key_data + sizeof(key_data));
static unsigned char iv_data[] =
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
static std::string iv_str(iv_data, iv_data + sizeof(iv_data));
TEST(CryptoUtilTest, DeriveAes128KeyTest) {
unsigned char label[] = { 0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1,
0x92, 0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08,
0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60,
0xd1, 0xcf, 0x6a, 0x4d, 0x40, 0xa1, 0xe3, 0xfe,
0xd3, 0xe9, 0xa6, 0x58, 0x4c, 0xd4, 0xad, 0xa4,
0xa2 };
unsigned char context[] = { 0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d, 0x6d,
0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5, 0x5b, 0x80,
0x81, 0x91, 0xff };
unsigned char output0[] = { 0xd5, 0xad, 0x2d, 0xb1, 0x5a, 0x06, 0xcb, 0x50,
0xf2, 0x59, 0x5a, 0xb2, 0xb2, 0x0d, 0x44, 0x4e };
unsigned char output1[] = { 0xdf, 0x38, 0x45, 0x97, 0x5d, 0x7a, 0x81, 0xb4,
0x94, 0x86, 0xaf, 0x0c, 0xdc, 0x4d, 0xeb, 0x62,
0x31, 0x39, 0x67, 0x8f, 0xff, 0x5d, 0x68, 0x35,
0xdc, 0x89, 0x5f, 0x47, 0xca, 0xe0, 0x2d, 0x3a,
0x10, 0x24, 0xf8, 0x7e, 0x5b, 0x70, 0xe1, 0xa3,
0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22 };
std::string label_str(label, label + sizeof(label));
std::string key_str(key_data, key_data + sizeof(key_data));
std::string context_str(context, context + sizeof(context));
std::string result = DeriveKey(key_str, label_str, context_str, 128);
std::string output_128(output0, output0 + sizeof(output0));
ASSERT_EQ(result, output_128);
result = DeriveKey(key_str, label_str, context_str, 384);
std::string output_384(output1, output1 + sizeof(output1));
ASSERT_EQ(result, output_384);
}
TEST(CryptoUtilTest, DeriveGroupSesionKey) {
unsigned char output[] = { 0x92, 0x6c, 0x2f, 0x5, 0xa6, 0x4f, 0xff, 0xb1,
0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1 };
std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128);
EXPECT_EQ(crypto_util::kAes128KeySizeBytes, group_session_key.size());
const std::string output_128(output, output + sizeof(output));
ASSERT_EQ(output_128, group_session_key);
}
TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
unsigned char message_data[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
std::string message(message_data, message_data + sizeof(message_data));
std::string signature(CreateSignatureHmacSha256(key_str, message));
ASSERT_EQ(signature.size(), 32);
ASSERT_TRUE(VerifySignatureHmacSha256(key_str, signature, message));
}
TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) {
unsigned char message_data[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
std::string message(message_data, message_data + sizeof(message_data));
// Test with bogus key;
std::string bogus_key("bogus");
std::string signature(CreateSignatureHmacSha256(bogus_key, message));
// This should still produce an hmac signature.
ASSERT_EQ(signature.size(), 32);
// Create valid signature to compare.
signature = CreateSignatureHmacSha256(key_str, message);
// Test with bogus key.
ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message));
// Test with munged signature.
signature[0] = 0xFF;
ASSERT_FALSE(VerifySignatureHmacSha256(key_str, signature, message));
// Test with bogus signature.
ASSERT_FALSE(VerifySignatureHmacSha256(key_str, "bogus", message));
}
TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) {
unsigned char message_data[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
std::string message(message_data, message_data + sizeof(message_data));
std::string signature(CreateSignatureHmacSha1(key_str, message));
ASSERT_EQ(20, signature.size());
ASSERT_TRUE(VerifySignatureHmacSha1(key_str, signature, message));
}
TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) {
unsigned char message_data[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
std::string message(message_data, message_data + sizeof(message_data));
// Test with bogus key;
std::string bogus_key("bogus");
std::string signature(CreateSignatureHmacSha1(bogus_key, message));
// This should still produce an hmac signature.
ASSERT_EQ(20, signature.size());
// Create valid signature to compare.
signature = CreateSignatureHmacSha1(key_str, message);
// Test with bogus key.
ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message));
// Test with munged signature.
signature[0] = 0xFF;
ASSERT_FALSE(VerifySignatureHmacSha1(key_str, signature, message));
// Test with bogus signature.
ASSERT_FALSE(VerifySignatureHmacSha1(key_str, "bogus", message));
}
TEST(CryptoUtilTest, DeriveIv) {
// First value in the pair is the key_id, second value is the expected IV.
std::pair<std::string, std::string> id_iv_pairs[] = {
{"1234567890123456", "3278234c7682d1a2e153af4912975f5f"},
{"0987654321098765", "cf09abd30f04b60544910791a6b904cf"}};
for (const auto& id_iv_pair : id_iv_pairs) {
SCOPED_TRACE(absl::StrCat("test case:", id_iv_pair.first));
EXPECT_EQ(id_iv_pair.second,
absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
// Repeat same call to verify derivied result is repeatable.
EXPECT_EQ(id_iv_pair.second,
absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
}
}
TEST(CryptoUtilTest, DeriveKeyId) {
// First value in the pair is the context, second value is the expected id.
std::pair<std::string, std::string> context_id_pairs[] = {
{"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"},
{"0987654321098765", "084fc6bece9688ccce6b1672d9b47e22"}};
for (const auto& context_id_pair : context_id_pairs) {
SCOPED_TRACE(absl::StrCat("test case:", context_id_pair.first));
EXPECT_EQ(context_id_pair.second,
absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
// Repeat same call to verify derivied result is repeatable.
EXPECT_EQ(context_id_pair.second,
absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
}
}
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) {
uint32_t cc_code;
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code));
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code));
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code));
}
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromString) {
uint32_t cc_code = 0;
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code));
ASSERT_EQ(kCENCSchemeID, cc_code);
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBC1Str, &cc_code));
ASSERT_EQ(kCBC1SchemeID, cc_code);
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENSStr, &cc_code));
ASSERT_EQ(kCENSSchemeID, cc_code);
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBCSStr, &cc_code));
ASSERT_EQ(kCBCSSchemeID, cc_code);
}
} // namespace crypto_util
} // namespace widevine

View File

@@ -0,0 +1,362 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Implements the DeviceStatusList class.
#include "common/device_status_list.h"
#include <time.h>
#include <memory>
#include "glog/logging.h"
#include "absl/strings/escaping.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "util/gtl/map_util.h"
#include "common/client_cert.h"
#include "common/drm_service_certificate.h"
#include "common/error_space.h"
#include "common/rsa_key.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/errors.pb.h"
namespace widevine {
namespace {
const char kSignedListTerminator[] = "}";
const char kSignedList[] = "signedList\":";
const std::size_t kSignedListLen = strlen(kSignedList);
} // namespace
DeviceStatusList* DeviceStatusList::Instance() {
// TODO(user): This is "ok" according to Google's Coding for Dummies, but
// we should inject the status list into the sessions. This will require
// exposing additional objects in the public interface.
static DeviceStatusList* device_status_list(nullptr);
if (!device_status_list) device_status_list = new DeviceStatusList;
return device_status_list;
}
DeviceStatusList::DeviceStatusList()
: creation_time_seconds_(0),
expiration_period_seconds_(0),
allow_unknown_devices_(true),
allow_test_only_devices_(false) {}
DeviceStatusList::~DeviceStatusList() {}
Status DeviceStatusList::UpdateStatusList(
const std::string& root_certificate_public_key,
const std::string& serialized_certificate_status_list,
uint32_t expiration_period_seconds) {
SignedDeviceCertificateStatusList signed_certificate_status_list;
if (!signed_certificate_status_list.ParseFromString(
serialized_certificate_status_list)) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"signed-certificate-status-list-parse-error");
}
if (!signed_certificate_status_list.has_certificate_status_list()) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list");
}
if (!signed_certificate_status_list.has_signature()) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list-signature");
}
std::unique_ptr<RsaPublicKey> root_key(
RsaPublicKey::Create(root_certificate_public_key));
if (root_key == nullptr) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-public-key");
}
if (!root_key->VerifySignature(
signed_certificate_status_list.certificate_status_list(),
signed_certificate_status_list.signature())) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"invalid-status-list-signature");
}
DeviceCertificateStatusList certificate_status_list;
if (!certificate_status_list.ParseFromString(
signed_certificate_status_list.certificate_status_list())) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"certificate-status-list-parse-error");
}
if (expiration_period_seconds &&
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
expiration_period_seconds))) {
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
"certificate-status-list-expired");
}
absl::WriterMutexLock lock(&status_map_lock_);
device_status_map_.clear();
for (int i = 0, n = certificate_status_list.certificate_status_size(); i < n;
i++) {
const DeviceCertificateStatus& cert_status =
certificate_status_list.certificate_status(i);
if (cert_status.has_device_info()) {
const ProvisionedDeviceInfo& device_info = cert_status.device_info();
if (device_info.has_system_id()) {
device_status_map_[device_info.system_id()] = cert_status;
} else {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"device-info-missing-system-id");
}
}
}
creation_time_seconds_ = certificate_status_list.creation_time_seconds();
expiration_period_seconds_ = expiration_period_seconds;
return OkStatus();
}
Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
ProvisionedDeviceInfo* device_info) {
CHECK(device_info);
// Keybox checks.
if (client_cert.type() == ClientIdentification::KEYBOX) {
if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) {
return Status(error_space, UNSUPPORTED_SYSTEM_ID,
"keybox-unsupported-system-id");
}
// Get device information from certificate status list if available.
if (!GetDeviceInfo(client_cert, device_info)) {
device_info->Clear();
}
return OkStatus();
}
// DRM certificate checks.
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-unsupported-token-type");
}
absl::ReaderMutexLock lock(&status_map_lock_);
if (expiration_period_seconds_ &&
(GetCurrentTime() >
(creation_time_seconds_ + expiration_period_seconds_))) {
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
"certificate-status-list-expired");
}
DeviceCertificateStatus* device_cert_status =
gtl::FindOrNull(device_status_map_, client_cert.system_id());
if (device_cert_status) {
*device_info = device_cert_status->device_info();
if (device_cert_status->status() ==
DeviceCertificateStatus::STATUS_REVOKED) {
if (IsRevokedSystemIdAllowed(client_cert.system_id())) {
LOG(WARNING) << "Allowing REVOKED device: "
<< device_info->ShortDebugString();
} else {
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
"device-certificate-revoked");
}
}
if ((device_cert_status->status() ==
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
!allow_test_only_devices_) {
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
"test-only-drm-certificate-not-allowed");
}
if (!client_cert.signed_by_provisioner() &&
(client_cert.signer_serial_number() !=
device_cert_status->drm_serial_number())) {
// Widevine-provisioned device, and the intermediate certificate serial
// number does not match that in the status list. If the status list is
// newer than the certificate, indicate an invalid certificate, so that
// the device re-provisions. If, on the other hand, the certificate status
// list is older than the certificate, the certificate is for all purposes
// unknown.
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"intermediate-certificate-serial-number-mismatch");
}
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown");
}
} else {
if (!allow_unknown_devices_) {
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown");
}
device_info->Clear();
}
return OkStatus();
}
bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
ProvisionedDeviceInfo* device_info) {
CHECK(device_info);
absl::ReaderMutexLock lock(&status_map_lock_);
DeviceCertificateStatus* device_cert_status =
gtl::FindOrNull(device_status_map_, client_cert.system_id());
if (device_cert_status) {
*device_info = device_cert_status->device_info();
return true;
}
return false;
}
bool DeviceStatusList::IsSystemIdActive(uint32_t system_id) {
absl::ReaderMutexLock lock(&status_map_lock_);
DeviceCertificateStatus* device_cert_status =
gtl::FindOrNull(device_status_map_, system_id);
if (!device_cert_status) {
return allow_unknown_devices_ ||
KeyboxClientCert::IsSystemIdKnown(system_id);
}
if (device_cert_status->status() ==
DeviceCertificateStatus::STATUS_TEST_ONLY) {
return allow_test_only_devices_;
}
if (device_cert_status) {
ProvisionedDeviceInfo device_info = device_cert_status->device_info();
if (device_cert_status->status() ==
DeviceCertificateStatus::STATUS_REVOKED) {
if (IsRevokedSystemIdAllowed(system_id)) {
LOG(WARNING) << "REVOKED system_id: " << system_id
<< " is allowed to be active";
return true;
}
}
}
return device_cert_status->status() !=
DeviceCertificateStatus::STATUS_REVOKED;
}
uint32_t DeviceStatusList::GetCurrentTime() const { return time(nullptr); }
void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) {
for (absl::string_view sp : absl::StrSplit(system_id_list, ',')) {
allowed_revoked_devices_.push_back(std::stoi(std::string(sp)));
}
std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end());
}
bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
auto it = std::binary_search(allowed_revoked_devices_.begin(),
allowed_revoked_devices_.end(), system_id);
return it;
}
Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
const std::string& certificate_provisioning_service_response,
std::string* signed_certificate_status_list, std::string* certificate_status_list) {
Status status = OkStatus();
size_t signed_list_start =
certificate_provisioning_service_response.find(kSignedList);
if (signed_list_start != std::string::npos) {
size_t signed_list_end = certificate_provisioning_service_response.find(
kSignedListTerminator, signed_list_start);
if (signed_list_end == std::string::npos) {
return Status(
error_space, error::INVALID_ARGUMENT,
"Unable to parse the certificate_provisioning_service_response. "
"SignedList not terminated.");
}
std::string signed_list(
certificate_provisioning_service_response.begin() + signed_list_start +
kSignedListLen,
certificate_provisioning_service_response.begin() + signed_list_end);
// Strip off quotes.
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\"'),
signed_list.end());
// Strip off spaces.
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), ' '),
signed_list.end());
// Strip off newlines.
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\n'),
signed_list.end());
// Strip off carriage return (the control-M character)
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\r'),
signed_list.end());
if (!absl::WebSafeBase64Unescape(signed_list,
signed_certificate_status_list)) {
if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) {
return Status(error_space, error::INVALID_ARGUMENT,
"Base64 decode of signedlist failed.");
}
}
} else {
// certificate_provisioning_service_response is the signed list and not a
// JSON message.
if (!absl::WebSafeBase64Unescape(certificate_provisioning_service_response,
signed_certificate_status_list)) {
if (!absl::Base64Unescape(certificate_provisioning_service_response,
signed_certificate_status_list)) {
return Status(error_space, error::INVALID_ARGUMENT,
"Base64 decode of certList failed.");
}
}
}
SignedDeviceCertificateStatusList signed_status_list;
if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"signed-certificate-status-list-parse-error");
}
if (!signed_status_list.has_certificate_status_list()) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list");
}
DeviceCertificateStatusList device_certificate_status_list;
if (!device_certificate_status_list.ParseFromString(
signed_status_list.certificate_status_list())) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"certificate-status-list-parse-error");
}
*certificate_status_list = signed_status_list.certificate_status_list();
return OkStatus();
}
Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
const std::string& version,
std::string* signed_device_certificate_status_list_request) {
if (version.empty()) {
return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty");
}
DCHECK(signed_device_certificate_status_list_request);
if (signed_device_certificate_status_list_request == nullptr) {
return Status(error_space, error::INVALID_ARGUMENT,
"Signed_device_certificate_status_list_request is empty");
}
// Construct SignedDeviceCertificateStatusListRequest.
DeviceCertificateStatusListRequest request;
request.set_sdk_version(version);
request.set_sdk_time_seconds(DeviceStatusList::Instance()->GetCurrentTime());
std::string device_certificate_status_list_request;
request.SerializeToString(&device_certificate_status_list_request);
SignedDeviceCertificateStatusListRequest signed_request;
signed_request.set_device_certificate_status_list_request(
device_certificate_status_list_request);
const DrmServiceCertificate* sc =
DrmServiceCertificate::GetDefaultDrmServiceCertificate();
if (sc == nullptr) {
signed_device_certificate_status_list_request->clear();
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
"Drm service certificate is not loaded.");
}
const RsaPrivateKey* private_key = sc->private_key();
if (private_key == nullptr) {
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
"Private key in the service certificate is null.");
}
std::string signature;
private_key->GenerateSignature(device_certificate_status_list_request,
&signature);
signed_request.set_signature(signature);
signed_request.SerializeToString(
signed_device_certificate_status_list_request);
return OkStatus();
}
} // namespace widevine

122
common/device_status_list.h Normal file
View File

@@ -0,0 +1,122 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// DeviceStatusList class header.
#ifndef COMMON_DEVICE_STATUS_LIST_H__
#define COMMON_DEVICE_STATUS_LIST_H__
#include <map>
#include <string>
#include "base/macros.h"
#include "absl/synchronization/mutex.h"
#include "common/status.h"
#include "protos/public/device_certificate_status.pb.h"
#include "protos/public/provisioned_device_info.pb.h"
namespace widevine {
class ClientCert;
// Manages the certificate status of devices. The list of
// DeviceCertificateStatus is provided by the DRM server. Each license
// request is checked to ensure the certificate in the request is valid and
// not revoked. Also checks to see if the intermediate certificates were
// updated where the system Id is the same, but the serial number changes.
// This case should cause the clients to re-provision.
class DeviceStatusList {
public:
// Returns a pointer to a singleton DeviceStatusList.
static DeviceStatusList* Instance();
DeviceStatusList();
virtual ~DeviceStatusList();
// Takes |serialized_certificate_status_list| and copies to an internal map of
// device certifcate status list. The internal map is used to verify
// a device was not revoked. Returns true is the list was successfully parsed.
Status UpdateStatusList(const std::string& root_certificate_public_key,
const std::string& serialized_certificate_status_list,
uint32_t expiration_period_seconds);
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
bool allow_unknown_devices() const { return allow_unknown_devices_; }
void set_allow_test_only_devices(bool allow) {
allow_test_only_devices_ = allow;
}
bool allow_test_only_devices() const { return allow_test_only_devices_; }
// Checks the device status list and returns either:
// OK
// UNSUPPORTED_SYSTEM_ID
// INVALID_DRM_CERTIFICATE
// DRM_DEVICE_CERTIFICATE_REVOKED
// DRM_DEVICE_CERTIFICATE_UNKNOWN
// If status is OK, a copy of the provisioned device info is copied
// into |device_info|. Caller owns |device_info| and it must not be null.
Status GetCertStatus(const ClientCert& client_cert,
widevine::ProvisionedDeviceInfo* device_info);
// Returns true if the pre-provisioning key or certificate for the specified
// system ID are active (not disallowed or revoked).
bool IsSystemIdActive(uint32_t system_id);
// Returns true if the system ID
// Returns true is a ProvisionedDeviceInfo exist based on <client_cert>.
// Caller owns <device_info> and it must not be null.
bool GetDeviceInfo(const ClientCert& client_cert,
widevine::ProvisionedDeviceInfo* device_info);
// Returns the current POSIX time.
virtual uint32_t GetCurrentTime() const;
// Enable delivery of licenses to revoked client devices. |system_id_list| is
// a comma separated list of systems Ids to allow even if revoked.
virtual void AllowRevokedDevices(const std::string& system_id_list);
/**
* Parses signed device certificate status list and certificate status list
* from certificateProvisoningServer response.
*
* @param certificate_provisioning_service_response
* @param signed_certificate_status_list
* @param certificate_status_list
* @return WvPLStatus - Status::OK if success, else error.
*/
static Status ExtractFromProvisioningServiceResponse(
const std::string& certificate_provisioning_service_response,
std::string* signed_certificate_status_list, std::string* certificate_status_list);
/**
* Constructs signed device certificate status list request string.
*
* @param signed_device_certificate_status_list_request
* @param version
* @return Status - Status::OK if success, else error.
*/
static Status GenerateSignedDeviceCertificateStatusListRequest(
const std::string& version,
std::string* signed_device_certificate_status_list_request);
private:
// Returns true if the system ID is allowed to be revoked.
// Caller owns |system_id|. They must not be null.
bool IsRevokedSystemIdAllowed(uint32_t system_id);
absl::Mutex status_map_lock_;
// Key is the system id for the device.
std::map<uint32_t, widevine::DeviceCertificateStatus> device_status_map_;
uint32_t creation_time_seconds_;
uint32_t expiration_period_seconds_;
bool allow_unknown_devices_;
bool allow_test_only_devices_;
// Contains the list of system_id values that are allowed to succeed even if
// revoked.
std::vector<uint32_t> allowed_revoked_devices_;
DISALLOW_COPY_AND_ASSIGN(DeviceStatusList);
};
} // namespace widevine
#endif // COMMON_DEVICE_STATUS_LIST_H__

View File

@@ -0,0 +1,376 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/device_status_list.h"
#include <stddef.h>
#include <memory>
#include <type_traits>
#include <utility>
#include "glog/logging.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/str_cat.h"
#include "common/client_cert.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/provisioned_device_info.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
namespace widevine {
using ::testing::_;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::ReturnRefOfCopy;
const uint32_t kValidCertSystemId = 100;
const uint32_t kRevokedCertSystemId = 101;
const uint32_t kValidPpkSystemId = 102;
const uint32_t kTestOnlyCertSystemId = 103;
const uint32_t kRevokedAllowedDeviceCertSystemId = 104;
const uint32_t kUnknownSystemId = 666;
const char kValidSerialNumber[] = "valid-serial-number";
const char kRevokedSerialNumber[] = "revoked-serial-number";
const char kRevokedAllowDeviceSerialNumber[] =
"revoked-allow-device-serial-number";
const char kTestOnlySerialNumber[] = "test_only-serial-number";
const char kMismatchSerialNumber[] = "mismatch-serial-number";
const char kDeviceModel[] = "device-model-x";
const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
const uint32_t kStatusListCreationTime = 17798001;
const uint32_t kDefaultExpirePeriod = 0;
class MockCertificateClientCert : public CertificateClientCert {
public:
MockCertificateClientCert() {}
MOCK_CONST_METHOD0(system_id, uint32_t());
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
};
class MockKeyboxClientCert : public KeyboxClientCert {
public:
MockKeyboxClientCert() {}
MOCK_CONST_METHOD0(system_id, uint32_t());
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
};
class DeviceStatusListTest : public ::testing::Test {
public:
~DeviceStatusListTest() override {}
void SetUp() override {
DeviceCertificateStatus *cert_status;
// Device cert with status RELEASED.
cert_status = cert_status_list_.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(kValidCertSystemId);
cert_status->set_drm_serial_number(kValidSerialNumber);
cert_status->mutable_device_info()->set_model(kDeviceModel);
cert_status->set_status(DeviceCertificateStatus::STATUS_RELEASED);
// Device cert with status REVOKED.
cert_status = cert_status_list_.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId);
cert_status->set_drm_serial_number(kRevokedSerialNumber);
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
// Device cert with status REVOKED ALLOWED DEVICE.
cert_status = cert_status_list_.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(
kRevokedAllowedDeviceCertSystemId);
cert_status->set_drm_serial_number(kRevokedAllowDeviceSerialNumber);
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
device_status_list_.AllowRevokedDevices(
absl::StrCat(kRevokedAllowedDeviceCertSystemId));
// Device cert with status TEST_ONLY.
cert_status = cert_status_list_.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(kTestOnlyCertSystemId);
cert_status->set_drm_serial_number(kTestOnlySerialNumber);
cert_status->set_status(DeviceCertificateStatus::STATUS_TEST_ONLY);
cert_status_list_.set_creation_time_seconds(kStatusListCreationTime);
cert_status_list_.SerializeToString(
signed_cert_status_list_.mutable_certificate_status_list());
std::unique_ptr<RsaPrivateKey> root_key(
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
ASSERT_TRUE(root_key);
ASSERT_TRUE(root_key->GenerateSignature(
signed_cert_status_list_.certificate_status_list(),
signed_cert_status_list_.mutable_signature()));
ASSERT_TRUE(
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
ASSERT_EQ(OkStatus(), device_status_list_.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, kDefaultExpirePeriod));
}
DeviceStatusList device_status_list_;
RsaTestKeys test_keys_;
DeviceCertificateStatusList cert_status_list_;
SignedDeviceCertificateStatusList signed_cert_status_list_;
std::string serialized_status_list_;
};
// Returns the number of DevcieCertificateStatus messages in the list.
TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
// Test case where the Certificate status is set to Valid.
ProvisionedDeviceInfo device_info;
MockCertificateClientCert valid_client_cert;
std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(valid_client_cert, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(valid_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model());
// Test case where the Certificate status is Revoked.
MockCertificateClientCert revoked_client_cert;
std::string revoked_drm_serial_number(kRevokedSerialNumber);
EXPECT_CALL(revoked_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(revoked_client_cert, system_id())
.WillRepeatedly(Return(kRevokedCertSystemId));
EXPECT_CALL(revoked_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(revoked_drm_serial_number));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED,
device_status_list_.GetCertStatus(revoked_client_cert, &device_info)
.error_code());
// Test case where the revoked cert is allowed.
device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId));
EXPECT_OK(
device_status_list_.GetCertStatus(revoked_client_cert, &device_info));
}
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
ProvisionedDeviceInfo device_info;
MockCertificateClientCert test_only_client_cert;
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
EXPECT_CALL(test_only_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(test_only_client_cert, system_id())
.WillRepeatedly(Return(kTestOnlyCertSystemId));
EXPECT_CALL(test_only_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
EXPECT_EQ(
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
device_status_list_.GetCertStatus(test_only_client_cert, &device_info)
.error_code());
}
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
ProvisionedDeviceInfo device_info;
MockCertificateClientCert test_only_client_cert;
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
device_status_list_.set_allow_test_only_devices(true);
EXPECT_CALL(test_only_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(test_only_client_cert, system_id())
.WillRepeatedly(Return(kTestOnlyCertSystemId));
EXPECT_CALL(test_only_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(test_only_client_cert,
&device_info));
}
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
std::multimap<uint32_t, std::string> preprov_keys;
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
// Test case where the Certificate status is set to Valid.
ProvisionedDeviceInfo device_info;
MockKeyboxClientCert valid_client_keybox;
std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_keybox, type())
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(valid_client_keybox, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(valid_client_keybox,
&device_info));
EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model());
MockKeyboxClientCert unknown_client_keybox;
EXPECT_CALL(unknown_client_keybox, type())
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(unknown_client_keybox, system_id())
.WillRepeatedly(Return(kUnknownSystemId));
EXPECT_EQ(
UNSUPPORTED_SYSTEM_ID,
device_status_list_.GetCertStatus(unknown_client_keybox, &device_info)
.error_code());
EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model());
}
TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
device_status_list_.set_allow_unknown_devices(true);
// Test case where the signer certificate is older than the current status
// list.
MockCertificateClientCert older_client_cert;
ProvisionedDeviceInfo device_info;
std::string mismatch_drm_serial_number(kMismatchSerialNumber);
EXPECT_CALL(older_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(older_client_cert, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(older_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(older_client_cert, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime - 1));
EXPECT_EQ(INVALID_DRM_CERTIFICATE,
device_status_list_.GetCertStatus(older_client_cert, &device_info)
.error_code());
// We allow this case only for certs signed by a provisioner cert.
EXPECT_CALL(older_client_cert, signed_by_provisioner())
.WillOnce(Return(true));
EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(older_client_cert, &device_info));
EXPECT_TRUE(device_info.has_system_id());
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
// Test case where the signer certificate is newer than the current status
// list, and unknown devices are allowed.
MockCertificateClientCert newer_client_cert1;
EXPECT_CALL(newer_client_cert1, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(newer_client_cert1, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(newer_client_cert1, signer_serial_number())
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_.GetCertStatus(newer_client_cert1, &device_info)
.error_code());
// Test case where the signer certificate is newer than the current status
// list, and unknown devices are not allowed.
device_status_list_.set_allow_unknown_devices(false);
MockCertificateClientCert newer_client_cert2;
EXPECT_CALL(newer_client_cert2, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(newer_client_cert2, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(newer_client_cert2, signer_serial_number())
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime + 1));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_.GetCertStatus(newer_client_cert2, &device_info)
.error_code());
}
TEST_F(DeviceStatusListTest, InvalidStatusList) {
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
device_status_list_
.UpdateStatusList(test_keys_.public_test_key_2_2048_bits(),
serialized_status_list_, 0)
.error_code());
++(*signed_cert_status_list_.mutable_certificate_status_list())[4];
ASSERT_TRUE(
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
device_status_list_
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 0)
.error_code());
}
class MockDeviceStatusList : public DeviceStatusList {
public:
MOCK_CONST_METHOD0(GetCurrentTime, uint32_t());
};
TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
MockDeviceStatusList mock_device_status_list;
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
.Times(2)
.WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100));
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100)
.error_code());
}
TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
MockDeviceStatusList mock_device_status_list;
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
.Times(3)
.WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100));
ProvisionedDeviceInfo device_info;
MockCertificateClientCert valid_client_cert;
std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(valid_client_cert, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(valid_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime - 1));
EXPECT_EQ(OkStatus(), mock_device_status_list.GetCertStatus(valid_client_cert,
&device_info));
EXPECT_EQ(
EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list.GetCertStatus(valid_client_cert, &device_info)
.error_code());
}
TEST_F(DeviceStatusListTest, IsSystemIdActive) {
std::multimap<uint32_t, std::string> preprov_keys;
preprov_keys.insert(
std::make_pair(kValidPpkSystemId, "00112233445566778899aabbccddeeff"));
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
device_status_list_.set_allow_unknown_devices(false);
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId));
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId));
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId));
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kUnknownSystemId));
device_status_list_.set_allow_unknown_devices(true);
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId));
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId));
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId));
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kUnknownSystemId));
EXPECT_TRUE(
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
}
} // namespace widevine

View File

@@ -0,0 +1,532 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2013 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// common_typos_disable. Successful / successfull.
#include "common/drm_root_certificate.h"
#include <memory>
#include "glog/logging.h"
#include "absl/memory/memory.h"
#include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h"
#include "common/error_space.h"
#include "common/rsa_key.h"
#include "common/sha_util.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
namespace widevine {
namespace {
const char kDevelopmentString[] = "dev"; // QA systems.
const char kProductionString[] = "prod"; // Production.
const char kTestingString[] = "test"; // Code development / unit tests.
const bool kUseCache = true;
// From common::TestDrmCertificates.
// TODO(user): common::test_certificates is a testonly target, consider
// how to use instead of dupliciating the test cert here.
static const unsigned char kTestRootCertificate[] = {
0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5,
0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78,
0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f,
0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca,
0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b,
0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46,
0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01,
0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c,
0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14,
0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd,
0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6,
0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde,
0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31,
0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a,
0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90,
0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49,
0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f,
0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda,
0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17,
0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30,
0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf,
0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16,
0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42,
0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9,
0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4,
0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a,
0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72,
0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde,
0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04,
0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b,
0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33,
0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67,
0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02,
0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a,
0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35,
0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe,
0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49,
0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6,
0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58,
0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce,
0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27,
0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5,
0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c,
0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e,
0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12,
0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54,
0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8,
0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1,
0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1,
0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8,
0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4,
0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9,
0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd,
0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9,
0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f,
0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92,
0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15,
0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0,
0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9,
0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1,
0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2,
0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c,
0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36,
0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35,
0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7,
0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44};
static const unsigned char kDevRootCertificate[] = {
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xc3, 0x94, 0x88,
0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
0x81, 0x00, 0xc0, 0x00, 0x36, 0x6f, 0x8e, 0xe9, 0xcf, 0x86, 0xdb, 0xcd,
0xdd, 0x4e, 0xfd, 0xcd, 0x45, 0xbf, 0x6d, 0x96, 0x05, 0x00, 0xb8, 0x72,
0xff, 0x9c, 0xb4, 0x39, 0xa8, 0xd8, 0xc0, 0x09, 0x73, 0xc0, 0x24, 0x6a,
0x39, 0x4d, 0x36, 0x3f, 0x9a, 0xe4, 0xb8, 0x76, 0xdc, 0x34, 0xe3, 0xee,
0x5f, 0xdd, 0x13, 0x20, 0x08, 0xdc, 0x4e, 0x6f, 0x4e, 0x9f, 0xc0, 0x36,
0xf9, 0xce, 0xc6, 0xb7, 0xdb, 0xe0, 0x51, 0x2d, 0x30, 0x0b, 0xae, 0x0a,
0x20, 0xd2, 0x29, 0x3c, 0x2c, 0x1d, 0x87, 0x65, 0xeb, 0x5f, 0x93, 0xd7,
0x3f, 0x12, 0x08, 0x50, 0x0e, 0x55, 0xf3, 0xf1, 0x19, 0xee, 0x18, 0x21,
0x6e, 0xea, 0xb6, 0x0a, 0x4a, 0x0b, 0x9c, 0x72, 0x37, 0xeb, 0x0b, 0x68,
0xfc, 0x52, 0x46, 0x62, 0xd0, 0xa2, 0x99, 0x66, 0xe2, 0x2b, 0x74, 0xdd,
0x5c, 0xaf, 0x9a, 0x03, 0xc4, 0x5d, 0x93, 0xfb, 0xcd, 0x45, 0x9a, 0xee,
0xfb, 0x7b, 0x18, 0x94, 0xc1, 0x8c, 0x82, 0x34, 0x7f, 0x02, 0x12, 0x21,
0xfc, 0x40, 0xc1, 0x50, 0xc9, 0xf4, 0x7c, 0xd5, 0x96, 0xbe, 0x55, 0x7f,
0x3c, 0x1d, 0x70, 0x34, 0xb4, 0xa2, 0x03, 0xc4, 0x3f, 0x89, 0x60, 0xe4,
0x24, 0x09, 0x1a, 0x74, 0xc4, 0xb6, 0x39, 0xf0, 0x34, 0x60, 0x8e, 0xa7,
0x5f, 0x02, 0x7f, 0xb9, 0x2a, 0xc5, 0xaa, 0xb2, 0x4c, 0x34, 0xd3, 0x5a,
0x5d, 0xfa, 0x07, 0xf2, 0xb9, 0xb3, 0xc1, 0xba, 0xab, 0xbe, 0x89, 0x99,
0xe3, 0x6d, 0x9b, 0xa9, 0xd3, 0xaf, 0x2a, 0x08, 0x76, 0xf3, 0x0e, 0xc9,
0xe0, 0xb3, 0xbf, 0x51, 0x0c, 0xc5, 0xf4, 0xf3, 0x15, 0x7b, 0x08, 0x11,
0x8f, 0x61, 0x1f, 0x61, 0x64, 0xdb, 0x15, 0x84, 0x5b, 0x8a, 0xd1, 0x28,
0x40, 0xde, 0xc5, 0x32, 0xb5, 0xad, 0xad, 0x65, 0x4c, 0xf5, 0xf7, 0xd1,
0x90, 0x14, 0x5d, 0xc2, 0x85, 0x98, 0xcc, 0xe9, 0xe6, 0x95, 0x42, 0xe1,
0x3e, 0xfc, 0x7f, 0xc4, 0x49, 0xed, 0x9c, 0xe4, 0x49, 0x3f, 0x03, 0x1b,
0x0d, 0xa0, 0xfb, 0xf5, 0x38, 0x49, 0xd2, 0xdf, 0xa3, 0x88, 0xb2, 0x76,
0x93, 0x08, 0x20, 0x18, 0xfe, 0xdc, 0x72, 0x6c, 0x6e, 0xbf, 0x61, 0x37,
0x03, 0xdb, 0xe5, 0x72, 0x68, 0xe0, 0x99, 0x2f, 0xb9, 0xe0, 0x2e, 0xbb,
0x9f, 0x96, 0x36, 0x61, 0xaa, 0x2d, 0xa4, 0x93, 0xe8, 0x50, 0x58, 0xe6,
0x61, 0xe1, 0x14, 0xcf, 0xac, 0x86, 0x98, 0x7f, 0x3c, 0x67, 0x16, 0xce,
0xb8, 0x70, 0x90, 0x3a, 0x5a, 0xd4, 0xe1, 0xe2, 0x35, 0x98, 0xbf, 0x93,
0x41, 0x11, 0xb2, 0x44, 0xb2, 0x64, 0xc2, 0xe7, 0x09, 0x45, 0xb7, 0x6f,
0xb0, 0xbd, 0x6e, 0xe8, 0x67, 0xfa, 0x8d, 0xd4, 0xfa, 0x4b, 0xef, 0xa8,
0x9d, 0x8a, 0x0a, 0xd9, 0x14, 0x77, 0x09, 0x11, 0x9e, 0xc3, 0x50, 0x14,
0x6c, 0x45, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x17, 0x01,
0x60, 0x24, 0xe1, 0xfd, 0x75, 0x60, 0x17, 0x5c, 0x5e, 0x6f, 0x9f, 0x7f,
0xdf, 0xee, 0xf0, 0xf7, 0x7d, 0xb2, 0x50, 0x65, 0x36, 0x26, 0x14, 0x19,
0x01, 0x5e, 0x98, 0x94, 0x65, 0x97, 0x83, 0xaa, 0x4a, 0x2b, 0x98, 0x2e,
0x02, 0xf3, 0xb2, 0xc9, 0xb2, 0xed, 0xd3, 0x1b, 0x20, 0x27, 0x9e, 0xe1,
0x25, 0xc7, 0x86, 0xf0, 0x66, 0x68, 0x5d, 0xd2, 0x3d, 0xa7, 0xbb, 0xbc,
0x22, 0xfc, 0x29, 0xfa, 0x17, 0x16, 0xf4, 0xa2, 0x00, 0x10, 0x87, 0xb4,
0x5d, 0x51, 0x45, 0x6b, 0xc8, 0xf4, 0x6b, 0xcc, 0x92, 0x91, 0xe7, 0xa7,
0x93, 0xbc, 0xc7, 0x2e, 0xdc, 0xac, 0x82, 0x2b, 0x85, 0x56, 0x7b, 0xae,
0xf2, 0xd8, 0xda, 0xa6, 0xd7, 0xfa, 0x6d, 0x70, 0x2a, 0x2e, 0xcf, 0x69,
0xef, 0x57, 0x91, 0xa7, 0xaa, 0x40, 0x15, 0x4a, 0x49, 0x1b, 0xbc, 0x36,
0xbb, 0x1c, 0x94, 0x33, 0x36, 0x61, 0x22, 0x9d, 0x22, 0x66, 0xf0, 0x88,
0x5e, 0x7c, 0x3c, 0xa5, 0xff, 0x81, 0xcf, 0x1a, 0x44, 0xa1, 0x2b, 0xdf,
0xc9, 0x3d, 0xd5, 0xc7, 0xc7, 0x3a, 0x75, 0xac, 0x29, 0xfa, 0xfd, 0x5b,
0xda, 0xf5, 0x8f, 0xd9, 0xdf, 0x08, 0xa4, 0x8d, 0x19, 0x4a, 0xa4, 0x79,
0x6e, 0x47, 0xf6, 0x07, 0xe0, 0xbd, 0xbf, 0x30, 0x3a, 0xf9, 0xf5, 0xc0,
0x90, 0x6d, 0x70, 0x27, 0x44, 0xa8, 0x5e, 0x70, 0xcd, 0x43, 0x3e, 0xaf,
0xf0, 0xd7, 0x20, 0xd3, 0x5e, 0x97, 0x2d, 0x32, 0x1a, 0x3d, 0x2d, 0x0f,
0x0f, 0xcf, 0xac, 0x4e, 0x88, 0x75, 0x98, 0x6c, 0xfa, 0xe8, 0x42, 0x58,
0x99, 0xaa, 0x45, 0x0c, 0x41, 0x0c, 0x6e, 0x27, 0x58, 0x57, 0xd2, 0x5b,
0x82, 0x3d, 0x75, 0x2f, 0x9e, 0xf3, 0xe4, 0x00, 0xcf, 0x91, 0x48, 0x25,
0xca, 0x98, 0xf2, 0x91, 0x6b, 0x41, 0xa5, 0xe8, 0xcd, 0x64, 0xa7, 0x2e,
0x78, 0xc7, 0x76, 0x82, 0x3f, 0xf8, 0x57, 0x8a, 0x9d, 0x78, 0x25, 0xad,
0xf3, 0x1a, 0x8b, 0xfc, 0x83, 0x9a, 0x98, 0x87, 0xe4, 0x55, 0x3e, 0x1c,
0xa7, 0x80, 0x8f, 0xd6, 0x76, 0xab, 0x03, 0xc7, 0x05, 0x66, 0xc3, 0xa0,
0x4c, 0x33, 0x1f, 0x39, 0x74, 0x1b, 0x2a, 0xbf, 0xe6, 0xb0, 0x9f, 0x6b,
0xc1, 0xd6, 0xd3, 0xf4, 0x46, 0x9b, 0xf3, 0xab, 0xca, 0x2e, 0x88, 0x3d,
0x84, 0x5f, 0xc9, 0x9b, 0x47, 0xbb, 0x57, 0x64, 0x08, 0x0e, 0x18, 0x74,
0x83, 0x44, 0xd4, 0xc3, 0x18, 0x97, 0xcf, 0x89, 0x6a, 0x49, 0x51, 0xc6,
0xff, 0x8d, 0x39, 0xc5, 0x23, 0xf9, 0xd5, 0x01, 0xd7, 0x2f, 0xa9, 0xa5,
0x5d, 0xa9, 0xf3, 0xc9, 0xfd, 0xc4, 0x52, 0x19, 0x7d, 0xf6, 0xa4, 0x2c,
0x0c, 0xa0, 0x07, 0xdf, 0x7b, 0x44, 0xd7, 0xe5, 0xbf, 0x57, 0x87, 0xc9,
0x8c, 0xfe, 0x30, 0xb2, 0x89, 0x5d, 0x00, 0x03, 0x3b, 0xe5};
static const unsigned char kProdRootCertificate[] = {
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xdd, 0x94, 0x88,
0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11,
0x97, 0x09, 0xe8, 0x68, 0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d,
0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0,
0x57, 0x03, 0x53, 0x4c, 0xf6, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3,
0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe,
0x66, 0xdf, 0xc5, 0x21, 0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46,
0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22,
0xbe, 0x34, 0x23, 0x8b, 0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48,
0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f,
0x0f, 0x92, 0xd6, 0x4c, 0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10,
0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53,
0x9a, 0x97, 0xeb, 0x84, 0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26,
0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e,
0x72, 0xa0, 0xfa, 0xc1, 0xbd, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4,
0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc,
0xfd, 0x82, 0x48, 0x2b, 0xb7, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba,
0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e,
0x65, 0xf5, 0xc8, 0x6c, 0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca,
0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98,
0x79, 0x52, 0x5e, 0x45, 0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba,
0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa,
0x4f, 0x5e, 0x48, 0x77, 0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19,
0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf,
0xe4, 0x29, 0x06, 0x5e, 0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed,
0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35,
0xec, 0x1f, 0x11, 0xb3, 0x24, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c,
0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d,
0x68, 0x15, 0x84, 0xff, 0xa5, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4,
0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12,
0xd2, 0x88, 0x6d, 0x41, 0x3d, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4,
0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5,
0xce, 0xa6, 0x96, 0x55, 0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33,
0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x58, 0xf1,
0xd6, 0x4d, 0x04, 0x09, 0x7b, 0xdf, 0xd7, 0xef, 0x5d, 0x3b, 0x02, 0x39,
0x17, 0xfa, 0x14, 0x36, 0x75, 0x4a, 0x38, 0x67, 0x85, 0x57, 0x12, 0xa7,
0x14, 0xee, 0x35, 0x16, 0xd5, 0x3d, 0xbf, 0x42, 0x86, 0xf6, 0x69, 0x00,
0x76, 0xcd, 0x93, 0xf4, 0x7c, 0xb2, 0xdf, 0x9e, 0x44, 0xcd, 0x4c, 0xd4,
0xae, 0x09, 0x18, 0x53, 0x44, 0x32, 0xec, 0xe0, 0x61, 0x1b, 0xe5, 0xda,
0x13, 0xd3, 0x55, 0xc5, 0xdd, 0x1a, 0xcb, 0x90, 0x1e, 0x7e, 0x5b, 0xc6,
0xe9, 0x0f, 0x22, 0x9f, 0xbe, 0x85, 0x02, 0xfe, 0x90, 0x31, 0xcc, 0x6b,
0x03, 0x84, 0xbd, 0x22, 0xc4, 0x55, 0xfa, 0xf5, 0xf2, 0x08, 0xcd, 0x65,
0x41, 0x58, 0xe8, 0x7d, 0x29, 0xda, 0x04, 0x58, 0x82, 0xf5, 0x37, 0x69,
0xbc, 0xf3, 0x5a, 0x57, 0x84, 0x17, 0x7b, 0x32, 0x87, 0x70, 0xb2, 0xb0,
0x76, 0x9c, 0xb2, 0xc3, 0x15, 0xd1, 0x11, 0x26, 0x2a, 0x23, 0x75, 0x99,
0x3e, 0xb9, 0x77, 0x22, 0x32, 0x0d, 0xbc, 0x1a, 0x19, 0xc1, 0xd5, 0x65,
0x90, 0x76, 0x55, 0x74, 0x0f, 0x0e, 0x69, 0x4d, 0x5f, 0x4d, 0x8f, 0x19,
0xaf, 0xdf, 0xd6, 0x16, 0x31, 0x94, 0xa8, 0x92, 0x5f, 0x4f, 0xbc, 0x7a,
0x31, 0xf8, 0xae, 0x8e, 0xad, 0x33, 0xb7, 0xe9, 0x30, 0xd0, 0x8c, 0x0a,
0x8a, 0x6c, 0x83, 0x35, 0xf8, 0x8a, 0x81, 0xb2, 0xfe, 0x1c, 0x88, 0xac,
0x2a, 0x66, 0xc5, 0xff, 0xbd, 0xe6, 0x17, 0xd0, 0x62, 0x0b, 0xdc, 0x8a,
0x45, 0xf7, 0xb0, 0x3e, 0x5a, 0xc8, 0x1e, 0x4a, 0x24, 0x2f, 0x6c, 0xa5,
0xe3, 0x1c, 0x88, 0x14, 0x83, 0xd5, 0xc5, 0xef, 0x5e, 0x9f, 0x3d, 0x85,
0x45, 0x73, 0xe2, 0x6b, 0x50, 0x52, 0x57, 0x4c, 0xfb, 0x92, 0x6c, 0x66,
0x75, 0x8a, 0xd6, 0x0d, 0x1b, 0xae, 0xf3, 0xec, 0xaf, 0x51, 0x22, 0x03,
0x5d, 0x0a, 0x2e, 0x63, 0x93, 0x9c, 0x0b, 0x01, 0x20, 0xa8, 0xa9, 0x84,
0x2e, 0x17, 0xca, 0xae, 0x73, 0xec, 0x22, 0x1b, 0x79, 0xae, 0xf6, 0xa0,
0x72, 0x2c, 0xdf, 0x07, 0x47, 0xdb, 0x88, 0x86, 0x30, 0x14, 0x78, 0x21,
0x11, 0x22, 0x88, 0xac, 0xd7, 0x54, 0x74, 0xf9, 0xf3, 0x26, 0xc2, 0xa5,
0x56, 0xc8, 0x56, 0x4f, 0x00, 0x29, 0x1d, 0x08, 0x7b, 0x7a, 0xfb, 0x95,
0x89, 0xc3, 0xee, 0x98, 0x54, 0x9e, 0x3c, 0x6b, 0x94, 0x05, 0x13, 0x12,
0xf6, 0x71, 0xb9, 0xab, 0x13, 0xc3, 0x0c, 0x9b, 0x46, 0x08, 0x7b, 0x3d,
0x32, 0x6a, 0x68, 0xca, 0x1e, 0x9c, 0x90, 0x62, 0xc5, 0xed, 0x10, 0xb9,
0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd, 0xc4, 0x46, 0xf5, 0xa3,
0x62, 0x13, 0x74, 0x02, 0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf,
0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5, 0x02, 0x0c};
} // namespace
// Caches an individual signature for a certificate with a specific serial
// number (signer).
struct VerifiedCertSignature {
VerifiedCertSignature(const std::string& cert, const std::string& sig,
const std::string& signer_sn)
: signed_cert(cert), signature(sig), signer_serial(signer_sn) {}
std::string signed_cert;
std::string signature;
std::string signer_serial;
};
// Map of certificate serial number to its signature.
typedef std::map<std::string, VerifiedCertSignature> VerifiedCertSignatures;
class VerifiedCertSignatureCache {
public:
explicit VerifiedCertSignatureCache(const RsaKeyFactory* key_factory)
: key_factory_(key_factory) {}
// Checks cache, on miss, uses public key. If successful, adds to
// cache.
Status VerifySignature(const std::string& cert, const std::string& serial_number,
const std::string& signature,
const std::string& signer_public_key,
const std::string& signer_serial_number) {
{
VerifiedCertSignatures::iterator cached_signature;
absl::ReaderMutexLock read_lock(&signature_cache_mutex_);
cached_signature = signature_cache_.find(serial_number);
if (cached_signature != signature_cache_.end()) {
// TODO(user): Log which of the following three conditions occurs.
if ((cert != cached_signature->second.signed_cert) ||
(signature != cached_signature->second.signature) ||
(signer_serial_number != cached_signature->second.signer_serial)) {
// Cached signature mismatch.
return Status(error_space, INVALID_SIGNATURE,
"cached-signature-mismatch");
}
// Cached signature match.
return OkStatus();
}
}
// Cache miss. Verify signature.
std::unique_ptr<RsaPublicKey> signer_key(
key_factory_->CreateFromPkcs1PublicKey(signer_public_key));
if (!signer_key) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-public-key");
}
if (!signer_key->VerifySignature(cert, signature)) {
return Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature");
}
// Add signature to cache.
absl::WriterMutexLock write_lock(&signature_cache_mutex_);
signature_cache_.emplace(
serial_number,
VerifiedCertSignature(cert, signature, signer_serial_number));
return OkStatus();
}
private:
VerifiedCertSignatures signature_cache_ GUARDED_BY(&signature_cache_mutex_);
absl::Mutex signature_cache_mutex_;
const RsaKeyFactory* key_factory_;
};
Status DrmRootCertificate::CreateByType(
CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) {
CHECK(cert);
return Create(cert_type, absl::make_unique<RsaKeyFactory>(), cert);
}
std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
CertificateType cert_type, Status* status) {
CHECK(status);
std::unique_ptr<DrmRootCertificate> new_root_cert;
*status = CreateByType(cert_type, &new_root_cert);
return new_root_cert;
}
Status DrmRootCertificate::CreateByTypeString(
const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert) {
CHECK(cert);
CertificateType cert_type;
if (cert_type_string == kDevelopmentString) {
cert_type = kCertificateTypeDevelopment;
} else if (cert_type_string == kProductionString) {
cert_type = kCertificateTypeProduction;
} else if (cert_type_string == kTestingString) {
cert_type = kCertificateTypeTesting;
} else {
return Status(error_space, INVALID_PARAMETER,
absl::StrCat("invalid-certificate-type ", cert_type_string));
}
return CreateByType(cert_type, cert);
}
Status DrmRootCertificate::Create(CertificateType cert_type,
std::unique_ptr<RsaKeyFactory> key_factory,
std::unique_ptr<DrmRootCertificate>* cert) {
DCHECK(cert);
std::string serialized_certificate;
switch (cert_type) {
case kCertificateTypeProduction: {
serialized_certificate.assign(
kProdRootCertificate,
kProdRootCertificate + sizeof(kProdRootCertificate));
break;
}
case kCertificateTypeDevelopment: {
serialized_certificate.assign(
kDevRootCertificate,
kDevRootCertificate + sizeof(kDevRootCertificate));
break;
}
case kCertificateTypeTesting: {
serialized_certificate.assign(
kTestRootCertificate,
kTestRootCertificate + sizeof(kTestRootCertificate));
break;
}
default:
return Status(error_space, INVALID_PARAMETER, "invalid-certificate-type");
}
SignedDrmCertificate signed_root_cert;
if (!signed_root_cert.ParseFromString(serialized_certificate)) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"signed-root-cert-deserialize-fail");
}
DrmCertificate root_cert;
if (!signed_root_cert.has_drm_certificate()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-root-device-certificate");
}
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"root-cert-deserialize-fail");
}
if (!root_cert.has_public_key()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-root-cert-public-key");
}
if (!signed_root_cert.has_signature()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-root-certificate-signature");
}
std::unique_ptr<RsaPublicKey> public_key(
key_factory->CreateFromPkcs1PublicKey(root_cert.public_key()));
if (!public_key) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-public-key");
}
if (!public_key->VerifySignature(signed_root_cert.drm_certificate(),
signed_root_cert.signature())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-certificate-signature");
}
cert->reset(new DrmRootCertificate(
cert_type, serialized_certificate, root_cert.serial_number(),
root_cert.public_key(), std::move(key_factory)));
return OkStatus();
}
DrmRootCertificate::DrmRootCertificate(
CertificateType type, const std::string& serialized_certificate,
const std::string& serial_number, const std::string& public_key,
std::unique_ptr<RsaKeyFactory> key_factory)
: type_(type),
serialized_certificate_(serialized_certificate),
serial_number_(serial_number),
public_key_(public_key),
key_factory_(std::move(key_factory)),
signature_cache_(new VerifiedCertSignatureCache(key_factory_.get())) {}
DrmRootCertificate::~DrmRootCertificate() {}
std::string DrmRootCertificate::GetDigest() const {
return absl::BytesToHexString(Sha256_Hash(serialized_certificate_));
}
Status DrmRootCertificate::VerifyCertificate(
const std::string& serialized_certificate,
SignedDrmCertificate* signed_certificate,
DrmCertificate* certificate) const {
std::unique_ptr<SignedDrmCertificate> local_signed_certificate;
if (!signed_certificate) {
local_signed_certificate = absl::make_unique<SignedDrmCertificate>();
signed_certificate = local_signed_certificate.get();
}
if (!signed_certificate->ParseFromString(serialized_certificate)) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signed-drm-certificate");
}
std::unique_ptr<DrmCertificate> local_certificate;
if (!certificate) {
local_certificate = absl::make_unique<DrmCertificate>();
certificate = local_certificate.get();
}
if (signed_certificate->drm_certificate().empty() ||
!certificate->ParseFromString(signed_certificate->drm_certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-drm-certificate");
}
if (certificate->serial_number().empty()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-serial-number");
}
if (!certificate->has_creation_time_seconds()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-creation-time");
}
if (certificate->public_key().empty()) {
return Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key");
}
// Verify signature chain, but do not use cache for leaf certificates.
return VerifySignatures(*signed_certificate, certificate->serial_number(),
!kUseCache);
}
// Recursively verifies certificates with their signing certs or the root.
// use_cache should be false when initially called so that signatures do not
// cached leaf certificates not signed with the root certificate, such as for
// the case of device-unique device certificates.
// Signatures for root-signed certificates are always cached, even if they are
// leaf certificates. For example service, and provisioner certificates.
Status DrmRootCertificate::VerifySignatures(
const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number,
bool use_cache) const {
if (!signed_cert.has_signer()) {
// Always use cache for root-signed certificates.
return signature_cache_->VerifySignature(
signed_cert.drm_certificate(), cert_serial_number,
signed_cert.signature(), public_key(), serial_number_);
}
DrmCertificate signer;
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-certificate");
}
// Verify the signer before verifying signed_cert.
Status status =
VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache);
if (!status.ok()) {
return status;
}
if (use_cache) {
status = signature_cache_->VerifySignature(
signed_cert.drm_certificate(), cert_serial_number,
signed_cert.signature(), signer.public_key(), signer.serial_number());
if (!status.ok()) {
return status;
}
} else {
std::unique_ptr<RsaPublicKey> signer_public_key(
key_factory_->CreateFromPkcs1PublicKey(signer.public_key()));
if (!signer_public_key) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-leaf-signer-public-key");
}
if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(),
signed_cert.signature())) {
return Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature");
}
}
return OkStatus();
}
} // namespace widevine

View File

@@ -0,0 +1,106 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2013 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Root device certificate holder class which deserializes, validates,
// and extracts the root certificate public key.
#ifndef COMMON_DRM_ROOT_CERTIFICATE_H_
#define COMMON_DRM_ROOT_CERTIFICATE_H_
// common_typos_disable. Successful / successfull.
#include <memory>
#include <string>
#include "base/macros.h"
#include "common/status.h"
#include "common/certificate_type.h"
namespace widevine {
class DrmCertificate;
class RsaKeyFactory;
class RsaPublicKey;
class SignedDrmCertificate;
class VerifiedCertSignatureCache;
// Root certificate and certificate chain verifier with internal caching.
// This object is thread-safe.
class DrmRootCertificate {
public:
virtual ~DrmRootCertificate();
// Creates a DrmRootCertificate object given a certificate type.
// |cert| may not be nullptr, and it points to a
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
// created const DrmRootCertificate* if successful. The caller assumes
// ownership of the new DrmRootCertificate. This method returns
// Status::OK on success, or appropriate error status otherwise.
static Status CreateByType(CertificateType cert_type,
std::unique_ptr<DrmRootCertificate>* cert);
// Variant on the method above to make CLIF happy until b/110539622 is fixed.
static std::unique_ptr<DrmRootCertificate> CreateByType(
CertificateType cert_type, Status* status);
// Creates a DrmRootCertificate object given a certificate type std::string, which
// must be one of "prod", "qa", or "test".
// |cert| may not be nullptr, and it points to a
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
// created const DrmRootCertificate* if successful. The caller assumes
// ownership of the new DrmRootCertificate. This method returns
// Status::OK on success, or appropriate error status otherwise.
static Status CreateByTypeString(const std::string& cert_type_string,
std::unique_ptr<DrmRootCertificate>* cert);
// |certificate| will contgain the DRM certificate upon successful return.
// May be null.
// Returns Status::OK if successful, or an appropriate error code otherwise.
virtual Status VerifyCertificate(const std::string& serialized_certificate,
SignedDrmCertificate* signed_certificate,
DrmCertificate* certificate) const;
// Returns the hex-encoded SHA-256 digest for this certificate.
virtual std::string GetDigest() const;
const CertificateType type() const { return type_; }
const std::string& public_key() const { return public_key_; }
protected:
DrmRootCertificate(CertificateType cert_type,
const std::string& serialized_certificate,
const std::string& serial_number, const std::string& public_key,
std::unique_ptr<RsaKeyFactory> key_factory);
private:
friend class DrmRootCertificateTest;
static Status Create(CertificateType cert_type,
std::unique_ptr<RsaKeyFactory> key_factory,
std::unique_ptr<DrmRootCertificate>* cert);
Status VerifySignatures(const SignedDrmCertificate& signed_cert,
const std::string& cert_serial_number,
bool use_cache) const;
CertificateType type_;
std::string serialized_certificate_;
std::string serial_number_;
std::string public_key_;
std::unique_ptr<RsaKeyFactory> key_factory_;
mutable std::unique_ptr<VerifiedCertSignatureCache> signature_cache_;
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmRootCertificate);
};
} // namespace widevine
#endif // COMMON_DRM_ROOT_CERTIFICATE_H_

View File

@@ -0,0 +1,262 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2013 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Unit tests for drm_root_certificate.cc
#include "common/drm_root_certificate.h"
#include <memory>
#include "google/protobuf/util/message_differencer.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "common/error_space.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h"
#include "common/test_drm_certificates.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
using google::protobuf::util::MessageDifferencer;
namespace widevine {
TEST(DrmRootCertificateCreateTest, TestCertificate) {
const std::string kTestCertificateHash(
"49f917b1bdfed78002a58e799a58e940"
"1fffaaed9d8d80752782b066757e2c8c");
std::unique_ptr<DrmRootCertificate> root_cert;
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
kCertificateTypeTesting, &root_cert));
ASSERT_TRUE(root_cert != nullptr);
EXPECT_EQ(kTestCertificateHash, root_cert->GetDigest());
}
TEST(DrmRootCertificateCreateTest, DevCertificate) {
const std::string kDevelopmentCertificateHash(
"0e25ee95476a770f30b98ac5ef778b3f"
"137b66c29385b84f547a361b4724b17d");
std::unique_ptr<DrmRootCertificate> root_cert;
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
kCertificateTypeDevelopment, &root_cert));
ASSERT_TRUE(root_cert != nullptr);
EXPECT_EQ(kDevelopmentCertificateHash, root_cert->GetDigest());
}
TEST(DrmRootCertificateCreateTest, ProdCertificate) {
const std::string kProductionCertificateHash(
"d62fdabc9286648a81f7d3bedaf2f5a5"
"27bbad39bc38da034ba98a21569adb9b");
std::unique_ptr<DrmRootCertificate> root_cert;
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
kCertificateTypeProduction, &root_cert));
ASSERT_TRUE(root_cert != nullptr);
EXPECT_EQ(kProductionCertificateHash, root_cert->GetDigest());
}
TEST(DrmRootCertificateTestCertificatesTest, Success) {
TestDrmCertificates test_certs;
std::unique_ptr<DrmRootCertificate> root_cert;
ASSERT_TRUE(
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert)
.ok());
EXPECT_TRUE(root_cert
->VerifyCertificate(test_certs.test_root_certificate(),
nullptr, nullptr)
.ok());
EXPECT_TRUE(
root_cert
->VerifyCertificate(test_certs.test_intermediate_certificate(),
nullptr, nullptr)
.ok());
EXPECT_TRUE(root_cert
->VerifyCertificate(test_certs.test_user_device_certificate(),
nullptr, nullptr)
.ok());
EXPECT_TRUE(root_cert
->VerifyCertificate(test_certs.test_service_certificate(),
nullptr, nullptr)
.ok());
}
class DrmRootCertificateTest : public testing::Test {
protected:
DrmRootCertificateTest() {
private_keys_.emplace_back(
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
private_keys_.emplace_back(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
private_keys_.emplace_back(
RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits()));
}
void SetUp() override {
drm_certificates_[0].set_serial_number("level 0");
drm_certificates_[0].set_creation_time_seconds(0);
drm_certificates_[0].set_public_key(
test_keys_.public_test_key_1_3072_bits());
drm_certificates_[1].set_serial_number("level 1");
drm_certificates_[1].set_creation_time_seconds(1);
drm_certificates_[1].set_public_key(
test_keys_.public_test_key_2_2048_bits());
drm_certificates_[2].set_serial_number("level 2");
drm_certificates_[2].set_creation_time_seconds(2);
drm_certificates_[2].set_public_key(
test_keys_.public_test_key_3_2048_bits());
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
kCertificateTypeTesting, &root_cert_));
}
void GenerateSignedDrmCertificate() {
SignedDrmCertificate* current_sc(&signed_drm_certificate_);
ASSERT_TRUE(drm_certificates_[2].SerializeToString(
current_sc->mutable_drm_certificate()));
ASSERT_TRUE(private_keys_[1]->GenerateSignature(
current_sc->drm_certificate(), current_sc->mutable_signature()));
current_sc = current_sc->mutable_signer();
ASSERT_TRUE(drm_certificates_[1].SerializeToString(
current_sc->mutable_drm_certificate()));
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
current_sc->drm_certificate(), current_sc->mutable_signature()));
current_sc = current_sc->mutable_signer();
ASSERT_TRUE(drm_certificates_[0].SerializeToString(
current_sc->mutable_drm_certificate()));
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
current_sc->drm_certificate(), current_sc->mutable_signature()));
}
RsaTestKeys test_keys_;
std::vector<std::unique_ptr<RsaPrivateKey>> private_keys_;
SignedDrmCertificate signed_drm_certificate_;
DrmCertificate drm_certificates_[3];
std::unique_ptr<DrmRootCertificate> root_cert_;
};
TEST_F(DrmRootCertificateTest, SuccessNoOutput) {
GenerateSignedDrmCertificate();
ASSERT_EQ(OkStatus(),
root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, SuccessWithOutput) {
GenerateSignedDrmCertificate();
SignedDrmCertificate out_signed_cert;
DrmCertificate out_cert;
ASSERT_EQ(OkStatus(), root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(),
&out_signed_cert, &out_cert));
EXPECT_TRUE(
MessageDifferencer::Equals(out_signed_cert, signed_drm_certificate_));
EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2]));
}
TEST_F(DrmRootCertificateTest, InvalidSignedDrmCertificate) {
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signed-drm-certificate"),
root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
GenerateSignedDrmCertificate();
signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage");
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-certificate"),
root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
GenerateSignedDrmCertificate();
signed_drm_certificate_.clear_drm_certificate();
EXPECT_EQ(
Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"),
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) {
GenerateSignedDrmCertificate();
signed_drm_certificate_.set_drm_certificate("junk");
EXPECT_EQ(
Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"),
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidPublicKey) {
drm_certificates_[0].set_public_key("rubbish");
GenerateSignedDrmCertificate();
EXPECT_EQ(
Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-signer-public-key"),
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, MissingPublicKey) {
drm_certificates_[2].clear_public_key();
GenerateSignedDrmCertificate();
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"),
root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, MissingCreationTime) {
drm_certificates_[2].clear_creation_time_seconds();
GenerateSignedDrmCertificate();
EXPECT_EQ(
Status(error_space, INVALID_DRM_CERTIFICATE, "missing-creation-time"),
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, MissingSerialNumber) {
drm_certificates_[2].set_serial_number("");
GenerateSignedDrmCertificate();
EXPECT_EQ(
Status(error_space, INVALID_DRM_CERTIFICATE, "missing-serial-number"),
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
GenerateSignedDrmCertificate();
signed_drm_certificate_.mutable_signer()->set_signature(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
EXPECT_EQ(
Status(error_space, INVALID_SIGNATURE, "cache-miss-invalid-signature"),
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) {
GenerateSignedDrmCertificate();
// Verify and cache.
ASSERT_EQ(OkStatus(),
root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
// Verify success using cache.
ASSERT_EQ(OkStatus(),
root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
// Verify failure using cache.
signed_drm_certificate_.mutable_signer()->set_signature(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
EXPECT_EQ(Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"),
root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
}
} // namespace widevine

View File

@@ -0,0 +1,286 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2013 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/drm_service_certificate.h"
#include <map>
#include <memory>
#include <utility>
#include "glog/logging.h"
#include "base/thread_annotations.h"
#include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h"
#include "util/gtl/map_util.h"
#include "common/aes_cbc_util.h"
#include "common/certificate_type.h"
#include "common/drm_root_certificate.h"
#include "common/error_space.h"
#include "common/rsa_util.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
namespace widevine {
namespace {
// Class used to hold global service certificate map.
class DrmServiceCertificateMap {
public:
DrmServiceCertificateMap();
~DrmServiceCertificateMap();
DrmServiceCertificateMap(const DrmServiceCertificateMap&) = delete;
DrmServiceCertificateMap& operator=(const DrmServiceCertificateMap&) = delete;
void Reset();
void AddCert(std::unique_ptr<DrmServiceCertificate> 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<std::string, std::unique_ptr<DrmServiceCertificate>> 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<DrmServiceCertificate> new_cert) {
absl::WriterMutexLock lock(&mutex_);
std::unique_ptr<DrmServiceCertificate>* previous_cert =
gtl::FindOrNull(map_, new_cert->serial_number());
if (previous_cert != nullptr) {
if (default_cert_ == previous_cert->get()) {
default_cert_ = nullptr;
}
}
if (default_cert_ == nullptr) {
default_cert_ = new_cert.get();
}
const std::string& serial_number = new_cert->serial_number();
map_[serial_number] = std::move(new_cert);
}
void DrmServiceCertificateMap::ClearDefaultDrmServiceCertificate() {
absl::WriterMutexLock lock(&mutex_);
default_cert_ = nullptr;
}
const DrmServiceCertificate* DrmServiceCertificateMap::GetDefaultCert() {
absl::ReaderMutexLock lock(&mutex_);
return default_cert_;
}
const DrmServiceCertificate* DrmServiceCertificateMap::GetCert(
const std::string& serial_number) {
absl::ReaderMutexLock lock(&mutex_);
return map_[serial_number].get();
}
DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
static auto* const kInstance = new DrmServiceCertificateMap();
return kInstance;
}
} // namespace
Status DrmServiceCertificate::AddDrmServiceCertificate(
const DrmRootCertificate* root_drm_cert, const std::string& service_certificate,
const std::string& service_private_key,
const std::string& service_private_key_passphrase) {
DrmCertificate drm_cert;
Status status =
root_drm_cert->VerifyCertificate(service_certificate, nullptr, &drm_cert);
if (!status.ok()) {
return status;
}
if (drm_cert.type() != DrmCertificate::SERVICE) {
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
"not-service-certificate");
}
if (drm_cert.provider_id().empty()) {
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
"missing-certificate-service-id");
}
std::unique_ptr<RsaPublicKey> public_key(
RsaPublicKey::Create(drm_cert.public_key()));
if (!public_key) {
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
"invalid-certificate-public-key");
}
std::string pkcs1_key;
if (!rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey(
service_private_key, service_private_key_passphrase, &pkcs1_key)) {
return Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
"key-decryption-failed");
}
std::unique_ptr<RsaPrivateKey> private_key(RsaPrivateKey::Create(pkcs1_key));
if (private_key == nullptr) {
return Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
"invalid-private-key");
}
std::unique_ptr<DrmServiceCertificate> new_cert(new DrmServiceCertificate(
service_certificate, drm_cert.provider_id(), drm_cert.serial_number(),
drm_cert.creation_time_seconds(), std::move(public_key),
std::move(private_key)));
DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert));
return OkStatus();
}
const DrmServiceCertificate*
DrmServiceCertificate::GetDefaultDrmServiceCertificate() {
return DrmServiceCertificateMap::GetInstance()->GetDefaultCert();
}
const DrmServiceCertificate*
DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() {
const DrmServiceCertificate* default_cert =
DrmServiceCertificateMap::GetInstance()->GetDefaultCert();
CHECK(default_cert) << "Service Certificate not set!";
return default_cert;
}
const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate(
const std::string& serial_number) {
return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number);
}
Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
const DrmRootCertificate* root_drm_cert, const std::string& service_certificate,
const std::string& service_private_key,
const std::string& service_private_key_passphrase) {
DrmServiceCertificateMap::GetInstance()->ClearDefaultDrmServiceCertificate();
return AddDrmServiceCertificate(root_drm_cert, service_certificate,
service_private_key,
service_private_key_passphrase);
}
Status DrmServiceCertificate::DecryptClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id) {
DCHECK(client_id);
if (encrypted_client_id.service_certificate_serial_number().empty()) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-service-certificate-serial-number");
}
if (encrypted_client_id.provider_id().empty()) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-service-id");
}
if (encrypted_client_id.encrypted_client_id().empty()) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-client-id");
}
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-client-id-iv");
}
if (encrypted_client_id.encrypted_privacy_key().empty()) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-privacy-key");
}
std::string privacy_key;
std::string provider_id;
const DrmServiceCertificate* cert = GetDrmServiceCertificate(
encrypted_client_id.service_certificate_serial_number());
if (!cert) {
return Status(
error_space, SERVICE_CERTIFICATE_NOT_FOUND,
"service-certificate-not-found (SN " +
absl::BytesToHexString(
encrypted_client_id.service_certificate_serial_number()) +
")");
}
if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(),
&privacy_key)) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"privacy-key-decryption-failed");
}
if (cert->provider_id() != encrypted_client_id.provider_id()) {
return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
std::string("provider-id-mismatch (") + cert->provider_id() +
" / " + encrypted_client_id.provider_id() + ")");
}
std::string serialized_client_id(crypto_util::DecryptAesCbc(
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
encrypted_client_id.encrypted_client_id()));
if (serialized_client_id.empty()) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"client-id-decryption-failed");
}
if (!client_id->ParseFromString(serialized_client_id)) {
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"client-id-parse-failed");
}
return OkStatus();
}
void DrmServiceCertificate::ResetServiceCertificates() {
DrmServiceCertificateMap::GetInstance()->Reset();
}
Status DrmServiceCertificate::ValidateDrmServiceCertificate() {
const DrmServiceCertificate* service_certificate =
GetDefaultDrmServiceCertificate();
if (!service_certificate) {
return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
"drm service certificate is not found.");
}
SignedDrmCertificate signed_cert;
if (!signed_cert.ParseFromString(service_certificate->certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"signed drm service certificate is failed to parse.");
}
DrmCertificate drm_cert;
if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"Drm service certificate is failed to parse.");
}
if (!drm_cert.has_creation_time_seconds()) {
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
"missing certificate creation time");
}
// TODO(user): Check creation_time_seconds field in DrmCertificate and also
// export the absl/time dependency through moe.
return OkStatus();
}
DrmServiceCertificate::DrmServiceCertificate(
const std::string& service_certificate, const std::string& provider_id,
const std::string& serial_number, const uint32_t creation_time_seconds,
std::unique_ptr<RsaPublicKey> public_key,
std::unique_ptr<RsaPrivateKey> 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

View File

@@ -0,0 +1,132 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2013 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Service certificate holder used to decrypt encrypted client credentials.
#ifndef COMMON_DRM_SERVICE_CERTIFICATE_H_
#define COMMON_DRM_SERVICE_CERTIFICATE_H_
#include <map>
#include <memory>
#include <string>
#include <cstdint>
#include "base/macros.h"
#include "common/certificate_type.h"
#include "common/rsa_key.h"
#include "common/status.h"
namespace widevine {
class RequestInspectorTest;
} // namespace widevine
namespace widevine {
class ClientIdentification;
class DrmRootCertificate;
class EncryptedClientIdentification;
// TODO(user): Add a DrmCertificateList class to provide the static method
// functionality.
class DrmServiceCertificate {
public:
// Create a new DrmServiceCertificate object and add it to the list of valid
// service certificates. |drm_root_cert| is the root certificate for the type
// of certifiate being added. |service_certificate| is a
// Google-generated certificate used to authenticate the service provider for
// purposes of device privacy, |service_private_key| is the encrypted PKCS#8
// private RSA key corresponding to the service certificate,
// |service_private_key_passphrase| is the password required to decrypt
// |service_private_key|.
// Returns status::OK if successful, or appropriate error code otherwise.
// If the default service certificate is not set, this certificate will be
// used as the default service certificate.
// This method is thread-safe.
static Status AddDrmServiceCertificate(
const DrmRootCertificate* root_drm_cert,
const std::string& service_certificate, const std::string& service_private_key,
const std::string& service_private_key_passphrase);
// Same as AddDrmServiceCertificate(), but will clear the default service
// certificate if it's set. This will result in this service certificate
// being set as the default service certificate.
static Status SetDefaultDrmServiceCertificate(
const DrmRootCertificate* root_drm_cert,
const std::string& service_certificate, const std::string& service_private_key,
const std::string& service_private_key_passphrase);
// Returns the default service certificate. Will return null if no default
// Service Certificate is set. This method is thread-safe.
static const DrmServiceCertificate* GetDefaultDrmServiceCertificate();
// Returns the default service certificate. Will abort if no default Service
// Certificate is set. This method is thread-safe.
static const DrmServiceCertificate* GetDefaultDrmServiceCertificateOrDie();
// Returns the service certificate with the given serial number if found, or
// null otherwise.
static const DrmServiceCertificate* GetDrmServiceCertificate(
const std::string& cert_serial_number);
// Decrypts the EncryptedClientIdentification message passed in
// |encrypted_client_id| into |client_id| using the private key for the
// certificate which was used to encrypt the information. |client_id| must
// not be NULL. Returns status::OK if successful, or an appropriate error
// otherwise. This method is thread-safe.
static Status DecryptClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id);
const std::string& certificate() const { return certificate_; }
const std::string& provider_id() const { return provider_id_; }
const std::string& serial_number() const { return serial_number_; }
const RsaPrivateKey* const private_key() const { return private_key_.get(); }
const RsaPublicKey* const public_key() const { return public_key_.get(); }
// Returns the validation result of drm service certificate. Returns
// status::OK if successful, or in case of error, contact
// widevine-tam@google.com to get the next valid service certificate renewed
// via get deviceCertificate StatusList.
static Status ValidateDrmServiceCertificate();
private:
friend class DrmServiceCertificateTest;
friend class widevine::RequestInspectorTest;
static Status AddDrmServiceCertificate(
const std::string& root_public_key, const std::string& service_certificate,
const std::string& service_private_key,
const std::string& service_private_key_passphrase);
static Status SetDefaultDrmServiceCertificate(
const std::string& root_public_key, const std::string& service_certificate,
const std::string& service_private_key,
const std::string& service_private_key_passphrase);
DrmServiceCertificate(const std::string& service_certificate,
const std::string& provider_id, const std::string& serial_number,
const uint32_t creation_time_seconds,
std::unique_ptr<RsaPublicKey> public_key,
std::unique_ptr<RsaPrivateKey> private_key);
static void ResetServiceCertificates();
std::string certificate_;
std::string provider_id_;
std::string serial_number_;
uint32_t creation_time_seconds_;
std::unique_ptr<RsaPublicKey> public_key_;
std::unique_ptr<RsaPrivateKey> private_key_;
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmServiceCertificate);
};
} // namespace widevine
#endif // COMMON_DRM_SERVICE_CERTIFICATE_H_

View File

@@ -0,0 +1,364 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2013 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/drm_service_certificate.h"
#include <memory>
#include "glog/logging.h"
#include "google/protobuf/util/message_differencer.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "common/aes_cbc_util.h"
#include "common/drm_root_certificate.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h"
#include "common/rsa_util.h"
#include "common/test_drm_certificates.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h" // IWYU pragma: keep
#include "protos/public/license_server_sdk.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
namespace widevine {
const char kPrivacyKey[] = "f7538b38acc78ec68c732ac665c55c65";
const char kIv[] = "09e9cda133ff5140bd2793173a04b5a3";
const char kPassphrase[] = "passphrase";
class DrmServiceCertificateTest : public ::testing::Test {
public:
DrmServiceCertificateTest()
: privacy_key_(absl::HexStringToBytes(kPrivacyKey)),
iv_(absl::HexStringToBytes(kIv)),
root_private_key_(
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())) {
EXPECT_TRUE(root_private_key_);
EXPECT_OK(
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
client_id_.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
client_id_.set_token(test_certs_.test_user_device_certificate());
}
void SetUp() override { DrmServiceCertificate::ResetServiceCertificates(); }
protected:
std::string GenerateDrmServiceCertificate(const std::string& serial_number,
const std::string& provider_id,
uint32_t creation_time_seconds,
const std::string& public_key) {
DrmCertificate cert;
cert.set_type(DrmCertificate::SERVICE);
cert.set_serial_number(serial_number);
cert.set_provider_id(provider_id);
cert.set_public_key(public_key);
cert.set_creation_time_seconds(creation_time_seconds);
SignedDrmCertificate signed_cert;
cert.SerializeToString(signed_cert.mutable_drm_certificate());
root_private_key_->GenerateSignature(signed_cert.drm_certificate(),
signed_cert.mutable_signature());
std::string serialized_cert;
signed_cert.SerializeToString(&serialized_cert);
return serialized_cert;
}
Status SetDefaultDrmServiceCertificate(const std::string& serial_number,
const std::string& provider_id,
uint32_t creation_time_seconds) {
std::string signed_cert(GenerateDrmServiceCertificate(
serial_number, provider_id, creation_time_seconds,
test_keys_.public_test_key_2_2048_bits()));
std::string encrypted_private_key;
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
&encrypted_private_key)) {
return Status(error::INTERNAL, "");
}
return DrmServiceCertificate::SetDefaultDrmServiceCertificate(
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
}
Status AddDrmServiceCertificate(const std::string& serial_number,
const std::string& provider_id,
uint32_t creation_time_seconds) {
std::string signed_cert(GenerateDrmServiceCertificate(
serial_number, provider_id, creation_time_seconds,
test_keys_.public_test_key_2_2048_bits()));
std::string encrypted_private_key;
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
&encrypted_private_key)) {
return Status(error::INTERNAL, "");
}
return DrmServiceCertificate::AddDrmServiceCertificate(
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
}
void EncryptClientIdentification(
const std::string& serial_number, const std::string& provider_id,
const std::string& public_key,
EncryptedClientIdentification* encrypted_client_id) {
CHECK(encrypted_client_id);
encrypted_client_id->set_provider_id(provider_id);
encrypted_client_id->set_service_certificate_serial_number(serial_number);
std::string serial_client_id;
client_id_.SerializeToString(&serial_client_id);
encrypted_client_id->set_encrypted_client_id(
crypto_util::EncryptAesCbc(privacy_key_, iv_, serial_client_id));
encrypted_client_id->set_encrypted_client_id_iv(iv_);
std::unique_ptr<RsaPublicKey> rsa_key(RsaPublicKey::Create(public_key));
ASSERT_TRUE(rsa_key.get());
rsa_key->Encrypt(privacy_key_,
encrypted_client_id->mutable_encrypted_privacy_key());
}
RsaTestKeys test_keys_;
TestDrmCertificates test_certs_;
std::string privacy_key_;
std::string iv_;
std::unique_ptr<RsaPrivateKey> root_private_key_;
std::unique_ptr<DrmRootCertificate> root_cert_;
ClientIdentification client_id_;
};
TEST_F(DrmServiceCertificateTest, BasicClientIdDecrypt) {
std::string serial_number("serial_number");
std::string provider_id("someservice.com");
uint32_t creation_time_seconds(1234);
EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id,
creation_time_seconds));
EncryptedClientIdentification encrypted_client_id;
EncryptClientIdentification(serial_number, provider_id,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
ClientIdentification decrypted_client_id;
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id));
}
TEST_F(DrmServiceCertificateTest, NoDefaultDrmServiceCertificate) {
ASSERT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate());
const auto& get_default_sc_or_die = []() {
DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie();
};
EXPECT_DEATH(get_default_sc_or_die(), "Service Certificate not set!");
}
TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) {
std::string serial_number1("serial_number1");
std::string provider_id1("someservice.com");
uint32_t creation_time_seconds1(1234);
std::string serial_number2("serial_number2");
uint32_t creation_time_seconds2(1234);
std::string bogus_serial_number("bogus-serial-number2");
EXPECT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate());
EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id1,
creation_time_seconds1));
// Expect this to pass because the serial number is allowed to change as long
// as the service Id is the same as before.
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id1,
creation_time_seconds2));
EncryptedClientIdentification encrypted_client_id;
EncryptClientIdentification(serial_number1, provider_id1,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
ClientIdentification decrypted_client_id;
EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id));
EncryptClientIdentification(serial_number2, provider_id1,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id));
EncryptClientIdentification(bogus_serial_number, provider_id1,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND,
DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id)
.error_code());
}
TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) {
std::string serial_number1("serial_number1");
std::string serial_number2("serial_number2");
std::string serial_number3("serial_number3");
std::string serial_number4("serial_number4");
std::string provider_id("someservice.com");
uint32_t creation_time_seconds(1234);
EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id,
creation_time_seconds));
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id,
creation_time_seconds + 1));
EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id,
creation_time_seconds - 1));
EncryptedClientIdentification encrypted_client_id;
EncryptClientIdentification(serial_number1, provider_id,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
ClientIdentification decrypted_client_id;
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id));
EncryptClientIdentification(serial_number2, provider_id,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id));
EncryptClientIdentification(serial_number3, provider_id,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id));
const DrmServiceCertificate* default_cert(
DrmServiceCertificate::GetDefaultDrmServiceCertificate());
ASSERT_TRUE(default_cert);
SignedDrmCertificate signed_cert;
ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate()));
DrmCertificate drm_cert;
ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate()));
EXPECT_EQ(serial_number1, drm_cert.serial_number());
EXPECT_OK(SetDefaultDrmServiceCertificate(serial_number4, provider_id,
creation_time_seconds));
default_cert = DrmServiceCertificate::GetDefaultDrmServiceCertificate();
ASSERT_TRUE(default_cert);
ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate()));
ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate()));
EXPECT_EQ(serial_number4, drm_cert.serial_number());
}
TEST_F(DrmServiceCertificateTest, DrmServiceCertificateNotFound) {
std::string serial_number("serial_number");
std::string provider_id("someservice.com");
uint32_t creation_time_seconds(1234);
EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id,
creation_time_seconds));
EncryptedClientIdentification encrypted_client_id;
EncryptClientIdentification("invalid_serial_number", provider_id,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
ClientIdentification decrypted_client_id;
EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND,
DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id)
.error_code());
}
TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
std::string serial_number("serial_number");
std::string provider_id("someservice.com");
uint32_t creation_time_seconds(1234);
ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id,
creation_time_seconds));
EncryptedClientIdentification encrypted_client_id;
EncryptClientIdentification(serial_number, provider_id,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
ClientIdentification decrypted_client_id;
ASSERT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id));
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id));
EncryptedClientIdentification invalid;
invalid = encrypted_client_id;
invalid.clear_encrypted_privacy_key();
EXPECT_EQ(
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
"missing-encrypted-privacy-key",
DrmServiceCertificate::DecryptClientIdentification(invalid,
&decrypted_client_id)
.ToString());
invalid = encrypted_client_id;
++(*invalid.mutable_encrypted_client_id_iv())[4];
EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
invalid, &decrypted_client_id));
invalid.clear_encrypted_client_id_iv();
EXPECT_EQ(
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
"missing-encrypted-client-id-iv",
DrmServiceCertificate::DecryptClientIdentification(invalid,
&decrypted_client_id)
.ToString());
invalid = encrypted_client_id;
++(*invalid.mutable_encrypted_client_id())[0];
EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
invalid, &decrypted_client_id));
invalid.clear_encrypted_client_id();
EXPECT_EQ(
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
"missing-encrypted-client-id",
DrmServiceCertificate::DecryptClientIdentification(invalid,
&decrypted_client_id)
.ToString());
}
TEST_F(DrmServiceCertificateTest, PrivateKeyDecryptError) {
std::string serial_number("serial_number");
std::string provider_id("someservice.com");
uint32_t creation_time_seconds(1234);
ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id,
creation_time_seconds));
EncryptedClientIdentification encrypted_client_id;
EncryptClientIdentification(serial_number, provider_id,
test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id);
ClientIdentification decrypted_client_id;
ASSERT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
encrypted_client_id, &decrypted_client_id));
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id));
EncryptedClientIdentification corrupted;
corrupted = encrypted_client_id;
++(*corrupted.mutable_encrypted_privacy_key())[20];
EXPECT_EQ(
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
"privacy-key-decryption-failed",
DrmServiceCertificate::DecryptClientIdentification(corrupted,
&decrypted_client_id)
.ToString());
}
// TODO(user): Add more unit tests for various fail cases (bad keys having
// to do with bad keys and bad certs).
} // namespace widevine

113
common/ecb_util.cc Normal file
View File

@@ -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<DES_cblock*>(reinterpret_cast<const DES_cblock*>(key.data()));
DES_set_key(keyblock + 0, schedule + 1);
DES_set_key(keyblock + 1, schedule + 0);
DES_cblock* srcblock =
const_cast<DES_cblock*>(reinterpret_cast<const DES_cblock*>(src.data()));
dst->resize(data_size);
DES_cblock* dstblock = reinterpret_cast<DES_cblock*>(&*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<const uint8_t*>(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<const uint8_t*>(src.data() + i),
reinterpret_cast<uint8_t*>(&(*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<const uint8_t*>(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<const uint8_t*>(src.data() + i),
reinterpret_cast<uint8_t*>(&(*dst)[i]), &aes_key);
}
return true;
}
} // namespace crypto_util
} // namespace widevine

59
common/ecb_util.h Normal file
View File

@@ -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 <string>
#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_

90
common/ecb_util_test.cc Normal file
View File

@@ -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 <string>
#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

19
common/error_space.cc Normal file
View File

@@ -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<Errors>::Get();
} // namespace widevine

20
common/error_space.h Normal file
View File

@@ -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_

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -39,7 +39,7 @@ bool GetContents(const std::string& file_name, std::string* contents) {
LOG(WARNING) << "Failed to read all file contents.";
return false;
}
return true;;
return true;
}
bool SetContents(const std::string& file_name, const std::string& contents) {

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -7,8 +7,8 @@
////////////////////////////////////////////////////////////////////////////////
#include "common/file_util.h"
#include "gtest/gtest.h"
#include "testing/gunit.h"
#include "absl/strings/str_cat.h"
namespace widevine {
@@ -19,7 +19,7 @@ TEST(FileUtilTest, EmptyFileName) {
}
TEST(FileUtilTest, BasicTest) {
const std::string file_path = FLAGS_test_tmpdir + "/file_util_test";
const std::string file_path = absl::StrCat("/tmp", "/file_util_test");
EXPECT_TRUE(SetContents(file_path, "test content"));
std::string contents;
EXPECT_TRUE(GetContents(file_path, &contents));

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -10,7 +10,7 @@
#define COMMON_MOCK_RSA_KEY_H_
#include <string>
#include "gmock/gmock.h"
#include "testing/gmock.h"
#include "common/rsa_key.h"
namespace widevine {
@@ -39,8 +39,8 @@ class MockRsaPublicKey : public RsaPublicKey {
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
std::string* encrypted_message));
MOCK_CONST_METHOD2(VerifySignature, bool(const std::string& message,
const std::string& signature));
MOCK_CONST_METHOD2(VerifySignature,
bool(const std::string& message, const std::string& signature));
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
@@ -49,19 +49,19 @@ class MockRsaPublicKey : public RsaPublicKey {
MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete;
};
class MockRsaKeyFactory : public RsaKeyFactory{
class MockRsaKeyFactory : public RsaKeyFactory {
public:
MockRsaKeyFactory() {}
~MockRsaKeyFactory() override {}
MOCK_METHOD1(CreateFromPkcs1PrivateKey,
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
MOCK_METHOD2(
MOCK_CONST_METHOD1(CreateFromPkcs1PrivateKey,
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
MOCK_CONST_METHOD2(
CreateFromPkcs8PrivateKey,
std::unique_ptr<RsaPrivateKey>(const std::string& private_key,
const std::string& private_key_passphrase));
MOCK_METHOD1(CreateFromPkcs1PublicKey,
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
MOCK_CONST_METHOD1(CreateFromPkcs1PublicKey,
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
private:
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -9,11 +9,12 @@
// RAII wrapper classes for cleaning up various OpenSSL dynamically allocated
// structures.
#ifndef COMMON_OPENSSL_UTIL_H__
#define COMMON_OPENSSL_UTIL_H__
#ifndef COMMON_OPENSSL_UTIL_H_
#define COMMON_OPENSSL_UTIL_H_
#include "openssl/bio.h"
#include "openssl/evp.h"
#include "openssl/pkcs7.h"
#include "openssl/rsa.h"
#include "openssl/x509v3.h"
@@ -46,6 +47,7 @@ using ScopedOpenSSLStackOnly =
using ScopedBIGNUM = ScopedOpenSSLType<BIGNUM, BN_free>;
using ScopedBIO = ScopedOpenSSLType<BIO, BIO_vfree>;
using ScopedPKCS7 = ScopedOpenSSLType<PKCS7, PKCS7_free>;
using ScopedPKEY = ScopedOpenSSLType<EVP_PKEY, EVP_PKEY_free>;
using ScopedRSA = ScopedOpenSSLType<RSA, RSA_free>;
using ScopedX509 = ScopedOpenSSLType<X509, X509_free>;
@@ -59,6 +61,7 @@ using ScopedX509StoreCtx =
ScopedOpenSSLType<X509_STORE_CTX, X509_STORE_CTX_free>;
using ScopedX509Req = ScopedOpenSSLType<X509_REQ, X509_REQ_free>;
using ScopedAsn1UtcTime = ScopedOpenSSLType<ASN1_UTCTIME, ASN1_UTCTIME_free>;
using ScopedAsn1Time = ScopedOpenSSLType<ASN1_TIME, ASN1_TIME_free>;
using ScopedAsn1Utc8String =
ScopedOpenSSLType<ASN1_UTF8STRING, ASN1_UTF8STRING_free>;
using ScopedAsn1Integer = ScopedOpenSSLType<ASN1_INTEGER, ASN1_INTEGER_free>;
@@ -74,4 +77,4 @@ using ScopedX509InfoStack =
ScopedOpenSSLStack<STACK_OF(X509_INFO), X509_INFO, X509_INFO_free>;
using ScopedX509InfoStackOnly = ScopedOpenSSLStackOnly<STACK_OF(X509_INFO)>;
#endif // COMMON_OPENSSL_UTIL_H__
#endif // COMMON_OPENSSL_UTIL_H_

105
common/python/BUILD Normal file
View File

@@ -0,0 +1,105 @@
################################################################################
# Copyright 2016 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
#
# Description:
# Build file for CLIF wrappers of Widevine common modules.
package(default_visibility = ["//visibility:public"])
load("//devtools/clif/python:clif_build_rule.bzl", "py_clif_cc")
py_clif_cc(
name = "aes_cbc_util",
srcs = ["aes_cbc_util.clif"],
deps = [
"//common:aes_cbc_util",
],
)
py_clif_cc(
name = "certificate_type",
srcs = ["certificate_type.clif"],
deps = [
"//common:certificate_type",
],
)
py_clif_cc(
name = "crypto_util",
srcs = ["crypto_util.clif"],
deps = [
"//common:crypto_util",
],
)
py_clif_cc(
name = "drm_root_certificate",
srcs = ["drm_root_certificate.clif"],
clif_deps = [
":certificate_type",
],
pyclif_deps = [
"//protos/public:drm_certificate_pyclif",
"//protos/public:signed_drm_certificate_pyclif",
],
deps = [
"//util/task/python:status_clif",
"//common:drm_root_certificate",
],
)
py_clif_cc(
name = "rsa_test_keys",
testonly = 1,
srcs = ["rsa_test_keys.clif"],
deps = [
"//common:rsa_test_keys",
],
)
py_clif_cc(
name = "rsa_key",
srcs = ["rsa_key.clif"],
deps = [
"//common:rsa_key",
],
)
py_clif_cc(
name = "drm_service_certificate",
srcs = ["drm_service_certificate.clif"],
clif_deps = [
":certificate_type",
":drm_root_certificate",
],
deps = [
"//util/task/python:status_clif",
"//common:drm_service_certificate",
],
)
py_clif_cc(
name = "signing_key_util",
srcs = ["signing_key_util.clif"],
pyclif_deps = [
"//protos/public:license_protocol_pyclif",
],
deps = [
"//common:signing_key_util",
],
)
py_clif_cc(
name = "test_drm_certificates",
testonly = 1,
srcs = ["test_drm_certificates.clif"],
deps = [
"//common:test_drm_certificates",
],
)

View File

@@ -0,0 +1,11 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
from "common/aes_cbc_util.h":
namespace `widevine::crypto_util`:
def `EncryptAesCbc` as Encrypt(key: bytes, iv: bytes, plaintext: bytes) -> bytes

View File

@@ -0,0 +1,11 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
from "common/certificate_type.h":
namespace `widevine`:
enum CertificateType

View File

@@ -0,0 +1,15 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
from "common/crypto_util.h":
namespace `widevine::crypto_util`:
const `kSigningKeyLabel` as SIGNING_KEY_LABEL: bytes
const `kSigningKeySizeBits` as SIGNING_KEY_SIZE_BITS: int
def DeriveKey(key: bytes, label: bytes, context: bytes, size_bits: uint32_t) -> bytes
def CreateSignatureHmacSha256(key: bytes, message: bytes) -> bytes

View File

@@ -0,0 +1,23 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
from "util/task/python/clif.h" import *
from "common/python/certificate_type.h" import *
from "protos/public/drm_certificate_pyclif.h" import *
from "protos/public/signed_drm_certificate_pyclif.h" import *
from "common/drm_root_certificate.h":
namespace `widevine`:
class DrmRootCertificate:
def VerifyCertificate(self,
serialized_certificate: bytes) -> (status: Status,
signed_certificate: SignedDrmCertificate,
certificate: DrmCertificate)
staticmethods from `DrmRootCertificate`:
def CreateByType(cert_type: CertificateType) -> (new_root_cert: DrmRootCertificate,
status: Status)

View File

@@ -0,0 +1,18 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
from "util/task/python/clif.h" import *
from "common/python/drm_root_certificate.h" import *
from "common/drm_service_certificate.h":
namespace `widevine`:
staticmethods from `DrmServiceCertificate`:
def AddDrmServiceCertificate(
root_certificate: DrmRootCertificate,
service_certificate: bytes,
service_private_key: bytes,
service_private_key_passphrase: bytes) -> Status

View File

@@ -0,0 +1,15 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
from "common/rsa_key.h":
namespace `widevine`:
class RsaPublicKey:
def VerifySignature(self, message: bytes, signature: bytes) -> bool
staticmethods from `RsaPublicKey`:
def Create(serialized_key: bytes) -> RsaPublicKey

View File

@@ -0,0 +1,20 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
from "common/rsa_test_keys.h":
namespace `widevine`:
class RsaTestKeys:
def private_test_key_1_3072_bits(self) -> bytes
def public_test_key_1_3072_bits(self) -> bytes
def private_test_key_2_2048_bits(self) -> bytes
def public_test_key_2_2048_bits(self) -> bytes
def private_test_key_3_2048_bits(self) -> bytes
def public_test_key_3_2048_bits(self) -> bytes
def private_test_key_2_carmichael_totient_2048_bits(self) -> bytes
def private_test_key_3_carmichael_totient_2048_bits(self) -> bytes
def private_test_key_4_carmichael_totient_2048_bits(self) -> bytes

View File

@@ -0,0 +1,14 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
from "protos/public/license_protocol_pyclif.h" import *
from "common/signing_key_util.h":
namespace `widevine`:
def SigningKeyMaterialSizeBits(protocol_version: ProtocolVersion) -> uint32_t
def GetClientSigningKey(derived_key: bytes, protocol_version: ProtocolVersion) -> bytes

View File

@@ -0,0 +1,15 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
from "common/test_drm_certificates.h":
namespace `widevine`:
class TestDrmCertificates:
def test_root_certificate(self) -> bytes
def test_intermediate_certificate(self) -> bytes
def test_user_device_certificate(self) -> bytes
def test_service_certificate(self) -> bytes

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -7,8 +7,7 @@
////////////////////////////////////////////////////////////////////////////////
#include "common/random_util.h"
#include "gtest/gtest.h"
#include "testing/gunit.h"
namespace widevine {

View File

@@ -0,0 +1,260 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2013 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/remote_attestation_verifier.h"
#include <stddef.h>
#include <memory>
#include "glog/logging.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
#include "common/client_id_util.h"
#include "common/drm_service_certificate.h"
#include "common/error_space.h"
#include "common/rsa_key.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/errors.pb.h"
namespace widevine {
const char kTestRootCaDerCert[] =
"30820403308202eba003020102020900a24f94af7ae6831f300d06092a86"
"4886f70d0101050500308197310b30090603550406130255533113301106"
"035504080c0a57617368696e67746f6e3111300f06035504070c084b6972"
"6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f"
"060355040b0c085769646576696e653115301306035504030c0c54657374"
"20526f6f742043413121301f06092a864886f70d010901161274696e736b"
"697040676f6f676c652e636f6d301e170d3133303831363030353731305a"
"170d3333303831353030353731305a308197310b30090603550406130255"
"533113301106035504080c0a57617368696e67746f6e3111300f06035504"
"070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049"
"6e633111300f060355040b0c085769646576696e65311530130603550403"
"0c0c5465737420526f6f742043413121301f06092a864886f70d01090116"
"1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648"
"86f70d01010105000382010f003082010a0282010100c6eee629d99f7736"
"2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c"
"e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319"
"0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3"
"f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93"
"52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7"
"e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f"
"d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20"
"79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7"
"3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30"
"ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680"
"144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405"
"30030101ff300d06092a864886f70d01010505000382010100779e9b98d3"
"ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee"
"79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124"
"ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436"
"026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140"
"ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053"
"0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258"
"e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82"
"d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4"
"c270d87191c13aefdd177b";
const char kProdRootCaDerCert[] =
"30820408308202f0a003020102020101300d06092a864886f70d01010505"
"00307d311830160603550403130f5072697661637920434120526f6f7431"
"123010060355040b13094368726f6d65204f5331133011060355040a130a"
"476f6f676c6520496e63311630140603550407130d4d6f756e7461696e20"
"56696577311330110603550408130a43616c69666f726e6961310b300906"
"0355040613025553301e170d3133303231383130313334325a170d333330"
"3231333130313334325a307d311830160603550403130f50726976616379"
"20434120526f6f7431123010060355040b13094368726f6d65204f533113"
"3011060355040a130a476f6f676c6520496e63311630140603550407130d"
"4d6f756e7461696e2056696577311330110603550408130a43616c69666f"
"726e6961310b300906035504061302555330820122300d06092a864886f7"
"0d01010105000382010f003082010a0282010100e10ea6819d3d066b421d"
"d7612de3eef9599f5d9a2a24bfd09caab543511cf22f615e29f989425a65"
"7396bf33603747719cfb0b4240cd682c7c558fec0176b4793be440752246"
"83648f5b12d02a838a2a8e55a4b645ed0a4a52b19252a23d34bf64a17ac7"
"11fe93a889086d943211b17d670f96442c9f367d38026000da79664e600e"
"e9259348f4fd74108e973d561e624e9f5eda77a085a6eb15fadb2cc7787c"
"7f30ef3b196f2a416a76fa9eb30d65753f5039d97bea70e82431d2962396"
"a34864f33b74d60707fea794c03c82e547abc2407fa7bad67bd09cdab49b"
"26e68754994d12a3845dbeceffe18de0d51fc6fa78676d89ea1e0fcff931"
"59bfb809519b0203010001a3819230818f30290603551d0e042204204b1d"
"148aa5380938812ed6a763f5dc2c318610d5fa9604d609cb2e0d8cec3289"
"302b0603551d230424302280204b1d148aa5380938812ed6a763f5dc2c31"
"8610d5fa9604d609cb2e0d8cec3289300e0603551d0f0101ff0404030201"
"06300f0603551d130101ff040530030101ff30140603551d200101ff040a"
"300830060604551d2000300d06092a864886f70d01010505000382010100"
"c40d84bc8d609b1b68b3caa7e841021838d7e392557d40debab3e0685e72"
"80541092dc913b0aa6150228d8fe5ab08cceefbac56952fa00ba614294d1"
"ba4fa170c86b27f9bf58666c46940f740c4be2795501b25e40b9702af07c"
"884926bd8beed036c503e5e42a223ff36271404ca4360a93dec92a02fd8d"
"ae8f756fc68aaa647e2159f0a7a95d1446e92362bd512f59daec02c5d152"
"c301b9807db998ba70c616364762a0a497aaa92eb7d92f3635169d3f74c6"
"40c738941759a8ab43677b80329d015bdcf8922b779a80f85f1e4a677659"
"c60de80152e8c526a7de46cac143a75af58f0806de81e15c97f616e1bffa"
"1c1c6b0d2438543bdfb2a21bd9bc7ae4";
const char kServiceIdFieldName[] = "OU";
const char kDeviceModeFieldName[] = "O";
const char kExpectedDeviceMode[] = "Chrome Device Content Protection";
RemoteAttestationVerifier& RemoteAttestationVerifier::get() {
static RemoteAttestationVerifier instance;
return instance;
}
void RemoteAttestationVerifier::EnableTestDrmCertificates(bool enable) {
absl::WriterMutexLock lock(&ca_mutex_);
enable_test_certificates_ = enable;
ca_.reset();
}
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
const std::string& message, const RemoteAttestation& remote_attestation,
std::string* remote_attestation_cert_sn) {
DCHECK(remote_attestation_cert_sn);
// Sanity check RemoteAttestation.
if (!remote_attestation.has_certificate()) {
return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-certificate-missing"));
}
if (!remote_attestation.has_salt()) {
return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-salt-missing"));
}
if (!remote_attestation.has_signature()) {
return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-signature-missing"));
}
// Decrypt ClientIdentification containing remote attestation certificate.
// A service cert would be looked up first, then that cert will be used
// to decrypt the ClientIdentification.
ClientIdentification client_id;
Status status = DrmServiceCertificate::DecryptClientIdentification(
remote_attestation.certificate(), &client_id);
if (!status.ok()) return status;
if (client_id.type() !=
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
return (Status(error_space, INVALID_MESSAGE,
std::string("remote-attestation-invalid-client-id-type (") +
absl::StrCat(client_id.type()) + ")"));
}
return VerifyRemoteAttestation(message, remote_attestation, client_id,
remote_attestation_cert_sn);
}
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
const std::string& message, const RemoteAttestation& remote_attestation,
const std::string& privacy_key) {
// Sanity check RemoteAttestation.
if (!remote_attestation.has_certificate()) {
return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-certificate-missing"));
}
if (!remote_attestation.has_salt()) {
return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-salt-missing"));
}
if (!remote_attestation.has_signature()) {
return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-signature-missing"));
}
// Decrypt ClientIdentification containing remote attestation certificate,
// directly using an explicitly provided key |privacy_key|.
ClientIdentification client_id;
Status status = DecryptEncryptedClientIdentification(
remote_attestation.certificate(), privacy_key, &client_id);
if (!status.ok()) return status;
if (client_id.type() !=
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
return (Status(error_space, INVALID_MESSAGE,
std::string("remote-attestation-invalid-client-id-type (") +
absl::StrCat(client_id.type()) + ")"));
}
std::string remote_attestation_cert_sn;
return VerifyRemoteAttestation(message, remote_attestation, client_id,
&remote_attestation_cert_sn);
}
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
const std::string& message, const RemoteAttestation& remote_attestation,
const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) {
if (!client_id.has_token()) {
return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-token-missing"));
}
// Load and verify the certificate chain.
std::unique_ptr<X509CertChain> cert_chain(new X509CertChain);
Status status = cert_chain->LoadPem(client_id.token());
if (!status.ok()) return status;
if (cert_chain->GetNumCerts() < 1) {
return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-empty-certificate-chain"));
}
std::string device_mode_string =
cert_chain->GetCert(0)->GetSubjectNameField(kDeviceModeFieldName);
if (device_mode_string != kExpectedDeviceMode) {
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
std::string("remote-attestation-device-not-verified (") +
device_mode_string + " / " + kDeviceModeFieldName +
")"));
}
ca_mutex_.ReaderLock();
if (ca_ == NULL) {
ca_mutex_.ReaderUnlock();
status = LoadCa();
if (!status.ok()) return status;
ca_mutex_.ReaderLock();
}
status = ca_->VerifyCertChain(*cert_chain);
ca_mutex_.ReaderUnlock();
if (!status.ok()) {
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
std::string("remote-attestation-cert-chain-validation-failed: ") +
status.error_message()));
}
// Verify the remote attestation signature.
std::unique_ptr<RsaPublicKey> leaf_key;
std::string message_with_salt = message + remote_attestation.salt();
for (size_t idx = 0; idx < cert_chain->GetNumCerts(); ++idx) {
if (!cert_chain->GetCert(idx)->IsCaCertificate()) {
leaf_key = cert_chain->GetCert(idx)->GetRsaPublicKey();
break;
}
}
if (!leaf_key) {
return Status(error_space, REMOTE_ATTESTATION_FAILED,
"remote-attestation-cert-chain-no-leaf");
}
if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt,
remote_attestation.signature())) {
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
"remote-attestation-signature-verification-failed: "));
}
*remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber();
return OkStatus();
}
Status RemoteAttestationVerifier::LoadCa() {
absl::WriterMutexLock lock(&ca_mutex_);
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
Status status = ca_cert->LoadDer(absl::HexStringToBytes(
enable_test_certificates_ ? kTestRootCaDerCert : kProdRootCaDerCert));
if (!status.ok()) {
return status;
}
ca_.reset(new X509CA(ca_cert.release()));
return OkStatus();
}
} // namespace widevine

View File

@@ -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 <map>
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "common/status.h"
#include "common/x509_cert.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/remote_attestation.pb.h"
namespace widevine {
// Singleton class used to do remote attestation. Access singleton instance via
// the get() method.
// TODO(user): This class is tested as part of the Session unit tests, but
// finer unit tests should be implemented for the failure cases.
class RemoteAttestationVerifier {
public:
RemoteAttestationVerifier() : enable_test_certificates_(false) {}
virtual ~RemoteAttestationVerifier() {}
// Singleton accessor.
static RemoteAttestationVerifier& get();
// Call to use the test (non-production) remote attestation root certificate.
// This method is thread-safe.
void EnableTestDrmCertificates(bool enable);
// Call to verify a RemoteAttestation challenge response, used in licensing
// protocol.
// |message| is the challenge message,
// |remote_attestation| is the remote attestation response to verify,
// |remote_attestation_cert_sn| is a pointer to a std::string which on successful
// return will contain the serial number for the client's remote attestation
// certificate.
// This method is thread-safe.
Status VerifyRemoteAttestation(const std::string& message,
const RemoteAttestation& remote_attestation,
std::string* remote_attestation_cert_sn);
// Call to verify a RemoteAttestation challenge response, used in certificate
// provisioning protocol.
// |message| is the challenge message,
// |remote_attestation| is the remote attestation response to verify,
// |privacy_key| is used to decrypt the EncryptedClientIdentification within
// the |remote_attestation| message.
// This method is thread-safe.
Status VerifyRemoteAttestation(const std::string& message,
const RemoteAttestation& remote_attestation,
const std::string& privacy_key);
private:
// Common subroutine to perform the verification.
// |message| is the challenge message,
// |remote_attestation| is the remote attestation response to verify,
// |client_id| is the decrypted client identification carrying the token,
// |remote_attestation_cert_sn| is a pointer to a std::string which on successful
// return will contain the serial number for the client's remote attestation
// certificate.
Status VerifyRemoteAttestation(const std::string& message,
const RemoteAttestation& remote_attestation,
const ClientIdentification& client_id,
std::string* remote_attestation_cert_sn);
Status LoadCa();
bool enable_test_certificates_;
absl::Mutex ca_mutex_;
std::unique_ptr<X509CA> ca_ GUARDED_BY(ca_mutex_);
DISALLOW_COPY_AND_ASSIGN(RemoteAttestationVerifier);
};
} // namespace widevine
#endif // COMMON_REMOTE_ATTESTATION_VERIFIER_H_

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -45,14 +45,22 @@ bool RsaKeyMatch(const RSA* key1, const RSA* key2) {
return BN_cmp(key1->n, key2->n) == 0;
}
std::string OpenSSLErrorString(uint32_t error) {
char buf[ERR_ERROR_STRING_BUF_LEN];
ERR_error_string_n(error, buf, sizeof(buf));
return buf;
}
} // namespace
namespace widevine {
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(CHECK_NOTNULL(key)) {}
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); }
RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key)
: key_(CHECK_NOTNULL(RSAPrivateKey_dup(rsa_key.key_))) {}
: key_(RSAPrivateKey_dup(rsa_key.key_)) {
CHECK(key_ != nullptr);
}
RsaPrivateKey::~RsaPrivateKey() { RSA_free(key_); }
@@ -61,7 +69,7 @@ RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) {
if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key)) return nullptr;
if (RSA_check_key(key) != 1) {
LOG(ERROR) << "Invalid private RSA key: "
<< ERR_error_string(ERR_get_error(), nullptr);
<< OpenSSLErrorString(ERR_get_error());
RSA_free(key);
}
return new RsaPrivateKey(key);
@@ -86,7 +94,7 @@ bool RsaPrivateKey::Decrypt(const std::string& encrypted_message,
RSA_PKCS1_OAEP_PADDING);
if (decrypted_size == -1) {
LOG(ERROR) << "RSA private decrypt failure: "
<< ERR_error_string(ERR_get_error(), nullptr);
<< OpenSSLErrorString(ERR_get_error());
return false;
}
decrypted_message->resize(decrypted_size);
@@ -112,7 +120,7 @@ bool RsaPrivateKey::GenerateSignature(const std::string& message,
reinterpret_cast<unsigned char*>(&message_digest[0]), EVP_sha1(),
EVP_sha1(), kPssSaltLength)) {
LOG(ERROR) << "RSA padding failure: "
<< ERR_error_string(ERR_get_error(), nullptr);
<< OpenSSLErrorString(ERR_get_error());
return false;
}
// Encrypt PSS padded digest.
@@ -123,7 +131,7 @@ bool RsaPrivateKey::GenerateSignature(const std::string& message,
key_, RSA_NO_PADDING) !=
static_cast<int>(signature->size())) {
LOG(ERROR) << "RSA private encrypt failure: "
<< ERR_error_string(ERR_get_error(), nullptr);
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
@@ -157,10 +165,12 @@ bool RsaPrivateKey::MatchesPublicKey(const RsaPublicKey& public_key) const {
uint32_t RsaPrivateKey::KeySize() const { return RSA_size(key_); }
RsaPublicKey::RsaPublicKey(RSA* key) : key_(CHECK_NOTNULL(key)) {}
RsaPublicKey::RsaPublicKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); }
RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key)
: key_(CHECK_NOTNULL(RSAPublicKey_dup(rsa_key.key_))) {}
: key_(RSAPublicKey_dup(rsa_key.key_)) {
CHECK(key_ != nullptr);
}
RsaPublicKey::~RsaPublicKey() { RSA_free(key_); }
@@ -169,7 +179,7 @@ RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) {
if (!rsa_util::DeserializeRsaPublicKey(serialized_key, &key)) return nullptr;
if (RSA_size(key) == 0) {
LOG(ERROR) << "Invalid public RSA key: "
<< ERR_error_string(ERR_get_error(), nullptr);
<< OpenSSLErrorString(ERR_get_error());
RSA_free(key);
}
return new RsaPublicKey(key);
@@ -192,7 +202,7 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
reinterpret_cast<unsigned char*>(&(*encrypted_message)[0]), key_,
RSA_PKCS1_OAEP_PADDING) != static_cast<int>(rsa_size)) {
LOG(ERROR) << "RSA public encrypt failure: "
<< ERR_error_string(ERR_get_error(), nullptr);
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
@@ -219,7 +229,7 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
reinterpret_cast<unsigned char*>(&padded_digest[0]), key_,
RSA_NO_PADDING) != static_cast<int>(rsa_size)) {
LOG(ERROR) << "RSA public decrypt failure: "
<< ERR_error_string(ERR_get_error(), nullptr);
<< OpenSSLErrorString(ERR_get_error());
return false;
}
// Hash the message using SHA1.
@@ -232,7 +242,7 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
reinterpret_cast<unsigned char*>(&padded_digest[0]),
kPssSaltLength) == 0) {
LOG(ERROR) << "RSA Verify PSS padding failure: "
<< ERR_error_string(ERR_get_error(), nullptr);
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
@@ -276,12 +286,12 @@ RsaKeyFactory::RsaKeyFactory() {}
RsaKeyFactory::~RsaKeyFactory() {}
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs1PrivateKey(
const std::string& private_key) {
const std::string& private_key) const {
return std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key));
}
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
const std::string& private_key, const std::string& private_key_passphrase) {
const std::string& private_key, const std::string& private_key_passphrase) const {
std::string pkcs1_key;
const bool result =
private_key_passphrase.empty()
@@ -296,7 +306,7 @@ std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
}
std::unique_ptr<RsaPublicKey> RsaKeyFactory::CreateFromPkcs1PublicKey(
const std::string& public_key) {
const std::string& public_key) const {
return std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key));
}

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -60,9 +60,12 @@ class RsaPrivateKey {
// Returns the RSA key size (modulus) in bytes.
virtual uint32_t KeySize() const;
private:
friend class RsaPublicKey;
friend class X509CertificateBuilder; // TODO(user): Get rid of this.
const RSA* key() const { return key_; }
private:
RSA* key_;
// SWIG appears to think this declaration is a syntax error. Excluding it for
@@ -93,7 +96,6 @@ class RsaPublicKey {
virtual bool VerifySignature(const std::string& message,
const std::string& signature) const;
// Verify a signature. This method takes two parameters: |message| which is a
// std::string containing the data which was signed, and |signature| which is a
// std::string containing the message SHA256 digest signature with PKCS#7
@@ -111,9 +113,12 @@ class RsaPublicKey {
// Returns the RSA key size (modulus) in bytes.
virtual uint32_t KeySize() const;
private:
friend class RsaPrivateKey;
friend class X509CertificateBuilder; // TODO(user): Get rid of this.
const RSA* key() const { return key_; }
private:
RSA* key_;
// SWIG appears to think this declaration is a syntax error. Excluding it for
@@ -131,16 +136,16 @@ class RsaKeyFactory {
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs1PrivateKey(
const std::string& private_key);
const std::string& private_key) const;
// Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or
// EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty).
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs8PrivateKey(
const std::string& private_key, const std::string& private_key_passphrase);
const std::string& private_key, const std::string& private_key_passphrase) const;
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
virtual std::unique_ptr<RsaPublicKey> CreateFromPkcs1PublicKey(
const std::string& public_key);
const std::string& public_key) const;
private:
DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory);

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -12,7 +12,7 @@
#include <memory>
#include "gtest/gtest.h"
#include "testing/gunit.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h"
#include "common/rsa_util.h"

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -18,6 +18,10 @@
namespace widevine {
// Container for test RSA keys
//
// Except for the keys noted below, these keys were generated using Euler's
// Totient.
//
class RsaTestKeys {
public:
RsaTestKeys();
@@ -47,6 +51,25 @@ class RsaTestKeys {
return public_key_3_2048_bits_;
}
// This key was converted to a Carmichael totient version from the original
// private_key_2_2048_bits_.
const std::string& private_test_key_2_carmichael_totient_2048_bits() const {
return private_key_2_carmichael_totient_2048_bits_;
}
// This key was converted to a Carmichael totient version from the original
// private_key_3_2048_bits_.
const std::string& private_test_key_3_carmichael_totient_2048_bits() const {
return private_key_3_carmichael_totient_2048_bits_;
}
// This key has been created using the Carmichael totient. This is
// useful for testing with some RSA implementations that had challenges
// with keys generated this way.
const std::string& private_test_key_4_carmichael_totient_2048_bits() const {
return private_key_4_carmichael_totient_2048_bits_;
}
private:
RsaTestKeys(const RsaTestKeys&) = delete;
RsaTestKeys& operator=(const RsaTestKeys&) = delete;
@@ -57,6 +80,11 @@ class RsaTestKeys {
std::string public_key_2_2048_bits_;
std::string private_key_3_2048_bits_;
std::string public_key_3_2048_bits_;
// Tests keys that use the Carmichael totient to calculate d.
std::string private_key_2_carmichael_totient_2048_bits_;
std::string private_key_3_carmichael_totient_2048_bits_;
std::string private_key_4_carmichael_totient_2048_bits_;
};
} // namespace widevine

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -13,38 +13,49 @@
#include "common/rsa_util.h"
#include <limits.h>
#include <cstring>
#include <memory>
#include "glog/logging.h"
#include "openssl/bio.h"
#include "openssl/evp.h"
#include "openssl/pem.h"
#include "openssl/x509.h"
namespace {
int BigNumGreaterThanPow2(const BIGNUM* b, int n) {
if (BN_is_negative(b) || n == INT_MAX) {
return 0;
}
int b_bits = BN_num_bits(b);
return b_bits > n + 1 || (b_bits == n + 1 && !BN_is_pow2(b));
}
} // anonymous namespace
namespace widevine {
namespace rsa_util {
static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
bool serialize_private_key) {
if (key == NULL) {
if (key == nullptr) {
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
<< " RSA key is NULL.";
<< " RSA key is nullptr.";
return false;
}
if (serialized_key == NULL) {
LOG(ERROR) << "Pointer to hold serialized RSA" << (serialize_private_key ?
"Private" : "Public") << "Key is NULL.";
if (serialized_key == nullptr) {
LOG(ERROR) << "Pointer to hold serialized RSA"
<< (serialize_private_key ? "Private" : "Public")
<< "Key is nullptr.";
return false;
}
BIO* bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
LOG(ERROR) << "BIO_new returned NULL";
if (bio == nullptr) {
LOG(ERROR) << "BIO_new returned nullptr";
return false;
}
bool success = false;
if ((serialize_private_key ?
i2d_RSAPrivateKey_bio(bio, const_cast<RSA*>(key)) :
i2d_RSAPublicKey_bio(bio, const_cast<RSA*>(key))) != 0) {
if ((serialize_private_key
? i2d_RSAPrivateKey_bio(bio, const_cast<RSA*>(key))
: i2d_RSAPublicKey_bio(bio, const_cast<RSA*>(key))) != 0) {
int serialized_size = BIO_pending(bio);
serialized_key->assign(serialized_size, 0);
if (BIO_read(bio, &(*serialized_key)[0], serialized_size) ==
@@ -54,8 +65,8 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
LOG(ERROR) << "BIO_read failure";
}
} else {
LOG(ERROR) << (serialize_private_key ? "Private" : "Public") <<
" key serialization failure";
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
<< " key serialization failure";
}
BIO_free(bio);
return success;
@@ -64,29 +75,31 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key,
bool deserialize_private_key) {
if (serialized_key.empty()) {
LOG(ERROR) << "Serialized RSA" << (deserialize_private_key ?
"Private" : "Public") << "Key is empty.";
LOG(ERROR) << "Serialized RSA"
<< (deserialize_private_key ? "Private" : "Public")
<< "Key is empty.";
return false;
}
if (key == NULL) {
LOG(ERROR) << "Pointer to hold new RSA " << (deserialize_private_key ?
"private" : "public") << " key is NULL.";
if (key == nullptr) {
LOG(ERROR) << "Pointer to hold new RSA "
<< (deserialize_private_key ? "private" : "public")
<< " key is nullptr.";
return false;
}
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
serialized_key.size());
if (bio == NULL) {
LOG(ERROR) << "BIO_new_mem_buf returned NULL";
if (bio == nullptr) {
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
return false;
}
*key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, NULL) :
d2i_RSAPublicKey_bio(bio, NULL);
*key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, nullptr)
: d2i_RSAPublicKey_bio(bio, nullptr);
BIO_free(bio);
if (*key == NULL) {
LOG(ERROR) << (deserialize_private_key ? "Private" : "Public") <<
" RSA key deserialization failure";
if (*key == nullptr) {
LOG(ERROR) << (deserialize_private_key ? "Private" : "Public")
<< " RSA key deserialization failure";
}
return *key != NULL;
return *key != nullptr;
}
bool SerializeRsaPrivateKey(const RSA* private_key,
@@ -111,12 +124,12 @@ bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
bool SerializePrivateKeyInfo(const RSA* private_key,
std::string* serialized_private_key) {
if (private_key == NULL) {
LOG(ERROR) << "Private RSA key is NULL.";
if (private_key == nullptr) {
LOG(ERROR) << "Private RSA key is nullptr.";
return false;
}
if (serialized_private_key == NULL) {
LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is NULL.";
if (serialized_private_key == nullptr) {
LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is nullptr.";
return false;
}
// The following method of serializing a PKCS#8 PrivateKeyInfo object
@@ -124,25 +137,25 @@ bool SerializePrivateKeyInfo(const RSA* private_key,
// mechanism via i2d_PKCS8PrivateKey_bio is broken in the current openssl
// version (1.0.0c). Please refer to b/8560683.
EVP_PKEY* evp = EVP_PKEY_new();
if (evp == NULL) {
LOG(ERROR) << "EVP_PKEY_new returned NULL.";
if (evp == nullptr) {
LOG(ERROR) << "EVP_PKEY_new returned nullptr.";
return false;
}
bool success = false;
PKCS8_PRIV_KEY_INFO *pkcs8_pki = NULL;
BIO* bio = NULL;
PKCS8_PRIV_KEY_INFO* pkcs8_pki = nullptr;
BIO* bio = nullptr;
if (EVP_PKEY_set1_RSA(evp, const_cast<RSA*>(private_key)) == 0) {
LOG(ERROR) << "EVP_PKEY_set1_RSA failed.";
goto cleanup;
}
pkcs8_pki = EVP_PKEY2PKCS8(evp);
if (pkcs8_pki == NULL) {
LOG(ERROR) << "EVP_PKEY2PKCS8 returned NULL.";
if (pkcs8_pki == nullptr) {
LOG(ERROR) << "EVP_PKEY2PKCS8 returned nullptr.";
goto cleanup;
}
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
LOG(ERROR) << "BIO_new returned NULL.";
if (bio == nullptr) {
LOG(ERROR) << "BIO_new returned nullptr.";
goto cleanup;
}
if (i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8_pki) == 0) {
@@ -161,10 +174,10 @@ bool SerializePrivateKeyInfo(const RSA* private_key,
success = true;
cleanup:
if (bio != NULL) {
if (bio != nullptr) {
BIO_free(bio);
}
if (pkcs8_pki != NULL) {
if (pkcs8_pki != nullptr) {
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
}
EVP_PKEY_free(evp);
@@ -177,8 +190,8 @@ bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
LOG(ERROR) << "Serialized PrivateKeyInfo is empty.";
return false;
}
if (private_key == NULL) {
LOG(ERROR) << "Pointer to hold new RSA private key is NULL.";
if (private_key == nullptr) {
LOG(ERROR) << "Pointer to hold new RSA private key is nullptr.";
return false;
}
// The following method of deserializing a PKCS#8 PrivateKeyInfo object
@@ -187,34 +200,34 @@ bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
// version (1.0.0c). Please refer to b/8560683.
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_private_key.data()),
serialized_private_key.size());
if (bio == NULL) {
LOG(ERROR) << "BIO_new_mem_buf returned NULL";
if (bio == nullptr) {
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
return false;
}
bool success = false;
EVP_PKEY* evp = NULL;
PKCS8_PRIV_KEY_INFO *pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
if (pkcs8_pki == NULL) {
LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL.";
EVP_PKEY* evp = nullptr;
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
if (pkcs8_pki == nullptr) {
LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr.";
goto cleanup;
}
evp = EVP_PKCS82PKEY(pkcs8_pki);
if (evp == NULL) {
LOG(ERROR) << "EVP_PKCS82PKEY returned NULL.";
if (evp == nullptr) {
LOG(ERROR) << "EVP_PKCS82PKEY returned nullptr.";
goto cleanup;
}
*private_key = EVP_PKEY_get1_RSA(evp);
if (*private_key == NULL) {
if (*private_key == nullptr) {
LOG(ERROR) << "PrivateKeyInfo did not contain an RSA key.";
goto cleanup;
}
success = true;
cleanup:
if (evp != NULL) {
if (evp != nullptr) {
EVP_PKEY_free(evp);
}
if (pkcs8_pki != NULL) {
if (pkcs8_pki != nullptr) {
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
}
BIO_free(bio);
@@ -223,7 +236,7 @@ cleanup:
bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
std::string* private_key_info) {
RSA* key = NULL;
RSA* key = nullptr;
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
bool success = SerializePrivateKeyInfo(key, private_key_info);
RSA_free(key);
@@ -234,7 +247,7 @@ bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
std::string* rsa_private_key) {
RSA* key = NULL;
RSA* key = nullptr;
if (DeserializePrivateKeyInfo(private_key_info, &key)) {
bool success = SerializeRsaPrivateKey(key, rsa_private_key);
RSA_free(key);
@@ -246,37 +259,38 @@ bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
const std::string& passphrase,
std::string* serialized_private_key) {
if (private_key == NULL) {
LOG(ERROR) << "Private RSA key is NULL.";
if (private_key == nullptr) {
LOG(ERROR) << "Private RSA key is nullptr.";
return false;
}
if (passphrase.empty()) {
LOG(ERROR) << "Passphrase for RSA key encryption is empty.";
return false;
}
if (serialized_private_key == NULL) {
LOG(ERROR) << "Pointer to hold serialized EncryptedPrivateKeyInfo is NULL.";
if (serialized_private_key == nullptr) {
LOG(ERROR)
<< "Pointer to hold serialized EncryptedPrivateKeyInfo is nullptr.";
return false;
}
EVP_PKEY* evp = EVP_PKEY_new();
if (evp == NULL) {
LOG(ERROR) << "EVP_PKEY_new returned NULL.";
if (evp == nullptr) {
LOG(ERROR) << "EVP_PKEY_new returned nullptr.";
return false;
}
bool success = false;
BIO* bio = NULL;
BIO* bio = nullptr;
if (EVP_PKEY_set1_RSA(evp, const_cast<RSA*>(private_key)) == 0) {
LOG(ERROR) << "EVP_PKEY_set1_RSA failed.";
goto cleanup;
}
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
LOG(ERROR) << "BIO_new returned NULL.";
if (bio == nullptr) {
LOG(ERROR) << "BIO_new returned nullptr.";
goto cleanup;
}
if (i2d_PKCS8PrivateKey_bio(bio, evp, EVP_aes_256_cbc(),
const_cast<char*>(passphrase.data()),
passphrase.size(), NULL, NULL) == 0) {
passphrase.size(), nullptr, nullptr) == 0) {
LOG(ERROR) << "i2d_PKCS8PrivateKey_bio failed.";
goto cleanup;
}
@@ -292,7 +306,7 @@ bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
success = true;
cleanup:
if (bio != NULL) {
if (bio != nullptr) {
BIO_free(bio);
}
EVP_PKEY_free(evp);
@@ -301,7 +315,7 @@ cleanup:
namespace {
// Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below.
int get_password(char *buf, int size, int rwflag, void *u) {
int get_password(char* buf, int size, int rwflag, void* u) {
CHECK(buf);
CHECK(u);
const std::string* pass(static_cast<const std::string*>(u));
@@ -324,32 +338,32 @@ bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_ke
LOG(ERROR) << "Passphrase for RSA key decryption is empty.";
return false;
}
if (private_key == NULL) {
LOG(ERROR) << "Pointer to hold new RSA private key is NULL.";
if (private_key == nullptr) {
LOG(ERROR) << "Pointer to hold new RSA private key is nullptr.";
return false;
}
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_private_key.data()),
serialized_private_key.size());
if (bio == NULL) {
LOG(ERROR) << "BIO_new_mem_buf returned NULL";
if (bio == nullptr) {
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
return false;
}
bool success = false;
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, NULL, get_password,
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, nullptr, get_password,
const_cast<std::string*>(&passphrase));
if (evp == NULL) {
LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned NULL.";
if (evp == nullptr) {
LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned nullptr.";
goto cleanup;
}
*private_key = EVP_PKEY_get1_RSA(evp);
if (*private_key == NULL) {
if (*private_key == nullptr) {
LOG(ERROR) << "EncryptedPrivateKeyInfo did not contain an RSA key.";
goto cleanup;
}
success = true;
cleanup:
if (evp != NULL) {
if (evp != nullptr) {
EVP_PKEY_free(evp);
}
BIO_free(bio);
@@ -359,11 +373,10 @@ cleanup:
bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
const std::string& passphrase,
std::string* private_key_info) {
RSA* key = NULL;
RSA* key = nullptr;
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
bool success = SerializeEncryptedPrivateKeyInfo(key,
passphrase,
private_key_info);
bool success =
SerializeEncryptedPrivateKeyInfo(key, passphrase, private_key_info);
RSA_free(key);
return success;
}
@@ -373,7 +386,7 @@ bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
const std::string& passphrase,
std::string* rsa_private_key) {
RSA* key = NULL;
RSA* key = nullptr;
if (DeserializeEncryptedPrivateKeyInfo(private_key_info, passphrase, &key)) {
bool success = SerializeRsaPrivateKey(key, rsa_private_key);
RSA_free(key);
@@ -382,5 +395,122 @@ bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
return false;
}
bool ConvertToEulerTotient(RSA* rsa) {
// RSA key generation requires computing e (public exponent) and d (private
// exponent), such that m^(e * d) = 1 (mod n) for all m coprime to n.
// BoringSSL previously computed this by taking the inverse of e modulo the
// Euler totient, (p - 1) * (q - 1). However, it now uses the Carmichael
// totient, lcm(p - 1, q - 1). These two methods produce equivalent RSA keys.
//
// This breaks some vendors' RSA code which use a custom RSA format (rather
// than the standard one in RFC 8017) which omits most of the required
// parameters. They then attempt to recover those parameters, but their
// recovery algorithm breaks when using the Carmichael totient. To work around
// this bug, re-compute the private exponent against the Euler totient.
const BIGNUM *e, *p, *q;
RSA_get0_key(rsa, nullptr /* n */, &e, nullptr /* d */);
RSA_get0_factors(rsa, &p, &q);
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
bssl::UniquePtr<BIGNUM> pm1(BN_new());
bssl::UniquePtr<BIGNUM> qm1(BN_new());
bssl::UniquePtr<BIGNUM> totient(BN_new());
bssl::UniquePtr<BIGNUM> 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(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<BN_CTX> ctx(BN_CTX_new());
bssl::UniquePtr<BIGNUM> pm1(BN_new());
bssl::UniquePtr<BIGNUM> qm1(BN_new());
bssl::UniquePtr<BIGNUM> gcd(BN_new());
bssl::UniquePtr<BIGNUM> totient(BN_new());
bssl::UniquePtr<BIGNUM> 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(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

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -99,7 +99,6 @@ bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
std::string* rsa_private_key);
// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo.
// - private_key is the RSA key to be serialized, which must not be NULL.
// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption.
@@ -147,6 +146,55 @@ bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
const std::string& passphrase,
std::string* rsa_private_key);
// This method changes |rsa| to use the more common, but not FIPS 186-4
// compliant, computation for the RSA private exponent (d). This used to be the
// form produced by the BoringSSL method RSA_generate_key. This changed in this
// CL: https://boringssl-review.googlesource.com/c/boringssl/+/15944
//
// This method is used to produce a "backward-compatible" version. Some vendor
// RSA implementations do not handle the new computation properly.
//
// - rsa is the openssl RSA structure. Must not be null. Caller retains
// ownership.
//
// Returns true if the key was successfully updated, false otherwise.
bool ConvertToEulerTotient(RSA* rsa);
// This is wrapper to the other SwitchToEulerTotient that supports deserializing
// and serializing from and to a DER encoded PKCS#1 RSAPrivateKey.
//
// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
// deserialized and converted.
// - euler_private_key is a pointer to the std::string to hold the converted and
// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the
// string. This parameter must not be NULL.
// Returns true if the key was successfully updated, false otherwise.
bool ConvertToEulerTotient(const std::string& private_key,
std::string* euler_private_key);
// This method changes |rsa| to use the FIPS 186-4 compliant computation for d.
// This uses the Carmichael totient. This is equivalent to the way the key
// is generated in BoringSSL as of this change:
// https://boringssl-review.googlesource.com/c/boringssl/+/15944
//
// This method is mostly used for testing. It allows tests to convert back and
// forth between forms and verify the output.
//
// Returns true if the key can be successfully updated, false otherwise.
bool ConvertToCarmichaelTotient(RSA* rsa);
// This is wrapper to the other SwitchToCarmichaelTotient that supports
// deserializing and serializing from and to a DER encoded PKCS#1 RSAPrivateKey.
//
// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
// deserialized and converted.
// - carmichael_private_key is a pointer to the std::string to hold the converted and
// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the
// string. This parameter must not be NULL.
// Returns true if the key was successfully updated, false otherwise.
bool ConvertToCarmichaelTotient(const std::string& private_key,
std::string* carmichael_private_key);
} // namespace rsa_util
} // namespace widevine

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -10,11 +10,24 @@
// Description:
// Unit test for rsa_util RSA utilties library.
#include "gtest/gtest.h"
#include "testing/base/public/test_utils.h"
#include <stddef.h>
#include <memory>
#include <cstdint>
#include "glog/logging.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "openssl/bn.h"
#include "common/rsa_test_keys.h"
#include "common/rsa_util.h"
using ::testing::NotNull;
namespace {
const uint32_t kRsaPublicExponent = 65537;
const int kTestRsaBits = 2048;
} // anonymous namespace
namespace widevine {
namespace rsa_util {
@@ -221,5 +234,120 @@ TEST_F(RsaUtilTest, Pkcs8FailOnInvalidPassword) {
RSA_free(test_output_key);
}
TEST_F(RsaUtilTest, ConvertToCarmichaelTotient_ExistingKey_Success) {
bssl::UniquePtr<RSA> 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<RSA> 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> rsa;
bssl::UniquePtr<RSA> private_key;
bssl::UniquePtr<BIGNUM> 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

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -29,4 +29,43 @@ std::string Sha256_Hash(const std::string& message) {
return digest;
}
std::string Sha512_Hash(const std::string& message) {
std::string digest;
digest.resize(SHA512_DIGEST_LENGTH);
SHA512(reinterpret_cast<const uint8_t*>(message.data()), message.size(),
reinterpret_cast<uint8_t*>(&digest[0]));
return digest;
}
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name) {
// 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<const char*>(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

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -11,6 +11,8 @@
#include <string>
#include <cstdint>
namespace widevine {
// Calculates SHA1 hash.
@@ -19,6 +21,14 @@ std::string Sha1_Hash(const std::string& message);
// Calculates SHA256 hash.
std::string Sha256_Hash(const std::string& message);
// Calculate SHA512 hash.
std::string Sha512_Hash(const std::string& message);
// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based,
// 16-byte binary representation. Name_space is a GUID prefix; name is a unique
// name in the namespace.
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name);
} // namespace widevine
#endif // COMMON_SHA_UTIL_H_

View File

@@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
@@ -7,8 +7,8 @@
////////////////////////////////////////////////////////////////////////////////
#include "common/sha_util.h"
#include "gtest/gtest.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
namespace widevine {
@@ -31,21 +31,41 @@ TEST(ShaUtilTest, Sha256Empty) {
TEST(ShaUtilTest, Sha1) {
const uint8_t kExpected[] = {
0x5f, 0xfd, 0x3b, 0x85, 0x2c, 0xcd, 0xd4, 0x48, 0x80, 0x9a,
0xbb, 0x17, 0x2e, 0x19, 0xbb, 0xb9, 0xc0, 0x1a, 0x43, 0xa4,
0x6b, 0x63, 0xa5, 0xe3, 0x6b, 0xfe, 0x54, 0x19, 0x4e, 0xfe,
0x9f, 0xe0, 0x29, 0x7e, 0xd8, 0x11, 0x0f, 0x10, 0x0d, 0x1d,
};
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
Sha1_Hash("random std::string"));
Sha1_Hash("random data"));
}
TEST(ShaUtilTest, Sha256) {
const uint8_t kExpected[] = {
0xa8, 0x2a, 0xf4, 0xe9, 0x6b, 0x5b, 0x8d, 0x82, 0x5a, 0x69, 0x10,
0xb9, 0x08, 0xec, 0xcd, 0xc4, 0x40, 0x1a, 0xf1, 0xe6, 0x63, 0xdb,
0x5e, 0xdf, 0x2d, 0x7d, 0xfb, 0x71, 0x2d, 0xb9, 0x65, 0x29,
0x62, 0x4b, 0x56, 0xb3, 0x38, 0x1b, 0xbc, 0xe0, 0x4f, 0x58, 0x88,
0x83, 0xb4, 0x2f, 0x4e, 0x27, 0xfe, 0xc0, 0x95, 0x56, 0xf8, 0x61,
0xcf, 0x94, 0x49, 0xe6, 0x5f, 0x26, 0xea, 0x70, 0xad, 0x88,
};
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
Sha256_Hash("random std::string"));
Sha256_Hash("random data"));
}
TEST(ShaUtilTest, Sha512) {
const uint8_t kExpected[] = {
0x8f, 0x49, 0x93, 0x1f, 0x4d, 0x4a, 0x67, 0x6f, 0x9a, 0x7e, 0x62,
0x60, 0xea, 0xe1, 0x54, 0xfe, 0xe2, 0x75, 0x3c, 0xec, 0x3b, 0xb2,
0x2e, 0xd7, 0x51, 0xcc, 0x39, 0xf9, 0x89, 0x69, 0xad, 0xd0, 0x14,
0xaa, 0xbe, 0x40, 0xce, 0xe3, 0xab, 0xef, 0x10, 0x2f, 0x24, 0x0e,
0xd8, 0x26, 0x7b, 0xb5, 0x7d, 0x86, 0xce, 0xbd, 0xd7, 0x32, 0x86,
0x3a, 0x5e, 0x9e, 0x8d, 0x23, 0x77, 0x10, 0x80, 0x0c};
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
Sha512_Hash("random data"));
}
TEST(ShaUtilTest, GenerateSha1Uuid) {
std::string name_space =
absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774");
std::string name = "some seed value";
EXPECT_EQ("730621a55c675c4086be38e72aefa03e",
absl::BytesToHexString(GenerateSha1Uuid(name_space, name)));
}
} // namespace widevine

61
common/signature_util.cc Normal file
View File

@@ -0,0 +1,61 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/signature_util.h"
#include <memory>
#include <string>
#include "common/aes_cbc_util.h"
#include "common/rsa_key.h"
#include "common/sha_util.h"
#include "common/status.h"
namespace widevine {
namespace signature_util {
Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
const std::string& aes_iv, std::string* signature) {
if (signature == nullptr) {
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
}
std::string hash = Sha1_Hash(message);
if (hash.empty()) {
return Status(error::INTERNAL, "Computed hash is empty");
}
std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash);
if (sig.empty()) {
return Status(error::INTERNAL, "Computed AES signature is empty");
}
*signature = sig;
return OkStatus();
}
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
std::string* signature) {
if (signature == nullptr) {
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
}
std::unique_ptr<RsaPrivateKey> rsa_private_key(
RsaPrivateKey::Create(private_key));
if (rsa_private_key == nullptr) {
return Status(error::INTERNAL, "Failed to construct a RsaPrivateKey");
}
std::string sig;
if (!rsa_private_key->GenerateSignature(message, &sig)) {
return Status(error::INTERNAL, "Failed to generate a RSA signature");
}
if (sig.empty()) {
return Status(error::INTERNAL, "Computed RSA signature is empty");
}
*signature = sig;
return OkStatus();
}
} // namespace signature_util
} // namespace widevine

34
common/signature_util.h Normal file
View File

@@ -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 <string>
#include "common/status.h"
namespace widevine {
namespace signature_util {
// Generates an AES signature of |message| using |aes_key| and |aes_iv|.
// Signature is returned via |signature| if generation was successful.
// Returns a Status that carries the details of error if generation failed.
Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
const std::string& aes_iv, std::string* signature);
// Generates a RSA signature of |message| using |private_key|.
// Signature is returned via |sigature| if generation was successful.
// Returns a Status that carries the details of error if generation failed.
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
std::string* signature);
} // namespace signature_util
} // namespace widevine
#endif // COMMON_SIGNATURE_UTIL_H_

View File

@@ -0,0 +1,44 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/signing_key_util.h"
#include "glog/logging.h"
#include "common/crypto_util.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
using crypto_util::kSigningKeySizeBits;
uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version) {
if (protocol_version <= VERSION_2_0) {
return kSigningKeySizeBits;
} else {
return kSigningKeySizeBits * 2;
}
}
using crypto_util::kSigningKeySizeBytes;
std::string GetClientSigningKey(const std::string& derived_key,
ProtocolVersion protocol_version) {
if (protocol_version == VERSION_2_0) {
DCHECK(derived_key.size() >= kSigningKeySizeBytes);
return derived_key.substr(0, kSigningKeySizeBytes);
} else {
DCHECK(derived_key.size() >= kSigningKeySizeBytes * 2);
return derived_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
}
}
std::string GetServerSigningKey(const std::string& derived_key) {
DCHECK(derived_key.size() >= kSigningKeySizeBytes);
return derived_key.substr(0, kSigningKeySizeBytes);
}
} // namespace widevine

55
common/signing_key_util.h Normal file
View File

@@ -0,0 +1,55 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Contains functions that are used to create and parse derived keys created
// using the NIST 800-108 KDF recommendation, using AES-CMAC PRF.
// NIST 800-108:
// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf
// AES-CMAC:
// http://tools.ietf.org/html/rfc4493
//
// Example usage:
// using video::widevine::common::crypto_util::DeriveKey;
// using widevine_server::sdk::VERSION_2_1;
//
// std::string derived_key = DeriveKey(key_str,
// label,
// context,
// SigningKeyMaterialSizeBits(VERSION_2_1));
// std::string server_derived_key = GetServerSigningKey(derived_key);
// std::string client_derived_key = GetClientSigninKey(derived_key);
#ifndef COMMON_SIGNING_KEY_UTIL_H_
#define COMMON_SIGNING_KEY_UTIL_H_
#include <string>
#include "base/macros.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
// Returns the size of the signing key based on the License Protocol
// Version. Signing keys for version 2.0 have a length of 256. Signing
// keys for version 2.1 have a length of 512.
uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version);
// Returns the client portion of the derived_key. The client portion
// depend on the size of the key. Keys that are 512 bits in length
// are assumed to be version 2.1 keys. The last 256 bits of those
// keys are returned. Keys that are 256 bits in length are returned
// in there entirety, version 2.0 keys.
std::string GetClientSigningKey(const std::string& derived_key,
ProtocolVersion protocol_version);
// Returns the server portion of the derived_key. The server portion
// is the first 256 bits of the key.
std::string GetServerSigningKey(const std::string& derived_key);
} // namespace widevine
#endif // COMMON_SIGNING_KEY_UTIL_H_

View File

@@ -0,0 +1,64 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/signing_key_util.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "common/crypto_util.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
namespace signing_key_util {
namespace {
const char* kFrontKeyHex =
"0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b";
const char* kBackKeyHex =
"0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d";
std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) {
if (protocol_version == widevine::VERSION_2_0) {
return absl::HexStringToBytes(kFrontKeyHex);
} else {
return absl::HexStringToBytes(kFrontKeyHex) +
absl::HexStringToBytes(kBackKeyHex);
}
}
} // namespace
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_0) {
ASSERT_EQ(crypto_util::kSigningKeySizeBits,
SigningKeyMaterialSizeBits(VERSION_2_0));
}
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_1) {
ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2,
SigningKeyMaterialSizeBits(VERSION_2_1));
}
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) {
ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey(
GenerateDerivedKey(VERSION_2_1))));
}
TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_1) {
ASSERT_EQ(kBackKeyHex, absl::BytesToHexString(GetClientSigningKey(
GenerateDerivedKey(VERSION_2_1), VERSION_2_1)));
}
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_0) {
ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey(
GenerateDerivedKey(VERSION_2_0))));
}
TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_0) {
ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetClientSigningKey(
GenerateDerivedKey(VERSION_2_0), VERSION_2_0)));
}
} // namespace signing_key_util
} // namespace widevine

74
common/status.cc Normal file
View File

@@ -0,0 +1,74 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/status.h"
#include <string>
#include "absl/base/macros.h"
#include "absl/strings/str_cat.h"
#include "util/error_space.h"
namespace widevine {
namespace {
const char* kGenericErrorStatusMessage[] = {"OK",
"unknown_error",
"unknown_error",
"invalid_argument",
"unknown_error",
"not_found",
"already_exists",
"permission_denied",
"unknown_error",
"unknown_error",
"unknown_error",
"unknown_error",
"unimplemented",
"internal",
"unavailable"};
} // namespace
class GenericErrorSpace : public util::ErrorSpaceImpl<GenericErrorSpace> {
public:
static std::string space_name();
static std::string code_to_string(int code);
};
std::string GenericErrorSpace::space_name() { return "generic"; }
std::string GenericErrorSpace::code_to_string(int code) {
static_assert(
ABSL_ARRAYSIZE(kGenericErrorStatusMessage) == error::NUM_ERRORS,
"mismatching generic error status message and generic error status.");
if (code >= 0 && code < error::NUM_ERRORS)
return kGenericErrorStatusMessage[code];
return std::to_string(code);
}
const util::ErrorSpace* Status::canonical_space() {
return GenericErrorSpace::Get();
}
std::string Status::ToString() const {
if (status_code_ == error::OK) return "OK";
return absl::StrCat("Errors::", error_space_->String(status_code_), ": ",
error_message_);
}
std::ostream& operator<<(std::ostream& os, const Status& x) {
os << x.ToString();
return os;
}
} // namespace widevine

108
common/status.h Normal file
View File

@@ -0,0 +1,108 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef COMMON_STATUS_H_
#define COMMON_STATUS_H_
#include <string>
#include "util/error_space.h"
namespace widevine {
namespace error {
enum StatusCode {
// Success.
OK = 0,
// Client specified an invalid argument.
INVALID_ARGUMENT = 3,
// Some requested entity (e.g., file or directory) was not found.
NOT_FOUND = 5,
// Some entity that we attempted to create (e.g., file or directory)
// already exists.
ALREADY_EXISTS = 6,
// The caller does not have permission to execute the specified
// operation. PERMISSION_DENIED must not be used for rejections
// caused by exhausting some resource (use RESOURCE_EXHAUSTED
// instead for those errors).
PERMISSION_DENIED = 7,
// Operation is not implemented or not supported/enabled in this service.
UNIMPLEMENTED = 12,
// Internal errors. Means some invariants expected by underlying
// system has been broken. If you see one of these errors,
// something is very broken.
INTERNAL = 13,
// Operation is not implemented or not supported/enabled in this service.
UNAVAILABLE = 14,
// Number of generic (non license related) errors.
NUM_ERRORS,
};
} // namespace error
class Status {
public:
Status() = default;
~Status() = default;
explicit Status(error::StatusCode c) : status_code_(c) {}
Status(error::StatusCode c, const std::string& error_message)
: status_code_(c), error_message_(error_message) {}
Status(const util::ErrorSpace* e, error::StatusCode c,
const std::string& error_message)
: error_space_(e), status_code_(c), error_message_(error_message) {}
Status(const util::ErrorSpace* e, int error, const std::string& error_message)
: error_space_(e), status_code_(error), error_message_(error_message) {}
bool ok() const { return status_code_ == error::OK; }
const util::ErrorSpace* error_space() const { return error_space_; }
static const util::ErrorSpace* canonical_space();
std::string ToString() const;
std::string error_message() const { return error_message_; }
int error_code() const { return status_code_; }
private:
const util::ErrorSpace* error_space_ = canonical_space();
int status_code_ = error::OK;
std::string error_message_;
};
inline Status OkStatus() { return Status(); }
inline bool operator==(const Status& s1, const Status& s2) {
return s1.error_space() == s2.error_space() &&
s1.error_code() == s2.error_code() &&
s1.error_message() == s2.error_message();
}
inline bool operator!=(const Status& s1, const Status& s2) {
return !(s1 == s2);
}
// Prints a human-readable representation of 'x' to 'os'.
std::ostream& operator<<(std::ostream& os, const Status& x);
#define CHECK_OK(expression) CHECK(expression.ok()) << expression.ToString()
} // namespace widevine
#endif // COMMON_STATUS_H_

61
common/status_test.cc Normal file
View File

@@ -0,0 +1,61 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2007 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/status.h"
#include "testing/gunit.h"
namespace widevine {
TEST(StatusTest, OK_Status) {
// test case for ok status.
Status status(error::OK);
EXPECT_EQ("OK", status.ToString());
}
TEST(StatusTest, OK_Status2) {
// test case for ok status.
Status status;
EXPECT_EQ("OK", status.ToString());
}
TEST(StatusTest, ALREADY_EXISTS_Status) {
Status status(error::ALREADY_EXISTS, "it is already exist");
EXPECT_EQ("Errors::already_exists: it is already exist", status.ToString());
}
// test case for status in boundary cases.
TEST(StatusTest, UNAVAILABLE_Status) {
Status status(error::UNAVAILABLE, "unavailable");
EXPECT_EQ("Errors::unavailable: unavailable", status.ToString());
}
TEST(StatusTest, NoNameCode) {
Status status(static_cast<error::StatusCode>(101), "Unknown error");
EXPECT_EQ("Errors::101: Unknown error", status.ToString());
}
TEST(StatusTest, EQUAL_OPERATOR) {
Status status1(error::ALREADY_EXISTS, "already exists 1");
Status status2(error::ALREADY_EXISTS, "already exists 1");
EXPECT_EQ(status1, status2);
}
TEST(StatusTest, NOT_EQUAL_OPERATOR) {
Status status1(error::ALREADY_EXISTS, "already exists");
Status status2(error::UNAVAILABLE, "unavailable");
EXPECT_NE(status1, status2);
}
TEST(StatusTest, NOT_EQUAL_OPERATOR_NONE_MSG) {
Status status1(error::ALREADY_EXISTS);
Status status2(error::UNAVAILABLE);
EXPECT_NE(status1, status2);
}
} // namespace widevine

36
common/string_util.cc Normal file
View File

@@ -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 <bitset>
#include <sstream>
#include <string>
#include "common/status.h"
namespace widevine {
namespace string_util {
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) {
if (output == nullptr) {
return Status(error::INTERNAL, "output is nullptr.");
}
std::stringstream sstream(bitset);
for (size_t i = 0; i < bitset.size(); i += 8) {
std::bitset<8> bits;
sstream >> bits;
char c = static_cast<char>(bits.to_ulong());
*output += c;
}
return OkStatus();
}
} // namespace string_util
} // namespace widevine

25
common/string_util.h Normal file
View File

@@ -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 <string>
#include "common/status.h"
namespace widevine {
namespace string_util {
// Converts std::string representation of a bitset to its binary equivalent string.
// For example, converts "01110100011001010111001101110100" to "test".
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output);
} // namespace string_util
} // namespace widevine
#endif // COMMON_STRING_UTIL_H_

View File

@@ -0,0 +1,26 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/string_util.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
namespace widevine {
namespace string_util {
TEST(StringUtilTest, BitsetStringToBinaryString) {
std::string input = "01110100011001010111001101110100";
std::string output;
EXPECT_OK(BitsetStringToBinaryString(input, &output));
EXPECT_EQ("test", output);
}
} // namespace string_util
} // namespace widevine

View File

@@ -0,0 +1,328 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
#include "common/test_drm_certificates.h"
namespace widevine {
static const unsigned char kTestRootCertificate[] = {
0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5,
0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78,
0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f,
0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca,
0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b,
0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46,
0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01,
0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c,
0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14,
0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd,
0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6,
0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde,
0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31,
0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a,
0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90,
0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49,
0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f,
0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda,
0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17,
0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30,
0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf,
0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16,
0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42,
0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9,
0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4,
0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a,
0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72,
0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde,
0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04,
0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b,
0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33,
0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67,
0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02,
0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a,
0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35,
0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe,
0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49,
0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6,
0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58,
0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce,
0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27,
0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5,
0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c,
0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e,
0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12,
0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54,
0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8,
0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1,
0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1,
0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8,
0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4,
0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9,
0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd,
0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9,
0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f,
0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92,
0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15,
0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0,
0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9,
0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1,
0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2,
0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c,
0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36,
0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35,
0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7,
0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44};
const unsigned char kTestIntermediateCertificate[] = {
0x0a, 0xaf, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a,
0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd,
0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67,
0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d,
0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f,
0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f,
0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf,
0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73,
0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5,
0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13,
0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad,
0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57,
0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24,
0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06,
0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61,
0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8,
0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb,
0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b,
0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b,
0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d,
0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb,
0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01,
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40,
0xa8, 0xd0, 0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf,
0xc9, 0x26, 0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4,
0x33, 0x8e, 0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b,
0xc3, 0x4f, 0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51,
0xce, 0x45, 0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4,
0x5a, 0x02, 0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9,
0xee, 0xc9, 0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb,
0x1f, 0x7a, 0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9,
0x26, 0xbe, 0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25,
0xdb, 0x85, 0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba,
0xb9, 0x3d, 0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80,
0x12, 0x72, 0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98,
0xab, 0x73, 0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1,
0x75, 0x79, 0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98,
0xe8, 0x40, 0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f,
0x6f, 0x23, 0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30,
0xaa, 0xcf, 0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b,
0x7f, 0x91, 0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a,
0x98, 0xad, 0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e,
0x6e, 0x9d, 0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04,
0x4f, 0x32, 0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52,
0xfc, 0xcc, 0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa,
0xb3, 0x77, 0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d,
0x3c, 0x74, 0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f,
0x13, 0x97, 0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94,
0x3b, 0x31, 0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b,
0x65, 0xe9, 0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d,
0x0a, 0xfd, 0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4,
0x83, 0xcf, 0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16,
0x7b, 0x66, 0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d,
0x94, 0x5d, 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b,
0x55, 0xf8, 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
const unsigned char kTestUserDrmCertificate[] = {
0x0a, 0xc1, 0x02, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42,
0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18,
0x91, 0xab, 0x4b, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51,
0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d,
0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c,
0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32,
0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7,
0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3,
0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7,
0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc,
0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93,
0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99,
0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc,
0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6,
0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a,
0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44,
0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec,
0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea,
0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e,
0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94,
0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8,
0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16,
0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd,
0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01,
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65,
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
0x12, 0x80, 0x02, 0x62, 0xd5, 0x8b, 0xb6, 0x52, 0x94, 0xcb, 0x25, 0xba,
0x68, 0x44, 0xdc, 0x6f, 0x03, 0xf6, 0x24, 0xc5, 0xba, 0x46, 0xd1, 0xa1,
0x83, 0xe7, 0xaf, 0x94, 0x96, 0x8b, 0x57, 0x89, 0xd6, 0xa0, 0x99, 0x6f,
0xed, 0xac, 0xfe, 0x6c, 0x9d, 0x80, 0x1c, 0xae, 0x34, 0xda, 0x49, 0x4f,
0x10, 0x22, 0x3c, 0xdd, 0x77, 0xa0, 0x9a, 0x79, 0x73, 0x68, 0x66, 0xa4,
0x6d, 0x1e, 0x82, 0xbf, 0xce, 0x06, 0x1a, 0x83, 0xcd, 0xa3, 0xed, 0x91,
0xbe, 0xb1, 0xfe, 0xf3, 0xde, 0x63, 0x96, 0xd5, 0x24, 0x44, 0x46, 0x94,
0x7f, 0xc2, 0x14, 0x19, 0x42, 0x08, 0x64, 0xef, 0x93, 0x81, 0x7a, 0x54,
0x8b, 0x6e, 0xd9, 0xf5, 0x14, 0x88, 0x6c, 0x39, 0x6f, 0x0f, 0x70, 0x91,
0x97, 0xd4, 0x24, 0x73, 0x9d, 0x12, 0x7a, 0xc8, 0x83, 0xd7, 0x2b, 0xc7,
0xb7, 0xe1, 0x20, 0x6c, 0x28, 0x11, 0x6f, 0x56, 0x82, 0xf6, 0x1c, 0x4f,
0x2d, 0x51, 0x0f, 0xd6, 0xd4, 0x14, 0xea, 0xac, 0x28, 0x66, 0xeb, 0x37,
0xca, 0x00, 0x49, 0xff, 0xed, 0x8e, 0x8c, 0x3e, 0x4b, 0x9b, 0x12, 0x0e,
0xbf, 0xcd, 0xb7, 0xe6, 0xed, 0xd6, 0x1f, 0x88, 0xe8, 0x99, 0x68, 0x1a,
0xf8, 0xbb, 0xa2, 0x33, 0xfa, 0xb6, 0x21, 0xdf, 0xba, 0x24, 0x5c, 0x19,
0xa2, 0xe7, 0x6f, 0x61, 0x90, 0x78, 0x21, 0xca, 0x2f, 0x84, 0xab, 0x9f,
0xff, 0x37, 0x14, 0x33, 0x83, 0x43, 0x98, 0xeb, 0xa9, 0x88, 0xde, 0xad,
0x3a, 0xd9, 0xe2, 0x5c, 0x26, 0xd3, 0x95, 0x72, 0xba, 0x8c, 0x77, 0xdf,
0x90, 0x67, 0x4e, 0xbc, 0xda, 0x83, 0x09, 0x22, 0x70, 0x51, 0x84, 0x70,
0x31, 0x25, 0x8b, 0xae, 0x5e, 0x19, 0xba, 0x97, 0xd7, 0x1f, 0x6a, 0xd7,
0x95, 0xcf, 0xde, 0x8f, 0x93, 0x69, 0x88, 0x11, 0xbe, 0x8c, 0x6a, 0xfb,
0x3c, 0x13, 0x87, 0x0e, 0x6c, 0xa5, 0xa0, 0x1a, 0xb5, 0x05, 0x0a, 0xaf,
0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18, 0xb2, 0x92,
0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40,
0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7,
0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56,
0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e,
0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34,
0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31,
0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e,
0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39,
0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2,
0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54,
0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3,
0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71,
0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96,
0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a,
0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c,
0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f,
0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca,
0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77,
0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27,
0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c,
0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c,
0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x28, 0xd2,
0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40, 0xa8, 0xd0,
0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf, 0xc9, 0x26,
0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4, 0x33, 0x8e,
0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b, 0xc3, 0x4f,
0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51, 0xce, 0x45,
0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4, 0x5a, 0x02,
0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9, 0xee, 0xc9,
0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb, 0x1f, 0x7a,
0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9, 0x26, 0xbe,
0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25, 0xdb, 0x85,
0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba, 0xb9, 0x3d,
0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80, 0x12, 0x72,
0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98, 0xab, 0x73,
0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1, 0x75, 0x79,
0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98, 0xe8, 0x40,
0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f, 0x6f, 0x23,
0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30, 0xaa, 0xcf,
0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b, 0x7f, 0x91,
0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a, 0x98, 0xad,
0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e, 0x6e, 0x9d,
0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04, 0x4f, 0x32,
0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52, 0xfc, 0xcc,
0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa, 0xb3, 0x77,
0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d, 0x3c, 0x74,
0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f, 0x13, 0x97,
0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94, 0x3b, 0x31,
0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b, 0x65, 0xe9,
0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d, 0x0a, 0xfd,
0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4, 0x83, 0xcf,
0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16, 0x7b, 0x66,
0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d, 0x94, 0x5d,
0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, 0x55, 0xf8,
0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
const unsigned char kTestDrmServiceCertificate[] = {
0x0a, 0xbc, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32,
0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18,
0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02,
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54,
0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58,
0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57,
0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa,
0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e,
0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda,
0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9,
0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2,
0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e,
0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a,
0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67,
0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56,
0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b,
0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83,
0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1,
0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f,
0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40,
0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46,
0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86,
0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b,
0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b,
0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00,
0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0x6e, 0xc3,
0x5a, 0x17, 0xa8, 0xf9, 0xef, 0xee, 0x67, 0x4d, 0x0a, 0xef, 0x57, 0x5e,
0xbc, 0x59, 0x3d, 0x22, 0x84, 0xa0, 0x0a, 0xf5, 0x84, 0x26, 0xb7, 0x8b,
0xab, 0x91, 0x3e, 0x4b, 0xb9, 0x91, 0x3c, 0x50, 0xc9, 0x08, 0x2f, 0x97,
0x0a, 0x91, 0xb5, 0x48, 0xe4, 0xba, 0xfd, 0x7b, 0xbd, 0xf0, 0xba, 0x08,
0xb3, 0x29, 0xb4, 0x23, 0x74, 0xaf, 0x3f, 0xe9, 0x77, 0x78, 0x3f, 0xdc,
0x3d, 0x8a, 0x37, 0xec, 0x1c, 0x3a, 0xff, 0x60, 0x8e, 0x10, 0x72, 0xaa,
0x97, 0x98, 0x56, 0xa0, 0x35, 0xa9, 0xbf, 0x43, 0x21, 0x6a, 0x15, 0x88,
0xba, 0xc0, 0x68, 0x01, 0x7b, 0xd7, 0x88, 0x2f, 0x1a, 0xc5, 0x1f, 0x54,
0xf0, 0xea, 0x36, 0xb7, 0xed, 0x49, 0x78, 0x09, 0xb1, 0x07, 0x46, 0xfe,
0xf4, 0xfa, 0x16, 0x0c, 0x46, 0x91, 0xe2, 0xa9, 0xe0, 0x8e, 0x97, 0xe5,
0xea, 0x2f, 0xd9, 0x94, 0x1e, 0xe7, 0xba, 0x28, 0x98, 0x92, 0xae, 0xb8,
0xb6, 0x6e, 0xf6, 0xd2, 0x50, 0xd3, 0x5b, 0x25, 0x12, 0x68, 0x5e, 0x07,
0x82, 0x64, 0x27, 0xfe, 0x1a, 0xcd, 0x38, 0xa8, 0x00, 0x53, 0x8c, 0x69,
0x51, 0x75, 0x71, 0xc2, 0x6a, 0x5f, 0x05, 0x13, 0x77, 0x2b, 0xc8, 0x6c,
0xab, 0xd2, 0x64, 0x27, 0xbd, 0x21, 0xfc, 0x33, 0x0a, 0x3a, 0x53, 0xa6,
0x28, 0x1c, 0x2a, 0xad, 0x23, 0x0a, 0x95, 0xe4, 0x38, 0x6b, 0x9b, 0x3e,
0x77, 0x7d, 0x96, 0x20, 0x42, 0xf5, 0x18, 0xbe, 0xb0, 0x78, 0xe4, 0xf0,
0x95, 0x6c, 0xd5, 0x30, 0xd6, 0xfc, 0x04, 0xe2, 0xf7, 0xff, 0x06, 0x6b,
0xaf, 0xf1, 0x9c, 0x10, 0xa6, 0xdb, 0xed, 0x4a, 0x18, 0x68, 0x87, 0xda,
0x43, 0x2c, 0x60, 0xc6, 0x0a, 0x72, 0x1e, 0x9f, 0x4b, 0x05, 0x80, 0x15,
0x17, 0x84, 0xf1, 0xee, 0xcc, 0x80, 0x25, 0x33, 0x87, 0x74, 0x02, 0x8c,
0xa1, 0xbb, 0xd9, 0x29, 0x33, 0x97, 0xbd, 0x5b, 0x1c, 0xed, 0xcc, 0x47,
0xda, 0x73, 0xae, 0xb1, 0x75, 0xac, 0xf7, 0x39, 0xbe, 0x67, 0xc3, 0xaf,
0x60, 0x07, 0xf5, 0xba, 0x81, 0xf4, 0x42, 0xad, 0x28, 0x8d, 0xe6, 0x63,
0xea, 0x8a, 0x0e, 0x71, 0x53, 0x6e, 0x62, 0x8a, 0x23, 0x4f, 0xad, 0x2a,
0x9a, 0xf6, 0xeb, 0xa8, 0x82, 0x83, 0xbb, 0x5f, 0xc9, 0x86, 0xd8, 0x76,
0xb9, 0xf3, 0xe7, 0x32, 0xdd, 0xe0, 0x44, 0x6a, 0xab, 0x78, 0xa0, 0x8c,
0xa4, 0x99, 0x6f, 0x71, 0x42, 0x8b, 0x31, 0x32, 0xbb, 0x80, 0x36, 0x61,
0x1c, 0xe5, 0x6d, 0x87, 0xf2, 0x68, 0xca, 0xcd, 0xe0, 0x5f, 0xa2, 0x68,
0x5b, 0xfc, 0x73, 0xc9, 0x26, 0x2b, 0x13, 0x05, 0x1c, 0xde, 0x19, 0xdf,
0x34, 0xba, 0xf5, 0xec, 0xaf, 0x26, 0xfb, 0x64, 0xc4, 0x38, 0x7e, 0xdb,
0x51, 0x28, 0x49, 0xa7, 0x12, 0x88, 0xa5, 0x6d, 0xa2, 0xfa};
TestDrmCertificates::TestDrmCertificates()
: test_root_certificate_(
kTestRootCertificate,
kTestRootCertificate + sizeof(kTestRootCertificate)),
test_intermediate_certificate_(
kTestIntermediateCertificate,
kTestIntermediateCertificate + sizeof(kTestIntermediateCertificate)),
test_user_device_certificate_(
kTestUserDrmCertificate,
kTestUserDrmCertificate + sizeof(kTestUserDrmCertificate)),
test_service_certificate_(
kTestDrmServiceCertificate,
kTestDrmServiceCertificate + sizeof(kTestDrmServiceCertificate)) {}
} // namespace widevine

View File

@@ -0,0 +1,54 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Class contains certificates that can be used for testing. Provides methods
// to retrieve a test root certificate, a test intermediate certificate and a
// test user device certificate.
#ifndef COMMON_TEST_DRM_CERTIFICATES_H_
#define COMMON_TEST_DRM_CERTIFICATES_H_
#include <string>
#include "base/macros.h"
namespace widevine {
class TestDrmCertificates {
public:
TestDrmCertificates();
virtual ~TestDrmCertificates() {}
// returns a test root certificate
const std::string& test_root_certificate() const { return test_root_certificate_; }
// returns a test intermediate certificate
const std::string& test_intermediate_certificate() const {
return test_intermediate_certificate_;
}
// returns an user device certificate
const std::string& test_user_device_certificate() const {
return test_user_device_certificate_;
}
// returns a service certificate
const std::string& test_service_certificate() const {
return test_service_certificate_;
}
private:
const std::string test_root_certificate_;
const std::string test_intermediate_certificate_;
const std::string test_user_device_certificate_;
const std::string test_service_certificate_;
DISALLOW_COPY_AND_ASSIGN(TestDrmCertificates);
};
} // namespace widevine
#endif // COMMON_TEST_DRM_CERTIFICATES_H_

71
common/test_utils.cc Normal file
View File

@@ -0,0 +1,71 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2013 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/test_utils.h"
#include <stddef.h>
#include <memory>
#include "glog/logging.h"
#include "openssl/pem.h"
#include "openssl/rsa.h"
#include "openssl/sha.h"
namespace widevine {
Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
const std::string& message,
std::string* signature) {
CHECK(signature);
if (pem_private_key.empty()) {
return Status(error::INVALID_ARGUMENT, "Empty PEM private key");
}
if (message.empty()) {
return Status(error::INVALID_ARGUMENT, "Empty message");
}
BIO* bio(NULL);
bio = BIO_new_mem_buf(const_cast<char*>(pem_private_key.data()),
pem_private_key.size());
if (bio == NULL) {
return Status(error::INTERNAL, "BIO allocation failed");
}
Status status;
RSA* key(NULL);
std::unique_ptr<char[]> sig_buffer;
unsigned int sig_size;
unsigned char digest[SHA256_DIGEST_LENGTH];
key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
if (key == NULL) {
status = Status(Status::canonical_space(), error::INVALID_ARGUMENT,
"PEM RSA private key load failed");
goto cleanup;
}
SHA256(reinterpret_cast<const unsigned char*>(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<unsigned char*>(sig_buffer.get()), &sig_size,
key) != 1) {
status = Status(Status::canonical_space(), error::INTERNAL,
"RSA private encrypt failed");
goto cleanup;
}
signature->assign(sig_buffer.get(), sig_size);
cleanup:
if (key != NULL) {
RSA_free(key);
}
if (bio != NULL) {
BIO_free(bio);
}
return status;
}
} // namespace widevine

32
common/test_utils.h Normal file
View File

@@ -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 <string>
#include "common/status.h"
namespace widevine {
// Generate RSA signature using the specified RSA private key, SHA256 digest,
// and PKCS#1 1.5 padding. |pem_private_key| is a PEM-encoded private RSA key,
// |message| is the message to be signed, and |signature| is a pointer to a
// std::string where the signature will be stored. The caller returns ownership of
// all paramters.
Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
const std::string& message,
std::string* signature);
} // namespace widevine
#endif // COMMON_TEST_UTILS_H_

View File

@@ -0,0 +1,43 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Helper methods for verifying VMP (Verified Media Pipeline) data.
#include "common/verified_media_pipeline.h"
#include "common/vmp_checker.h"
namespace widevine {
Status VerifyVmpData(const std::string& vmp_data,
PlatformVerificationStatus* platform_verification_status) {
*platform_verification_status = PLATFORM_UNVERIFIED;
VmpChecker::Result vmp_result;
Status status = VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result);
if (status.ok()) {
switch (vmp_result) {
case VmpChecker::kUnverified:
*platform_verification_status = PLATFORM_UNVERIFIED;
break;
case VmpChecker::kVerified:
*platform_verification_status = PLATFORM_SOFTWARE_VERIFIED;
break;
case VmpChecker::kSecureStorageVerified:
*platform_verification_status =
PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED;
break;
case VmpChecker::kTampered:
*platform_verification_status = PLATFORM_TAMPERED;
break;
}
} else {
*platform_verification_status = PLATFORM_TAMPERED;
}
return status;
}
} // namespace widevine

View File

@@ -0,0 +1,27 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Helper methods for verifying VMP (Verified Media Pipeline) data.
#ifndef COMMON_VERIFIED_MEDIA_PIPELINE_H_
#define COMMON_VERIFIED_MEDIA_PIPELINE_H_
#include <string>
#include "common/status.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
// Retrieve the PlatformVerificationStatus for |vmp_data|. The
// PlatformVerificationStatus is defined at
Status VerifyVmpData(const std::string& vmp_data,
PlatformVerificationStatus* platform_verification_status);
} // namespace widevine
#endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_

358
common/vmp_checker.cc Normal file
View File

@@ -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 <stddef.h>
#include <vector>
#include <cstdint>
#include "glog/logging.h"
#include "common/certificate_type.h"
#include "common/error_space.h"
#include "common/rsa_key.h"
#include "common/x509_cert.h"
#include "protos/public/errors.pb.h"
#include "protos/public/verified_media_pipeline.pb.h"
namespace {
const uint32_t kBlessedBinaryFlag = 0x00000001;
const uint8_t kDevVmpCodeSigningDrmRootCertificate[] = {
0x30, 0x82, 0x04, 0xb8, 0x30, 0x82, 0x03, 0x20, 0x02, 0x09, 0x00, 0xc5,
0xf8, 0x2f, 0x03, 0x8f, 0xac, 0xf1, 0x58, 0x30, 0x0d, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31,
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69,
0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31,
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69,
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03,
0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e,
0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69,
0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73, 0x6b,
0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
0x6d, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x30, 0x32, 0x38, 0x30,
0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x31, 0x36,
0x31, 0x30, 0x30, 0x34, 0x30, 0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x30,
0x81, 0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e,
0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b,
0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06,
0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57,
0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06,
0x03, 0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69,
0x6e, 0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73,
0x69, 0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73,
0x6b, 0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63,
0x6f, 0x6d, 0x30, 0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
0x8f, 0x00, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xd2,
0x6f, 0x60, 0x7f, 0xff, 0x7c, 0xbd, 0xa4, 0xe5, 0x8c, 0xa6, 0xcf, 0xde,
0x22, 0x6d, 0x3a, 0x5e, 0x83, 0xa9, 0x0e, 0x9b, 0xd4, 0x93, 0xb8, 0xb0,
0xe0, 0x5d, 0x03, 0x3d, 0xc1, 0x00, 0xb8, 0x1a, 0xcc, 0x84, 0x31, 0xfb,
0x9e, 0x97, 0x79, 0x17, 0x04, 0x48, 0xe7, 0x13, 0x98, 0x0a, 0x47, 0x41,
0xac, 0x6c, 0x52, 0xb0, 0xca, 0x9e, 0xfb, 0xfd, 0x78, 0x65, 0xd0, 0xd6,
0x12, 0x07, 0x7e, 0x24, 0x65, 0x46, 0x6c, 0xb9, 0x23, 0xbb, 0xdc, 0x02,
0x03, 0xc7, 0xb0, 0x02, 0xc1, 0xd3, 0x10, 0x59, 0xe7, 0x0b, 0x45, 0x13,
0x73, 0x5f, 0xae, 0x58, 0xcd, 0xbf, 0x42, 0x8a, 0xac, 0xf5, 0x6a, 0x1e,
0x75, 0x26, 0xb1, 0x69, 0x07, 0xad, 0xf5, 0xfd, 0x4c, 0xaa, 0x66, 0x55,
0x74, 0x56, 0x9a, 0x9e, 0x40, 0x78, 0x77, 0x9a, 0x39, 0x7a, 0x37, 0xe4,
0x25, 0x6b, 0x07, 0x09, 0xbe, 0xe2, 0x0d, 0x23, 0x83, 0xfc, 0x94, 0x9f,
0x26, 0x98, 0x0e, 0x49, 0x81, 0x7b, 0xf4, 0xe6, 0xd4, 0xda, 0x7a, 0xc9,
0xa4, 0x14, 0x5a, 0xa9, 0xaf, 0x0c, 0xd9, 0xf1, 0xbc, 0xd8, 0x6c, 0xd2,
0xd4, 0x1b, 0x82, 0x10, 0x3d, 0x87, 0xf1, 0x81, 0xe6, 0x1a, 0xb7, 0xfa,
0xfa, 0x1f, 0x9c, 0xde, 0xa1, 0x3f, 0x11, 0xb1, 0xd8, 0x26, 0xd1, 0x86,
0x21, 0xdc, 0x03, 0xcb, 0xd6, 0x40, 0xfb, 0x5f, 0xb0, 0x84, 0x7f, 0x69,
0x9e, 0xa3, 0xbc, 0xfb, 0x03, 0xb6, 0xc1, 0xb9, 0x23, 0xb1, 0x20, 0x6f,
0x71, 0xf5, 0x7a, 0x3b, 0x84, 0x30, 0xa8, 0x59, 0xc0, 0x8f, 0x75, 0xfd,
0xcb, 0xe1, 0xc3, 0x5f, 0xe7, 0x8a, 0xb0, 0xe9, 0xf8, 0xef, 0x04, 0x4b,
0x4a, 0xf6, 0xc3, 0x7d, 0x08, 0xfe, 0x08, 0x52, 0x2e, 0xbc, 0x1f, 0x65,
0xf6, 0x51, 0xb7, 0xd8, 0x24, 0x21, 0x49, 0x1d, 0x7f, 0x16, 0x28, 0x14,
0xd9, 0xc2, 0x19, 0xdb, 0xa2, 0xc6, 0xf0, 0x3a, 0x2d, 0x98, 0x70, 0x72,
0x45, 0xf7, 0x80, 0x37, 0x56, 0x0b, 0x0c, 0x6f, 0x80, 0xf1, 0x8c, 0xe2,
0xf3, 0x4d, 0x16, 0xc5, 0x74, 0x90, 0x34, 0x1e, 0x57, 0x3c, 0xde, 0xf1,
0xc4, 0x8c, 0x17, 0x09, 0xd3, 0xc5, 0x92, 0x9d, 0xcf, 0xdc, 0x7b, 0x4f,
0xae, 0x20, 0x10, 0xd7, 0x04, 0x56, 0x9d, 0x9a, 0xa9, 0xd8, 0x06, 0x4a,
0xd6, 0x68, 0xd4, 0x83, 0xcb, 0x7d, 0xe2, 0x62, 0xd1, 0x99, 0xb8, 0x9d,
0x81, 0xc7, 0xfc, 0x94, 0x69, 0x0d, 0x2a, 0x1c, 0x99, 0xcf, 0x40, 0xc3,
0xfd, 0xe9, 0x64, 0x5b, 0xc3, 0x1d, 0xda, 0x1c, 0x89, 0xab, 0x34, 0x1b,
0x53, 0x6e, 0xad, 0xf0, 0x6e, 0x97, 0x87, 0xe8, 0xfb, 0x0c, 0x96, 0x93,
0x1b, 0x52, 0x82, 0x6a, 0xba, 0x0f, 0xe3, 0x5d, 0xc4, 0x17, 0xdc, 0xe4,
0x31, 0x78, 0x12, 0x26, 0x10, 0x74, 0x14, 0x7c, 0x45, 0xb2, 0xb9, 0x02,
0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00,
0x8f, 0x5b, 0x2a, 0x40, 0xce, 0xae, 0xa2, 0x79, 0xfc, 0xc5, 0x5e, 0x94,
0xc8, 0x10, 0x4b, 0xf1, 0x21, 0x7f, 0x4b, 0xeb, 0x81, 0x3d, 0xb8, 0x26,
0x83, 0x84, 0x76, 0x79, 0xda, 0x35, 0xfc, 0xdf, 0xfe, 0x10, 0x7a, 0xd5,
0x17, 0xc0, 0xad, 0x0d, 0xf9, 0x3f, 0xa6, 0xa1, 0xcd, 0x6c, 0x9c, 0x3b,
0x52, 0xbd, 0x04, 0xf9, 0xe2, 0x9e, 0x86, 0x83, 0x98, 0x60, 0x01, 0x99,
0xb7, 0xbd, 0x02, 0x87, 0xd8, 0xea, 0x65, 0xaa, 0x60, 0x6f, 0x33, 0x50,
0x25, 0x84, 0xb7, 0x42, 0x63, 0x39, 0xfd, 0x17, 0x67, 0x74, 0x88, 0x66,
0xe2, 0x38, 0x59, 0xf6, 0x9b, 0x98, 0x95, 0xdd, 0x54, 0x2c, 0x69, 0x6a,
0x0a, 0x51, 0x66, 0x4d, 0x65, 0xc0, 0x58, 0x3e, 0xcf, 0x15, 0x63, 0x7a,
0x32, 0xc8, 0xfb, 0xe7, 0x11, 0x2e, 0x25, 0x17, 0x52, 0x4d, 0x8e, 0x6b,
0x6d, 0x58, 0x4e, 0xf6, 0xd6, 0xb0, 0xfa, 0x0d, 0x7a, 0xb1, 0x44, 0x52,
0x9a, 0x6c, 0x90, 0x38, 0x68, 0xa5, 0xa9, 0x9b, 0xc5, 0x45, 0x09, 0xfa,
0xaa, 0x8c, 0xfe, 0x91, 0x55, 0x93, 0x35, 0x52, 0x45, 0xbb, 0xaa, 0x5b,
0xf0, 0x63, 0x53, 0x13, 0xcf, 0x48, 0x7b, 0xaa, 0x20, 0xa0, 0x07, 0x43,
0x1d, 0xd7, 0xb3, 0x4a, 0x1b, 0x8c, 0x51, 0xb5, 0xf6, 0xb9, 0x5b, 0x13,
0x02, 0x74, 0x3e, 0x48, 0xde, 0xec, 0xeb, 0x65, 0xfe, 0xf2, 0x61, 0xe5,
0x68, 0xd5, 0xea, 0xd9, 0x79, 0xa6, 0x71, 0xb1, 0x57, 0x0f, 0xbc, 0xd0,
0x31, 0x4f, 0xff, 0xc5, 0x95, 0xe8, 0xee, 0x70, 0x18, 0xb9, 0xbc, 0x19,
0xcd, 0x3a, 0x06, 0x75, 0xe4, 0x57, 0xc1, 0x2e, 0x32, 0x19, 0xdd, 0x2e,
0x45, 0xc0, 0x19, 0xe6, 0x72, 0x81, 0x2c, 0xb6, 0xed, 0x1c, 0xd4, 0xef,
0x42, 0x18, 0x44, 0x44, 0x75, 0xd6, 0x29, 0x81, 0xe1, 0xf7, 0x5b, 0x48,
0xa3, 0xf8, 0x92, 0x54, 0xd0, 0x79, 0xa1, 0xe1, 0x8e, 0xa8, 0x98, 0x2d,
0x57, 0x5d, 0xb5, 0x5a, 0x01, 0x1b, 0xb3, 0xcf, 0x5f, 0x64, 0x2e, 0x70,
0xba, 0xa0, 0x41, 0xbb, 0xd4, 0x82, 0x28, 0x3c, 0xb1, 0x81, 0x76, 0xd6,
0x85, 0x2e, 0xc6, 0x01, 0x7f, 0xae, 0xc3, 0x17, 0x2f, 0xed, 0xbe, 0xad,
0xa2, 0x7c, 0x53, 0xc6, 0x77, 0x73, 0x1d, 0x19, 0x90, 0x2a, 0xf8, 0xd5,
0x50, 0x65, 0xc1, 0x22, 0x3c, 0x24, 0x96, 0xeb, 0x7b, 0x53, 0x8f, 0xbf,
0xd9, 0xf7, 0x80, 0xa8, 0x76, 0x72, 0xea, 0xb7, 0x7f, 0xf7, 0xa2, 0x52,
0xc7, 0xa7, 0xd6, 0x88, 0xa1, 0x38, 0x40, 0x5d, 0xcd, 0xdb, 0xe3, 0x8e,
0xc7, 0xf9, 0x39, 0xbe, 0xfa, 0x27, 0x41, 0x73, 0x3a, 0x1c, 0xb3, 0x03,
0xf1, 0x36, 0xea, 0xe8, 0xb3, 0xe1, 0x6e, 0x59, 0xcc, 0xe2, 0x75, 0x2b,
0xf9, 0x55, 0xb9, 0xc2, 0xdf, 0x0a, 0x8d, 0x8d, 0xd1, 0x62, 0x3b, 0x86};
const uint8_t kProdVmpCodeSigningDrmRootCertificate[] = {
0x30, 0x82, 0x04, 0xd7, 0x30, 0x82, 0x03, 0x3f, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x11, 0x00, 0xca, 0xa4, 0xbd, 0x6b, 0x93, 0x56, 0x4a, 0xf1,
0x84, 0xef, 0x5e, 0xed, 0xe8, 0xf7, 0xe2, 0x0b, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31,
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69,
0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31,
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69,
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e,
0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x72,
0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x37,
0x30, 0x31, 0x32, 0x34, 0x32, 0x33, 0x33, 0x38, 0x31, 0x34, 0x5a, 0x18,
0x0f, 0x33, 0x30, 0x31, 0x36, 0x30, 0x35, 0x32, 0x37, 0x32, 0x33, 0x33,
0x38, 0x31, 0x34, 0x5a, 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e,
0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31,
0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31,
0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69,
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73,
0x69, 0x67, 0x6e, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30,
0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30,
0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xe0, 0xb5, 0xc5, 0x6d,
0x04, 0xc1, 0x97, 0xaf, 0xe7, 0x62, 0xfb, 0x84, 0xdc, 0xd1, 0xf4, 0xb1,
0xb5, 0xa2, 0x7c, 0xca, 0x31, 0xf8, 0xce, 0xa7, 0x7a, 0x92, 0xc2, 0xbe,
0x14, 0xdc, 0x85, 0x9f, 0x18, 0x9e, 0x78, 0xba, 0x65, 0x05, 0x56, 0x88,
0x88, 0xdc, 0x1f, 0x4f, 0x24, 0x7f, 0xf5, 0x26, 0x6f, 0x6e, 0xcc, 0x04,
0x2f, 0x38, 0xb8, 0xcd, 0x27, 0xd7, 0x9e, 0x07, 0xd3, 0xa9, 0xd4, 0x6b,
0x84, 0xfe, 0xf8, 0xac, 0x9c, 0x53, 0xdf, 0x7a, 0x45, 0x5e, 0x77, 0xf8,
0x4e, 0x88, 0x00, 0x5c, 0x6f, 0xb6, 0xa7, 0x0b, 0x4b, 0x63, 0x57, 0x92,
0x7a, 0x7b, 0x3d, 0x20, 0x88, 0x3e, 0x7b, 0xb4, 0x28, 0x2b, 0x63, 0x81,
0xd4, 0x0c, 0x4c, 0xb7, 0x54, 0x68, 0x68, 0x2c, 0x0d, 0xf5, 0xa6, 0x9e,
0x16, 0x93, 0x76, 0xdd, 0xc0, 0xd5, 0x93, 0x65, 0x99, 0x90, 0x17, 0x2d,
0x2b, 0xdc, 0x6f, 0xaf, 0x58, 0xfd, 0x78, 0xe9, 0xf5, 0xde, 0x2e, 0x36,
0x95, 0xf0, 0xcf, 0x25, 0x41, 0x3d, 0x4f, 0x37, 0xd1, 0x70, 0x5b, 0xb5,
0xc0, 0xc8, 0xf3, 0x63, 0xa3, 0xda, 0x9f, 0x94, 0xdb, 0xf8, 0x51, 0xf2,
0xa9, 0xe5, 0x67, 0x0e, 0x29, 0xb3, 0x45, 0x12, 0xc5, 0x42, 0xf9, 0x3b,
0x38, 0xf9, 0xa5, 0x7b, 0x41, 0x88, 0x6f, 0x32, 0x62, 0x03, 0x5f, 0xfd,
0x35, 0x97, 0xc2, 0x83, 0x15, 0xb6, 0x56, 0x4f, 0xbb, 0x81, 0x39, 0x37,
0xf2, 0x9c, 0x2a, 0x61, 0xa8, 0x63, 0x5f, 0xa0, 0x27, 0x30, 0x06, 0xd4,
0xcb, 0x9d, 0xb7, 0xe9, 0xf2, 0xae, 0xd6, 0xc9, 0xcd, 0x72, 0xa3, 0xe6,
0xf8, 0x54, 0x03, 0x6e, 0xe1, 0x95, 0x03, 0xdd, 0x7a, 0x85, 0xb3, 0x5c,
0xa7, 0xca, 0x99, 0xec, 0xa6, 0xe8, 0x1a, 0xb2, 0x72, 0xe1, 0x91, 0x2d,
0x97, 0xe3, 0x2a, 0x9c, 0x42, 0xaa, 0x45, 0xf2, 0x8e, 0x51, 0xc0, 0xd8,
0x21, 0x83, 0x66, 0x07, 0xb5, 0x20, 0xb8, 0x28, 0xa5, 0xde, 0xfb, 0x4e,
0x2e, 0xc7, 0x70, 0x9b, 0x3d, 0x52, 0x66, 0x24, 0xc5, 0xa2, 0x2e, 0x49,
0x54, 0x5c, 0xfd, 0xc0, 0xde, 0xf6, 0x9d, 0xb4, 0x70, 0x31, 0x2b, 0xac,
0x14, 0xfb, 0x19, 0x9e, 0x89, 0xd5, 0x07, 0x87, 0xa0, 0xd8, 0x15, 0x96,
0xe9, 0xf7, 0x91, 0x36, 0x52, 0x83, 0x3c, 0x2c, 0xfa, 0xb5, 0xc4, 0xc6,
0x1a, 0x34, 0xf0, 0x53, 0x94, 0x15, 0x82, 0xa2, 0x2f, 0x98, 0xbb, 0x49,
0xca, 0xf7, 0xe0, 0xcb, 0x9e, 0x3c, 0xa6, 0x64, 0x59, 0x77, 0x63, 0xd1,
0x05, 0x03, 0x99, 0x6c, 0x50, 0x08, 0xec, 0x64, 0x86, 0xf7, 0x97, 0xaf,
0xf8, 0xcc, 0xdf, 0x91, 0xc7, 0x2c, 0x15, 0x0f, 0xa7, 0x0e, 0x02, 0x33,
0x63, 0x84, 0xb7, 0x7e, 0xd3, 0x10, 0x89, 0x05, 0x2d, 0xb4, 0x68, 0x38,
0xe0, 0x00, 0x49, 0xda, 0xaa, 0xb9, 0xf9, 0xbf, 0x02, 0x03, 0x01, 0x00,
0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
0x04, 0x16, 0x04, 0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f,
0xd0, 0x9a, 0xd9, 0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86,
0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f, 0xd0, 0x9a, 0xd9,
0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86, 0x30, 0x0c, 0x06,
0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00, 0xc1, 0x81, 0x17, 0x08, 0x0a,
0xcb, 0xae, 0x54, 0x92, 0xed, 0x0f, 0xc0, 0xf0, 0x83, 0xb8, 0xe9, 0x2a,
0xd5, 0x65, 0x18, 0xb5, 0x92, 0xfc, 0x67, 0x9c, 0x39, 0x9e, 0x3a, 0x93,
0x1f, 0x91, 0x7b, 0x35, 0x6d, 0x09, 0x6c, 0x3c, 0x02, 0x85, 0xe8, 0xc6,
0x0c, 0x6b, 0x35, 0xeb, 0x8c, 0xe4, 0x44, 0x80, 0xc5, 0x4e, 0x65, 0xa6,
0xbc, 0x25, 0x93, 0x5a, 0xed, 0x5a, 0xd1, 0x5a, 0xcf, 0xf1, 0xbe, 0xd5,
0x46, 0x7c, 0x78, 0xfe, 0xb6, 0xa6, 0x9c, 0x85, 0xa9, 0xe2, 0x13, 0x70,
0x99, 0x03, 0x5a, 0x3a, 0xa8, 0x7b, 0xdd, 0x50, 0x76, 0x83, 0xfe, 0x49,
0xc0, 0x5e, 0xc7, 0xf1, 0x18, 0xc0, 0xe6, 0xb7, 0xc7, 0xe4, 0x9d, 0x54,
0xaf, 0x25, 0xdf, 0xe4, 0x81, 0xc1, 0xe9, 0xaa, 0x7c, 0x05, 0x20, 0xfa,
0x91, 0x47, 0xd1, 0x4a, 0xe2, 0x24, 0x6f, 0x72, 0x22, 0x69, 0xd0, 0x89,
0x78, 0x2c, 0x9a, 0x16, 0x7d, 0x92, 0xdd, 0x64, 0x6c, 0xc8, 0xbf, 0x33,
0xe3, 0x91, 0xb9, 0xb5, 0x16, 0xfe, 0xfd, 0xa2, 0xdb, 0x82, 0xf0, 0xec,
0xac, 0xe7, 0xf9, 0x2e, 0xac, 0x50, 0xf0, 0xcf, 0xba, 0xe1, 0x55, 0xa9,
0xb0, 0xd9, 0x66, 0x3f, 0xb6, 0xee, 0x57, 0xff, 0x8d, 0x43, 0xb7, 0xc3,
0xb4, 0x44, 0xa9, 0xcc, 0x99, 0xa2, 0xbf, 0xac, 0x4f, 0xec, 0xed, 0xb5,
0xd0, 0x3f, 0xa9, 0x50, 0x3b, 0xc1, 0x24, 0xf2, 0xd0, 0xef, 0x5c, 0xbf,
0x5c, 0xc2, 0x41, 0x29, 0xbe, 0xb6, 0x76, 0x5a, 0x19, 0xde, 0x67, 0x1c,
0x2a, 0x67, 0xae, 0x07, 0xe8, 0xfa, 0x49, 0xf1, 0x81, 0xbc, 0x22, 0xa1,
0xe6, 0x5d, 0x27, 0x76, 0x0f, 0x4d, 0x41, 0x57, 0xcf, 0x0f, 0x12, 0x2f,
0xdd, 0x20, 0x88, 0xcf, 0xc8, 0xa7, 0xc0, 0xe5, 0xec, 0xcd, 0xb9, 0xa7,
0x1c, 0x29, 0x55, 0xed, 0x67, 0xf9, 0x38, 0x33, 0xea, 0x85, 0xe9, 0x69,
0x5a, 0x7c, 0xfe, 0x37, 0x3b, 0xdd, 0x61, 0x5f, 0xaa, 0xc3, 0x18, 0xbc,
0x58, 0x95, 0x39, 0x61, 0x79, 0xa1, 0x46, 0xcc, 0xc0, 0xe7, 0xd6, 0x52,
0x3c, 0xc7, 0xfa, 0xed, 0x89, 0x06, 0xeb, 0xd4, 0x5e, 0x9c, 0xa5, 0x55,
0x8c, 0xe3, 0x5f, 0xe6, 0xb4, 0x0a, 0xf4, 0xf6, 0x7d, 0xeb, 0x64, 0x74,
0xa9, 0x1a, 0x8d, 0x6e, 0xf1, 0x41, 0xc7, 0x7e, 0xc6, 0x26, 0x3a, 0x47,
0x70, 0x49, 0x07, 0x27, 0xa2, 0xb9, 0xc6, 0x79, 0x9d, 0x94, 0xf5, 0x51,
0x69, 0xdf, 0xbd, 0x84, 0xee, 0xaa, 0x46, 0xea, 0x4b, 0x27, 0xb6, 0x5c,
0xac, 0xcf, 0x4a, 0x48, 0x12, 0x40, 0x86, 0x80, 0xd4, 0xb8, 0x0a, 0xb1,
0x9f, 0xdc, 0x68, 0x60, 0x14, 0x33, 0x1d, 0x88, 0xfb, 0xa2, 0xfc, 0x49,
0x0c, 0xa9, 0x76, 0x2d, 0xd7, 0x32, 0x3f, 0x77, 0xdb, 0x62, 0x8c, 0x35,
0x88, 0x5d, 0x66, 0xc9, 0x8d, 0x07, 0xe5};
const char kCodeSigningDevelopmentFlagOid[] = "1.3.6.1.4.1.11129.4.1.2";
const char kSecureStorageFlagOid[] = "1.3.6.1.4.1.11129.4.1.3";
} // namespace
namespace widevine {
VmpChecker::VmpChecker() : allow_development_vmp_(false) {}
VmpChecker::~VmpChecker() {}
Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
Status status = ca_cert->LoadDer(
cert_type == kCertificateTypeProduction
? std::string(reinterpret_cast<const char*>(
kProdVmpCodeSigningDrmRootCertificate),
sizeof(kProdVmpCodeSigningDrmRootCertificate))
: std::string(reinterpret_cast<const char*>(
kDevVmpCodeSigningDrmRootCertificate),
sizeof(kDevVmpCodeSigningDrmRootCertificate)));
if (!status.ok()) return status;
ca_.reset(new X509CA(ca_cert.release()));
return OkStatus();
}
VmpChecker* VmpChecker::Instance() {
static VmpChecker instance;
return &instance;
}
// Verify VMP data and return appropriate result.
Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) {
DCHECK(!vmp_data.empty());
DCHECK(result);
if (!ca_) return Status(error_space, CERT_CHAIN_NOT_SELECTED, "");
vmp::VmpData vmp_data_obj;
if (!vmp_data_obj.ParseFromString(vmp_data)) {
LOG(INFO) << "Error deserializing VmpData.";
return Status(error_space, INVALID_MESSAGE, "vmp-data-deserialize-failed");
}
std::vector<std::unique_ptr<X509Cert>> code_signing_certs;
const std::string kDevelopmentFlagOid(kCodeSigningDevelopmentFlagOid);
bool secure_storage_verified(true);
for (int cert_idx = 0; cert_idx < vmp_data_obj.certificates_size();
++cert_idx) {
code_signing_certs.emplace_back(new X509Cert);
Status status(code_signing_certs.back()->LoadDer(
vmp_data_obj.certificates(cert_idx)));
if (!status.ok()) return status;
if (!allow_development_vmp_) {
bool dev_flag;
if (code_signing_certs.back()->GetV3BooleanExtension(kDevelopmentFlagOid,
&dev_flag) &&
dev_flag) {
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
"development-vmp-certificate-not-allowed");
}
}
status = ca_->VerifyCert(*code_signing_certs.back());
if (!status.ok()) return status;
if (secure_storage_verified) {
bool secure_storage_flag;
if (!code_signing_certs.back()->GetV3BooleanExtension(
kSecureStorageFlagOid, &secure_storage_flag) ||
!secure_storage_flag) {
secure_storage_verified = false;
}
}
}
size_t num_blessed_binaries(0);
for (int binary_idx = 0; binary_idx < vmp_data_obj.signed_binary_info_size();
++binary_idx) {
const vmp::VmpData::SignedBinaryInfo& binary_info(
vmp_data_obj.signed_binary_info(binary_idx));
if (binary_info.signature().empty()) {
LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\".";
*result = kTampered;
return OkStatus();
}
if (binary_info.certificate_index() >= code_signing_certs.size()) {
LOG(INFO) << "Invalid code signing certificate index.";
*result = kTampered;
return OkStatus();
}
X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get();
std::unique_ptr<RsaPublicKey> key(cert->GetRsaPublicKey());
std::string message(binary_info.binary_hash());
message += binary_info.flags() & 0xff;
if (!key->VerifySignature(message, binary_info.signature())) {
LOG(INFO) << "Code signature verification failed for file \""
<< binary_info.file_name() << "\".";
*result = kTampered;
return OkStatus();
}
if (binary_info.flags() & kBlessedBinaryFlag) ++num_blessed_binaries;
}
if (num_blessed_binaries != 1) {
LOG(INFO) << "Invalid number of blessed binaries (" << num_blessed_binaries
<< ").";
*result = kTampered;
return OkStatus();
}
VLOG(2) << "VMP verification success. Secure storage: "
<< secure_storage_verified;
*result = secure_storage_verified ? kSecureStorageVerified : kVerified;
return OkStatus();
}
} // namespace widevine

57
common/vmp_checker.h Normal file
View File

@@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Singleton object which validates VMP (Verified Media Pipeline) data for
// purposes of platform software verification.
#ifndef COMMON_VMP_CHECKER_H_
#define COMMON_VMP_CHECKER_H_
#include <memory>
#include <string>
#include "common/certificate_type.h"
#include "common/status.h"
namespace widevine {
class X509CA;
class VmpChecker {
public:
enum Result {
kUnverified = 0,
kVerified = 1,
kSecureStorageVerified = 2,
kTampered = 3
};
// Singleton accessor.
static VmpChecker* Instance();
// Select the type of root to use. Not thread-safe.
virtual Status SelectCertificateType(CertificateType cert_type);
// Verify VMP data and return appropriate result.
virtual Status VerifyVmpData(const std::string& vmp_data, Result* result);
// Enable/disable development code signing certificates.
void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; }
bool allow_development_vmp() const { return allow_development_vmp_; }
private:
VmpChecker();
~VmpChecker();
std::unique_ptr<X509CA> ca_;
bool allow_development_vmp_ = false;
};
} // namespace widevine
#endif // COMMON_VMP_CHECKER_H_

321
common/vmp_checker_test.cc Normal file
View File

@@ -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 <memory>
#include "glog/logging.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "common/rsa_key.h"
#include "common/vmp_checker.h"
#include "protos/public/errors.pb.h"
#include "protos/public/verified_media_pipeline.pb.h"
namespace widevine {
namespace {
const uint32_t kBlessedBinaryFlag = 0x00000001;
const char kDevVmpCodeSigningKey[] =
"308204a50201000282010100b3a7da87309390688388d614a5a37e7ee73a"
"c3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd"
"8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6"
"d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b"
"b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51"
"957dc4608fd34c999a7b35245bce2795715d8a5957a0872398473eed4860"
"29286f51d4118927e88634ad040034e061fd3f58632961761b1fbb8faf45"
"391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706"
"424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203"
"01000102820101008befeb1ff28e7ea56a0f63f1a133c08c48c0553efe86"
"07cfd9d216d981aef81a81db226b47277a6d32d09207df88e03316247ae7"
"39325456a00644bbfacd6dc2990851f047ccdc1226bec21afac9eacfb957"
"1e51889cfb6dac853fcdd1bb1593bd5528cdd3cbbb32c69183ef018fd3f5"
"3153f097fd3de9aca7998a6f4522e60c5e76ecc717033a2be16c233fe3e2"
"94c0ae68179a1ae5d33d51f2c0bcda35cffa21e1814374af1c014c280cfb"
"09e294a099f9a6f003d3da026af0f53029cad3d401178b0db53ca97fd166"
"a5eddf1384ddd92c55ab31758e0651e41dd7b24f2c438d58de00f55343cc"
"a461b94ff6358fc37f404ecbda2365d3e75391566248d3e102818100e4c8"
"783343b43fc127f5077fb35b37145ab6ee5f9a802e8100161ce441cbdbdc"
"1eb15f50bbef1f5088ba83ff914977862dffc63bd364172c9044e1131dbb"
"ff98755daa49670a014163b3c121395d880ad874b9ebdf66ff72a245c2b5"
"4f8fe1e233e72fd9cc253cbcd144f52537e9b7ed16b8a6d328de61c41f86"
"ea1d9ef326fb02818100c90738b2cf61af785e53393d9e96bc2f4d79358b"
"7bd487a242d170b8372af0a26af4980534a099647a4f38efb1a47d9dcdcf"
"a4ea6036396ef333e4f1de62ec529c3122f0d3b6abc818918494a52b028b"
"2cdf06bf2b450d5b99da7f203ff011318c030a01659c4c56244c2c312528"
"e30dd3b7709022d8fd14a2dba23a855da02f0281804f8ebeede4cf5b9449"
"d6d582bcd62d73309088984a5be4d00b3da55262e7074fa684bbc69173f8"
"09c36248e0a89f49a7297bd66d9b7724efe4436f997c2f92146c4be4199e"
"71463a7cf75763bc552027d559d2058a2c810c560db845e0a30243ed14a9"
"f92d1a8de2834b5d8c51c33ea87dcc3c8715a12f9249fc5a916e62d3dd02"
"8181008f74c7d1528cb35b82748174a7a789c377d5f790025e382c62e273"
"3e02a071f875baf681407d1af9c90e9fe2ed322532679cb6634b2566f6f6"
"37223a3828ffdc33fa1ca51f704c460ec2498a8a13974d1a484dd83e5898"
"9fb5bb66dcecc3b4815719141acb182ea18a659163c0d0dcb7114ee6d4f5"
"09441165e6b66e6c9dd3a102818100c449003eee6d9a0b724b66027a791e"
"3b86a72b2098ab085c2e69c276627fd6beeee98918de14ce15d17e059906"
"a178015d04e205918203ffa6c1324868d5c0a019f6fe82335403743322ee"
"14b1159692194ac22d9056c0c39309d249b33abaddb0d08ee87cc053a12a"
"83f12d261525e1b37df643c3e55770ef247d5d094816a0";
const char kDevVmpCodeSigningCert[] =
"3082056a308203d2a003020102020f112233445566778899aabbccddeeff300d06092a86"
"4886f70d01010b050030819c310b30090603550406130255533113301106035504080c0a"
"57617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d060355"
"040a0c06476f6f676c653111300f060355040b0c085769646576696e65311e301c060355"
"04030c157769646576696e652d6465762d636f64657369676e3121301f06092a864886f7"
"0d010901161274696e736b697040676f6f676c652e636f6d301e170d3137313030393231"
"323835385a170d3237313030373231323835385a3081a0310b3009060355040613025553"
"3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b"
"6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465"
"76696e653122302006035504030c197769646576696e652d6465762d766d702d636f6465"
"7369676e3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e"
"636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100"
"b3a7da87309390688388d614a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5d"
"e2f7b409b1eac2dc578b2bfd8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c"
"62c71d894fe6d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b"
"b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd3"
"4c999a7b35245bce2795715d8a5957a0872398473eed486029286f51d4118927e88634ad"
"040034e061fd3f58632961761b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be"
"2fd258331e7a86baa7394706424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3b"
"b73008150203010001a38201213082011d301d0603551d0e041604147266b4ce84aafd02"
"b1159cd2fa04c2553c6c02463081bb0603551d230481b33081b0a181a2a4819f30819c31"
"0b30090603550406130255533113301106035504080c0a57617368696e67746f6e311130"
"0f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c65311130"
"0f060355040b0c085769646576696e65311e301c06035504030c157769646576696e652d"
"6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b6970"
"40676f6f676c652e636f6d820900c5f82f038facf15830090603551d1304023000300b06"
"03551d0f04040302078030130603551d25040c300a06082b060105050703033011060a2b"
"06010401d67904010204030101ff300d06092a864886f70d01010b05000382018100aa23"
"6a5c0e23d5bf67c9f80f893d8347ba489541cf7f4ab7dfffda0ca21a3372e8ee8cfea863"
"628b9e0795904bc0e7495517246143c7b8555884e82fe1c305f0f4c3575447d4e7ce3243"
"4e1e0cf11712d537cd434c11d1328b814c94dbd0bab802e8fed5390da5f0cd719ce0e366"
"47620bcf40ed3945c80ab19beb7728080a74d4ff5d62564f47b32c4915c1f14890d379c8"
"8060f0bac73301defda06275a2e1a2024f92f0b4700d4e50d2d6f8d033715a362d7ca5ab"
"6d2dae20d8cefa2d3fc4f61e1734802984e5078dd6d957719fa75ea10dd02d983f7e383b"
"10fa92be7add70238388e63ec2c7ec49a37aa0c8a2566c46e9755cd9ce654c6d42053d1e"
"1cd9555f8a3bc5e426857072a8e8b44a756543893b1d29dabf31a2301597df2666612f23"
"a442613526e19f2aa9b2ea49f16f14b16794e053967a21b821c7b2495b2e02b01344a339"
"9d7e31dd71982cf21a546b1947bbce236381d717070c27096b6a91413abc69c3c0759574"
"4b7e91daf24b2a0acfd85924669f00292a4bd0c57b4c";
const char kDevSecureStorageVmpCodeSigningCert[] =
"30820571308203d9a0030201020203112233300d06092a864886f70d01010b050030819c"
"310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3111"
"300f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c653111"
"300f060355040b0c085769646576696e65311e301c06035504030c157769646576696e65"
"2d6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b69"
"7040676f6f676c652e636f6d301e170d3138303232323231323834395a170d3238303232"
"303231323834395a3081a0310b30090603550406130255533113301106035504080c0a57"
"617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d06035504"
"0a0c06476f6f676c653111300f060355040b0c085769646576696e653122302006035504"
"030c197769646576696e652d6465762d766d702d636f64657369676e3121301f06092a86"
"4886f70d010901161274696e736b697040676f6f676c652e636f6d30820122300d06092a"
"864886f70d01010105000382010f003082010a0282010100b3a7da87309390688388d614"
"a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd"
"8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6d5524e1b28af"
"1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646bb50d3a13d8687ad304005b0b"
"0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd34c999a7b35245bce2795715d"
"8a5957a0872398473eed486029286f51d4118927e88634ad040034e061fd3f5863296176"
"1b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706"
"424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203010001a38201"
"3430820130301d0603551d0e041604147266b4ce84aafd02b1159cd2fa04c2553c6c0246"
"3081bb0603551d230481b33081b0a181a2a4819f30819c310b3009060355040613025553"
"3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b"
"6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465"
"76696e65311e301c06035504030c157769646576696e652d6465762d636f64657369676e"
"3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e636f6d82"
"0900c5f82f038facf15830090603551d1304023000300b0603551d0f0404030207803013"
"0603551d25040c300a06082b060105050703033011060a2b06010401d679040102040301"
"01ff3011060a2b06010401d67904010304030101ff300d06092a864886f70d01010b0500"
"038201810069482a9bb125b17c76c77f86004e36ca4fc36bbde9c86901481c70d165e3cf"
"1c8541264190b3e7106c33ff4920a99bc9f939298091dc72ff898d22f5017b04d213eb60"
"626382656fdd66b9cd1830d98ef89d8e405b2d7ce9445db8de2ca365438b848f85d5266b"
"c211c8934154bb88ddb9e6f9aaff761814c1c0da90dd499f174507b5f4e89339e8abd157"
"8b3238d8f5c9dcb1f76e4d810679c2eca3583144f1ac0ce955b7c6a2cc9fc9f3d6c87069"
"28e301ebc3844e53f4905ff60803110dfc3b4f74b50ae1baffd091daad3ea29925f8009e"
"adc471a9ae673d9a9a003901d962f58fede85b2f65d9a470725459b19a69b06ae49179a3"
"395286b7d7039b32985a6db5b30bcd5cbed975d9a68de44fbcd1dbc8b6fbf67746b87d30"
"122c1cd8ed303a8bec7e23d284c0de35cfdccf261c317efe192efa84d2600bba3f9af846"
"8f89953a98f92c97b13d320f484627790d5b7b407f29f343c41154efbcf06e98632a3f3d"
"7138184a6f40af2435b3d98054ed9f4c3aa9ecf95c5c3014d3aa4d12f2";
const char kSameAsPrevious[] = "";
} // namespace
class VmpCheckerTest : public ::testing::Test {
public:
void SetUp() override {
ASSERT_OK(
VmpChecker::Instance()->SelectCertificateType(kCertificateTypeTesting));
vmp_data_.Clear();
VmpChecker::Instance()->set_allow_development_vmp(true);
signing_key_.reset(
RsaPrivateKey::Create(absl::HexStringToBytes(kDevVmpCodeSigningKey)));
ASSERT_TRUE(signing_key_);
}
// Adds a binary to the VMP data to be verified. If |signing_cert| is
// |kSameAsPrevious| (empty), then the binary is signed using the same
// certificate as the previously added binary. This means that the first
// call to this function should not use |kSameAsPrevious|.
void AddVmpBinary(const std::string& signing_cert, const std::string& file_name,
const std::string& binary_hash, uint32_t flags) {
DCHECK(!signing_cert.empty() || !vmp_data_.certificates().empty());
if (!signing_cert.empty()) {
*vmp_data_.add_certificates() = absl::HexStringToBytes(signing_cert);
}
vmp::VmpData::SignedBinaryInfo* new_binary =
vmp_data_.add_signed_binary_info();
new_binary->set_file_name(file_name);
new_binary->set_certificate_index(vmp_data_.certificates_size() - 1);
new_binary->set_binary_hash(binary_hash);
new_binary->set_flags(flags);
std::string message(binary_hash);
message += flags & 0xff;
std::string signature;
ASSERT_TRUE(signing_key_->GenerateSignature(message, &signature));
new_binary->set_signature(signature);
}
const std::string GetVmpData() {
std::string result;
vmp_data_.SerializeToString(&result);
return result;
}
protected:
vmp::VmpData vmp_data_;
std::unique_ptr<RsaPrivateKey> 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

View File

@@ -0,0 +1,19 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Common Encryption (CENC) system ID for Widevine DRM.
#include "common/widevine_system_id.h"
namespace widevine {
const uint8_t kWidevineSystemId[16] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
0xd5, 0x1d, 0x21, 0xed};
} // namespace widevine

View File

@@ -0,0 +1,22 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Common Encryption (CENC) system ID for Widevine DRM.
#ifndef COMMON_WIDEVINE_SYSTEM_ID_H_
#define COMMON_WIDEVINE_SYSTEM_ID_H_
#include <cstdint>
namespace widevine {
extern const uint8_t kWidevineSystemId[16];
} // namespace widevine
#endif // COMMON_WIDEVINE_SYSTEM_ID_H_

86
common/wvm_test_keys.cc Normal file
View File

@@ -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 <vector>
#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<WvmTokenHandler::PreprovKey> 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<uint32_t, std::string> GetPreprovKeyTable() {
return {{kTestSystemId, std::string(kTestPreprovKeyHex)}};
}
std::multimap<uint32_t, std::string> GetPreprovKeyMultimap() {
return {{kTestSystemId, kBadPreprovKey1Hex},
{kTestSystemId, kTestPreprovKeyHex},
{kTestSystemId, kBadPreprovKey2Hex}};
}
} // namespace wvm_test_keys
} // namespace widevine

58
common/wvm_test_keys.h Normal file
View File

@@ -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 <map>
#include <string>
#include <vector>
#include <cstdint>
#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<WvmTokenHandler::PreprovKey> 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<uint32_t, std::string> 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<uint32_t, std::string> GetPreprovKeyMultimap();
} // namespace wvm_test_keys
} // namespace widevine
#endif // COMMON_WVM_TEST_KEYS_H_

311
common/wvm_token_handler.cc Normal file
View File

@@ -0,0 +1,311 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/wvm_token_handler.h"
#include <vector>
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "util/endian/endian.h"
#include "util/gtl/map_util.h"
#include "common/aes_cbc_util.h"
#include "common/ecb_util.h"
#include "common/sha_util.h"
#include "common/status.h"
namespace widevine {
namespace {
const int kKeyboxFlagInsecure = 1;
const int kSystemIdSamsungTVFactory = 8;
const int kPreProvisioningKeySizeBytes = 16;
const int kKeyboxSizeBytes = 72;
const std::string kZeroIV(16, '\0');
class PreprovKeysMap {
public:
void Update(const std::vector<WvmTokenHandler::PreprovKey>& key_vector);
std::vector<WvmTokenHandler::PreprovKey> GetPreprovKeys(uint32_t system_id);
bool IsSystemIdKnown(uint32_t system_id);
bool IsEmpty();
static PreprovKeysMap* GetSingleton();
private:
absl::Mutex mutex_;
std::multimap<uint32_t, WvmTokenHandler::PreprovKey> preprov_keys_;
};
void PreprovKeysMap::Update(
const std::vector<WvmTokenHandler::PreprovKey>& 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<WvmTokenHandler::PreprovKey> PreprovKeysMap::GetPreprovKeys(
uint32_t system_id) {
absl::ReaderMutexLock lock(&mutex_);
std::vector<WvmTokenHandler::PreprovKey> 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<PreprovKey>& keyvec) {
PreprovKeysMap::GetSingleton()->Update(keyvec);
}
bool WvmTokenHandler::IsSystemIdKnown(uint32_t system_id) {
return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id);
}
Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
std::string* device_key_out,
Cipher* cipher_out,
bool* insecure_out) {
const std::string default_make_model;
return DecryptDeviceKey(token, default_make_model, device_key_out, cipher_out,
insecure_out);
}
Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
const std::string& make_model,
std::string* device_key_out,
Cipher* cipher_out,
bool* insecure_out) {
DCHECK(device_key_out);
// DCHECK below is commented out because preprov_keys_ being nullptr
// is a valid test in wvm_token_handler_test.cc. If we have
// DCHECK(preprov_keys_) here it would thrown an failure in Kokoro
// presubmit because evidently Kokoro does debug build.
// DCHECK(preprov_keys_);
if (token.size() < kKeyboxSizeBytes) {
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
}
if (PreprovKeysMap::GetSingleton()->IsEmpty()) {
return Status(error::INVALID_ARGUMENT,
"Pre-provisioning key map is nullptr.");
}
uint32_t system_id = GetSystemId(token);
// There may be multiple preprov keys for a system ID; try them all.
std::vector<PreprovKey> key_vector =
PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id);
Status status;
// First pass through the matching system Ids is an attempt to find an
// alternate preprov key specific to this make/model.
const PreprovKey* preferred_ppk = NULL;
for (const PreprovKey& ppk : key_vector) {
if (!ppk.model_filter.empty() && ppk.model_filter == make_model) {
preferred_ppk = &ppk;
break;
}
}
for (const PreprovKey& ppk : key_vector) {
if (preferred_ppk && preferred_ppk != &ppk) {
continue;
}
uint32_t version;
status = DecryptDeviceKeyWithPreprovKey(
ppk.key_bytes, token, device_key_out, insecure_out, &version);
if (version != 2) {
// Only version 2 keyboxes supported.
return Status(error::PERMISSION_DENIED,
absl::StrCat("invalid-keybox-version ", version));
}
if (status.ok()) {
if (cipher_out) {
*cipher_out = ppk.cipher;
}
return status;
}
}
if (!status.ok()) {
// Return error from last attempt.
return status;
}
return Status(
error::NOT_FOUND,
absl::StrCat("Unknown system id: ", system_id).c_str()); // NOLINT
}
// Decrypt a token using the preprov key for its system ID, and use the
// decrypted device key to encrypt the given asset key. Returns the encrypted
// asset key in |result|.
// On failure, returns an error from the Widevine Server SDK error space.
Status WvmTokenHandler::GetEncryptedAssetKey(absl::string_view token,
absl::string_view raw_asset_key,
const std::string& make_model,
std::string* result) {
std::string device_key;
Cipher cipher = AES;
Status status =
DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr);
if (!status.ok()) {
return status;
}
return EncryptAssetKey(device_key, raw_asset_key, cipher, result);
}
uint32_t WvmTokenHandler::GetSystemId(absl::string_view token) {
uint32_t system_id = 0;
if (token.size() >= 8) {
// Bytes 4-8 contain the little-endian system ID.
system_id = BigEndian::Load32(token.data() + 4);
}
return system_id;
}
std::string WvmTokenHandler::GetEncryptedUniqueId(absl::string_view token) {
std::string encrypted_unique_id("");
if (token.size() >= 24) {
encrypted_unique_id = std::string(token.substr(8, 16));
}
return encrypted_unique_id;
}
Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
absl::string_view preprov_key, absl::string_view token,
std::string* device_key_out) {
return DecryptDeviceKeyWithPreprovKey(preprov_key, token, device_key_out,
nullptr, nullptr);
}
Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
absl::string_view preprov_key, absl::string_view token,
std::string* device_key_out, bool* insecure_out, uint32_t* version) {
CHECK(device_key_out);
if (token.size() < kKeyboxSizeBytes) {
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
}
if (version) {
*version = BigEndian::Load32(token.data());
}
// This was checked at initialization, so if it fails now something is wrong.
CHECK_EQ(preprov_key.size(), kPreProvisioningKeySizeBytes);
absl::string_view encrypted_unique_id = token.substr(8, 16);
std::string unique_id = crypto_util::DecryptAesCbcNoPad(
std::string(preprov_key), kZeroIV, std::string(encrypted_unique_id));
if (unique_id.size() != 16) {
// Decrypting 16 bytes should result in 16 bytes.
LOG(WARNING) << "Internal error decrypting unique id from token.";
return Status(error::INTERNAL, "Wrong size after decrypt/16.");
}
absl::string_view encrypted_bits = token.substr(24, 48);
std::string decrypted_bits = crypto_util::DecryptAesCbcNoPad(
unique_id, kZeroIV, std::string(encrypted_bits));
if (decrypted_bits.size() != 48) {
// Decrypting 48 bytes should result in 48 bytes.
LOG(WARNING) << "Internal error decrypting device key from token.";
return Status(error::INTERNAL, "Wrong size after decrypt/48.");
}
uint8_t keybox_flags = decrypted_bits[36];
absl::string_view device_key =
absl::string_view(decrypted_bits).substr(0, 16);
absl::string_view expected_hash =
absl::string_view(decrypted_bits).substr(16, 20);
std::string actual_hash = Sha1_Hash(std::string(device_key));
if (GetSystemId(token) == kSystemIdSamsungTVFactory) {
// Keyboxes with this system ID have corrupted bytes starting after the
// first 16 bytes of the hash, so we use only the uncorrupted part.
expected_hash = expected_hash.substr(0, 16);
actual_hash.resize(16);
keybox_flags = 0;
}
if (expected_hash != actual_hash) {
return Status(error::PERMISSION_DENIED, "Keybox validation failed.");
}
*device_key_out = std::string(device_key);
if (insecure_out) {
*insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0;
}
return OkStatus();
}
Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key,
absl::string_view raw_asset_key,
Cipher cipher, std::string* result) {
CHECK(result);
if (device_key.size() != 16) {
return Status(error::INVALID_ARGUMENT, "Invalid device key: size != 16");
}
if (raw_asset_key.size() < 16) {
return Status(error::INVALID_ARGUMENT, "Invalid asset key: size < 16");
}
// Truncate extra characters in the key; wvm always uses 16.
absl::string_view asset_key = raw_asset_key.substr(0, 16);
switch (cipher) {
case DES3:
if (!crypto_util::Encrypt3DesEcb(device_key, asset_key, result)) {
return Status(error::INTERNAL, "Error encrypting asset key with 3DES.");
}
return OkStatus();
case AES:
if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) {
return Status(error::INTERNAL, "Error encrypting asset key with AES.");
}
return OkStatus();
case PASS_THRU:
result->assign(raw_asset_key.data(), raw_asset_key.size());
return OkStatus();
default:
return Status(error::INVALID_ARGUMENT, "Unknown cipher type");
}
}
} // namespace widevine

125
common/wvm_token_handler.h Normal file
View File

@@ -0,0 +1,125 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef COMMON_WVM_TOKEN_HANDLER_H_
#define COMMON_WVM_TOKEN_HANDLER_H_
#include <map>
#include <vector>
#include "base/macros.h"
#include "absl/strings/string_view.h"
#include "common/status.h"
namespace widevine {
// Class for decoding the encrypted token that comes from a WVM classic keybox.
//
// Internally, this class keeps a multimap from system ID to the preprov key(s)
// for that system, so construction is relatively expensive; don't create an
// instance of this class per-request.
//
// Errors in this file are returned in the canonical space, but are chosen so
// that it's possible to map different failure modes to the appropriate codes
// in the WVM or server SDK error spaces:
// OK - success.
// NOT_FOUND - system id from token wasn't in the preprov key table.
// PERMISSION_DENIED - hash of device key didn't match.
// INVALID_ARGUMENT - token or a key is wrong size or otherwise invalid.
// INTERNAL_ERROR - something went wrong that shouldn't have been able to.
class WvmTokenHandler {
public:
// Cipher type to use for encrypting asset keys. This matches the enum in
// video/widevine/lockbox/public/key.proto.
enum Cipher {
DES3 = 0,
AES = 1,
PASS_THRU = 2,
};
struct PreprovKey {
// Utility constructor.
PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher,
const std::string& model_filter);
PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher);
// Constructor if the cipher isn't needed (i.e. modular DRM).
PreprovKey(uint32_t system_id, const std::string& key_bytes);
uint32_t system_id;
std::string key_bytes;
Cipher cipher;
// If set, the make/model in the license request must match this value.
std::string model_filter;
};
// Set pre-provisioning keys from the given vector. This may be called
// concurrently with other methods.
static void SetPreprovKeys(const std::vector<PreprovKey>& preprov_keys);
// Returns true if system_id is in the preprov key table.
static bool IsSystemIdKnown(uint32_t system_id);
// Decrypt a token using the preprov key for its system ID, and
// return the decrypted device key in result.
// On failure, returns one of the errors listed above.
// cipher_out may be null; if not, *cipher_out will be set to the cipher type
// to use with the device key.
// insecure_out may be null; if not, *insecure_out will be set to the
// decrypted value of the 'insecure keybox' flag.
static Status DecryptDeviceKey(absl::string_view token,
std::string* device_key_out, Cipher* cipher_out,
bool* insecure_out);
// Same as above, except takes in the make/model from the license request.
// For legacy WVM license, we have some special cases where we need to inspect
// the make/model as we apply alternate keys.
static Status DecryptDeviceKey(absl::string_view token,
const std::string& make_model,
std::string* device_key_out, Cipher* cipher_out,
bool* insecure_out);
// Decrypt a token using the preprov key for its system ID, and use the
// decrypted device key to encrypt the given asset key. Returns the encrypted
// asset key in result.
static Status GetEncryptedAssetKey(absl::string_view token,
absl::string_view raw_asset_key,
const std::string& make_model, std::string* result);
// Extract the system ID component of a token (bytes 4-8).
static uint32_t GetSystemId(absl::string_view token);
// Extract the encrypted unique ID component of a token (bytes 8-24).
static std::string GetEncryptedUniqueId(absl::string_view token);
// Try to decrypt a token using the provided preprov key, and return the
// decrypted device key in result.
//
// Note that the if the input std::string lengths are correct (16 and 72 bytes),
// the only possible cause of failure is the decrypted device key hash
// being incorrect.
static Status DecryptDeviceKeyWithPreprovKey(
absl::string_view preprov_key_bytes, absl::string_view token,
std::string* device_key_out);
// Same as above, but allows extracting the 'insecure keybox' flag and keybox
// version.
static Status DecryptDeviceKeyWithPreprovKey(
absl::string_view preprov_key_bytes, absl::string_view token,
std::string* device_key_out, bool* insecure_out, uint32_t* version);
// Given a decrypted device key as returned by DecryptToken(), use it to
// encrypt an asset key with the given cipher.
static Status EncryptAssetKey(absl::string_view device_key,
absl::string_view raw_asset_key, Cipher cipher,
std::string* result);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler);
};
} // namespace widevine
#endif // COMMON_WVM_TOKEN_HANDLER_H_

View File

@@ -0,0 +1,229 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/wvm_token_handler.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "common/wvm_test_keys.h"
using widevine::wvm_test_keys::kTestSystemId;
using widevine::wvm_test_keys::kTestSystemId3Des;
using widevine::wvm_test_keys::kTestPreprovKeyHex;
using widevine::wvm_test_keys::kTestDeviceKey1Hex;
using widevine::wvm_test_keys::kTestDeviceKey2Hex;
using widevine::wvm_test_keys::kTestDeviceKey3DesHex;
using widevine::wvm_test_keys::kTestToken1Hex;
using widevine::wvm_test_keys::kTestToken2Hex;
using widevine::wvm_test_keys::kTestToken3DesHex;
using widevine::wvm_test_keys::GetPreprovKeyVector;
namespace widevine {
using absl::BytesToHexString;
using absl::HexStringToBytes;
TEST(WvmTokenHandlerTest, GetSystemId) {
EXPECT_EQ(kTestSystemId,
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex)));
EXPECT_EQ(kTestSystemId,
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken2Hex)));
}
TEST(WvmTokenHandlerTest, GetEncryptedUniqueId) {
EXPECT_EQ(
HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"),
WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken1Hex)));
EXPECT_EQ(
HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"),
WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken2Hex)));
}
TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
Status status;
std::string device_key;
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken1Hex),
&device_key);
EXPECT_OK(status) << status;
EXPECT_EQ(kTestDeviceKey1Hex, BytesToHexString(device_key));
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken2Hex),
&device_key);
EXPECT_OK(status);
EXPECT_EQ(kTestDeviceKey2Hex, BytesToHexString(device_key));
// Test with invalid token. Hash failure should produce PERMISSION_DENIED.
device_key.clear();
std::string token = HexStringToBytes(
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e");
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
HexStringToBytes(kTestPreprovKeyHex), token, &device_key);
EXPECT_FALSE(status.ok());
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
EXPECT_TRUE(device_key.empty());
}
// See b/68798704 for background of this test.
// It is important to keep this test *before* all the DecryptDeviceKey* tests
// below, in which WvmTokenHandler::SetPreprovKeys() would be called.
TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) {
// Not calling WvmTokenHandler::SetPreprovKeys()
// So preprov_keys_ would be nullptr.
Status status;
std::string device_key;
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
&device_key, nullptr, nullptr);
EXPECT_FALSE(status.ok());
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
}
// Same tests as DecryptDeviceKeyWithPreprovKey(), but we use the handler's
// table of preprov keys instead of providing our own.
TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
Status status;
std::string device_key;
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
&device_key, nullptr, nullptr);
EXPECT_OK(status);
EXPECT_EQ(HexStringToBytes(kTestDeviceKey1Hex), device_key);
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken2Hex),
&device_key, nullptr, nullptr);
EXPECT_OK(status);
EXPECT_EQ(HexStringToBytes(kTestDeviceKey2Hex), device_key);
// Test with invalid token. Hash failure should produce PERMISSION_DENIED.
device_key.clear();
std::string token = HexStringToBytes(
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e");
status =
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
EXPECT_FALSE(status.ok());
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
EXPECT_TRUE(device_key.empty());
// Test with nonexistent system id. Should produce NOT_FOUND.
device_key.clear();
token = HexStringToBytes(
"00000002555555559e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e");
status =
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
EXPECT_FALSE(status.ok());
EXPECT_EQ(error::NOT_FOUND, status.error_code());
EXPECT_TRUE(device_key.empty());
}
TEST(WvmTokenHandlerTest, GetEncryptedAssetKey) {
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
std::string raw_asset_key = "asset-key-000000";
std::string asset_key;
std::string make_model;
Status status = WvmTokenHandler::GetEncryptedAssetKey(
HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key);
EXPECT_OK(status);
EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key));
status = WvmTokenHandler::GetEncryptedAssetKey(
HexStringToBytes(kTestToken2Hex), raw_asset_key, make_model, &asset_key);
EXPECT_OK(status);
EXPECT_EQ("091802159bf8da12aecfcdfb092075c8", BytesToHexString(asset_key));
// Check 3DES encryption of asset keys
status = WvmTokenHandler::EncryptAssetKey(
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
WvmTokenHandler::DES3, &asset_key);
EXPECT_OK(status);
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
asset_key.clear();
status = WvmTokenHandler::GetEncryptedAssetKey(
HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model,
&asset_key);
EXPECT_OK(status);
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
// Test with pass-thru (Cipher=PASS_THRU).
asset_key.clear();
status = WvmTokenHandler::EncryptAssetKey(
HexStringToBytes(kTestDeviceKey1Hex), raw_asset_key,
WvmTokenHandler::PASS_THRU, &asset_key);
EXPECT_OK(status);
EXPECT_EQ(BytesToHexString(raw_asset_key), BytesToHexString(asset_key));
}
TEST(WvmTokenHandlerTest, FilterOnMakeModel) {
// Use all good keys, but only the key for LG:BD572 should work. It works
// by setting only that one to DES3. All other AES ppks will encrypt the
// asset key incorrectly.
std::vector<WvmTokenHandler::PreprovKey> ppks;
ppks.push_back(WvmTokenHandler::PreprovKey(
kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex),
WvmTokenHandler::AES, ""));
ppks.push_back(WvmTokenHandler::PreprovKey(
kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex),
WvmTokenHandler::DES3, "LG:BD572"));
ppks.push_back(WvmTokenHandler::PreprovKey(
kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex),
WvmTokenHandler::AES, ""));
WvmTokenHandler::SetPreprovKeys(ppks);
std::string raw_asset_key = "asset-key-000000";
std::string asset_key;
// Check 3DES encryption of asset keys
Status status = WvmTokenHandler::EncryptAssetKey(
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
WvmTokenHandler::DES3, &asset_key);
EXPECT_OK(status);
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
asset_key.clear();
std::string make_model;
status = WvmTokenHandler::GetEncryptedAssetKey(
HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model,
&asset_key);
EXPECT_OK(status);
// Should fail because the asset key was encrypted with AES instead of DES3.
EXPECT_NE("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
// Set the make/model so we find and use the correct ppk.
make_model = "LG:BD572";
status = WvmTokenHandler::GetEncryptedAssetKey(
HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model,
&asset_key);
EXPECT_OK(status);
// Should work because the asset key was encrypted with DES3.
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
}
TEST(WvmTokenHandlerTest, AncientKeybox) {
Status status;
std::string device_key;
std::string v1_token(
std::string(kTestPreprovKeyHex).replace(0, 16, "0000000100000001"));
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex),
&device_key);
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
EXPECT_TRUE(device_key.empty());
}
} // namespace widevine

458
common/x509_cert.cc Normal file
View File

@@ -0,0 +1,458 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/x509_cert.h"
#include <memory>
#include <cstdint>
#include "glog/logging.h"
#include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h"
#include "openssl/bio.h"
#include "openssl/evp.h"
#include "openssl/pem.h"
#include "openssl/pkcs7.h"
#include "openssl/x509.h"
#include "openssl/x509v3.h"
#include "common/openssl_util.h"
#include "common/rsa_key.h"
namespace {
// Serializes the X509 |certificate| into a PEM-encoded string. Returns true
// on success, false otherwise. The caller retains ownership of
// |certificate| and |serialized_certificate|. |serialized_certificate| must
// not be NULL.
bool PemEncodeX509Certificate(const X509& certificate,
std::string* serialized_certificate) {
CHECK(serialized_certificate) << "serialized_certificate can not be null.";
ScopedBIO bio(BIO_new(BIO_s_mem()));
if (bio == nullptr) {
return false;
}
// The const_cast is necessary for the openssl call.
PEM_write_bio_X509(bio.get(), const_cast<X509*>(&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> X509Cert::FromOpenSslCert(ScopedX509 certificate) {
return std::unique_ptr<X509Cert>(new X509Cert(certificate.release()));
}
X509Cert::X509Cert() : openssl_cert_(NULL) {}
X509Cert::~X509Cert() {
if (openssl_cert_ != NULL) {
X509_free(openssl_cert_);
}
}
X509Cert::X509Cert(X509* openssl_cert) : openssl_cert_(openssl_cert) {}
Status X509Cert::LoadPem(const std::string& pem_cert) {
if (pem_cert.empty()) {
return Status(error::INVALID_ARGUMENT, "Empty PEM certificate");
}
BIO* bio(NULL);
X509* new_cert(NULL);
bio = BIO_new_mem_buf(const_cast<char*>(pem_cert.data()), pem_cert.size());
if (bio == NULL) {
return Status(error::INTERNAL, "BIO allocation failed");
}
Status status;
new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
if (new_cert == NULL) {
status = Status(error::INVALID_ARGUMENT, "PEM certificate load failed");
goto cleanup;
}
if (openssl_cert_ != NULL) {
X509_free(openssl_cert_);
}
openssl_cert_ = new_cert;
cleanup:
if (bio != NULL) {
BIO_free(bio);
}
return status;
}
Status X509Cert::LoadDer(const std::string& der_cert) {
if (der_cert.empty()) {
return Status(error::INVALID_ARGUMENT, "Empty DER certificate");
}
const unsigned char* cert_data =
reinterpret_cast<const unsigned char*>(der_cert.data());
X509* new_cert = d2i_X509(NULL, &cert_data, der_cert.size());
if (new_cert == NULL) {
return Status(error::INVALID_ARGUMENT, "DER certificate load failed");
}
if (openssl_cert_ != NULL) {
X509_free(openssl_cert_);
}
openssl_cert_ = new_cert;
return OkStatus();
}
std::string X509Cert::GetPem() const {
std::string serialized_certificate;
if (!PemEncodeX509Certificate(*openssl_cert_, &serialized_certificate)) {
return "";
}
return serialized_certificate;
}
std::unique_ptr<RsaPublicKey> X509Cert::GetRsaPublicKey() const {
ScopedPKEY pkey(X509_get_pubkey(openssl_cert_));
return std::unique_ptr<RsaPublicKey>(
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<char[]> buffer(new char[size]);
int bytes_read = BIO_read(bio, buffer.get(), size);
if (bytes_read == size) {
subject_name_.assign(buffer.get(), bytes_read);
}
BIO_free(bio);
}
}
}
return subject_name_;
}
std::string X509Cert::GetSubjectNameField(const std::string& field) {
if (field.empty()) {
return std::string();
}
const std::string& subject = GetSubjectName();
size_t start_pos = subject.find(field + "=");
if (start_pos == std::string::npos) {
return std::string();
}
start_pos += field.size() + 1;
size_t end_pos = subject.find(",", start_pos);
if (end_pos == std::string::npos) {
end_pos = subject.size();
}
return subject.substr(start_pos, end_pos - start_pos);
}
std::string X509Cert::GetSerialNumber() const {
if (openssl_cert_ == NULL) {
return std::string();
}
BIGNUM* bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(openssl_cert_), NULL);
if (bn == NULL) {
return std::string();
}
std::string result;
char* openssl_sn = BN_bn2hex(bn);
if (openssl_sn != NULL) {
result = absl::HexStringToBytes(openssl_sn);
OPENSSL_free(openssl_sn);
}
BN_free(bn);
return result;
}
bool X509Cert::GetNotBeforeSeconds(int64_t* valid_start_seconds) const {
if (openssl_cert_ == nullptr) {
return false;
}
return Asn1TimeToEpochSeconds(X509_get0_notBefore(openssl_cert_),
valid_start_seconds)
.ok();
}
bool X509Cert::GetNotAfterSeconds(int64_t* valid_end_seconds) const {
if (openssl_cert_ == nullptr) {
return false;
}
return Asn1TimeToEpochSeconds(X509_get0_notAfter(openssl_cert_),
valid_end_seconds)
.ok();
}
bool X509Cert::IsCaCertificate() const {
return X509_check_ca(openssl_cert_) != 0;
}
bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const {
ScopedAsn1Object extension_name(OBJ_txt2obj(oid.c_str(), 1));
int ext_pos = X509_get_ext_by_OBJ(openssl_cert_, extension_name.get(), -1);
if (ext_pos < 0) return false;
X509_EXTENSION* extension(X509_get_ext(openssl_cert_, ext_pos));
if (!extension) return false;
ASN1_OCTET_STRING* extension_data(X509_EXTENSION_get_data(extension));
if (!extension_data) return false;
if ((extension_data->length != 3) || (extension_data->data[0] != 1) ||
(extension_data->data[1] != 1))
return false;
*value = extension_data->data[2] != 0;
return true;
}
Status X509Cert::Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
int64_t* epoch_seconds) const {
if (asn1_time == nullptr) {
// This code is exported to shared source. The exported code does not yet
// support MakeStatus.
// NOLINTNEXTLINE
return Status(error::INVALID_ARGUMENT, "asn1_time cannot be null.");
}
if (epoch_seconds == nullptr) {
// NOLINTNEXTLINE
return Status(error::INVALID_ARGUMENT, "epoch_seconds cannot be null.");
}
ScopedAsn1Time epoch_time(ASN1_TIME_new());
if (!ASN1_TIME_set(epoch_time.get(), 0)) {
// NOLINTNEXTLINE
return Status(error::INTERNAL, "Failed to set epoch time.");
}
int day = 0;
int seconds = 0;
if (!ASN1_TIME_diff(&day, &seconds, epoch_time.get(), asn1_time)) {
// NOLINTNEXTLINE
return Status(error::INTERNAL,
"Failed to convert asn1 time to epoch time.");
}
*epoch_seconds = 24L * 3600L * day + seconds;
return OkStatus();
}
X509CertChain::~X509CertChain() { Reset(); }
void X509CertChain::Reset() {
for (auto certp : cert_chain_) {
delete certp;
}
cert_chain_.clear();
}
Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
static const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----";
static const char kEndCertificate[] = "-----END CERTIFICATE-----";
Reset();
size_t begin_pos = pem_cert_chain.find(kBeginCertificate);
while (begin_pos != std::string::npos) {
size_t end_pos = pem_cert_chain.find(
kEndCertificate, begin_pos + sizeof(kBeginCertificate) - 1);
if (end_pos != std::string::npos) {
end_pos += sizeof(kEndCertificate) - 1;
std::unique_ptr<X509Cert> new_cert(new X509Cert);
Status status = new_cert->LoadPem(
pem_cert_chain.substr(begin_pos, end_pos - begin_pos));
if (!status.ok()) {
return status;
}
cert_chain_.push_back(new_cert.release());
begin_pos = pem_cert_chain.find(kBeginCertificate, end_pos);
}
}
return OkStatus();
}
Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) {
ScopedX509Stack cert_stack(sk_X509_new_null());
CBS cbs;
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pk7_cert_chain.data()),
pk7_cert_chain.size());
if (!PKCS7_get_certificates(cert_stack.get(), &cbs)) {
return Status(error::INVALID_ARGUMENT,
"Unable to load PKCS#7 certificate chain");
}
while (sk_X509_num(cert_stack.get()) > 0) {
cert_chain_.insert(cert_chain_.begin(),
new X509Cert(sk_X509_pop(cert_stack.get())));
}
return OkStatus();
}
std::string X509CertChain::GetPkcs7() {
std::string pkcs7_cert;
ScopedX509Stack cert_stack(sk_X509_new_null());
for (X509Cert* cert : cert_chain_) {
// X509 stack takes ownership of certificates. Copy certificates to retain
// |cert_chain_|.
X509Cert cert_copy;
if (!cert_copy.LoadPem(cert->GetPem()).ok()) {
LOG(WARNING) << "Certificate chain serialization failed";
return "";
}
X509* openssl_cert_copy = const_cast<X509*>(cert_copy.openssl_cert());
cert_copy.openssl_cert_ = nullptr;
sk_X509_push(cert_stack.get(), openssl_cert_copy);
}
ScopedPKCS7 pkcs7(
PKCS7_sign(nullptr, nullptr, cert_stack.get(), nullptr, PKCS7_DETACHED));
if (!pkcs7) {
LOG(WARNING) << "Could not convert certificate chain to PKCS7";
return "";
}
ScopedBIO bio(BIO_new(BIO_s_mem()));
if (bio.get() == nullptr || !i2d_PKCS7_bio(bio.get(), pkcs7.get())) {
LOG(WARNING) << "Failed writing PKCS7 to bio";
return "";
}
int cert_size = BIO_pending(bio.get());
pkcs7_cert.resize(cert_size);
if (BIO_read(bio.get(), &pkcs7_cert[0], cert_size) != cert_size) {
LOG(WARNING) << "BIO_read failure";
return "";
}
return pkcs7_cert;
}
X509Cert* X509CertChain::GetCert(size_t cert_index) const {
if (cert_index >= cert_chain_.size()) {
return NULL;
}
return cert_chain_[cert_index];
}
X509CA::X509CA(X509Cert* ca_cert) : ca_cert_(ca_cert), openssl_store_(NULL) {}
X509CA::~X509CA() {
if (openssl_store_ != NULL) {
X509_STORE_free(openssl_store_);
}
}
Status X509CA::InitializeStore() {
absl::WriterMutexLock lock(&openssl_store_mutex_);
if (openssl_store_ == NULL) {
if (ca_cert_ == NULL) {
return Status(error::INTERNAL, "CA X.509Cert is NULL");
}
openssl_store_ = X509_STORE_new();
if (openssl_store_ == NULL) {
return Status(error::INTERNAL, "Failed to allocate X.509 store");
}
if (X509_STORE_add_cert(openssl_store_,
const_cast<X509*>(ca_cert_->openssl_cert())) == 0) {
X509_STORE_free(openssl_store_);
openssl_store_ = NULL;
return Status(error::INTERNAL,
"Failed to add X.509 CA certificate to store");
}
}
return OkStatus();
}
Status X509CA::VerifyCert(const X509Cert& cert) {
return OpenSslX509Verify(cert.openssl_cert(), nullptr);
}
Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) {
if (cert_chain.GetNumCerts() < 1) {
return Status(error::INVALID_ARGUMENT,
"Cannot verify empty certificate chain");
}
ScopedX509StackOnly intermediates(sk_X509_new_null());
if (!intermediates) {
return Status(error::INTERNAL,
"Failed to allocate X.509 intermediate certificate stack");
}
const X509Cert* leaf_cert(nullptr);
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
if (cert_chain.GetCert(idx)->IsCaCertificate()) {
sk_X509_push(intermediates.get(),
const_cast<X509*>(cert_chain.GetCert(idx)->openssl_cert()));
} else {
leaf_cert = cert_chain.GetCert(idx);
}
}
if (!leaf_cert) {
return Status(error::INVALID_ARGUMENT,
"X.509 certificate chain without leaf certificate.");
}
return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get());
}
Status X509CA::VerifyCertWithChain(const X509Cert& cert,
const X509CertChain& cert_chain) {
ScopedX509StackOnly intermediates(sk_X509_new_null());
if (!intermediates) {
// MakeStatus is now preferred. But we don't support it in the exported
// version, yet. So, ignore lint here.
// NOLINTNEXTLINE
return Status(error::INTERNAL,
"Failed to allocate X.509 intermediate certificate stack");
}
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
sk_X509_push(intermediates.get(),
const_cast<X509*>(cert_chain.GetCert(idx)->openssl_cert()));
}
return OpenSslX509Verify(cert.openssl_cert(), intermediates.get());
}
Status X509CA::OpenSslX509Verify(const X509* cert,
STACK_OF(X509) * intermediates) {
DCHECK(cert);
absl::ReaderMutexLock lock(&openssl_store_mutex_);
if (openssl_store_ == NULL) {
openssl_store_mutex_.ReaderUnlock();
Status status = InitializeStore();
if (!status.ok()) {
return status;
}
openssl_store_mutex_.ReaderLock();
}
ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new());
if (!store_ctx) {
return Status(error::INTERNAL, "Failed to allocate X.509 store context");
}
if (X509_STORE_CTX_init(store_ctx.get(), openssl_store_,
const_cast<X509*>(cert), intermediates) == 0) {
return Status(error::INTERNAL, "Failed to initialize X.509 store context");
}
int x509_status = X509_verify_cert(store_ctx.get());
if (x509_status != 1) {
return Status(error::INTERNAL,
std::string("X.509 certificate chain validation failed: ") +
X509_verify_cert_error_string(
X509_STORE_CTX_get_error(store_ctx.get())));
}
return OkStatus();
}
} // namespace widevine

176
common/x509_cert.h Normal file
View File

@@ -0,0 +1,176 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// X.509 certificate classes used by the license server SDK.
#ifndef COMMON_X509_CERT_H_
#define COMMON_X509_CERT_H_
#include <stddef.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "openssl/pem.h"
#include "openssl/x509.h"
#include "openssl/x509v3.h"
#include "common/openssl_util.h"
#include "common/rsa_key.h"
#include "common/status.h"
namespace widevine {
// NOTE: All Status codes are in the canonical error space.
// Class which holds a single X.509 certificates.
class X509Cert {
public:
// Load the certificate from an openssl X509 certificate instance.
static std::unique_ptr<X509Cert> FromOpenSslCert(ScopedX509 openssl_cert_);
X509Cert();
virtual ~X509Cert();
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
// a PEM-encoded certificate.
Status LoadPem(const std::string& pem_cert);
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
// a DER-encoded certificate.
Status LoadDer(const std::string& der_cert);
// Return a std::string containing the PEM-encoded certificate.
std::string GetPem() const;
// Returns certificate RSA public key. nullptr if not found, or if key type
// is not RSA.
std::unique_ptr<RsaPublicKey> GetRsaPublicKey() const;
// Returns the internal OpenSSL X509 certificate.
const X509* openssl_cert() const { return openssl_cert_; }
// Returns the certificate subject name.
const std::string& GetSubjectName();
// Returns a field within the certificate subject name, or an empty std::string
// if the field is not found.
std::string GetSubjectNameField(const std::string& field);
// Returns the certificate serial number, binary encoded, or an empty std::string
// if an error occurs.
std::string GetSerialNumber() const;
// Gets the start of the validity period for the certificate in seconds
// since the epoch. |valid_start_seconds| must not be null. Returns true on
// success, false otherwise.
bool GetNotBeforeSeconds(int64_t* valid_start_seconds) const;
// Gets the end of the validity period for the certificate in seconds
// since the epoch. |valid_end_seconds| must not be null. Returns true on
// success, false otherwise.
bool GetNotAfterSeconds(int64_t* valid_end_seconds) const;
// Returns true if the certificate is a CA (root or intermediate) certificate.
bool IsCaCertificate() const;
// Gets the value of an X.509 V3 extension encoded as a boolean. |oid| is the
// object identifier sequence for the extension, and |value| is a pointer to
// an integer which will contain the extension value upon successful return.
// Returns true if successful, or false otherwise.
bool GetV3BooleanExtension(const std::string& oid, bool* value) const;
private:
explicit X509Cert(X509* openssl_cert);
Status Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
int64_t* epoch_seconds) const;
X509* openssl_cert_;
std::string subject_name_;
friend class X509CertChain;
DISALLOW_COPY_AND_ASSIGN(X509Cert);
};
// Class which holds a chain of X.509 certificates.
class X509CertChain {
public:
X509CertChain() {}
virtual ~X509CertChain();
// Loads a chain of PEM-encoded X.509 certificates. Takes a single parameter,
// |pem_cert_chain|, which is the concatenation of a number of PEM X.509
// certificates, beginning with the leaf certificate, and ending with the
// certificate signed by the root CA.
Status LoadPem(const std::string& pem_cert_chain);
// Loads a chain of DER-encoded PKCS#7 certificates. Takes a single parameter,
// |pk7_cert_chain|, which is a DER-encoded PKCS#7 X.509 certificate
// container.
Status LoadPkcs7(const std::string& pk7_cert_chain);
// Writes the |cert_chain_| to a DER-encoded PKCS#7 X.509 cryptographic
// message. The final message does not include signed data.
std::string GetPkcs7();
// Returns the number of certificates in the chain.
size_t GetNumCerts() const { return cert_chain_.size(); }
// Returns the X509Cert at the specified chain index (0 start). Returns
// NULL if cert_index is out of range. The X509CertChain retains ownwership
// of the object returned.
X509Cert* GetCert(size_t cert_index) const;
private:
void Reset();
std::vector<X509Cert*> cert_chain_;
DISALLOW_COPY_AND_ASSIGN(X509CertChain);
};
// CA class which holds the root CA cert, and verifies certificate chains.
class X509CA {
public:
// New object assumes ownership of |ca_cert|.
explicit X509CA(X509Cert* ca_cert);
virtual ~X509CA();
// Does X.509 PKI validation of |cert| against the root CA certificate
// used when constructing X509CA. This method is thread-safe.
Status VerifyCert(const X509Cert& cert);
// Does X.509 PKI validation of |cert_chain| against the root CA certificate
// used when constructing X509CA. This method is thread-safe.
Status VerifyCertChain(const X509CertChain& cert_chain);
// Does X.509 PKI validation of |cert| using the |cert_chain|
// certificates. This method allows |cert| to be an ICA. This method is
// thread-safe.
Status VerifyCertWithChain(const X509Cert& cert,
const X509CertChain& cert_chain);
private:
Status InitializeStore();
Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * intermediates);
std::unique_ptr<X509Cert> 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_

531
common/x509_cert_test.cc Normal file
View File

@@ -0,0 +1,531 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2017 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include <memory>
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "common/rsa_key.h"
#include "common/test_utils.h"
#include "common/x509_cert.h"
namespace widevine {
const char kTestRootCaDerCert[] =
"30820403308202eba003020102020900a24f94af7ae6831f300d06092a86"
"4886f70d0101050500308197310b30090603550406130255533113301106"
"035504080c0a57617368696e67746f6e3111300f06035504070c084b6972"
"6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f"
"060355040b0c085769646576696e653115301306035504030c0c54657374"
"20526f6f742043413121301f06092a864886f70d010901161274696e736b"
"697040676f6f676c652e636f6d301e170d3133303831363030353731305a"
"170d3333303831353030353731305a308197310b30090603550406130255"
"533113301106035504080c0a57617368696e67746f6e3111300f06035504"
"070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049"
"6e633111300f060355040b0c085769646576696e65311530130603550403"
"0c0c5465737420526f6f742043413121301f06092a864886f70d01090116"
"1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648"
"86f70d01010105000382010f003082010a0282010100c6eee629d99f7736"
"2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c"
"e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319"
"0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3"
"f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93"
"52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7"
"e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f"
"d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20"
"79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7"
"3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30"
"ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680"
"144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405"
"30030101ff300d06092a864886f70d01010505000382010100779e9b98d3"
"ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee"
"79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124"
"ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436"
"026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140"
"ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053"
"0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258"
"e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82"
"d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4"
"c270d87191c13aefdd177b";
const char kTestRootCaPemCert[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIEAzCCAuugAwIBAgIJAKJPlK965oMfMA0GCSqGSIb3DQEBBQUAMIGXMQswCQYD\n"
"VQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQx\n"
"EzARBgNVBAoMCkdvb2dsZSBJbmMxETAPBgNVBAsMCFdpZGV2aW5lMRUwEwYDVQQD\n"
"DAxUZXN0IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNv\n"
"bTAeFw0xMzA4MTYwMDU3MTBaFw0zMzA4MTUwMDU3MTBaMIGXMQswCQYDVQQGEwJV\n"
"UzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxEzARBgNV\n"
"BAoMCkdvb2dsZSBJbmMxETAPBgNVBAsMCFdpZGV2aW5lMRUwEwYDVQQDDAxUZXN0\n"
"IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n"
"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbu5inZn3c2LbVUXtHW37NhbHQs\n"
"YX1f1I8vv8s/LsQKCAvQTVUc5RlHGou07Fwsdb+KLSyvP4XZDp45OR372q5oBRMZ\n"
"DacbGyrkgpoVxEvBsZsXE0hEuUxvBtkhYzMjZXTz8RsNEMPGIUEOQmMMV86ekBBX\n"
"7aXDwiA+4q2AWg2TUvqR2kWm9IdbRSTBk8Qv2QSKECBOWyyCA0Arp2Dn4bQSbD4q\n"
"tCWPK/KM0xcN6Mc4pqH0z8wGSfqV8UFP2dCd1PURvAqb86WESjNNngpLlSXSeJvm\n"
"q6/i0MwgedzwMP+pvorj/iyrTr36SU1IqoxjJk0x4iCKnCj3PgEDzhZGg78CAwEA\n"
"AaNQME4wHQYDVR0OBBYEFE0w/xgaxPENqZ5qEsAeAqzK34QKMB8GA1UdIwQYMBaA\n"
"FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n"
"BQADggEBAHeem5jT7AZvKYYpA6AOnJglnZh8BLnmoubDOB7lnsHdDX3uedphLk36\n"
"o0ZciRaZPtet67JzQN4gyhAQZ/g0KyEk7A1dtTEne0ZTw7xysqja6uEg5TSOGjOP\n"
"bmjnEpQ2Am54Ak8E12axMiUuwVJALc7CgXQ0aqC6mX1/GvFA/wJb7IQfgDm6ENfM\n"
"CYzyRVT4y7KqMYdSBcZ98vBTDYeE+vY8T5ReYto3TK1hVeauRPWXvP9FZuoqrEJY\n"
"5K6BVpwO3dHfaSlTK0U4vSBLL/WEfLRqxzg8lv6C0i3poTxQksksKXAhxRoqClJQ\n"
"zybCcf8mLyWnc4rkwnDYcZHBOu/dF3s=\n"
"-----END CERTIFICATE-----\n";
const char kTestPemCert[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n"
"VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n"
"Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n"
"ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n"
"MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n"
"BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n"
"aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n"
"Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n"
"aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n"
"DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n"
"l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n"
"krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n"
"wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n"
"fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n"
"FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n"
"qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n"
"EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n"
"f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n"
"QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n"
"7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n"
"3rDeUI+Odg==\n"
"-----END CERTIFICATE-----\n";
const char kTestPemCertSubjectField_C[] = "US";
const char kTestPemCertSubjectField_CN[] =
"stable id/emailAddress=tinskip@google.com";
const char kTestPemCertSerialNumber[] = "\002";
const int64_t kTestPemCertNotBeforeSeconds = 1376689440;
const int64_t kTestPemCertNotAfterSeconds = 2007755040;
const char kTestPemCertChain[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n"
"VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n"
"Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n"
"ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n"
"MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n"
"BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n"
"aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n"
"Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n"
"aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n"
"DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n"
"l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n"
"krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n"
"wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n"
"fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n"
"FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n"
"qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n"
"EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n"
"f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n"
"QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n"
"7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n"
"3rDeUI+Odg==\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n"
"EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n"
"DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n"
"b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n"
"ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n"
"BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n"
"bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n"
"ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n"
"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n"
"f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n"
"oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n"
"UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n"
"gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n"
"9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n"
"AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n"
"FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n"
"BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n"
"xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n"
"HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n"
"ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n"
"qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n"
"6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n"
"-----END CERTIFICATE-----\n";
const char kTestPemIca[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n"
"EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n"
"DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n"
"b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n"
"ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n"
"BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n"
"bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n"
"ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n"
"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n"
"f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n"
"oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n"
"UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n"
"gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n"
"9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n"
"AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n"
"FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n"
"BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n"
"xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n"
"HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n"
"ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n"
"qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n"
"6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n"
"-----END CERTIFICATE-----\n";
const char kTestPk7CertChain[] =
"308207fb06092a864886f70d010702a08207ec308207e80201013100300b"
"06092a864886f70d010701a08207ce308203c3308202ab020102300d0609"
"2a864886f70d010105050030819f310b3009060355040613025553311330"
"1106035504080c0a57617368696e67746f6e3111300f06035504070c084b"
"69726b6c616e6431133011060355040a0c0a476f6f676c6520496e633111"
"300f060355040b0c085769646576696e65311d301b06035504030c145465"
"737420496e7465726d6564696174652043413121301f06092a864886f70d"
"010901161274696e736b697040676f6f676c652e636f6d301e170d313330"
"3831363231343430305a170d3333303831353231343430305a3081ae310b"
"30090603550406130255533113301106035504080c0a57617368696e6774"
"6f6e3111300f06035504070c084b69726b6c616e6431293027060355040a"
"0c204368726f6d652044657669636520436f6e74656e742050726f746563"
"74696f6e31153013060355040b0c0c74657374696e672e74657374311230"
"1006035504030c09737461626c652069643121301f06092a864886f70d01"
"0901161274696e736b697040676f6f676c652e636f6d30820122300d0609"
"2a864886f70d01010105000382010f003082010a0282010100a95b0eab2d"
"38ad1394bb4964e1b3cac8c3e3c657127a70b62d1c4002ba25c37d86dc29"
"1de98197301b22e3888de636b5fbe4976b8838e9cd3200222acfc3a6ef55"
"6de7570150a7bb113bff2ad6230feb835666a9dc505c5a93a3ee7612bb5e"
"32bbb892b39a36043add213ddf8c91d0c485cee49bbd381314b3fb847b9e"
"69e83cf8252f4115345a80b66a4317cd8d46e80915c3263f96e7c0fd7110"
"5e03db86f3faed8c51f9ec7009fcd01a8941e6192588abb1d2b9b30df708"
"88730cd080da13a9ee7db9bc04a3f19594bf42bcd3640afd43918cca372e"
"d174c8d5f927195ac4e2964c87c81efa89dd720210c5704e85f24a142a1f"
"6123fbac1c6d6ac2bee2770203010001300d06092a864886f70d01010505"
"0003820101004dc35f68ba5f2db5faab3d6a28c2d868d7b8388d17f2df19"
"357a8476ac8d7780bb91275a424c0dba7540ab0d4269d5332d18bc39fd7c"
"73095011704de1b3d378e0c2a1b3e34bbd587183e10ecbc641c4371900ba"
"04ec872824d8a6a81c2083c4185cc8f85ac009e75e7f8b7221ec6aeb7888"
"c75229993067a589e77e073cbf86a7a6134e0c728c4411f2f0125170aa53"
"145168e7bd5ce4e8c442384a10ea7d783ef61ae120b4ad1fee35d81f66d1"
"4f896648bc60d8bd636f0837a888e85f68237882f2cd417e153cceec6e49"
"5909f20e377f649ba577b151b099aecc080849ea957ab6985d4d69e2eb5b"
"aaeb404e698705c62191724a04a91fdeb0de508f8e7630820403308202eb"
"a003020102020101300d06092a864886f70d0101050500308197310b3009"
"0603550406130255533113301106035504080c0a57617368696e67746f6e"
"3111300f06035504070c084b69726b6c616e6431133011060355040a0c0a"
"476f6f676c6520496e633111300f060355040b0c085769646576696e6531"
"15301306035504030c0c5465737420526f6f742043413121301f06092a86"
"4886f70d010901161274696e736b697040676f6f676c652e636f6d301e17"
"0d3133303831363231343134365a170d3333303831353231343134365a30"
"819f310b30090603550406130255533113301106035504080c0a57617368"
"696e67746f6e3111300f06035504070c084b69726b6c616e643113301106"
"0355040a0c0a476f6f676c6520496e633111300f060355040b0c08576964"
"6576696e65311d301b06035504030c145465737420496e7465726d656469"
"6174652043413121301f06092a864886f70d010901161274696e736b6970"
"40676f6f676c652e636f6d30820122300d06092a864886f70d0101010500"
"0382010f003082010a0282010100da28062eb1dc8f489dcb3acad94f8c1b"
"bbc84cb97fa04b7f9351104e67426ef846decf000ed8543b711cffa98b5b"
"b2c6c2f77d4af1f74e2ce2d7573512bce7fdb39fbfa8a193d86fc7c7e9d8"
"25ee872661ec82d234a683bfbe662692e8eb233d25131366846c07ba43e6"
"6162c01b7642fb67674d50947d6a484b12a96a7d7e2ae5b580d0cdcfff6f"
"dfa8d60c4b4c34d74370a5df8ac9d6dc793d2b1d04e64c656ca8208b8266"
"8ff5af1a4113123f4767e4839c73f2b54279dfc358319e3185e1a04feca6"
"c2331341adb8013b0893c6010c21c299f481accc07297ed297e6b6b75de3"
"f315609c9f08a5a6969a13fd9759791c01e85cf1ddf1b64740619c952015"
"0203010001a350304e301d0603551d0e0416041439b7e60cccea5975dc78"
"5951b5b9b83c3b062793301f0603551d230418301680144d30ff181ac4f1"
"0da99e6a12c01e02accadf840a300c0603551d13040530030101ff300d06"
"092a864886f70d01010505000382010100b8fefd9f3281f595367715d0df"
"54b0073094651ee4c13e5685d417fd123a676b73c2ed3dcd8391cf1bc624"
"0dd701f113f385b1bb0e7686cfcf038187aecac98c955adc73cec2ced0c1"
"c6c6c6ce9b06b501eaeca0fe31f31a021da054f7472f898ab3e64eab6720"
"4894522b33996a1b5241dd4d244dc786ff5b2ac11000bbde0162a316d625"
"b2afd32a0124425413ab92c59d6b8a599022e9eb6be4ec8b4e72cd8fac77"
"fa5166c03530c7fbc43ea48c36c2e27995ffe29aa28eaab602266022e950"
"5ada3c87a9754f324ea52f5466764e1480eb11bec6e62b36f6bd87962308"
"6c5d8e52a68dd65532efea42241990853ffc2cedcb64f9f4328e3b6d9dc2"
"1d2f69a1003100";
const char kTestCertPrivateKey[] =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEowIBAAKCAQEAqVsOqy04rROUu0lk4bPKyMPjxlcSenC2LRxAArolw32G3Ckd\n"
"6YGXMBsi44iN5ja1++SXa4g46c0yACIqz8Om71Vt51cBUKe7ETv/KtYjD+uDVmap\n"
"3FBcWpOj7nYSu14yu7iSs5o2BDrdIT3fjJHQxIXO5Ju9OBMUs/uEe55p6Dz4JS9B\n"
"FTRagLZqQxfNjUboCRXDJj+W58D9cRBeA9uG8/rtjFH57HAJ/NAaiUHmGSWIq7HS\n"
"ubMN9wiIcwzQgNoTqe59ubwEo/GVlL9CvNNkCv1DkYzKNy7RdMjV+ScZWsTilkyH\n"
"yB76id1yAhDFcE6F8koUKh9hI/usHG1qwr7idwIDAQABAoIBADdwlZa30QvnkxLU\n"
"be/s+X9LkS8GpgfrCdgunU3HPkGGwDUmSKJ+R835tCwkMb+hPWXeaStMhsUS5UFh\n"
"7f3hoK5MmxPWSZnrrrNvnpKZUxUNFgucxBJZREJqfom7oVow9g6511xwKSqtUmJl\n"
"bN8JhPwwiZAQ45qNtINO3QnSy/y4IGrUPgjMpmJa26a+JhduTRq+LMPu2wz+HxS1\n"
"Vf2q0H1IOJr/kimMFMaBRYErNclFa8VIFjwjz5reH5lJyptajGhruor6EK1qqhNc\n"
"zPSRY4TZH5QcjM46zui6l3tL9e32j6oUd4mAp4HhH0fws/pwawFYECI+M+7OCjgK\n"
"y+qSJ1ECgYEA1g+L0yN4i+uScs7EpsYJfaRP1PMtGnUsof64Pg6i9IKcuf5mi5Kp\n"
"aIgZdXAZIzsACH5XbfuC5Srs4565k/9XrHehLcuBzodulrzwmOUDbJAxIDw4uTUX\n"
"95W0uK9UqyGLyM8wNYs/EzhveSFL8fnFWzOAL/+HshQpKCBzedSU+G0CgYEAyolH\n"
"xws2mim7rSrYyRz1Vj02rLZuBUR7cPaHDxjjuuSUbI2nsDRsm6ZUCNlJtReHBkpH\n"
"eW5iClBGkksVsJJYJBmyDw6a3mnj0mfxBnh9zGaHQi0RCuOwmYlu2L/XVQXiMFKT\n"
"gffazuvysg7N/bz7CJjm8PRRx/cAxxFfAozdf/MCgYEAtBagLCHLaOvnaW9LQoOZ\n"
"uHpkL2PmrjumMSN7HbpyngLEmDXPT90zaR4XTRXiECGzBXJFW+IdXW+fnGANANXx\n"
"jMeYck6kBn0qLOcIA5moJ82nhtcjYa2pXEI2qKnZMaAnWen1RRbBGgqAvgelPQ5F\n"
"W1UYo0j3gHo1peynOff+3IECgYAsP53M4KhHOgLkrE28cnUvKCR/y0NyJyoI3fNX\n"
"2wo11KaQqMoP9wQbZVVKsZ4m0EMRnrzKzNDii/M/FuRgNTjIekyqeXhgSyYY29iO\n"
"n1hshaHbVVk51dDJWns7I3559tUZ1ZCgfnPxbR8Sw6VBYD4//JfH4LjVRSOIWkU1\n"
"m2zw/QKBgGE55o0xrCywF3wDUtFa6vgpsOfZu9IblsWktSbD/lk1YOqGpU//B0O4\n"
"GqihOQT7E9kDNusspFUGpZrE0T0B+GW1T9iTR0zd+lC+qExv2ggDJoH063DnH5OU\n"
"Qz2M8LESeFxf6ZlBxkcyrk6G1RAy7lUs9fHhfmpEJLVv4DTCuWDl\n"
"-----END RSA PRIVATE KEY-----\n";
const char kTestMessage[] =
"c8635a17ccc672c941d0cc287715411a0a0222613a04d47693a53eb7f32c"
"1ebae1f5d916a815b880426362c42f5f18f694a380756e0452018c70b3e4"
"f72ebb5269cb7233a3b8a2a1840e33ca9d473224d17ff91bae6b8d4ff2d1"
"8e5c89b5fc8a52c4f791c2063ab1a29ffd3372db483e4975c1c9c7408bf6"
"dfe5696e256e86b75313c501ab781175971b9411a73c444592afb1ec1667"
"2bfb935715ef5302f3bef712d2296be4f64ef2dc861f0611b06c35d0a5c2"
"5ff9f4a2563f265f109d2fa8f8165d7891b8a83c84520eaa284d49a4f76e"
"ac158204a5bdf018edd9401ae6593092ba97970be9a58b10720a235c9158"
"b9f235f9dda3de05990cef8c2fd04920a2a434bd5b6aa75767762d89b964"
"90e42524855a7eab49a8f82ac593e4df01990206d3fa98329aa50e31db89"
"b46b82ee0073851826f77aabb3779738a6f311b79f54d036a98dca4881ef"
"88c3cbfc86ac358c7bd107dc234d3772fc707df01637354dcb9270c7aefa"
"852dd21818ede33ab7154c32f25268b82f89b344e6469b81b6699df68c56"
"a6e61f1dd8f140f3be4edce755ceee8ee7868f45a17f8b4b4b0988f45815"
"1b43d07dcb0cd80b1ffa37b824e0abc25897cb41c242a3db845bedd37adf"
"88a13c0b2f0b158464b02f9fd97ad6e87b92c13cbeee5e69d183cc898c4e"
"0cfa9c59abde74a437d030cb966137ffe9abe6be71ed21ef751cdea73625"
"7cff9e378718f7d7e9c4d567cbec8e0afdfab0585b8ed0d5f8de159b6524"
"22c90737b44c84603ba1131f557604fe4e6b4d91e45363903b8db179cee0"
"a50f2ae73394973c8671df7a7b2eeb8341a3417727cfe43290a67ac3ad02"
"a52c3d1698c2c28a46268518aea66cecb40f43f50bb9cea4ed1d49ceb51d"
"9967fabccccc7237a36b6cecda5916234730d7b3ca3295519d77b7516824"
"10e8a238b6345e8d28132f60423a13fdf4b6a6cf272cef9a0833abb4b86d"
"9828af45442a390e241b2b8c3290671da4a163d7e55fea7828098c0749ca"
"ff65145dd6b4a6e4c65d214801bb8302d8914864e99c4d0b390b8126d4bc"
"0353e376e69aba56cf71b9943a47dcffa07c6a24986a077f69b7bec6bd9c"
"357e211875453bdadd9bfc4526f96c458e0052d27a903611c09a9c7b5f51"
"83daad078aec0e79ef991d102d4af492773f1509a265c5644cbab3253e34"
"3015e4305fffd17ce0261bcb232cfa0e1dcc71f83dc1aac490e526f6269f"
"606d0e0e556bb30b774c2208ed3771474be23f39b7fc21dcbf304a923d9c";
const char kTestDevCodeSigningCert[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIFDjCCA3agAwIBAgIPESIzRFVmd4iZqrvM3e7/MA0GCSqGSIb3DQEBCwUAMIGc\n"
"MQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2ly\n"
"a2xhbmQxDzANBgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNV\n"
"BAMMFXdpZGV2aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tp\n"
"cEBnb29nbGUuY29tMB4XDTE3MTAwOTIwMjUwNloXDTI3MTAwNzIwMjUwNlowRTEL\n"
"MAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy\n"
"bmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
"ggEBAObsg/w+dJedH3x5KEsXdA/5sunWc8G+iZl0wMcngh2DiwmOSkKf68uCK/iW\n"
"T0a2XGgk13zl1HuKrjatgc7n6E1j/sqDZBGkr0q1wQgsdzm3qvGZoDG/+Z2U23WU\n"
"kX6ZcyIYUbpO2VtQELEl6DgNwoUi/9Yp+vCb6lsItpSZ1WRD9NhbWh1MxZxj1s18\n"
"OYcEzpEYg4/vHTVhocUR/1Rp9M9yn0nH1MUdtjhgBM3BmlRH7TA/nF111A4+GzMN\n"
"qyqfb0/6yXE64Ca3+fGg1hstfUUXkpmjjNPhYJ6QTgA3Xfrz04a4uwB+pSliF3SD\n"
"gip7O3rDyK0ES55lGpZ7B3s3TakCAwEAAaOCASEwggEdMB0GA1UdDgQWBBQ2jJme\n"
"0BuaGrhgFGJR2i59HR+DizCBuwYDVR0jBIGzMIGwoYGipIGfMIGcMQswCQYDVQQG\n"
"EwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxDzAN\n"
"BgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNVBAMMFXdpZGV2\n"
"aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tpcEBnb29nbGUu\n"
"Y29tggkAxfgvA4+s8VgwCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAww\n"
"CgYIKwYBBQUHAwMwEQYKKwYBBAHWeQQBAgQDAQH/MA0GCSqGSIb3DQEBCwUAA4IB\n"
"gQAtan04ZGie7rRsKpb1F6t7xs48KE6cj6L99B5dgl37fZaZIQ3XE2vbmmmY5YTx\n"
"wofCkvOZMXHeQfJEK5GIK49TW/lAR+3kJUJzSh+N67f0X8O1pUl97IUFsbi6PTw/\n"
"mjhu197Kdy/OxPu/csOkEChuOfJLagRxXtIXeIyaeVOmn6fkFTOMOL2BusWOPuIs\n"
"9OmOQ+UHXpMuX4c2x9iO4NzZwwI/MgULLCrd/c73q199H+ttdPFoNs8+xGdodqA/\n"
"NFlHtMHMLMKVGpazAf+JW1/c3nb8L3S0nw4q7vPWi216RdZTfKfSIs/f/IW3CYJh\n"
"/IAuHOYvlD0GdSOFZHfhrnAvKhJ2iRu32psN87L9rL5EL22LT8csV/gLMc3SZ35n\n"
"/viuYcTDnMbe9S/Mge3mMJ9XHD5XBhN3hzmGDQEUdRS5MXrYdY32viPE7f+GAO9s\n"
"5MXS+h+FxQ6QUar2q1zHc/0Gr1hLzA6HYBmI0/AF8LsHs799XjrMKHkSBN6UQkC1\n"
"hRk=\n"
"-----END CERTIFICATE-----\n";
const char kDevCertFlagOid[] = "1.3.6.1.4.1.11129.4.1.2";
const bool kTestDevCodeSigningCertFlagValue = true;
TEST(X509CertTest, LoadCert) {
X509Cert test_cert;
EXPECT_EQ(OkStatus(),
test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
EXPECT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
// TODO(user): Add more specific status checks to failure tests.
EXPECT_NE(OkStatus(), test_cert.LoadDer("bad cert"));
EXPECT_NE(OkStatus(), test_cert.LoadPem("bad cert"));
EXPECT_NE(OkStatus(), test_cert.LoadDer(""));
EXPECT_NE(OkStatus(), test_cert.LoadPem(""));
}
TEST(X509CertTest, VerifySignature) {
X509Cert test_cert;
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
std::string message(absl::HexStringToBytes(kTestMessage));
std::string signature;
ASSERT_EQ(OkStatus(), GenerateRsaSignatureSha256Pkcs1(kTestCertPrivateKey,
message, &signature));
std::unique_ptr<RsaPublicKey> pub_key(test_cert.GetRsaPublicKey());
ASSERT_TRUE(pub_key);
EXPECT_TRUE(pub_key->VerifySignatureSha256Pkcs7(message, signature));
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, "bad signature"));
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("bad digest", signature));
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, ""));
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("", signature));
}
TEST(X509CertTest, GetSubjectNameField) {
X509Cert test_cert;
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
EXPECT_EQ(kTestPemCertSubjectField_C, test_cert.GetSubjectNameField("C"));
EXPECT_EQ(kTestPemCertSubjectField_CN, test_cert.GetSubjectNameField("CN"));
EXPECT_EQ("", test_cert.GetSubjectNameField("invalid_field"));
}
TEST(X509CertTest, GetSerialNumber) {
X509Cert test_cert;
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber());
}
TEST(X509CertTest, GetNotBeforeSeconds) {
X509Cert test_cert;
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
int64_t not_before_seconds = 0;
ASSERT_TRUE(test_cert.GetNotBeforeSeconds(&not_before_seconds));
EXPECT_EQ(kTestPemCertNotBeforeSeconds, not_before_seconds);
}
TEST(X509CertTest, GetNotAfterSeconds) {
X509Cert test_cert;
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
int64_t not_after_seconds = 0;
ASSERT_TRUE(test_cert.GetNotAfterSeconds(&not_after_seconds));
EXPECT_EQ(kTestPemCertNotAfterSeconds, not_after_seconds);
}
TEST(X509CertTest, CertChain) {
X509CertChain test_chain;
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
ASSERT_EQ(2, test_chain.GetNumCerts());
EXPECT_FALSE(test_chain.GetCert(0) == NULL);
EXPECT_FALSE(test_chain.GetCert(1) == NULL);
EXPECT_TRUE(test_chain.GetCert(2) == NULL);
}
TEST(X509CertTest, IsCaCertificate) {
X509CertChain test_chain;
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
ASSERT_EQ(2, test_chain.GetNumCerts());
EXPECT_FALSE(test_chain.GetCert(0)->IsCaCertificate());
EXPECT_TRUE(test_chain.GetCert(1)->IsCaCertificate());
}
TEST(X509CertTest, ChainVerificationPem) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
ASSERT_EQ(OkStatus(),
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
X509CA ca(ca_cert.release());
X509CertChain test_chain;
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert));
ASSERT_EQ(1, test_chain.GetNumCerts());
EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain));
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
}
TEST(X509CertTest, ChainVerificationPkcs7) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
ASSERT_EQ(OkStatus(),
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
X509CA ca(ca_cert.release());
X509CertChain test_chain;
ASSERT_EQ(OkStatus(),
test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain)));
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert));
ASSERT_EQ(1, test_chain.GetNumCerts());
EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain));
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
}
TEST(X509CertTest, VerifyCertWithChainIca) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
X509CA ca(ca_cert.release());
// Verify the ICA with the root succeeds.
X509CertChain test_chain;
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
ASSERT_EQ(1, test_chain.GetNumCerts());
X509Cert ica_cert;
ASSERT_EQ(OkStatus(), ica_cert.LoadPem(kTestPemIca));
EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(ica_cert, test_chain));
}
TEST(X509CertTest, VerifyCertWithChainLeaf) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
X509CA ca(ca_cert.release());
// Verify the leaf with the root and ICA succeeds.
X509CertChain test_chain;
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemIca));
ASSERT_EQ(1, test_chain.GetNumCerts());
X509Cert leaf_cert;
ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert));
EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
}
TEST(X509CertTest, VerifyCertWithChainLeafMissincIca) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
X509CA ca(ca_cert.release());
// Verify the leaf with only the root fails (ICA missing).
X509CertChain test_chain;
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
ASSERT_EQ(1, test_chain.GetNumCerts());
X509Cert leaf_cert;
ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert));
EXPECT_NE(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
}
TEST(X509CertTest, GetPkcs7) {
X509CertChain test_chain;
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
std::string pkcs7_certificate = test_chain.GetPkcs7();
ASSERT_NE(pkcs7_certificate.size(), 0);
X509CertChain new_test_chain;
ASSERT_EQ(OkStatus(), new_test_chain.LoadPkcs7(pkcs7_certificate));
ASSERT_EQ(test_chain.GetNumCerts(), new_test_chain.GetNumCerts());
for (int i = 0; i < test_chain.GetNumCerts(); i++) {
ASSERT_EQ(test_chain.GetCert(i)->GetPem(),
new_test_chain.GetCert(i)->GetPem());
}
}
TEST(X509CertTest, BooleanExtension) {
std::unique_ptr<X509Cert> cert1(new X509Cert);
ASSERT_EQ(OkStatus(), cert1->LoadPem(kTestPemCert));
bool extension_value;
EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
std::unique_ptr<X509Cert> cert2(new X509Cert);
ASSERT_EQ(OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert));
ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value);
}
} // namespace widevine

View File

@@ -1,5 +1,5 @@
################################################################################
# Copyright 2016 Google Inc.
# Copyright 2016 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More