Export provisioning sdk
Change-Id: I4d47d80444c9507f84896767dc676112ca11e901
This commit is contained in:
4
LICENSE
Normal file
4
LICENSE
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Copyright 2017 Google, Inc.
|
||||||
|
|
||||||
|
This software is licensed under the terms defined in the Widevine Master License Agreement.
|
||||||
|
For a copy of this agreement, please contact widevine-licensing@google.com.
|
||||||
77
WORKSPACE
Normal file
77
WORKSPACE
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
workspace(name = "provisioning_sdk")
|
||||||
|
|
||||||
|
git_repository(
|
||||||
|
name = "protobuf_repo",
|
||||||
|
remote = "https://github.com/google/protobuf.git",
|
||||||
|
tag = "v3.0.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
git_repository(
|
||||||
|
name = "boringssl_repo",
|
||||||
|
commit = "bbcaa15b0647816b9a1a9b9e0d209cd6712f0105", # 2016-07-11
|
||||||
|
remote = "https://github.com/google/boringssl.git",
|
||||||
|
)
|
||||||
|
|
||||||
|
git_repository(
|
||||||
|
name = "gflags_repo",
|
||||||
|
commit = "fe57e5af4db74ab298523f06d2c43aa895ba9f98", # 2016-07-20
|
||||||
|
remote = "https://github.com/gflags/gflags.git",
|
||||||
|
)
|
||||||
|
|
||||||
|
new_git_repository(
|
||||||
|
name = "googletest_repo",
|
||||||
|
build_file = "gtest.BUILD",
|
||||||
|
commit = "ec44c6c1675c25b9827aacd08c02433cccde7780", # 2016-07-26
|
||||||
|
remote = "https://github.com/google/googletest.git",
|
||||||
|
)
|
||||||
|
|
||||||
|
new_git_repository(
|
||||||
|
name = "glog_repo",
|
||||||
|
build_file = "glog.BUILD",
|
||||||
|
commit = "0472b91c5defdf90cff7292e3bf7bd86770a9a0a", # 2016-07-13
|
||||||
|
remote = "https://github.com/google/glog.git",
|
||||||
|
)
|
||||||
|
|
||||||
|
# py_proto_library depends on six indirectly (py_proto_library uses
|
||||||
|
# protobuf_python as its default runtime which depends on six).
|
||||||
|
new_http_archive(
|
||||||
|
name = "six_archive",
|
||||||
|
build_file = "@protobuf_repo//:six.BUILD",
|
||||||
|
sha256 = "105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a",
|
||||||
|
url = "https://pypi.python.org/packages/source/s/six/six-1.10.0.tar.gz#md5=34eed507548117b2ab523ab14b2f8b55",
|
||||||
|
)
|
||||||
|
|
||||||
|
bind(
|
||||||
|
name = "six",
|
||||||
|
actual = "@six_archive//:six",
|
||||||
|
)
|
||||||
|
|
||||||
|
bind(
|
||||||
|
name = "protobuf",
|
||||||
|
actual = "@protobuf_repo//:protobuf",
|
||||||
|
)
|
||||||
|
|
||||||
|
bind(
|
||||||
|
name = "openssl",
|
||||||
|
actual = "@boringssl_repo//:crypto",
|
||||||
|
)
|
||||||
|
|
||||||
|
bind(
|
||||||
|
name = "gflags",
|
||||||
|
actual = "@gflags_repo//:gflags",
|
||||||
|
)
|
||||||
|
|
||||||
|
bind(
|
||||||
|
name = "gtest",
|
||||||
|
actual = "@googletest_repo//:gtest",
|
||||||
|
)
|
||||||
|
|
||||||
|
bind(
|
||||||
|
name = "gtest_main",
|
||||||
|
actual = "@googletest_repo//:gtest_main",
|
||||||
|
)
|
||||||
|
|
||||||
|
bind(
|
||||||
|
name = "glog",
|
||||||
|
actual = "@glog_repo//:glog",
|
||||||
|
)
|
||||||
24
base/BUILD
Normal file
24
base/BUILD
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
package(
|
||||||
|
default_visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "base",
|
||||||
|
hdrs = [
|
||||||
|
"macros.h",
|
||||||
|
"mutex.h",
|
||||||
|
"thread_annotations.h",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//external:gflags",
|
||||||
|
"//external:glog",
|
||||||
|
],
|
||||||
|
)
|
||||||
113
base/macros.h
Normal file
113
base/macros.h
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef BASE_MACROS_H_
|
||||||
|
#define BASE_MACROS_H_
|
||||||
|
|
||||||
|
#include <stddef.h> // For size_t
|
||||||
|
|
||||||
|
// DISALLOW_COPY_AND_ASSIGN disallows the copy constructor and copy assignment
|
||||||
|
// operator. DISALLOW_IMPLICIT_CONSTRUCTORS is like DISALLOW_COPY_AND_ASSIGN,
|
||||||
|
// but also disallows the default constructor, intended to help make a
|
||||||
|
// class uninstantiable.
|
||||||
|
//
|
||||||
|
// These must be placed in the private: declarations for a class.
|
||||||
|
//
|
||||||
|
// Note: New code should prefer static_assert over COMPILE_ASSERT.
|
||||||
|
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||||
|
TypeName(const TypeName&) = delete; \
|
||||||
|
TypeName& operator=(const TypeName&) = delete
|
||||||
|
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||||
|
TypeName() = delete; \
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||||
|
|
||||||
|
// The arraysize(arr) macro returns the # of elements in an array arr.
|
||||||
|
// The expression is a compile-time constant, and therefore can be
|
||||||
|
// used in defining new arrays, for example. If you use arraysize on
|
||||||
|
// a pointer by mistake, you will get a compile-time error.
|
||||||
|
//
|
||||||
|
// This template function declaration is used in defining arraysize.
|
||||||
|
// Note that the function doesn't need an implementation, as we only
|
||||||
|
// use its type.
|
||||||
|
template <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
|
||||||
|
|
||||||
|
#endif // BASE_MACROS_H_
|
||||||
91
base/mutex.h
Normal file
91
base/mutex.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef BASE_MUTEX_H_
|
||||||
|
#define BASE_MUTEX_H_
|
||||||
|
|
||||||
|
#include <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_
|
||||||
212
base/thread_annotations.h
Normal file
212
base/thread_annotations.h
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// This header file contains the macro definitions for thread safety
|
||||||
|
// annotations that allow the developers to document the locking policies
|
||||||
|
// of their multi-threaded code. The annotations can also help program
|
||||||
|
// analysis tools to identify potential thread safety issues.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// The annotations are implemented using GCC's "attributes" extension.
|
||||||
|
// Using the macros defined here instead of the raw GCC attributes allows
|
||||||
|
// for portability and future compatibility.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BASE_THREAD_ANNOTATIONS_H_
|
||||||
|
#define BASE_THREAD_ANNOTATIONS_H_
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && defined(__SUPPORT_TS_ANNOTATION__) && !defined(SWIG)
|
||||||
|
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
||||||
|
#else
|
||||||
|
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
|
||||||
|
// Document if a shared variable/field needs to be protected by a lock.
|
||||||
|
// GUARDED_BY allows the user to specify a particular lock that should be
|
||||||
|
// held when accessing the annotated variable, while GUARDED_VAR only
|
||||||
|
// indicates a shared variable should be guarded (by any lock). GUARDED_VAR
|
||||||
|
// is primarily used when the client cannot express the name of the lock.
|
||||||
|
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||||
|
#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded)
|
||||||
|
|
||||||
|
// Document if the memory location pointed to by a pointer should be guarded
|
||||||
|
// by a lock when dereferencing the pointer. Similar to GUARDED_VAR,
|
||||||
|
// PT_GUARDED_VAR is primarily used when the client cannot express the name
|
||||||
|
// of the lock. Note that a pointer variable to a shared memory location
|
||||||
|
// could itself be a shared variable. For example, if a shared global pointer
|
||||||
|
// q, which is guarded by mu1, points to a shared memory location that is
|
||||||
|
// guarded by mu2, q should be annotated as follows:
|
||||||
|
// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
|
||||||
|
#define PT_GUARDED_BY(x) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded_by(x))
|
||||||
|
#define PT_GUARDED_VAR \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded)
|
||||||
|
|
||||||
|
// Document the acquisition order between locks that can be held
|
||||||
|
// simultaneously by a thread. For any two locks that need to be annotated
|
||||||
|
// to establish an acquisition order, only one of them needs the annotation.
|
||||||
|
// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
|
||||||
|
// and ACQUIRED_BEFORE.)
|
||||||
|
#define ACQUIRED_AFTER(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
|
||||||
|
#define ACQUIRED_BEFORE(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
|
||||||
|
|
||||||
|
// The following three annotations document the lock requirements for
|
||||||
|
// functions/methods.
|
||||||
|
|
||||||
|
// Document if a function expects certain locks to be held before it is called
|
||||||
|
#define EXCLUSIVE_LOCKS_REQUIRED(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define SHARED_LOCKS_REQUIRED(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
|
||||||
|
|
||||||
|
// Document the locks acquired in the body of the function. These locks
|
||||||
|
// non-reentrant).
|
||||||
|
#define LOCKS_EXCLUDED(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
|
||||||
|
|
||||||
|
// Document the lock the annotated function returns without acquiring it.
|
||||||
|
#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||||
|
|
||||||
|
// Document if a class/type is a lockable type (such as the Mutex class).
|
||||||
|
#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable)
|
||||||
|
|
||||||
|
// Document if a class is a scoped lockable type (such as the MutexLock class).
|
||||||
|
#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||||
|
|
||||||
|
// The following annotations specify lock and unlock primitives.
|
||||||
|
#define EXCLUSIVE_LOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define SHARED_LOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define SHARED_TRYLOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define UNLOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(unlock(__VA_ARGS__))
|
||||||
|
|
||||||
|
// An escape hatch for thread safety analysis to ignore the annotated function.
|
||||||
|
#define NO_THREAD_SAFETY_ANALYSIS \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||||
|
|
||||||
|
// Used to mark functions that need to be fixed, because they are producing
|
||||||
|
// thread safety warnings. This macro is intended primarily for use by the
|
||||||
|
// compiler team; it allows new thread safety warnings to be rolled out
|
||||||
|
// without breaking existing code. Code which triggers the new warnings are
|
||||||
|
// marked with a FIXME, and referred back to the code owners to fix.
|
||||||
|
#define NO_THREAD_SAFETY_ANALYSIS_FIXME \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||||
|
|
||||||
|
// NO_THREAD_SAFETY_ANALYSIS_OPT turns off thread-safety checking in the
|
||||||
|
// annotated function in opt (NDEBUG) mode. It is for use specifically when
|
||||||
|
// the thread-safety checker is failing in opt mode on an otherwise correct
|
||||||
|
// piece of code.
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define NO_THREAD_SAFETY_ANALYSIS_OPT NO_THREAD_SAFETY_ANALYSIS
|
||||||
|
#else
|
||||||
|
#define NO_THREAD_SAFETY_ANALYSIS_OPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TS_UNCHECKED should be placed around lock expressions that are not valid
|
||||||
|
// C++ syntax, but which are present for documentation purposes. The
|
||||||
|
// expressions are passed unchanged to gcc, which will usually treat them
|
||||||
|
// as the universal lock.
|
||||||
|
#define TS_UNCHECKED(x) x
|
||||||
|
|
||||||
|
// TS_FIXME is used to mark lock expressions that are not valid C++ syntax.
|
||||||
|
// This annotation should eventually be either fixed, or changed to
|
||||||
|
// TS_UNCHECKED.
|
||||||
|
#define TS_FIXME(x) x
|
||||||
|
|
||||||
|
// This is used to pass different annotations to gcc and clang, in cases where
|
||||||
|
// gcc would reject a lock expression (e.g. &MyClass::mu_) that is accepted
|
||||||
|
// by clang. This is seldom needed, since GCC usually ignores invalid lock
|
||||||
|
// expressions except in certain cases, such as LOCK_RETURNED.
|
||||||
|
#define TS_CLANG_ONLY(CLANG_EXPR, GCC_EXPR) GCC_EXPR
|
||||||
|
|
||||||
|
// Clang Attributes
|
||||||
|
// The names of attributes in the clang analysis are slightly different
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define GUARDED_BY(x) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||||
|
|
||||||
|
#define GUARDED_VAR \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(guarded)
|
||||||
|
|
||||||
|
#define PT_GUARDED_BY(x) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
|
||||||
|
|
||||||
|
#define PT_GUARDED_VAR \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded)
|
||||||
|
|
||||||
|
#define ACQUIRED_AFTER(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define ACQUIRED_BEFORE(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define EXCLUSIVE_LOCKS_REQUIRED(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define SHARED_LOCKS_REQUIRED(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define LOCKS_EXCLUDED(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define LOCK_RETURNED(x) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||||
|
|
||||||
|
#define LOCKABLE \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(lockable)
|
||||||
|
|
||||||
|
#define SCOPED_LOCKABLE \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||||
|
|
||||||
|
#define EXCLUSIVE_LOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define SHARED_LOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define SHARED_TRYLOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define UNLOCK_FUNCTION(...) \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define NO_THREAD_SAFETY_ANALYSIS \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||||
|
|
||||||
|
#define NO_THREAD_SAFETY_ANALYSIS_OPT
|
||||||
|
|
||||||
|
#define NO_THREAD_SAFETY_ANALYSIS_FIXME \
|
||||||
|
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||||
|
|
||||||
|
#define TS_UNCHECKED(x) ""
|
||||||
|
|
||||||
|
#define TS_FIXME(x) ""
|
||||||
|
|
||||||
|
#define TS_CLANG_ONLY(CLANG_EXPR, GCC_EXPR) CLANG_EXPR
|
||||||
|
|
||||||
|
#endif // defined(__clang__)
|
||||||
|
|
||||||
|
#endif // BASE_THREAD_ANNOTATIONS_H_
|
||||||
164
common/BUILD
Normal file
164
common/BUILD
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Description:
|
||||||
|
# Build file for code common to multiple Widevine services.
|
||||||
|
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "rsa_util",
|
||||||
|
srcs = ["rsa_util.cc"],
|
||||||
|
hdrs = ["rsa_util.h"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "rsa_util_test",
|
||||||
|
size = "medium",
|
||||||
|
timeout = "short",
|
||||||
|
srcs = ["rsa_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":rsa_test_keys",
|
||||||
|
":rsa_util",
|
||||||
|
"//external:gtest",
|
||||||
|
"//external:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "openssl_util",
|
||||||
|
hdrs = ["openssl_util.h"],
|
||||||
|
deps = [
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "rsa_key",
|
||||||
|
srcs = ["rsa_key.cc"],
|
||||||
|
hdrs = ["rsa_key.h"],
|
||||||
|
deps = [
|
||||||
|
":rsa_util",
|
||||||
|
":sha_util",
|
||||||
|
"//base",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "rsa_key_test",
|
||||||
|
size = "medium",
|
||||||
|
timeout = "short",
|
||||||
|
srcs = ["rsa_key_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":rsa_key",
|
||||||
|
":rsa_test_keys",
|
||||||
|
"//external:gtest",
|
||||||
|
"//external:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "rsa_test_keys",
|
||||||
|
testonly = 1,
|
||||||
|
srcs = ["rsa_test_keys.cc"],
|
||||||
|
hdrs = ["rsa_test_keys.h"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "mock_rsa_key",
|
||||||
|
testonly = 1,
|
||||||
|
hdrs = ["mock_rsa_key.h"],
|
||||||
|
deps = [
|
||||||
|
":rsa_key",
|
||||||
|
"//external:gtest",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "aes_cbc_util",
|
||||||
|
srcs = ["aes_cbc_util.cc"],
|
||||||
|
hdrs = ["aes_cbc_util.h"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "aes_cbc_util_test",
|
||||||
|
srcs = ["aes_cbc_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":aes_cbc_util",
|
||||||
|
"//external:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "random_util",
|
||||||
|
srcs = ["random_util.cc"],
|
||||||
|
hdrs = ["random_util.h"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "random_util_test",
|
||||||
|
srcs = ["random_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":random_util",
|
||||||
|
"//external:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "sha_util",
|
||||||
|
srcs = ["sha_util.cc"],
|
||||||
|
hdrs = ["sha_util.h"],
|
||||||
|
deps = [
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "sha_util_test",
|
||||||
|
srcs = ["sha_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":sha_util",
|
||||||
|
"//external:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "file_util",
|
||||||
|
srcs = ["file_util.cc"],
|
||||||
|
hdrs = ["file_util.h"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "file_util_test",
|
||||||
|
srcs = ["file_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":file_util",
|
||||||
|
"//external:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
93
common/aes_cbc_util.cc
Normal file
93
common/aes_cbc_util.cc
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/aes_cbc_util.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "openssl/aes.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace crypto_util {
|
||||||
|
|
||||||
|
// Encrypts the provided plantext std::string using AES-CBC encryption.
|
||||||
|
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
||||||
|
const std::string& plaintext) {
|
||||||
|
if (iv.size() != AES_BLOCK_SIZE) return "";
|
||||||
|
|
||||||
|
const size_t num_padding_bytes =
|
||||||
|
AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE);
|
||||||
|
std::string padded_text = plaintext;
|
||||||
|
padded_text.append(num_padding_bytes, static_cast<char>(num_padding_bytes));
|
||||||
|
|
||||||
|
AES_KEY aes_key;
|
||||||
|
if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||||
|
key.size() * 8, &aes_key) != 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string encrypted(padded_text);
|
||||||
|
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_key, &local_iv[0], AES_ENCRYPT);
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypts the AES-CBC encrypted text. Returns an empty std::string on error or
|
||||||
|
// the plaintext on success.
|
||||||
|
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
||||||
|
const std::string& ciphertext) {
|
||||||
|
if (ciphertext.empty()) return "";
|
||||||
|
if (iv.size() != AES_BLOCK_SIZE) return "";
|
||||||
|
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) return "";
|
||||||
|
|
||||||
|
AES_KEY aes_key;
|
||||||
|
if (AES_set_decrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||||
|
key.size() * 8, &aes_key) != 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cleartext(ciphertext);
|
||||||
|
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 "";
|
||||||
|
for (uint8_t i = 0; i < num_padding_bytes; ++i) {
|
||||||
|
if (cleartext[cleartext.size() - 1 - i] != num_padding_bytes) return "";
|
||||||
|
}
|
||||||
|
cleartext.resize(cleartext.size() - num_padding_bytes);
|
||||||
|
return cleartext;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||||
|
const std::string& ciphertext) {
|
||||||
|
std::vector<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 "";
|
||||||
|
|
||||||
|
AES_KEY aes_key;
|
||||||
|
if (AES_set_decrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||||
|
key.size() * 8, &aes_key) != 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cleartext(ciphertext);
|
||||||
|
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);
|
||||||
|
return cleartext;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crypto_util
|
||||||
|
} // namespace widevine
|
||||||
38
common/aes_cbc_util.h
Normal file
38
common/aes_cbc_util.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_AES_CBC_UTIL_H_
|
||||||
|
#define COMMON_AES_CBC_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace crypto_util {
|
||||||
|
|
||||||
|
// Helper for wrapping AES CBC encryption. Uses PKCS5 padding.
|
||||||
|
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
||||||
|
const std::string& plaintext);
|
||||||
|
|
||||||
|
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
|
||||||
|
// empty std::string on error or the plaintext on success. Uses PKCS5 padding.
|
||||||
|
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
||||||
|
const std::string& ciphertext);
|
||||||
|
|
||||||
|
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
|
||||||
|
// empty std::string on error or the plaintext on success.
|
||||||
|
// Uses no padding; fails if the ciphertext is not a multiple of 16 bytes.
|
||||||
|
// This is used to decrypt the encrypted blob in the WVM keyboxes, with
|
||||||
|
// a zero iv.
|
||||||
|
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||||
|
const std::string& ciphertext);
|
||||||
|
|
||||||
|
} // namespace crypto_util
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_AES_CBC_UTIL_H_
|
||||||
|
|
||||||
137
common/aes_cbc_util_test.cc
Normal file
137
common/aes_cbc_util_test.cc
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/aes_cbc_util.h"
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const uint8_t kKey[] = {
|
||||||
|
0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e,
|
||||||
|
0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t kIv[] = {
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace crypto_util {
|
||||||
|
|
||||||
|
TEST(CryptoUtilTest, EncryptAndDecryptAesCbc) {
|
||||||
|
std::string plain_text("Foo");
|
||||||
|
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
||||||
|
std::string expected_ciphertext(
|
||||||
|
"\xCF\x1A\x3\x1C\x9C\x8C\xB9Z\xEC\xC0\x17\xDCRxX\xD7");
|
||||||
|
ASSERT_EQ(0, ciphertext.size() % 16);
|
||||||
|
ASSERT_GT(ciphertext.size(), plain_text.size());
|
||||||
|
ASSERT_EQ(expected_ciphertext, ciphertext);
|
||||||
|
|
||||||
|
std::string decrypted = DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)), ciphertext);
|
||||||
|
ASSERT_EQ(plain_text, decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CryptoUtilTest, DecryptAesCbcNoPad) {
|
||||||
|
const uint8_t kKey[] = {
|
||||||
|
0xdd, 0x71, 0x39, 0xea, 0xfa, 0xce, 0xed, 0x7c,
|
||||||
|
0xda, 0x9f, 0x25, 0xda, 0x8a, 0xa9, 0x15, 0xea,
|
||||||
|
};
|
||||||
|
const uint8_t kIv[] = {
|
||||||
|
0x5d, 0x16, 0x44, 0xea, 0xec, 0x11, 0xf9, 0x83,
|
||||||
|
0x14, 0x75, 0x41, 0xe4, 0x6e, 0xeb, 0x27, 0x74,
|
||||||
|
};
|
||||||
|
const uint8_t kCiphertext[] = {
|
||||||
|
0x6d, 0xa6, 0xda, 0xe4, 0xee, 0x40, 0x09, 0x17,
|
||||||
|
0x54, 0x7b, 0xba, 0xa5, 0x27, 0xb8, 0x82, 0x1b,
|
||||||
|
};
|
||||||
|
const uint8_t kExpectedPlaintext[] = {
|
||||||
|
0xb3, 0x49, 0xd4, 0x80, 0x9e, 0x91, 0x06, 0x87,
|
||||||
|
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10,
|
||||||
|
};
|
||||||
|
const uint8_t kExpectedPlaintextEmptyIv[] = {
|
||||||
|
0xee, 0x5f, 0x90, 0x6a, 0x72, 0x80, 0xff, 0x04,
|
||||||
|
0x14, 0x75, 0x41, 0xa4, 0x6e, 0xeb, 0x27, 0x64,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string decrypted = DecryptAesCbcNoPad(
|
||||||
|
std::string(kKey, kKey + sizeof(kKey)), std::string(kIv, kIv + sizeof(kIv)),
|
||||||
|
std::string(kCiphertext, kCiphertext + sizeof(kCiphertext)));
|
||||||
|
ASSERT_EQ(std::string(kExpectedPlaintext,
|
||||||
|
kExpectedPlaintext + sizeof(kExpectedPlaintext)),
|
||||||
|
decrypted);
|
||||||
|
|
||||||
|
std::string dummy_iv;
|
||||||
|
decrypted = DecryptAesCbcNoPad(
|
||||||
|
std::string(kKey, kKey + sizeof(kKey)), dummy_iv,
|
||||||
|
std::string(kCiphertext, kCiphertext + sizeof(kCiphertext)));
|
||||||
|
ASSERT_EQ(
|
||||||
|
std::string(kExpectedPlaintextEmptyIv,
|
||||||
|
kExpectedPlaintextEmptyIv + sizeof(kExpectedPlaintextEmptyIv)),
|
||||||
|
decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CryptoUtilTest, TestFailedEncrypt) {
|
||||||
|
// Test with bogus initialization vector.
|
||||||
|
std::string plain_text("Foo");
|
||||||
|
std::string bogus_iv("bogus");
|
||||||
|
std::string ciphertext =
|
||||||
|
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, plain_text);
|
||||||
|
ASSERT_EQ(ciphertext.size(), 0);
|
||||||
|
|
||||||
|
// Test with bogus key.
|
||||||
|
std::string bogus_key("bogus");
|
||||||
|
ciphertext =
|
||||||
|
EncryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
||||||
|
ASSERT_EQ(ciphertext.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CryptoUtilTest, TestFailedDecrypt) {
|
||||||
|
// First, encrypt the data.
|
||||||
|
std::string plain_text("Foo");
|
||||||
|
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
||||||
|
ASSERT_NE(ciphertext.size(), 0);
|
||||||
|
|
||||||
|
// Test Decrypt with bogus iv.
|
||||||
|
std::string bogus_iv("bogus");
|
||||||
|
plain_text =
|
||||||
|
DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, ciphertext);
|
||||||
|
ASSERT_EQ(plain_text.size(), 0);
|
||||||
|
|
||||||
|
// Test Decrypt with bogus key.
|
||||||
|
std::string bogus_key("bogus");
|
||||||
|
plain_text =
|
||||||
|
DecryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), ciphertext);
|
||||||
|
ASSERT_EQ(plain_text.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CryptoUtilTest, TestEmptyEncrypt) {
|
||||||
|
EXPECT_EQ("\xDBx\xD9\x91\xE8\x1D\xD9\x19\x80r\x12\x89\xD7Kp\xEB",
|
||||||
|
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CryptoUtilTest, TestEmptyDecryptAesCbc) {
|
||||||
|
EXPECT_EQ("", DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CryptoUtilTest, TestEmptyDecryptAesCbcNoPad) {
|
||||||
|
EXPECT_EQ("", DecryptAesCbcNoPad(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crypto_util
|
||||||
|
} // namespace widevine
|
||||||
63
common/file_util.cc
Normal file
63
common/file_util.cc
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/file_util.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
bool GetContents(const std::string& file_name, std::string* contents) {
|
||||||
|
if (file_name.empty()) {
|
||||||
|
LOG(WARNING) << "File name is empty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FILE* file = fopen(file_name.c_str(), "r");
|
||||||
|
if (!file) {
|
||||||
|
LOG(WARNING) << "Unable to open file " << file_name;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
contents->clear();
|
||||||
|
const size_t kReadSize = 0x1000;
|
||||||
|
char buffer[kReadSize];
|
||||||
|
while (true) {
|
||||||
|
size_t size_read = fread(buffer, sizeof(char), kReadSize, file);
|
||||||
|
if (size_read == 0) break;
|
||||||
|
contents->append(buffer, size_read);
|
||||||
|
}
|
||||||
|
const bool eof = feof(file);
|
||||||
|
fclose(file);
|
||||||
|
if (!eof) {
|
||||||
|
LOG(WARNING) << "Failed to read all file contents.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetContents(const std::string& file_name, const std::string& contents) {
|
||||||
|
if (file_name.empty()) {
|
||||||
|
LOG(WARNING) << "File name is empty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FILE* file = fopen(file_name.c_str(), "w");
|
||||||
|
if (!file) {
|
||||||
|
LOG(WARNING) << "Unable to open file " << file_name;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const size_t size_written =
|
||||||
|
fwrite(contents.data(), sizeof(char), contents.size(), file);
|
||||||
|
if (size_written != contents.size())
|
||||||
|
LOG(WARNING) << "Failed to write to " << file_name;
|
||||||
|
fclose(file);
|
||||||
|
return size_written == contents.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
27
common/file_util.h
Normal file
27
common/file_util.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// File util wrapper to be used in partner sdks. Implemented using generic file
|
||||||
|
// apis.
|
||||||
|
|
||||||
|
#ifndef COMMON_FILE_UTIL_H_
|
||||||
|
#define COMMON_FILE_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Read file to string.
|
||||||
|
bool GetContents(const std::string& file_name, std::string* contents);
|
||||||
|
|
||||||
|
// Write file.
|
||||||
|
bool SetContents(const std::string& file_name, const std::string& contents);
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_FILE_UTIL_H_
|
||||||
29
common/file_util_test.cc
Normal file
29
common/file_util_test.cc
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/file_util.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
TEST(FileUtilTest, EmptyFileName) {
|
||||||
|
std::string contents;
|
||||||
|
EXPECT_FALSE(GetContents("", &contents));
|
||||||
|
EXPECT_FALSE(SetContents("", "test content"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileUtilTest, BasicTest) {
|
||||||
|
const std::string file_path = FLAGS_test_tmpdir + "/file_util_test";
|
||||||
|
EXPECT_TRUE(SetContents(file_path, "test content"));
|
||||||
|
std::string contents;
|
||||||
|
EXPECT_TRUE(GetContents(file_path, &contents));
|
||||||
|
EXPECT_EQ("test content", contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
71
common/mock_rsa_key.h
Normal file
71
common/mock_rsa_key.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_MOCK_RSA_KEY_H_
|
||||||
|
#define COMMON_MOCK_RSA_KEY_H_
|
||||||
|
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class MockRsaPrivateKey : public RsaPrivateKey {
|
||||||
|
public:
|
||||||
|
MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {}
|
||||||
|
~MockRsaPrivateKey() override {}
|
||||||
|
|
||||||
|
MOCK_CONST_METHOD2(Decrypt, bool(const std::string& encrypted_message,
|
||||||
|
std::string* decrypted_message));
|
||||||
|
MOCK_CONST_METHOD2(GenerateSignature,
|
||||||
|
bool(const std::string& message, std::string* signature));
|
||||||
|
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
||||||
|
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
||||||
|
|
||||||
|
private:
|
||||||
|
MockRsaPrivateKey(const MockRsaPrivateKey&) = delete;
|
||||||
|
MockRsaPrivateKey& operator=(const MockRsaPrivateKey&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockRsaPublicKey : public RsaPublicKey {
|
||||||
|
public:
|
||||||
|
MockRsaPublicKey() : RsaPublicKey(RSA_new()) {}
|
||||||
|
~MockRsaPublicKey() override {}
|
||||||
|
|
||||||
|
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
|
||||||
|
std::string* encrypted_message));
|
||||||
|
MOCK_CONST_METHOD2(VerifySignature, bool(const std::string& message,
|
||||||
|
const std::string& signature));
|
||||||
|
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
||||||
|
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
||||||
|
|
||||||
|
private:
|
||||||
|
MockRsaPublicKey(const MockRsaPublicKey&) = delete;
|
||||||
|
MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockRsaKeyFactory : public RsaKeyFactory{
|
||||||
|
public:
|
||||||
|
MockRsaKeyFactory() {}
|
||||||
|
~MockRsaKeyFactory() override {}
|
||||||
|
|
||||||
|
MOCK_METHOD1(CreateFromPkcs1PrivateKey,
|
||||||
|
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
|
||||||
|
MOCK_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));
|
||||||
|
|
||||||
|
private:
|
||||||
|
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;
|
||||||
|
MockRsaKeyFactory& operator=(const MockRsaKeyFactory&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_MOCK_RSA_KEY_H_
|
||||||
70
common/openssl_util.h
Normal file
70
common/openssl_util.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMMON_OPENSSL_UTIL_H__
|
||||||
|
#define COMMON_OPENSSL_UTIL_H__
|
||||||
|
|
||||||
|
#include "openssl/bio.h"
|
||||||
|
#include "openssl/evp.h"
|
||||||
|
#include "openssl/rsa.h"
|
||||||
|
#include "openssl/x509v3.h"
|
||||||
|
|
||||||
|
template <typename T, void (*func)(T *)>
|
||||||
|
struct OpenSSLDeleter {
|
||||||
|
void operator()(T *obj) { func(obj); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename StackType, typename T, void (*func)(T *)>
|
||||||
|
struct OpenSSLStackDeleter {
|
||||||
|
void operator()(StackType *obj) {
|
||||||
|
sk_pop_free(reinterpret_cast<_STACK *>(obj),
|
||||||
|
reinterpret_cast<void (*)(void *)>(func));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename StackType>
|
||||||
|
struct OpenSSLStackOnlyDeleter {
|
||||||
|
void operator()(StackType *obj) { sk_free(reinterpret_cast<_STACK *>(obj)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, void (*func)(T *)>
|
||||||
|
using ScopedOpenSSLType = std::unique_ptr<T, OpenSSLDeleter<T, func>>;
|
||||||
|
template <typename StackType, typename T, void (*func)(T *)>
|
||||||
|
using ScopedOpenSSLStack =
|
||||||
|
std::unique_ptr<StackType, OpenSSLStackDeleter<StackType, T, func>>;
|
||||||
|
template <typename StackType>
|
||||||
|
using ScopedOpenSSLStackOnly =
|
||||||
|
std::unique_ptr<StackType, OpenSSLStackOnlyDeleter<StackType>>;
|
||||||
|
|
||||||
|
using ScopedBIO = ScopedOpenSSLType<BIO, BIO_vfree>;
|
||||||
|
using ScopedPKEY = ScopedOpenSSLType<EVP_PKEY, EVP_PKEY_free>;
|
||||||
|
using ScopedRSA = ScopedOpenSSLType<RSA, RSA_free>;
|
||||||
|
using ScopedX509 = ScopedOpenSSLType<X509, X509_free>;
|
||||||
|
using ScopedX509Extension =
|
||||||
|
ScopedOpenSSLType<X509_EXTENSION, X509_EXTENSION_free>;
|
||||||
|
using ScopedX509Name = ScopedOpenSSLType<X509_NAME, X509_NAME_free>;
|
||||||
|
using ScopedX509NameEntry =
|
||||||
|
ScopedOpenSSLType<X509_NAME_ENTRY, X509_NAME_ENTRY_free>;
|
||||||
|
using ScopedX509Store = ScopedOpenSSLType<X509_STORE, X509_STORE_free>;
|
||||||
|
using ScopedX509StoreCtx =
|
||||||
|
ScopedOpenSSLType<X509_STORE_CTX, X509_STORE_CTX_free>;
|
||||||
|
using ScopedAsn1UtcTime = ScopedOpenSSLType<ASN1_UTCTIME, ASN1_UTCTIME_free>;
|
||||||
|
using ScopedAsn1Utc8String =
|
||||||
|
ScopedOpenSSLType<ASN1_UTF8STRING, ASN1_UTF8STRING_free>;
|
||||||
|
using ScopedAsn1Integer = ScopedOpenSSLType<ASN1_INTEGER, ASN1_INTEGER_free>;
|
||||||
|
|
||||||
|
// XxxStack deallocates the stack and its members while XxxStackOnly deallocates
|
||||||
|
// the stack only.
|
||||||
|
using ScopedX509Stack = ScopedOpenSSLStack<STACK_OF(X509), X509, X509_free>;
|
||||||
|
using ScopedX509StackOnly = ScopedOpenSSLStackOnly<STACK_OF(X509)>;
|
||||||
|
using ScopedX509InfoStack =
|
||||||
|
ScopedOpenSSLStack<STACK_OF(X509_INFO), X509_INFO, X509_INFO_free>;
|
||||||
|
using ScopedX509InfoStackOnly = ScopedOpenSSLStackOnly<STACK_OF(X509_INFO)>;
|
||||||
|
|
||||||
|
#endif // COMMON_OPENSSL_UTIL_H__
|
||||||
22
common/random_util.cc
Normal file
22
common/random_util.cc
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/random_util.h"
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "openssl/rand.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
bool RandomBytes(size_t num_bytes, std::string* output) {
|
||||||
|
DCHECK(output);
|
||||||
|
output->resize(num_bytes);
|
||||||
|
return RAND_bytes(reinterpret_cast<uint8_t*>(&(*output)[0]), num_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
22
common/random_util.h
Normal file
22
common/random_util.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_RANDOM_UTIL_H_
|
||||||
|
#define COMMON_RANDOM_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Generates a random string.
|
||||||
|
// Return true on success, false otherwise.
|
||||||
|
bool RandomBytes(size_t num_bytes, std::string* output);
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_RANDOM_UTIL_H_
|
||||||
29
common/random_util_test.cc
Normal file
29
common/random_util_test.cc
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/random_util.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
TEST(RandomUtilTest, Test) {
|
||||||
|
std::string output;
|
||||||
|
ASSERT_TRUE(RandomBytes(16u, &output));
|
||||||
|
EXPECT_EQ(16u, output.size());
|
||||||
|
|
||||||
|
std::string output2;
|
||||||
|
ASSERT_TRUE(RandomBytes(16u, &output2));
|
||||||
|
EXPECT_EQ(16u, output2.size());
|
||||||
|
EXPECT_NE(output, output2);
|
||||||
|
|
||||||
|
ASSERT_TRUE(RandomBytes(10u, &output2));
|
||||||
|
EXPECT_EQ(10u, output2.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
296
common/rsa_key.cc
Normal file
296
common/rsa_key.cc
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Definition of classes representing RSA private and public keys used
|
||||||
|
// for message signing, signature verification, encryption and decryption.
|
||||||
|
//
|
||||||
|
// RSA signature details:
|
||||||
|
// Algorithm: RSASSA-PSS
|
||||||
|
// Hash algorithm: SHA1
|
||||||
|
// Mask generation function: mgf1SHA1
|
||||||
|
// Salt length: 20 bytes
|
||||||
|
// Trailer field: 0xbc
|
||||||
|
//
|
||||||
|
// RSA encryption details:
|
||||||
|
// Algorithm: RSA-OAEP
|
||||||
|
// Mask generation function: mgf1SHA1
|
||||||
|
// Label (encoding paramter): empty std::string
|
||||||
|
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "openssl/bn.h"
|
||||||
|
#include "openssl/err.h"
|
||||||
|
#include "openssl/evp.h"
|
||||||
|
#include "openssl/rsa.h"
|
||||||
|
#include "openssl/sha.h"
|
||||||
|
#include "common/rsa_util.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
|
||||||
|
static const int kPssSaltLength = 20;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Check if two RSA keys match. If matches, they are either a public-private key
|
||||||
|
// pair or the same public key or the same private key.
|
||||||
|
bool RsaKeyMatch(const RSA* key1, const RSA* key2) {
|
||||||
|
if (!key1 || !key2) return false;
|
||||||
|
return BN_cmp(key1->n, key2->n) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(CHECK_NOTNULL(key)) {}
|
||||||
|
|
||||||
|
RsaPrivateKey::~RsaPrivateKey() { RSA_free(key_); }
|
||||||
|
|
||||||
|
RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) {
|
||||||
|
RSA* key;
|
||||||
|
if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key)) return nullptr;
|
||||||
|
if (RSA_check_key(key) != 1) {
|
||||||
|
LOG(ERROR) << "Invalid private RSA key: "
|
||||||
|
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||||
|
RSA_free(key);
|
||||||
|
}
|
||||||
|
return new RsaPrivateKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPrivateKey::Decrypt(const std::string& encrypted_message,
|
||||||
|
std::string* decrypted_message) const {
|
||||||
|
DCHECK(decrypted_message);
|
||||||
|
|
||||||
|
size_t rsa_size = RSA_size(key_);
|
||||||
|
if (encrypted_message.size() != rsa_size) {
|
||||||
|
LOG(ERROR) << "Encrypted RSA message has the wrong size (expected "
|
||||||
|
<< rsa_size << ", actual " << encrypted_message.size() << ")";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
decrypted_message->assign(rsa_size, 0);
|
||||||
|
int decrypted_size = RSA_private_decrypt(
|
||||||
|
rsa_size,
|
||||||
|
const_cast<unsigned char*>(
|
||||||
|
reinterpret_cast<const unsigned char*>(encrypted_message.data())),
|
||||||
|
reinterpret_cast<unsigned char*>(&(*decrypted_message)[0]), key_,
|
||||||
|
RSA_PKCS1_OAEP_PADDING);
|
||||||
|
if (decrypted_size == -1) {
|
||||||
|
LOG(ERROR) << "RSA private decrypt failure: "
|
||||||
|
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
decrypted_message->resize(decrypted_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPrivateKey::GenerateSignature(const std::string& message,
|
||||||
|
std::string* signature) const {
|
||||||
|
DCHECK(signature);
|
||||||
|
|
||||||
|
if (message.empty()) {
|
||||||
|
LOG(ERROR) << "Message to be signed is empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Hash the message using SHA1.
|
||||||
|
std::string message_digest = Sha1_Hash(message);
|
||||||
|
|
||||||
|
// Add PSS padding.
|
||||||
|
size_t rsa_size = RSA_size(key_);
|
||||||
|
std::string padded_digest(rsa_size, 0);
|
||||||
|
if (!RSA_padding_add_PKCS1_PSS_mgf1(
|
||||||
|
key_, reinterpret_cast<unsigned char*>(&padded_digest[0]),
|
||||||
|
reinterpret_cast<unsigned char*>(&message_digest[0]), EVP_sha1(),
|
||||||
|
EVP_sha1(), kPssSaltLength)) {
|
||||||
|
LOG(ERROR) << "RSA padding failure: "
|
||||||
|
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Encrypt PSS padded digest.
|
||||||
|
signature->assign(rsa_size, 0);
|
||||||
|
if (RSA_private_encrypt(padded_digest.size(),
|
||||||
|
reinterpret_cast<unsigned char*>(&padded_digest[0]),
|
||||||
|
reinterpret_cast<unsigned char*>(&(*signature)[0]),
|
||||||
|
key_, RSA_NO_PADDING) !=
|
||||||
|
static_cast<int>(signature->size())) {
|
||||||
|
LOG(ERROR) << "RSA private encrypt failure: "
|
||||||
|
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPrivateKey::GenerateSignatureSha256Pkcs7(const std::string& message,
|
||||||
|
std::string* signature) const {
|
||||||
|
DCHECK(signature);
|
||||||
|
if (message.empty()) {
|
||||||
|
LOG(ERROR) << "Empty signature verification message";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||||
|
SHA256(reinterpret_cast<const unsigned char*>(message.data()),
|
||||||
|
message.size(),
|
||||||
|
digest);
|
||||||
|
unsigned int sig_len = RSA_size(key_);
|
||||||
|
signature->resize(sig_len);
|
||||||
|
return RSA_sign(NID_sha256,
|
||||||
|
digest,
|
||||||
|
sizeof(digest),
|
||||||
|
reinterpret_cast<unsigned char*>(&(*signature)[0]),
|
||||||
|
&sig_len,
|
||||||
|
key_) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPrivateKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const {
|
||||||
|
return RsaKeyMatch(key(), private_key.key());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPrivateKey::MatchesPublicKey(const RsaPublicKey& public_key) const {
|
||||||
|
return RsaKeyMatch(key(), public_key.key());
|
||||||
|
}
|
||||||
|
|
||||||
|
RsaPublicKey::RsaPublicKey(RSA* key) : key_(CHECK_NOTNULL(key)) {}
|
||||||
|
|
||||||
|
RsaPublicKey::~RsaPublicKey() { RSA_free(key_); }
|
||||||
|
|
||||||
|
RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) {
|
||||||
|
RSA* key;
|
||||||
|
if (!rsa_util::DeserializeRsaPublicKey(serialized_key, &key)) return nullptr;
|
||||||
|
if (RSA_size(key) == 0) {
|
||||||
|
LOG(ERROR) << "Invalid public RSA key: "
|
||||||
|
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||||
|
RSA_free(key);
|
||||||
|
}
|
||||||
|
return new RsaPublicKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
||||||
|
std::string* encrypted_message) const {
|
||||||
|
DCHECK(encrypted_message);
|
||||||
|
|
||||||
|
if (clear_message.empty()) {
|
||||||
|
LOG(ERROR) << "Message to be encrypted is empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t rsa_size = RSA_size(key_);
|
||||||
|
encrypted_message->assign(rsa_size, 0);
|
||||||
|
if (RSA_public_encrypt(
|
||||||
|
clear_message.size(),
|
||||||
|
const_cast<unsigned char*>(
|
||||||
|
reinterpret_cast<const unsigned char*>(clear_message.data())),
|
||||||
|
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);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature) const {
|
||||||
|
if (message.empty()) {
|
||||||
|
LOG(ERROR) << "Signed message is empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t rsa_size = RSA_size(key_);
|
||||||
|
if (signature.size() != rsa_size) {
|
||||||
|
LOG(ERROR) << "Message signature is of the wrong size (expected "
|
||||||
|
<< rsa_size << ", actual " << signature.size() << ")";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Decrypt the signature.
|
||||||
|
std::string padded_digest(signature.size(), 0);
|
||||||
|
if (RSA_public_decrypt(
|
||||||
|
signature.size(),
|
||||||
|
const_cast<unsigned char*>(
|
||||||
|
reinterpret_cast<const unsigned char*>(signature.data())),
|
||||||
|
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);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Hash the message using SHA1.
|
||||||
|
std::string message_digest = Sha1_Hash(message);
|
||||||
|
|
||||||
|
// Verify PSS padding.
|
||||||
|
return RSA_verify_PKCS1_PSS_mgf1(
|
||||||
|
key_, reinterpret_cast<unsigned char*>(&message_digest[0]),
|
||||||
|
EVP_sha1(), EVP_sha1(),
|
||||||
|
reinterpret_cast<unsigned char*>(&padded_digest[0]),
|
||||||
|
kPssSaltLength) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPublicKey::VerifySignatureSha256Pkcs7(
|
||||||
|
const std::string& message, const std::string& signature) const {
|
||||||
|
if (message.empty()) {
|
||||||
|
LOG(ERROR) << "Empty signature verification message";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (signature.empty()) {
|
||||||
|
LOG(ERROR) << "Empty signature";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signature.size() != RSA_size(key_)) {
|
||||||
|
LOG(ERROR) << "RSA signature has the wrong size";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||||
|
SHA256(reinterpret_cast<const unsigned char*>(message.data()),
|
||||||
|
message.size(),
|
||||||
|
digest);
|
||||||
|
return RSA_verify(NID_sha256,
|
||||||
|
digest,
|
||||||
|
sizeof(digest),
|
||||||
|
reinterpret_cast<const unsigned char*>(signature.data()),
|
||||||
|
signature.size(),
|
||||||
|
key_) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPublicKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const {
|
||||||
|
return RsaKeyMatch(key(), private_key.key());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPublicKey::MatchesPublicKey(const RsaPublicKey& public_key) const {
|
||||||
|
return RsaKeyMatch(key(), public_key.key());
|
||||||
|
}
|
||||||
|
|
||||||
|
RsaKeyFactory::RsaKeyFactory() {}
|
||||||
|
|
||||||
|
RsaKeyFactory::~RsaKeyFactory() {}
|
||||||
|
|
||||||
|
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs1PrivateKey(
|
||||||
|
const std::string& private_key) {
|
||||||
|
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) {
|
||||||
|
std::string pkcs1_key;
|
||||||
|
const bool result =
|
||||||
|
private_key_passphrase.empty()
|
||||||
|
? rsa_util::PrivateKeyInfoToRsaPrivateKey(private_key, &pkcs1_key)
|
||||||
|
: rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey(
|
||||||
|
private_key, private_key_passphrase, &pkcs1_key);
|
||||||
|
if (!result) {
|
||||||
|
LOG(WARNING) << "Failed to get pkcs1_key.";
|
||||||
|
return std::unique_ptr<RsaPrivateKey>();
|
||||||
|
}
|
||||||
|
return std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(pkcs1_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<RsaPublicKey> RsaKeyFactory::CreateFromPkcs1PublicKey(
|
||||||
|
const std::string& public_key) {
|
||||||
|
return std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
131
common/rsa_key.h
Normal file
131
common/rsa_key.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Declaration of classes representing RSA private and public keys used
|
||||||
|
// for message signing, signature verification, encryption and decryption.
|
||||||
|
|
||||||
|
#ifndef COMMON_RSA_KEY_H_
|
||||||
|
#define COMMON_RSA_KEY_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "openssl/rsa.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class RsaPublicKey;
|
||||||
|
|
||||||
|
class RsaPrivateKey {
|
||||||
|
public:
|
||||||
|
explicit RsaPrivateKey(RSA* key);
|
||||||
|
virtual ~RsaPrivateKey();
|
||||||
|
|
||||||
|
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
|
||||||
|
// Returns NULL on failure.
|
||||||
|
static RsaPrivateKey* Create(const std::string& serialized_key);
|
||||||
|
|
||||||
|
// Decrypt a message using RSA-OAEP. Caller retains ownership of all
|
||||||
|
// parameters. Returns true if successful, false otherwise.
|
||||||
|
virtual bool Decrypt(const std::string& encrypted_message,
|
||||||
|
std::string* decrypted_message) const;
|
||||||
|
|
||||||
|
// Generate RSSASSA-PSS signature. Caller retains ownership of all parameters.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
virtual bool GenerateSignature(const std::string& message,
|
||||||
|
std::string* signature) const;
|
||||||
|
|
||||||
|
// Generate SHA256 digest, PKCS#7 padded signature. Caller retains ownership
|
||||||
|
// of all parameters. Returns true if successful, false otherwise.
|
||||||
|
virtual bool GenerateSignatureSha256Pkcs7(const std::string& message,
|
||||||
|
std::string* signature) const;
|
||||||
|
|
||||||
|
// Return true if the underlying key matches with |private_key|.
|
||||||
|
virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const;
|
||||||
|
|
||||||
|
// Return true if the underlying key is a public-private key pair with
|
||||||
|
// |public_key|.
|
||||||
|
virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const;
|
||||||
|
|
||||||
|
const RSA* key() const { return key_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RSA* key_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(RsaPrivateKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
class RsaPublicKey {
|
||||||
|
public:
|
||||||
|
explicit RsaPublicKey(RSA* key);
|
||||||
|
virtual ~RsaPublicKey();
|
||||||
|
|
||||||
|
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
|
||||||
|
// Returns NULL on failure.
|
||||||
|
static RsaPublicKey* Create(const std::string& serialized_key);
|
||||||
|
|
||||||
|
// Encrypt a message using RSA-OAEP. Caller retains ownership of all
|
||||||
|
// parameters. Returns true if successful, false otherwise.
|
||||||
|
virtual bool Encrypt(const std::string& clear_message,
|
||||||
|
std::string* encrypted_message) const;
|
||||||
|
|
||||||
|
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters.
|
||||||
|
// Returns true if validation succeeds, false otherwise.
|
||||||
|
virtual bool VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature) const;
|
||||||
|
|
||||||
|
|
||||||
|
// Verify a signature. This method takes two parameters: |message| which is a
|
||||||
|
// std::string containing the data which was signed, and |signature| which is a
|
||||||
|
// std::string containing the message SHA256 digest signature with PKCS#7
|
||||||
|
// padding. Returns true if verification succeeds, false otherwise.
|
||||||
|
virtual bool VerifySignatureSha256Pkcs7(const std::string& message,
|
||||||
|
const std::string& signature) const;
|
||||||
|
|
||||||
|
// Return true if the underlying key is a public-private key pair with
|
||||||
|
// |private_key|.
|
||||||
|
virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const;
|
||||||
|
|
||||||
|
// Return true if the underlying key matches with |public_key|.
|
||||||
|
virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const;
|
||||||
|
|
||||||
|
const RSA* key() const { return key_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RSA* key_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(RsaPublicKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
class RsaKeyFactory {
|
||||||
|
public:
|
||||||
|
RsaKeyFactory();
|
||||||
|
virtual ~RsaKeyFactory();
|
||||||
|
|
||||||
|
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
|
||||||
|
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs1PrivateKey(
|
||||||
|
const std::string& private_key);
|
||||||
|
|
||||||
|
// Create an RsaPrivateKey object using an encrypted PKCS#8 RSAPrivateKey.
|
||||||
|
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs8PrivateKey(
|
||||||
|
const std::string& private_key, const std::string& private_key_passphrase);
|
||||||
|
|
||||||
|
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
|
||||||
|
virtual std::unique_ptr<RsaPublicKey> CreateFromPkcs1PublicKey(
|
||||||
|
const std::string& public_key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_RSA_KEY_H_
|
||||||
227
common/rsa_key_test.cc
Normal file
227
common/rsa_key_test.cc
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Unit test for rsa_key RSA encryption and signing.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "common/rsa_test_keys.h"
|
||||||
|
#include "common/rsa_util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
static const char kTestMessage[] =
|
||||||
|
"A fool thinks himself to be wise, but a wise man knows himself to be a "
|
||||||
|
"fool.";
|
||||||
|
|
||||||
|
class RsaKeyTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void TestEncryption(std::unique_ptr<RsaPrivateKey> private_key,
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key);
|
||||||
|
|
||||||
|
void TestSigning(std::unique_ptr<RsaPrivateKey> private_key,
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key);
|
||||||
|
|
||||||
|
void TestSigningSha256Pkcs7(std::unique_ptr<RsaPrivateKey> private_key,
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key);
|
||||||
|
|
||||||
|
RsaTestKeys test_keys_;
|
||||||
|
RsaKeyFactory factory_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RsaKeyTest::TestEncryption(std::unique_ptr<RsaPrivateKey> private_key,
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key) {
|
||||||
|
ASSERT_TRUE(private_key);
|
||||||
|
ASSERT_TRUE(public_key);
|
||||||
|
std::string encrypted_message;
|
||||||
|
EXPECT_TRUE(public_key->Encrypt(kTestMessage, &encrypted_message));
|
||||||
|
std::string decrypted_message;
|
||||||
|
EXPECT_TRUE(private_key->Decrypt(encrypted_message, &decrypted_message));
|
||||||
|
EXPECT_EQ(kTestMessage, decrypted_message);
|
||||||
|
// Add a byte to the encrypted message.
|
||||||
|
std::string bad_enc_message(encrypted_message);
|
||||||
|
bad_enc_message += '\0';
|
||||||
|
EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message));
|
||||||
|
// Remove a byte from the encrypted message.
|
||||||
|
bad_enc_message = encrypted_message.substr(0, encrypted_message.size() - 1);
|
||||||
|
EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message));
|
||||||
|
// Change a byte in the encrypted message.
|
||||||
|
bad_enc_message = encrypted_message;
|
||||||
|
bad_enc_message[128] ^= 0x55;
|
||||||
|
EXPECT_FALSE(private_key->Decrypt(bad_enc_message, &decrypted_message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsaKeyTest::TestSigning(std::unique_ptr<RsaPrivateKey> private_key,
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key) {
|
||||||
|
ASSERT_TRUE(private_key);
|
||||||
|
ASSERT_TRUE(public_key);
|
||||||
|
std::string signature;
|
||||||
|
EXPECT_TRUE(private_key->GenerateSignature(kTestMessage, &signature));
|
||||||
|
EXPECT_TRUE(public_key->VerifySignature(kTestMessage, signature));
|
||||||
|
// Add a byte to the signature.
|
||||||
|
std::string bad_signature(signature);
|
||||||
|
bad_signature += '\0';
|
||||||
|
EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature));
|
||||||
|
// Remove a byte from the signature.
|
||||||
|
bad_signature = signature.substr(0, signature.size() - 1);
|
||||||
|
EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature));
|
||||||
|
// Change a byte in the signature.
|
||||||
|
bad_signature = signature;
|
||||||
|
bad_signature[32] ^= 0x55;
|
||||||
|
EXPECT_FALSE(public_key->VerifySignature(kTestMessage, bad_signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsaKeyTest::TestSigningSha256Pkcs7(
|
||||||
|
std::unique_ptr<RsaPrivateKey> private_key,
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key) {
|
||||||
|
ASSERT_TRUE(private_key);
|
||||||
|
ASSERT_TRUE(public_key);
|
||||||
|
std::string signature;
|
||||||
|
EXPECT_TRUE(
|
||||||
|
private_key->GenerateSignatureSha256Pkcs7(kTestMessage, &signature));
|
||||||
|
EXPECT_TRUE(public_key->VerifySignatureSha256Pkcs7(kTestMessage, signature));
|
||||||
|
// Add a byte to the signature.
|
||||||
|
std::string bad_signature(signature);
|
||||||
|
bad_signature += '\0';
|
||||||
|
EXPECT_FALSE(
|
||||||
|
public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature));
|
||||||
|
// Remove a byte from the signature.
|
||||||
|
bad_signature = signature.substr(0, signature.size() - 1);
|
||||||
|
EXPECT_FALSE(
|
||||||
|
public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature));
|
||||||
|
// Change a byte in the signature.
|
||||||
|
bad_signature = signature;
|
||||||
|
bad_signature[32] ^= 0x55;
|
||||||
|
EXPECT_FALSE(
|
||||||
|
public_key->VerifySignatureSha256Pkcs7(kTestMessage, bad_signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaKeyTest, BadKey) {
|
||||||
|
std::unique_ptr<RsaPrivateKey> private_key(
|
||||||
|
RsaPrivateKey::Create("bad_private_key"));
|
||||||
|
EXPECT_TRUE(!private_key);
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key(
|
||||||
|
RsaPublicKey::Create("bad_public_key"));
|
||||||
|
EXPECT_TRUE(!public_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaKeyTest, EncryptAndDecrypt_3072) {
|
||||||
|
const std::string& private_key = test_keys_.private_test_key_1_3072_bits();
|
||||||
|
const std::string& public_key = test_keys_.public_test_key_1_3072_bits();
|
||||||
|
TestEncryption(
|
||||||
|
std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key)),
|
||||||
|
std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key)));
|
||||||
|
TestEncryption(factory_.CreateFromPkcs1PrivateKey(private_key),
|
||||||
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaKeyTest, EncryptAndDecrypt_2048) {
|
||||||
|
const std::string& private_key = test_keys_.private_test_key_2_2048_bits();
|
||||||
|
const std::string& public_key = test_keys_.public_test_key_2_2048_bits();
|
||||||
|
TestEncryption(
|
||||||
|
std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key)),
|
||||||
|
std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key)));
|
||||||
|
|
||||||
|
std::string pkcs8_key;
|
||||||
|
std::string passphrase("passphrase");
|
||||||
|
ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||||
|
private_key, passphrase, &pkcs8_key));
|
||||||
|
TestEncryption(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase),
|
||||||
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
|
|
||||||
|
ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key));
|
||||||
|
TestEncryption(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()),
|
||||||
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaKeyTest, SignAndVerify_3072) {
|
||||||
|
const std::string& private_key = test_keys_.private_test_key_1_3072_bits();
|
||||||
|
const std::string& public_key = test_keys_.public_test_key_1_3072_bits();
|
||||||
|
TestSigning(
|
||||||
|
std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key)),
|
||||||
|
std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key)));
|
||||||
|
TestSigning(factory_.CreateFromPkcs1PrivateKey(private_key),
|
||||||
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaKeyTest, SignAndVerify_2048) {
|
||||||
|
const std::string& private_key = test_keys_.private_test_key_2_2048_bits();
|
||||||
|
const std::string& public_key = test_keys_.public_test_key_2_2048_bits();
|
||||||
|
TestSigning(
|
||||||
|
std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key)),
|
||||||
|
std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key)));
|
||||||
|
|
||||||
|
std::string pkcs8_key;
|
||||||
|
std::string passphrase("passphrase");
|
||||||
|
ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||||
|
private_key, passphrase, &pkcs8_key));
|
||||||
|
TestSigning(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase),
|
||||||
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
|
|
||||||
|
ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key));
|
||||||
|
TestSigning(factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()),
|
||||||
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_3072) {
|
||||||
|
const std::string& private_key = test_keys_.private_test_key_1_3072_bits();
|
||||||
|
const std::string& public_key = test_keys_.public_test_key_1_3072_bits();
|
||||||
|
TestSigningSha256Pkcs7(
|
||||||
|
std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key)),
|
||||||
|
std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key)));
|
||||||
|
TestSigningSha256Pkcs7(factory_.CreateFromPkcs1PrivateKey(private_key),
|
||||||
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_2048) {
|
||||||
|
const std::string& private_key = test_keys_.private_test_key_2_2048_bits();
|
||||||
|
const std::string& public_key = test_keys_.public_test_key_2_2048_bits();
|
||||||
|
TestSigningSha256Pkcs7(
|
||||||
|
std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key)),
|
||||||
|
std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key)));
|
||||||
|
|
||||||
|
std::string pkcs8_key;
|
||||||
|
std::string passphrase("passphrase");
|
||||||
|
ASSERT_TRUE(rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||||
|
private_key, passphrase, &pkcs8_key));
|
||||||
|
TestSigningSha256Pkcs7(
|
||||||
|
factory_.CreateFromPkcs8PrivateKey(pkcs8_key, passphrase),
|
||||||
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
|
|
||||||
|
ASSERT_TRUE(rsa_util::RsaPrivateKeyToPrivateKeyInfo(private_key, &pkcs8_key));
|
||||||
|
TestSigningSha256Pkcs7(
|
||||||
|
factory_.CreateFromPkcs8PrivateKey(pkcs8_key, std::string()),
|
||||||
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaKeyTest, RsaKeyMatch) {
|
||||||
|
std::unique_ptr<RsaPrivateKey> private_key2(
|
||||||
|
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
|
||||||
|
std::unique_ptr<RsaPrivateKey> private_key3(
|
||||||
|
RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits()));
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key2(
|
||||||
|
RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits()));
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key3(
|
||||||
|
RsaPublicKey::Create(test_keys_.public_test_key_3_2048_bits()));
|
||||||
|
|
||||||
|
EXPECT_TRUE(public_key2->MatchesPublicKey(*public_key2));
|
||||||
|
EXPECT_FALSE(public_key2->MatchesPublicKey(*public_key3));
|
||||||
|
EXPECT_TRUE(public_key2->MatchesPrivateKey(*private_key2));
|
||||||
|
EXPECT_FALSE(public_key2->MatchesPrivateKey(*private_key3));
|
||||||
|
|
||||||
|
EXPECT_TRUE(private_key2->MatchesPublicKey(*public_key2));
|
||||||
|
EXPECT_FALSE(private_key2->MatchesPublicKey(*public_key3));
|
||||||
|
EXPECT_TRUE(private_key2->MatchesPrivateKey(*private_key2));
|
||||||
|
EXPECT_FALSE(private_key2->MatchesPrivateKey(*private_key3));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
682
common/rsa_test_keys.cc
Normal file
682
common/rsa_test_keys.cc
Normal file
@@ -0,0 +1,682 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// RSA keys generated using fake_prng for purposes of testing.
|
||||||
|
|
||||||
|
#include "common/rsa_test_keys.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
static const unsigned char kTestRsaPrivateKey1_3072[] = {
|
||||||
|
0x30, 0x82, 0x06, 0xe3, 0x02, 0x01, 0x00, 0x02,
|
||||||
|
0x82, 0x01, 0x81, 0x00, 0xa5, 0x62, 0x07, 0xdf,
|
||||||
|
0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0,
|
||||||
|
0x78, 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09,
|
||||||
|
0x9d, 0x35, 0x3f, 0xf3, 0x0f, 0xe9, 0x61, 0x96,
|
||||||
|
0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2,
|
||||||
|
0xca, 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7,
|
||||||
|
0x08, 0x47, 0xe4, 0x55, 0x1b, 0x83, 0xbe, 0x10,
|
||||||
|
0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29,
|
||||||
|
0x46, 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4,
|
||||||
|
0x08, 0xc3, 0x04, 0x17, 0x01, 0xb5, 0x11, 0x53,
|
||||||
|
0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37,
|
||||||
|
0x5c, 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2,
|
||||||
|
0x82, 0xa0, 0x92, 0xf1, 0x14, 0xf1, 0x22, 0xff,
|
||||||
|
0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86,
|
||||||
|
0xcd, 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d,
|
||||||
|
0xfd, 0xa4, 0x01, 0xe3, 0xb6, 0x0e, 0x85, 0xe4,
|
||||||
|
0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4,
|
||||||
|
0xde, 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f,
|
||||||
|
0x24, 0xb9, 0xb0, 0xbb, 0x31, 0xa0, 0xee, 0x6a,
|
||||||
|
0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee,
|
||||||
|
0x3a, 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42,
|
||||||
|
0x51, 0xbb, 0xfa, 0xbb, 0x90, 0x97, 0x2c, 0x77,
|
||||||
|
0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca,
|
||||||
|
0x49, 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47,
|
||||||
|
0xda, 0xe2, 0xbd, 0xf0, 0x5f, 0x07, 0x53, 0x8a,
|
||||||
|
0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6,
|
||||||
|
0xda, 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde,
|
||||||
|
0x3a, 0xe1, 0xc8, 0xdb, 0x17, 0xe8, 0xc9, 0x3a,
|
||||||
|
0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab,
|
||||||
|
0x30, 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9,
|
||||||
|
0xf5, 0xca, 0x15, 0x26, 0xaf, 0x39, 0xf3, 0x5d,
|
||||||
|
0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10,
|
||||||
|
0x16, 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02,
|
||||||
|
0x82, 0xd0, 0x60, 0x0b, 0x42, 0x72, 0x85, 0xec,
|
||||||
|
0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21,
|
||||||
|
0xf9, 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9,
|
||||||
|
0x6b, 0xc9, 0x12, 0x64, 0xe4, 0x3a, 0x3b, 0xc9,
|
||||||
|
0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05,
|
||||||
|
0x4a, 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed,
|
||||||
|
0x82, 0xb2, 0xf0, 0xd1, 0x72, 0x71, 0x04, 0x35,
|
||||||
|
0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab,
|
||||||
|
0xde, 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8,
|
||||||
|
0xce, 0x6d, 0x87, 0x50, 0x04, 0xb5, 0xd7, 0x24,
|
||||||
|
0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85,
|
||||||
|
0x1b, 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18,
|
||||||
|
0x1e, 0x6f, 0xd7, 0xf0, 0x33, 0x61, 0x53, 0x7e,
|
||||||
|
0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b,
|
||||||
|
0x67, 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b,
|
||||||
|
0x83, 0x9c, 0xc2, 0x87, 0x02, 0x03, 0x01, 0x00,
|
||||||
|
0x01, 0x02, 0x82, 0x01, 0x80, 0x5a, 0x09, 0x3f,
|
||||||
|
0x9e, 0x2e, 0x4d, 0x26, 0x50, 0x7b, 0x70, 0x21,
|
||||||
|
0xb0, 0x0c, 0x25, 0x21, 0x1f, 0xd9, 0x89, 0x5a,
|
||||||
|
0xca, 0x35, 0x23, 0x0b, 0x58, 0xa9, 0x7d, 0xf6,
|
||||||
|
0x19, 0xc4, 0x29, 0x87, 0xc7, 0xd4, 0x94, 0x85,
|
||||||
|
0xb4, 0x2c, 0xaf, 0x62, 0xb1, 0xe8, 0x62, 0x5b,
|
||||||
|
0xda, 0xdb, 0x70, 0x40, 0x37, 0xb1, 0x4e, 0x0c,
|
||||||
|
0xc8, 0x62, 0xee, 0xa2, 0xfc, 0x3c, 0xd2, 0x39,
|
||||||
|
0x90, 0x15, 0x2c, 0xba, 0x20, 0x50, 0xb7, 0x82,
|
||||||
|
0x2a, 0xa0, 0x76, 0x83, 0x20, 0x7f, 0x56, 0x73,
|
||||||
|
0x43, 0x8a, 0x9b, 0xa7, 0x6c, 0x63, 0xb6, 0xad,
|
||||||
|
0x56, 0xb2, 0x8a, 0xb2, 0xbc, 0x8f, 0xe2, 0xef,
|
||||||
|
0x83, 0x9d, 0x98, 0x0b, 0xc7, 0x62, 0x0e, 0x51,
|
||||||
|
0x6e, 0x57, 0x1d, 0x1b, 0x0e, 0x3a, 0xea, 0x3b,
|
||||||
|
0x76, 0x63, 0x35, 0xd0, 0xd1, 0xcf, 0xbe, 0xad,
|
||||||
|
0xbb, 0x1d, 0xde, 0x0f, 0x05, 0x48, 0x55, 0x29,
|
||||||
|
0xc1, 0xbc, 0x21, 0xc7, 0x87, 0xf2, 0x75, 0x12,
|
||||||
|
0x7d, 0x92, 0x9e, 0xbf, 0xad, 0x04, 0x68, 0xc4,
|
||||||
|
0xc9, 0x9d, 0x35, 0xd6, 0xa8, 0x62, 0xc1, 0x69,
|
||||||
|
0x6a, 0xb6, 0x41, 0xb7, 0x37, 0x66, 0xdf, 0xb2,
|
||||||
|
0xb9, 0x8c, 0x8b, 0x15, 0x08, 0x4c, 0x3d, 0xf1,
|
||||||
|
0xed, 0x82, 0x0f, 0xe3, 0xd5, 0xff, 0x46, 0xbd,
|
||||||
|
0xf7, 0x85, 0x43, 0xc0, 0x8b, 0xba, 0x47, 0xf1,
|
||||||
|
0x41, 0x57, 0xc3, 0x7f, 0x8b, 0x0d, 0x48, 0xea,
|
||||||
|
0xc2, 0xed, 0xc0, 0x69, 0x84, 0xb6, 0x32, 0x08,
|
||||||
|
0x49, 0x74, 0x14, 0x84, 0xa4, 0x1b, 0x48, 0x5b,
|
||||||
|
0xec, 0xd3, 0x0b, 0x12, 0x2b, 0x4c, 0x9e, 0x5c,
|
||||||
|
0x01, 0x60, 0xad, 0xef, 0xcb, 0x2b, 0x56, 0x84,
|
||||||
|
0x07, 0xfa, 0x62, 0xc6, 0x08, 0x92, 0x98, 0x70,
|
||||||
|
0xc9, 0x5b, 0x18, 0xc8, 0xfa, 0x27, 0x0c, 0xe2,
|
||||||
|
0xbd, 0xfb, 0x3e, 0x43, 0xa5, 0xb7, 0x06, 0x2c,
|
||||||
|
0x4e, 0xf1, 0x07, 0x5d, 0x8d, 0xdd, 0x53, 0xc5,
|
||||||
|
0x8c, 0x4a, 0xf2, 0x2f, 0x8e, 0x80, 0x96, 0x16,
|
||||||
|
0xc0, 0xfc, 0xf9, 0x20, 0x4f, 0x35, 0xc7, 0x53,
|
||||||
|
0x8b, 0x2d, 0x37, 0x43, 0x93, 0x3d, 0x74, 0x3f,
|
||||||
|
0x63, 0xf7, 0x0b, 0xbd, 0x46, 0xe4, 0x51, 0x67,
|
||||||
|
0x33, 0x57, 0x15, 0xf5, 0x59, 0x27, 0x66, 0xe8,
|
||||||
|
0xe2, 0x4b, 0xa3, 0x93, 0x03, 0x8a, 0x9c, 0x05,
|
||||||
|
0x13, 0xf2, 0xcb, 0xf7, 0x9c, 0x68, 0xe7, 0x16,
|
||||||
|
0x4b, 0x8e, 0x59, 0x71, 0x2b, 0x73, 0x9b, 0xb9,
|
||||||
|
0xae, 0x50, 0xfa, 0xd7, 0xd3, 0x34, 0x17, 0x1d,
|
||||||
|
0x62, 0x88, 0xbd, 0x8c, 0xba, 0x5a, 0x6b, 0x6a,
|
||||||
|
0x5e, 0xb3, 0xa5, 0x80, 0xca, 0xbb, 0xb9, 0xb5,
|
||||||
|
0xa8, 0x2e, 0xb1, 0x61, 0x6e, 0xd5, 0xd6, 0x62,
|
||||||
|
0x98, 0x4a, 0xb0, 0xb8, 0x76, 0xa9, 0x19, 0x5c,
|
||||||
|
0xe2, 0xbe, 0xb3, 0x9b, 0x4a, 0x39, 0xf5, 0xe6,
|
||||||
|
0xbb, 0x11, 0x6e, 0x13, 0x13, 0x38, 0xb8, 0x1f,
|
||||||
|
0x21, 0x19, 0xf5, 0xa7, 0x76, 0x93, 0xb3, 0x56,
|
||||||
|
0xfa, 0xcc, 0x74, 0xbc, 0x19, 0x02, 0x81, 0xc1,
|
||||||
|
0x00, 0xd1, 0xd1, 0x72, 0x57, 0xe5, 0xb0, 0x1c,
|
||||||
|
0x09, 0x05, 0xbb, 0x55, 0x89, 0x3c, 0x4a, 0x81,
|
||||||
|
0x90, 0x9a, 0xf9, 0x32, 0x63, 0x41, 0xad, 0x6a,
|
||||||
|
0x5f, 0x65, 0x94, 0x92, 0xcc, 0xf7, 0xc7, 0x53,
|
||||||
|
0x93, 0xa0, 0xf7, 0xbe, 0x48, 0x82, 0x63, 0x31,
|
||||||
|
0x7b, 0xd0, 0x82, 0x09, 0xbb, 0x0a, 0xbc, 0x60,
|
||||||
|
0xc9, 0x4d, 0x83, 0xe4, 0x5d, 0x50, 0xe6, 0x5f,
|
||||||
|
0x8b, 0x47, 0x07, 0xa3, 0x3a, 0x36, 0x97, 0xaa,
|
||||||
|
0x21, 0x70, 0x7f, 0xd5, 0x6c, 0xb0, 0x56, 0xf5,
|
||||||
|
0x5c, 0x48, 0x74, 0x2a, 0xdd, 0xfe, 0x94, 0x83,
|
||||||
|
0x05, 0xe0, 0x3d, 0x5d, 0xdd, 0x5a, 0x05, 0xcb,
|
||||||
|
0x47, 0xd7, 0xf9, 0x89, 0x55, 0xaa, 0x0b, 0x21,
|
||||||
|
0xc0, 0x71, 0x5d, 0xe1, 0x4c, 0x6a, 0x45, 0x86,
|
||||||
|
0x86, 0xf2, 0xb9, 0x38, 0x6a, 0x56, 0x51, 0x0d,
|
||||||
|
0x7d, 0xac, 0x30, 0x31, 0xca, 0x2d, 0xaa, 0xaa,
|
||||||
|
0xba, 0xcc, 0x12, 0x40, 0xc1, 0x0d, 0xa6, 0xc1,
|
||||||
|
0x7d, 0x22, 0xec, 0xb6, 0x51, 0x45, 0xfe, 0x4e,
|
||||||
|
0xbb, 0x4a, 0xd2, 0xba, 0x9b, 0xa2, 0xcc, 0x28,
|
||||||
|
0x2b, 0x01, 0x53, 0x53, 0xf3, 0xa9, 0x5a, 0x8f,
|
||||||
|
0xeb, 0xb7, 0xb8, 0x62, 0x6b, 0x8a, 0x79, 0x24,
|
||||||
|
0xcc, 0x86, 0x34, 0x45, 0xe2, 0xad, 0x1d, 0xd0,
|
||||||
|
0x4c, 0xc9, 0x77, 0x2a, 0xf9, 0x1a, 0xe8, 0x58,
|
||||||
|
0x78, 0x51, 0x8a, 0xea, 0x3f, 0x90, 0x36, 0x46,
|
||||||
|
0x2a, 0xc0, 0x71, 0x41, 0x83, 0x2c, 0x48, 0xee,
|
||||||
|
0xc5, 0x02, 0x81, 0xc1, 0x00, 0xc9, 0xc8, 0xce,
|
||||||
|
0xc4, 0x50, 0xb2, 0x26, 0xcb, 0x35, 0x78, 0x55,
|
||||||
|
0x3c, 0xcc, 0xf0, 0x7e, 0xba, 0xad, 0xeb, 0x58,
|
||||||
|
0xe9, 0xb5, 0x78, 0x2f, 0x43, 0x5f, 0x07, 0x47,
|
||||||
|
0x56, 0x05, 0x41, 0x38, 0x71, 0xe1, 0x58, 0x62,
|
||||||
|
0xb1, 0x8e, 0xbc, 0xf9, 0x80, 0x04, 0x22, 0x39,
|
||||||
|
0x22, 0x24, 0x28, 0x86, 0x9c, 0x00, 0x44, 0x5f,
|
||||||
|
0xc4, 0x97, 0xe6, 0x71, 0x5f, 0x1f, 0x58, 0xea,
|
||||||
|
0x75, 0x18, 0x0c, 0x23, 0x63, 0x09, 0xc5, 0x98,
|
||||||
|
0xc4, 0x6d, 0x23, 0xc2, 0x2c, 0x93, 0x6a, 0x26,
|
||||||
|
0xe4, 0x3d, 0x8d, 0xa1, 0x39, 0x70, 0x34, 0x25,
|
||||||
|
0xcd, 0xbc, 0x82, 0x78, 0x2b, 0xf3, 0x7e, 0x81,
|
||||||
|
0xb6, 0x5f, 0xc5, 0x69, 0xd0, 0x81, 0x69, 0x50,
|
||||||
|
0x2f, 0x17, 0x0c, 0x17, 0x3c, 0x0b, 0x45, 0x38,
|
||||||
|
0xce, 0xe3, 0xbf, 0x8a, 0x50, 0x0a, 0x00, 0x74,
|
||||||
|
0x7e, 0x7a, 0xd8, 0x55, 0x52, 0x6b, 0x82, 0xfb,
|
||||||
|
0x34, 0x15, 0x73, 0x6a, 0xf4, 0x51, 0x9b, 0x9f,
|
||||||
|
0xa0, 0x45, 0xb9, 0x76, 0xe5, 0xd3, 0xd5, 0xf4,
|
||||||
|
0xa9, 0xa4, 0xcd, 0x42, 0x2f, 0x29, 0x89, 0xec,
|
||||||
|
0x28, 0x5f, 0x03, 0x45, 0x27, 0xaf, 0x8c, 0x39,
|
||||||
|
0x3e, 0x59, 0x9d, 0xaf, 0x27, 0x5d, 0x17, 0x53,
|
||||||
|
0x17, 0xeb, 0x8d, 0x7f, 0x3d, 0xb8, 0x2a, 0x50,
|
||||||
|
0x1e, 0xb5, 0xc5, 0x04, 0xab, 0x9c, 0xa7, 0xaa,
|
||||||
|
0x86, 0x41, 0xb9, 0x36, 0x29, 0x9e, 0xd2, 0xd8,
|
||||||
|
0xde, 0x5f, 0xde, 0x80, 0xdb, 0x02, 0x81, 0xc0,
|
||||||
|
0x03, 0xf3, 0x5f, 0xa5, 0xcc, 0x0b, 0x5e, 0xdb,
|
||||||
|
0xc4, 0xa1, 0xdc, 0x60, 0x73, 0x24, 0x2c, 0x00,
|
||||||
|
0x5f, 0x0a, 0xa6, 0x2a, 0x3c, 0x48, 0x59, 0xa2,
|
||||||
|
0x66, 0x35, 0x3f, 0xf6, 0x60, 0x0b, 0xfe, 0xc4,
|
||||||
|
0xde, 0xd9, 0x0b, 0x5a, 0x2e, 0x2a, 0x53, 0xfa,
|
||||||
|
0x32, 0xd8, 0xdf, 0xfa, 0x07, 0x9f, 0xb8, 0x6a,
|
||||||
|
0xd1, 0xec, 0xd3, 0xd5, 0xf5, 0xfa, 0x00, 0x7e,
|
||||||
|
0x8c, 0xdd, 0xd5, 0xf2, 0xf8, 0xa8, 0x2e, 0x69,
|
||||||
|
0xe6, 0xc6, 0x61, 0x6c, 0x64, 0x7d, 0x9e, 0xad,
|
||||||
|
0x18, 0x28, 0x27, 0xce, 0x7a, 0x46, 0xad, 0x98,
|
||||||
|
0xe4, 0xba, 0x03, 0x14, 0x71, 0xe7, 0x7e, 0x06,
|
||||||
|
0x62, 0x48, 0xae, 0x8f, 0x50, 0x5e, 0x59, 0x4a,
|
||||||
|
0x58, 0x58, 0x1e, 0x2f, 0xe4, 0x28, 0x5e, 0xfa,
|
||||||
|
0x17, 0x83, 0xe9, 0x4e, 0x07, 0x46, 0x0b, 0x6c,
|
||||||
|
0xfc, 0x5b, 0x03, 0xf4, 0xfc, 0x9b, 0x24, 0x0f,
|
||||||
|
0xd4, 0x5b, 0xdb, 0xa0, 0x46, 0xf3, 0x86, 0xdd,
|
||||||
|
0x26, 0x55, 0x32, 0xb1, 0xa1, 0x11, 0xc2, 0xc5,
|
||||||
|
0xc0, 0x08, 0xeb, 0xbe, 0x96, 0x78, 0x25, 0xa1,
|
||||||
|
0x79, 0xaa, 0xe9, 0xff, 0xc2, 0x86, 0x94, 0x03,
|
||||||
|
0x2a, 0x38, 0x6c, 0x91, 0xfd, 0xcf, 0x7e, 0x23,
|
||||||
|
0xe3, 0xbb, 0x04, 0x3d, 0xda, 0x68, 0x9f, 0x4d,
|
||||||
|
0x72, 0xd5, 0xad, 0x97, 0x77, 0x2c, 0x3c, 0xce,
|
||||||
|
0x37, 0x2a, 0xd8, 0x72, 0x4d, 0xf2, 0xd7, 0xab,
|
||||||
|
0x62, 0x68, 0x3f, 0x85, 0x8a, 0xc5, 0xec, 0xc9,
|
||||||
|
0x02, 0x81, 0xc1, 0x00, 0x92, 0x43, 0x0c, 0x1d,
|
||||||
|
0x20, 0xa1, 0x01, 0x9d, 0xaa, 0x54, 0x5e, 0xf4,
|
||||||
|
0x83, 0x58, 0x8f, 0x83, 0xa1, 0x2d, 0x46, 0x75,
|
||||||
|
0xa1, 0x24, 0x4c, 0x9d, 0xf8, 0xf3, 0xbd, 0xb1,
|
||||||
|
0x8c, 0x7d, 0x89, 0xfc, 0x81, 0xeb, 0x1f, 0x1e,
|
||||||
|
0xb4, 0xe8, 0x25, 0xb1, 0xb5, 0x4d, 0x59, 0x3c,
|
||||||
|
0x76, 0x19, 0x29, 0xf9, 0x49, 0xf8, 0x45, 0xb2,
|
||||||
|
0xaa, 0xa8, 0x4e, 0xe5, 0x34, 0x43, 0xaf, 0x2e,
|
||||||
|
0xd1, 0x0f, 0x7b, 0x56, 0xfe, 0x6e, 0x4c, 0x1d,
|
||||||
|
0x95, 0x3e, 0xa6, 0x30, 0xc9, 0x69, 0xd8, 0x66,
|
||||||
|
0xf8, 0x77, 0x00, 0xb6, 0x31, 0xae, 0x9a, 0xf8,
|
||||||
|
0x55, 0xfb, 0xfc, 0x3f, 0x5f, 0x70, 0x03, 0x75,
|
||||||
|
0xbe, 0x55, 0xca, 0x2d, 0x68, 0xa0, 0x7d, 0x8e,
|
||||||
|
0xa4, 0x96, 0x0f, 0x01, 0x66, 0xe9, 0xf6, 0x13,
|
||||||
|
0x80, 0xe2, 0x05, 0xcf, 0x9e, 0x70, 0x56, 0x00,
|
||||||
|
0x97, 0xea, 0xd7, 0x6d, 0xb6, 0xa0, 0x6a, 0x95,
|
||||||
|
0x86, 0x36, 0xf2, 0xff, 0xc5, 0x67, 0x98, 0x7d,
|
||||||
|
0x04, 0x0d, 0x3b, 0x31, 0xbc, 0x2b, 0x09, 0xfd,
|
||||||
|
0x2d, 0x87, 0xda, 0xc1, 0x74, 0xca, 0x94, 0x73,
|
||||||
|
0x6e, 0xeb, 0x5f, 0xe5, 0x34, 0x49, 0xdf, 0xf4,
|
||||||
|
0x61, 0xe0, 0xfa, 0x64, 0xfe, 0x05, 0x3a, 0x25,
|
||||||
|
0xcc, 0x87, 0xf4, 0x03, 0x38, 0xca, 0xf2, 0xe8,
|
||||||
|
0x4f, 0xb9, 0x4f, 0x79, 0x55, 0x43, 0xf3, 0x46,
|
||||||
|
0xfd, 0xbc, 0xd2, 0x95, 0xb8, 0x99, 0xfc, 0xb8,
|
||||||
|
0xb3, 0xa5, 0x04, 0xa1, 0x02, 0x81, 0xc0, 0x47,
|
||||||
|
0xc6, 0x9c, 0x18, 0x54, 0xe5, 0xbb, 0xf9, 0xf4,
|
||||||
|
0x38, 0xd2, 0xc0, 0xd1, 0x1a, 0xcc, 0xdb, 0x06,
|
||||||
|
0x87, 0x75, 0x1f, 0x13, 0xa2, 0x7f, 0x8b, 0x45,
|
||||||
|
0x54, 0xcb, 0x43, 0xf8, 0xbb, 0x94, 0xd6, 0x2e,
|
||||||
|
0x56, 0x5c, 0x69, 0x6d, 0x83, 0xb5, 0x45, 0x46,
|
||||||
|
0x68, 0x5c, 0x76, 0x1e, 0x6c, 0x0c, 0x53, 0x59,
|
||||||
|
0xcc, 0x19, 0xc7, 0x81, 0x62, 0x66, 0x92, 0x02,
|
||||||
|
0x8f, 0xa6, 0xdb, 0x50, 0x1c, 0x67, 0xfc, 0x82,
|
||||||
|
0x56, 0x2b, 0x4b, 0x1f, 0x97, 0x87, 0xc4, 0x7d,
|
||||||
|
0x20, 0xda, 0xd3, 0x3f, 0x28, 0xf9, 0x55, 0xfe,
|
||||||
|
0x84, 0x50, 0xc5, 0x3b, 0xd4, 0xaf, 0xf5, 0x3d,
|
||||||
|
0x43, 0xce, 0xdc, 0x55, 0x11, 0x87, 0xdb, 0x72,
|
||||||
|
0x66, 0xcc, 0x83, 0xc4, 0x8b, 0x20, 0xae, 0x59,
|
||||||
|
0x4d, 0xeb, 0xac, 0xb5, 0x4a, 0xec, 0x66, 0x09,
|
||||||
|
0x37, 0x55, 0x14, 0x21, 0x57, 0xff, 0x0a, 0xac,
|
||||||
|
0xda, 0xb1, 0xae, 0x31, 0xab, 0x41, 0x30, 0x65,
|
||||||
|
0x02, 0x83, 0xd1, 0xdb, 0x65, 0xb7, 0x52, 0xa7,
|
||||||
|
0x21, 0x9f, 0x1f, 0x8f, 0x69, 0x23, 0x3b, 0xb8,
|
||||||
|
0xf9, 0x6d, 0xe7, 0xc1, 0x53, 0x9f, 0x8f, 0x67,
|
||||||
|
0xfc, 0x6e, 0x20, 0x18, 0x31, 0x89, 0xe7, 0xbb,
|
||||||
|
0xd4, 0xc1, 0x03, 0x67, 0xd6, 0xa5, 0x76, 0xc9,
|
||||||
|
0xea, 0x97, 0x93, 0x02, 0xca, 0x44, 0x52, 0x55,
|
||||||
|
0x0f, 0xed, 0x55, 0xb5, 0x49, 0xd6, 0x94, 0x59,
|
||||||
|
0xee, 0xcc, 0x1b, 0x5a, 0x00, 0x3d, 0xcd };
|
||||||
|
|
||||||
|
static const unsigned char kTestRsaPublicKey1_3072[] = {
|
||||||
|
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81,
|
||||||
|
0x00, 0xa5, 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74,
|
||||||
|
0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, 0x76, 0xbe,
|
||||||
|
0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f,
|
||||||
|
0xf3, 0x0f, 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e,
|
||||||
|
0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, 0xe4, 0xdd,
|
||||||
|
0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4,
|
||||||
|
0x55, 0x1b, 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08,
|
||||||
|
0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, 0xc2, 0x65,
|
||||||
|
0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04,
|
||||||
|
0x17, 0x01, 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34,
|
||||||
|
0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, 0xb4, 0x7a,
|
||||||
|
0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92,
|
||||||
|
0xf1, 0x14, 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf,
|
||||||
|
0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, 0xa0, 0x0a,
|
||||||
|
0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01,
|
||||||
|
0xe3, 0xb6, 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61,
|
||||||
|
0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, 0xf2, 0x59,
|
||||||
|
0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0,
|
||||||
|
0xbb, 0x31, 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30,
|
||||||
|
0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, 0xae, 0xb2,
|
||||||
|
0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa,
|
||||||
|
0xbb, 0x90, 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c,
|
||||||
|
0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, 0x94, 0x53,
|
||||||
|
0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd,
|
||||||
|
0xf0, 0x5f, 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a,
|
||||||
|
0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, 0xea, 0x1e,
|
||||||
|
0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8,
|
||||||
|
0xdb, 0x17, 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d,
|
||||||
|
0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, 0xb7, 0xf5,
|
||||||
|
0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15,
|
||||||
|
0x26, 0xaf, 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3,
|
||||||
|
0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, 0x9c, 0xee,
|
||||||
|
0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60,
|
||||||
|
0x0b, 0x42, 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c,
|
||||||
|
0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, 0xa6, 0x82,
|
||||||
|
0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12,
|
||||||
|
0x64, 0xe4, 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0,
|
||||||
|
0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, 0xe9, 0x4c,
|
||||||
|
0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0,
|
||||||
|
0xd1, 0x72, 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16,
|
||||||
|
0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, 0x8f, 0xe1,
|
||||||
|
0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87,
|
||||||
|
0x50, 0x04, 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c,
|
||||||
|
0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, 0x38, 0xff,
|
||||||
|
0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7,
|
||||||
|
0xf0, 0x33, 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d,
|
||||||
|
0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, 0xd5, 0xf0,
|
||||||
|
0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2,
|
||||||
|
0x87, 0x02, 0x03, 0x01, 0x00, 0x01 };
|
||||||
|
|
||||||
|
static const unsigned char kTestRsaPrivateKey2_2048[] = {
|
||||||
|
0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02,
|
||||||
|
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60,
|
||||||
|
0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4,
|
||||||
|
0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58,
|
||||||
|
0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88,
|
||||||
|
0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e,
|
||||||
|
0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa,
|
||||||
|
0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9,
|
||||||
|
0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7,
|
||||||
|
0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda,
|
||||||
|
0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb,
|
||||||
|
0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68,
|
||||||
|
0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2,
|
||||||
|
0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2,
|
||||||
|
0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2,
|
||||||
|
0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a,
|
||||||
|
0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c,
|
||||||
|
0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e,
|
||||||
|
0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56,
|
||||||
|
0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3,
|
||||||
|
0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88,
|
||||||
|
0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83,
|
||||||
|
0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4,
|
||||||
|
0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8,
|
||||||
|
0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f,
|
||||||
|
0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18,
|
||||||
|
0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e,
|
||||||
|
0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46,
|
||||||
|
0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c,
|
||||||
|
0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29,
|
||||||
|
0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b,
|
||||||
|
0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4,
|
||||||
|
0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f,
|
||||||
|
0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00,
|
||||||
|
0x01, 0x02, 0x82, 0x01, 0x00, 0x5e, 0x79, 0x65,
|
||||||
|
0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, 0x45, 0x0f,
|
||||||
|
0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, 0xd5, 0xde,
|
||||||
|
0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97, 0xeb, 0x3f,
|
||||||
|
0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, 0x3b, 0x5c,
|
||||||
|
0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, 0x46, 0xf5,
|
||||||
|
0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc, 0x45, 0x38,
|
||||||
|
0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, 0x5e, 0x79,
|
||||||
|
0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, 0xc1, 0x6b,
|
||||||
|
0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9, 0x0a, 0xfc,
|
||||||
|
0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, 0x68, 0x7b,
|
||||||
|
0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, 0xa7, 0x79,
|
||||||
|
0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7, 0x00, 0x21,
|
||||||
|
0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, 0xbe, 0x09,
|
||||||
|
0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, 0x0e, 0x97,
|
||||||
|
0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88, 0x7a, 0xd1,
|
||||||
|
0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, 0x25, 0x0b,
|
||||||
|
0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, 0xb6, 0xda,
|
||||||
|
0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14, 0x46, 0xc7,
|
||||||
|
0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, 0xde, 0x00,
|
||||||
|
0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, 0x68, 0x71,
|
||||||
|
0x17, 0x66, 0x12, 0x1a, 0x87, 0x27, 0xf7, 0xef,
|
||||||
|
0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, 0x6f, 0x35,
|
||||||
|
0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, 0x13, 0x86,
|
||||||
|
0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64, 0xe8, 0x86,
|
||||||
|
0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, 0x1f, 0x51,
|
||||||
|
0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, 0x70, 0x98,
|
||||||
|
0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f, 0x45, 0x6a,
|
||||||
|
0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, 0xf6, 0x06,
|
||||||
|
0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, 0x96, 0xa9,
|
||||||
|
0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21, 0xe0, 0xac,
|
||||||
|
0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, 0xb2, 0x51,
|
||||||
|
0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, 0x81, 0x81,
|
||||||
|
0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, 0x45, 0x2d,
|
||||||
|
0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, 0x78, 0xcc,
|
||||||
|
0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60,
|
||||||
|
0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, 0xb6, 0x7d,
|
||||||
|
0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, 0x84, 0x68,
|
||||||
|
0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51,
|
||||||
|
0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, 0xe6, 0x4d,
|
||||||
|
0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, 0x3a, 0x83,
|
||||||
|
0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7,
|
||||||
|
0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, 0x9e, 0xf7,
|
||||||
|
0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, 0x18, 0x6c,
|
||||||
|
0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f,
|
||||||
|
0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, 0x06, 0x74,
|
||||||
|
0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, 0x3c, 0x2d,
|
||||||
|
0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85,
|
||||||
|
0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, 0xf8, 0xcd,
|
||||||
|
0x37, 0x02, 0x81, 0x81, 0x00, 0xce, 0x15, 0x43,
|
||||||
|
0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41,
|
||||||
|
0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, 0x23, 0x87,
|
||||||
|
0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, 0xcb, 0x43,
|
||||||
|
0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb,
|
||||||
|
0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, 0x6d, 0x61,
|
||||||
|
0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, 0xa7, 0x5b,
|
||||||
|
0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99,
|
||||||
|
0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, 0xd8, 0x3e,
|
||||||
|
0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, 0xc1, 0xee,
|
||||||
|
0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4,
|
||||||
|
0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, 0x46, 0xcd,
|
||||||
|
0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, 0x0d, 0x6c,
|
||||||
|
0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb,
|
||||||
|
0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, 0xab, 0x54,
|
||||||
|
0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, 0x53, 0xa8,
|
||||||
|
0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80,
|
||||||
|
0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, 0xff, 0x73,
|
||||||
|
0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, 0x0a, 0x2d,
|
||||||
|
0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90,
|
||||||
|
0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, 0x79, 0xa0,
|
||||||
|
0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, 0xeb, 0xdb,
|
||||||
|
0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1,
|
||||||
|
0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, 0x33, 0xb2,
|
||||||
|
0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, 0x37, 0x43,
|
||||||
|
0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82,
|
||||||
|
0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, 0x0f, 0x62,
|
||||||
|
0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, 0xf9, 0xca,
|
||||||
|
0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28,
|
||||||
|
0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, 0x02, 0x71,
|
||||||
|
0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, 0xb1, 0x31,
|
||||||
|
0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95,
|
||||||
|
0x41, 0x29, 0x40, 0x19, 0x83, 0x35, 0x24, 0x69,
|
||||||
|
0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, 0xcc, 0x3b,
|
||||||
|
0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76,
|
||||||
|
0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, 0xcb, 0x47,
|
||||||
|
0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, 0x6a, 0xab,
|
||||||
|
0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2,
|
||||||
|
0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, 0xa8, 0xcd,
|
||||||
|
0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, 0x5f, 0xdf,
|
||||||
|
0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b,
|
||||||
|
0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, 0x4b, 0x81,
|
||||||
|
0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, 0x77, 0xe6,
|
||||||
|
0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa,
|
||||||
|
0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, 0x3b, 0x70,
|
||||||
|
0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, 0xd9, 0xbe,
|
||||||
|
0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff,
|
||||||
|
0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, 0x6f, 0x44,
|
||||||
|
0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, 0xac, 0x21,
|
||||||
|
0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c,
|
||||||
|
0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, 0xe7, 0xa6,
|
||||||
|
0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, 0x28, 0x66,
|
||||||
|
0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7,
|
||||||
|
0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, 0x81, 0x7b,
|
||||||
|
0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, 0x70, 0xcf,
|
||||||
|
0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61,
|
||||||
|
0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, 0xf0, 0xad,
|
||||||
|
0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, 0x50, 0xcb,
|
||||||
|
0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29,
|
||||||
|
0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, 0x40, 0xda,
|
||||||
|
0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, 0xa2, 0x0d,
|
||||||
|
0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4,
|
||||||
|
0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, 0x47, 0xb2,
|
||||||
|
0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, 0x72, 0x2c,
|
||||||
|
0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe,
|
||||||
|
0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 };
|
||||||
|
|
||||||
|
static const unsigned char kTestRsaPublicKey2_2048[] = {
|
||||||
|
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
|
||||||
|
0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd,
|
||||||
|
0x54, 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94,
|
||||||
|
0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7,
|
||||||
|
0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61,
|
||||||
|
0x57, 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f,
|
||||||
|
0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e,
|
||||||
|
0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1,
|
||||||
|
0x4e, 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9,
|
||||||
|
0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31,
|
||||||
|
0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92,
|
||||||
|
0xf9, 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a,
|
||||||
|
0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39,
|
||||||
|
0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71,
|
||||||
|
0x8e, 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a,
|
||||||
|
0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54,
|
||||||
|
0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a,
|
||||||
|
0x67, 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82,
|
||||||
|
0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71,
|
||||||
|
0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a,
|
||||||
|
0x8b, 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66,
|
||||||
|
0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a,
|
||||||
|
0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b,
|
||||||
|
0xf1, 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2,
|
||||||
|
0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f,
|
||||||
|
0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe,
|
||||||
|
0x40, 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce,
|
||||||
|
0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77,
|
||||||
|
0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e,
|
||||||
|
0x86, 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97,
|
||||||
|
0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c,
|
||||||
|
0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04,
|
||||||
|
0x6b, 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a,
|
||||||
|
0x05, 0x02, 0x03, 0x01, 0x00, 0x01 };
|
||||||
|
|
||||||
|
static const unsigned char kTestRsaPrivateKey3_2048[] = {
|
||||||
|
0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02,
|
||||||
|
0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e,
|
||||||
|
0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40,
|
||||||
|
0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51,
|
||||||
|
0x3d, 0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04,
|
||||||
|
0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44,
|
||||||
|
0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c,
|
||||||
|
0x32, 0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d,
|
||||||
|
0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08,
|
||||||
|
0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78,
|
||||||
|
0xa3, 0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f,
|
||||||
|
0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d,
|
||||||
|
0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34,
|
||||||
|
0xbc, 0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99,
|
||||||
|
0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d,
|
||||||
|
0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57,
|
||||||
|
0x99, 0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3,
|
||||||
|
0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55,
|
||||||
|
0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a,
|
||||||
|
0xb6, 0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8,
|
||||||
|
0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7,
|
||||||
|
0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3,
|
||||||
|
0x44, 0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85,
|
||||||
|
0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06,
|
||||||
|
0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d,
|
||||||
|
0xea, 0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4,
|
||||||
|
0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae,
|
||||||
|
0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf,
|
||||||
|
0x94, 0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8,
|
||||||
|
0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84,
|
||||||
|
0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa,
|
||||||
|
0x16, 0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6,
|
||||||
|
0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b,
|
||||||
|
0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00,
|
||||||
|
0x01, 0x02, 0x82, 0x01, 0x00, 0x43, 0x8f, 0x19,
|
||||||
|
0x83, 0xb1, 0x27, 0x4e, 0xee, 0x98, 0xba, 0xcb,
|
||||||
|
0x54, 0xa0, 0x77, 0x11, 0x6d, 0xd4, 0x25, 0x31,
|
||||||
|
0x8c, 0xb0, 0x01, 0xcf, 0xe6, 0x80, 0x83, 0x14,
|
||||||
|
0x40, 0x67, 0x39, 0x33, 0x67, 0x03, 0x1e, 0xa0,
|
||||||
|
0x8b, 0xd1, 0x1d, 0xfd, 0x80, 0xa4, 0xb9, 0xe7,
|
||||||
|
0x57, 0x5e, 0xc8, 0x8e, 0x79, 0x71, 0xd5, 0x6b,
|
||||||
|
0x09, 0xe9, 0x2b, 0x41, 0xa0, 0x33, 0x64, 0xc9,
|
||||||
|
0x66, 0x33, 0xa1, 0xb1, 0x55, 0x07, 0x55, 0x98,
|
||||||
|
0x53, 0x10, 0xe6, 0xc0, 0x39, 0x6d, 0x61, 0xd9,
|
||||||
|
0xe8, 0x16, 0x52, 0x28, 0xe4, 0x2b, 0xda, 0x27,
|
||||||
|
0x01, 0xaf, 0x21, 0x4a, 0xe8, 0x55, 0x1d, 0x0b,
|
||||||
|
0xd1, 0x1c, 0xdc, 0xfd, 0xb3, 0x0b, 0xa6, 0x5c,
|
||||||
|
0xcc, 0x6e, 0x77, 0xb8, 0xe0, 0xd1, 0x4e, 0x0a,
|
||||||
|
0xd7, 0x7a, 0x5e, 0x18, 0xc3, 0xfb, 0xe9, 0xa1,
|
||||||
|
0x9c, 0xc3, 0x9c, 0xd4, 0x4a, 0x7e, 0x70, 0x72,
|
||||||
|
0x11, 0x18, 0x24, 0x56, 0x24, 0xdf, 0xf8, 0xba,
|
||||||
|
0xac, 0x5b, 0x54, 0xd3, 0xc4, 0x65, 0x69, 0xc8,
|
||||||
|
0x79, 0x94, 0x16, 0x88, 0x9a, 0x68, 0x1c, 0xbc,
|
||||||
|
0xd4, 0xca, 0xec, 0x5e, 0x07, 0x4a, 0xc9, 0x54,
|
||||||
|
0x7a, 0x4b, 0xdb, 0x19, 0x88, 0xf6, 0xbe, 0x50,
|
||||||
|
0x9d, 0x9e, 0x9d, 0x88, 0x5b, 0x4a, 0x23, 0x86,
|
||||||
|
0x2b, 0xa9, 0xa6, 0x6c, 0x70, 0x7d, 0xe1, 0x11,
|
||||||
|
0xba, 0xbf, 0x03, 0x2e, 0xf1, 0x46, 0x7e, 0x1b,
|
||||||
|
0xed, 0x06, 0x11, 0x57, 0xad, 0x4a, 0xcb, 0xe5,
|
||||||
|
0xb1, 0x11, 0x05, 0x0a, 0x30, 0xb1, 0x73, 0x79,
|
||||||
|
0xcd, 0x7a, 0x04, 0xcc, 0x70, 0xe9, 0x95, 0xe4,
|
||||||
|
0x27, 0xc2, 0xd5, 0x2d, 0x92, 0x44, 0xdf, 0xb4,
|
||||||
|
0x94, 0xa8, 0x73, 0xa1, 0x4a, 0xc3, 0xcc, 0xc4,
|
||||||
|
0x0e, 0x8d, 0xa1, 0x6a, 0xc2, 0xd8, 0x03, 0x7f,
|
||||||
|
0xfa, 0xa7, 0x76, 0x0d, 0xad, 0x87, 0x88, 0xa0,
|
||||||
|
0x77, 0xaf, 0x3b, 0x23, 0xd1, 0x66, 0x0b, 0x31,
|
||||||
|
0x2b, 0xaf, 0xef, 0xd5, 0x41, 0x02, 0x81, 0x81,
|
||||||
|
0x00, 0xdb, 0xc1, 0xe7, 0xdd, 0xba, 0x3c, 0x1f,
|
||||||
|
0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb, 0xd2, 0x47,
|
||||||
|
0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5, 0xda, 0xc2,
|
||||||
|
0x25, 0x64, 0x0a, 0x02, 0xbc, 0x7d, 0x7f, 0x50,
|
||||||
|
0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d, 0x77, 0x6d,
|
||||||
|
0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd, 0x18, 0xe9,
|
||||||
|
0x53, 0x0b, 0x5c, 0xa2, 0x71, 0x14, 0x02, 0xfd,
|
||||||
|
0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c, 0x2a, 0x4e,
|
||||||
|
0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26, 0xd0, 0xd8,
|
||||||
|
0x3d, 0xcf, 0x8c, 0x89, 0x65, 0x6c, 0x35, 0x19,
|
||||||
|
0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49, 0xcd, 0x6d,
|
||||||
|
0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9, 0xba, 0x43,
|
||||||
|
0xe5, 0xdd, 0x39, 0x3a, 0x78, 0x8f, 0x07, 0xb8,
|
||||||
|
0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8, 0x07, 0x02,
|
||||||
|
0x9b, 0x79, 0x26, 0x32, 0x22, 0x38, 0x91, 0x01,
|
||||||
|
0x90, 0x81, 0x29, 0x94, 0xad, 0x77, 0xeb, 0x86,
|
||||||
|
0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x29, 0x88,
|
||||||
|
0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77, 0x5d, 0x32,
|
||||||
|
0x12, 0x5e, 0xdf, 0x28, 0x0c, 0x96, 0x0d, 0xa8,
|
||||||
|
0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0, 0x41, 0xcb,
|
||||||
|
0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2, 0xfb, 0xd2,
|
||||||
|
0xf3, 0xf2, 0x95, 0xff, 0xd8, 0x33, 0x3f, 0x8c,
|
||||||
|
0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe, 0x32, 0x66,
|
||||||
|
0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe, 0x05, 0xb1,
|
||||||
|
0xbc, 0xbe, 0x44, 0x09, 0xb4, 0xfe, 0x95, 0x06,
|
||||||
|
0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d, 0x22, 0xa0,
|
||||||
|
0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09, 0x2c, 0xb8,
|
||||||
|
0xcc, 0xeb, 0x10, 0x4d, 0xa7, 0xd0, 0x4b, 0x46,
|
||||||
|
0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47, 0x55, 0xd3,
|
||||||
|
0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e, 0xf9, 0x5b,
|
||||||
|
0x7b, 0xae, 0x6d, 0x08, 0x9f, 0x0c, 0x2a, 0xe1,
|
||||||
|
0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55, 0xa3, 0x6d,
|
||||||
|
0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02, 0x81, 0x80,
|
||||||
|
0x61, 0x73, 0x3d, 0x64, 0xff, 0xdf, 0x05, 0x8d,
|
||||||
|
0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d, 0x7d, 0x53,
|
||||||
|
0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4, 0x72, 0x8f,
|
||||||
|
0xae, 0x2b, 0x1a, 0x47, 0x87, 0xc7, 0x5b, 0x78,
|
||||||
|
0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3, 0x8c, 0x0c,
|
||||||
|
0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a, 0x88, 0x7d,
|
||||||
|
0x40, 0xfe, 0x95, 0x32, 0x5b, 0xd3, 0x6f, 0x90,
|
||||||
|
0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d, 0xc5, 0xc7,
|
||||||
|
0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b, 0xcb, 0x78,
|
||||||
|
0xad, 0x7e, 0xfe, 0xb6, 0xb1, 0x23, 0x63, 0x01,
|
||||||
|
0x94, 0x9a, 0x99, 0x05, 0x63, 0xda, 0xea, 0xf1,
|
||||||
|
0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b, 0x35, 0xec,
|
||||||
|
0xcb, 0x0b, 0x43, 0xb8, 0x8e, 0x84, 0xb8, 0x09,
|
||||||
|
0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c, 0x7c, 0x95,
|
||||||
|
0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08, 0x09, 0x8f,
|
||||||
|
0x91, 0x17, 0x65, 0x4c, 0xff, 0x6e, 0xbc, 0x51,
|
||||||
|
0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d, 0x9d, 0xd8,
|
||||||
|
0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f, 0x85, 0xd7,
|
||||||
|
0xce, 0x72, 0x02, 0x38, 0xf2, 0x0f, 0x9c, 0x27,
|
||||||
|
0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d, 0x02, 0x19,
|
||||||
|
0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb, 0x61, 0x8e,
|
||||||
|
0xe6, 0xb8, 0xfc, 0x07, 0x3c, 0xd1, 0x1b, 0x16,
|
||||||
|
0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26, 0xb2, 0xbd,
|
||||||
|
0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11, 0xc5, 0xdd,
|
||||||
|
0xf9, 0xdb, 0x4e, 0x48, 0x52, 0xd8, 0xe6, 0x4b,
|
||||||
|
0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1, 0x7c, 0xd3,
|
||||||
|
0x51, 0x6b, 0x45, 0x46, 0x54, 0x11, 0x1a, 0x71,
|
||||||
|
0xd3, 0xcd, 0x6b, 0x8f, 0x79, 0x22, 0x83, 0x02,
|
||||||
|
0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed, 0x32, 0xd8,
|
||||||
|
0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec, 0x2c, 0x7e,
|
||||||
|
0xa4, 0x89, 0xdc, 0xbf, 0xf9, 0x0d, 0x32, 0xc8,
|
||||||
|
0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc, 0x70, 0x4e,
|
||||||
|
0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81, 0x81, 0x00,
|
||||||
|
0xd3, 0x35, 0x1b, 0x19, 0x75, 0x3f, 0x61, 0xf2,
|
||||||
|
0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf, 0x0c, 0x0a,
|
||||||
|
0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b, 0x13, 0x4d,
|
||||||
|
0x1f, 0x86, 0x58, 0x4f, 0xd8, 0xee, 0xfa, 0x76,
|
||||||
|
0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2, 0xd2, 0x62,
|
||||||
|
0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08, 0xa9, 0x83,
|
||||||
|
0x07, 0xac, 0x8c, 0xa3, 0x7e, 0x18, 0xc0, 0x1c,
|
||||||
|
0x57, 0x63, 0x8d, 0x05, 0x17, 0x47, 0x1b, 0xd3,
|
||||||
|
0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8, 0x1a, 0x43,
|
||||||
|
0x04, 0x36, 0xc8, 0x19, 0xbe, 0xdc, 0xa6, 0xe5,
|
||||||
|
0x0f, 0x25, 0x62, 0x24, 0x96, 0x92, 0xb6, 0xb3,
|
||||||
|
0x97, 0xad, 0x57, 0x9a, 0x90, 0x37, 0x4e, 0x31,
|
||||||
|
0x44, 0x74, 0xfa, 0x7c, 0xb4, 0xea, 0xfc, 0x15,
|
||||||
|
0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e, 0xed, 0x5b,
|
||||||
|
0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b,
|
||||||
|
0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8, 0xee, 0x24 };
|
||||||
|
|
||||||
|
static const unsigned char kTestRsaPublicKey3_2048[] = {
|
||||||
|
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
|
||||||
|
0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb,
|
||||||
|
0x43, 0x51, 0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89,
|
||||||
|
0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83,
|
||||||
|
0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5,
|
||||||
|
0xa7, 0x5c, 0x61, 0x36, 0x44, 0xb3, 0x08, 0x05,
|
||||||
|
0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a,
|
||||||
|
0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f,
|
||||||
|
0x35, 0xb7, 0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10,
|
||||||
|
0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74,
|
||||||
|
0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f,
|
||||||
|
0xc2, 0xd7, 0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b,
|
||||||
|
0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47,
|
||||||
|
0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e,
|
||||||
|
0x56, 0x93, 0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95,
|
||||||
|
0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03,
|
||||||
|
0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75,
|
||||||
|
0x53, 0xfc, 0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4,
|
||||||
|
0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c,
|
||||||
|
0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c,
|
||||||
|
0xe2, 0x6a, 0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b,
|
||||||
|
0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0,
|
||||||
|
0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7,
|
||||||
|
0x20, 0xec, 0x24, 0x05, 0x06, 0xd9, 0x84, 0x51,
|
||||||
|
0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a,
|
||||||
|
0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f,
|
||||||
|
0x59, 0x6e, 0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee,
|
||||||
|
0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87,
|
||||||
|
0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3,
|
||||||
|
0xbc, 0xe8, 0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a,
|
||||||
|
0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f,
|
||||||
|
0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b,
|
||||||
|
0xa7, 0xdd, 0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69,
|
||||||
|
0x6d, 0x02, 0x03, 0x01, 0x00, 0x01 };
|
||||||
|
|
||||||
|
RsaTestKeys::RsaTestKeys() :
|
||||||
|
private_key_1_3072_bits_(kTestRsaPrivateKey1_3072, kTestRsaPrivateKey1_3072
|
||||||
|
+ sizeof(kTestRsaPrivateKey1_3072)),
|
||||||
|
public_key_1_3072_bits_(kTestRsaPublicKey1_3072, kTestRsaPublicKey1_3072
|
||||||
|
+ sizeof(kTestRsaPublicKey1_3072)),
|
||||||
|
private_key_2_2048_bits_(kTestRsaPrivateKey2_2048, kTestRsaPrivateKey2_2048
|
||||||
|
+ sizeof(kTestRsaPrivateKey2_2048)),
|
||||||
|
public_key_2_2048_bits_(kTestRsaPublicKey2_2048, kTestRsaPublicKey2_2048
|
||||||
|
+ sizeof(kTestRsaPublicKey2_2048)),
|
||||||
|
private_key_3_2048_bits_(kTestRsaPrivateKey3_2048, kTestRsaPrivateKey3_2048
|
||||||
|
+ sizeof(kTestRsaPrivateKey3_2048)),
|
||||||
|
public_key_3_2048_bits_(kTestRsaPublicKey3_2048, kTestRsaPublicKey3_2048
|
||||||
|
+ sizeof(kTestRsaPublicKey3_2048)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
64
common/rsa_test_keys.h
Normal file
64
common/rsa_test_keys.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// RSA keys generated using fake_prng for purposes of testing.
|
||||||
|
|
||||||
|
#ifndef COMMON_RSA_TEST_KEYS_H_
|
||||||
|
#define COMMON_RSA_TEST_KEYS_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Container for test RSA keys
|
||||||
|
class RsaTestKeys {
|
||||||
|
public:
|
||||||
|
RsaTestKeys();
|
||||||
|
|
||||||
|
// Returns 3072-bit private RSA test key 1
|
||||||
|
const std::string& private_test_key_1_3072_bits() const {
|
||||||
|
return private_key_1_3072_bits_;
|
||||||
|
}
|
||||||
|
// Returns 3072-bit public RSA test key 1
|
||||||
|
const std::string& public_test_key_1_3072_bits() const {
|
||||||
|
return public_key_1_3072_bits_;
|
||||||
|
}
|
||||||
|
// Returns 2048-bit private RSA test key 2
|
||||||
|
const std::string& private_test_key_2_2048_bits() const {
|
||||||
|
return private_key_2_2048_bits_;
|
||||||
|
}
|
||||||
|
// Returns 2048-bit public RSA test key 2
|
||||||
|
const std::string& public_test_key_2_2048_bits() const {
|
||||||
|
return public_key_2_2048_bits_;
|
||||||
|
}
|
||||||
|
// Returns 2048-bit private RSA test key 3
|
||||||
|
const std::string& private_test_key_3_2048_bits() const {
|
||||||
|
return private_key_3_2048_bits_;
|
||||||
|
}
|
||||||
|
// Returns 2048-bit public RSA test key 3
|
||||||
|
const std::string& public_test_key_3_2048_bits() const {
|
||||||
|
return public_key_3_2048_bits_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RsaTestKeys(const RsaTestKeys&) = delete;
|
||||||
|
RsaTestKeys& operator=(const RsaTestKeys&) = delete;
|
||||||
|
|
||||||
|
std::string private_key_1_3072_bits_;
|
||||||
|
std::string public_key_1_3072_bits_;
|
||||||
|
std::string private_key_2_2048_bits_;
|
||||||
|
std::string public_key_2_2048_bits_;
|
||||||
|
std::string private_key_3_2048_bits_;
|
||||||
|
std::string public_key_3_2048_bits_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_RSA_TEST_KEYS_H_
|
||||||
386
common/rsa_util.cc
Normal file
386
common/rsa_util.cc
Normal file
@@ -0,0 +1,386 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// RSA utility functions for serializing and deserializing RSA keys,
|
||||||
|
// encryption, and signing.
|
||||||
|
|
||||||
|
#include "common/rsa_util.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "openssl/bio.h"
|
||||||
|
#include "openssl/evp.h"
|
||||||
|
#include "openssl/pem.h"
|
||||||
|
#include "openssl/x509.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace rsa_util {
|
||||||
|
|
||||||
|
static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
|
||||||
|
bool serialize_private_key) {
|
||||||
|
if (key == NULL) {
|
||||||
|
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
|
||||||
|
<< " RSA key is NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (serialized_key == NULL) {
|
||||||
|
LOG(ERROR) << "Pointer to hold serialized RSA" << (serialize_private_key ?
|
||||||
|
"Private" : "Public") << "Key is NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BIO* bio = BIO_new(BIO_s_mem());
|
||||||
|
if (bio == NULL) {
|
||||||
|
LOG(ERROR) << "BIO_new returned NULL";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
if ((serialize_private_key ?
|
||||||
|
i2d_RSAPrivateKey_bio(bio, const_cast<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) ==
|
||||||
|
serialized_size) {
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
LOG(ERROR) << "BIO_read failure";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(ERROR) << (serialize_private_key ? "Private" : "Public") <<
|
||||||
|
" key serialization failure";
|
||||||
|
}
|
||||||
|
BIO_free(bio);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key,
|
||||||
|
bool deserialize_private_key) {
|
||||||
|
if (serialized_key.empty()) {
|
||||||
|
LOG(ERROR) << "Serialized RSA" << (deserialize_private_key ?
|
||||||
|
"Private" : "Public") << "Key is empty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (key == NULL) {
|
||||||
|
LOG(ERROR) << "Pointer to hold new RSA " << (deserialize_private_key ?
|
||||||
|
"private" : "public") << " key is NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
|
||||||
|
serialized_key.size());
|
||||||
|
if (bio == NULL) {
|
||||||
|
LOG(ERROR) << "BIO_new_mem_buf returned NULL";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, NULL) :
|
||||||
|
d2i_RSAPublicKey_bio(bio, NULL);
|
||||||
|
BIO_free(bio);
|
||||||
|
if (*key == NULL) {
|
||||||
|
LOG(ERROR) << (deserialize_private_key ? "Private" : "Public") <<
|
||||||
|
" RSA key deserialization failure";
|
||||||
|
}
|
||||||
|
return *key != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SerializeRsaPrivateKey(const RSA* private_key,
|
||||||
|
std::string* serialized_private_key) {
|
||||||
|
return SerializeRsaKey(private_key, serialized_private_key, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeserializeRsaPrivateKey(const std::string& serialized_private_key,
|
||||||
|
RSA** private_key) {
|
||||||
|
return DeserializeRsaKey(serialized_private_key, private_key, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SerializeRsaPublicKey(const RSA* public_key,
|
||||||
|
std::string* serialized_public_key) {
|
||||||
|
return SerializeRsaKey(public_key, serialized_public_key, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
|
||||||
|
RSA** public_key) {
|
||||||
|
return DeserializeRsaKey(serialized_public_key, public_key, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||||
|
std::string* serialized_private_key) {
|
||||||
|
if (private_key == NULL) {
|
||||||
|
LOG(ERROR) << "Private RSA key is NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (serialized_private_key == NULL) {
|
||||||
|
LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// The following method of serializing a PKCS#8 PrivateKeyInfo object
|
||||||
|
// was obtained from analyzing the openssl utility code, as the official
|
||||||
|
// mechanism via i2d_PKCS8PrivateKey_bio is broken in the current openssl
|
||||||
|
// version (1.0.0c). Please refer to b/8560683.
|
||||||
|
EVP_PKEY* evp = EVP_PKEY_new();
|
||||||
|
if (evp == NULL) {
|
||||||
|
LOG(ERROR) << "EVP_PKEY_new returned NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
PKCS8_PRIV_KEY_INFO *pkcs8_pki = NULL;
|
||||||
|
BIO* bio = NULL;
|
||||||
|
if (EVP_PKEY_set1_RSA(evp, const_cast<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.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
bio = BIO_new(BIO_s_mem());
|
||||||
|
if (bio == NULL) {
|
||||||
|
LOG(ERROR) << "BIO_new returned NULL.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8_pki) == 0) {
|
||||||
|
LOG(ERROR) << "i2d_PKCS8_PRIV_KEY_INFO_bio failed.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int serialized_size = BIO_pending(bio);
|
||||||
|
serialized_private_key->assign(serialized_size, 0);
|
||||||
|
if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) !=
|
||||||
|
serialized_size) {
|
||||||
|
LOG(ERROR) << "BIO_read failed.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (bio != NULL) {
|
||||||
|
BIO_free(bio);
|
||||||
|
}
|
||||||
|
if (pkcs8_pki != NULL) {
|
||||||
|
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
|
||||||
|
}
|
||||||
|
EVP_PKEY_free(evp);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
|
||||||
|
RSA** private_key) {
|
||||||
|
if (serialized_private_key.empty()) {
|
||||||
|
LOG(ERROR) << "Serialized PrivateKeyInfo is empty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (private_key == NULL) {
|
||||||
|
LOG(ERROR) << "Pointer to hold new RSA private key is NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// The following method of deserializing a PKCS#8 PrivateKeyInfo object
|
||||||
|
// was obtained from analyzing the openssl utility code, as the official
|
||||||
|
// mechanism via d2i_PKCS8PrivateKey_bio is broken in the current openssl
|
||||||
|
// version (1.0.0c). Please refer to b/8560683.
|
||||||
|
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_private_key.data()),
|
||||||
|
serialized_private_key.size());
|
||||||
|
if (bio == NULL) {
|
||||||
|
LOG(ERROR) << "BIO_new_mem_buf returned NULL";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
EVP_PKEY* evp = NULL;
|
||||||
|
PKCS8_PRIV_KEY_INFO *pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
|
||||||
|
if (pkcs8_pki == NULL) {
|
||||||
|
LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
evp = EVP_PKCS82PKEY(pkcs8_pki);
|
||||||
|
if (evp == NULL) {
|
||||||
|
LOG(ERROR) << "EVP_PKCS82PKEY returned NULL.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
*private_key = EVP_PKEY_get1_RSA(evp);
|
||||||
|
if (*private_key == NULL) {
|
||||||
|
LOG(ERROR) << "PrivateKeyInfo did not contain an RSA key.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (evp != NULL) {
|
||||||
|
EVP_PKEY_free(evp);
|
||||||
|
}
|
||||||
|
if (pkcs8_pki != NULL) {
|
||||||
|
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
|
||||||
|
}
|
||||||
|
BIO_free(bio);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
|
||||||
|
std::string* private_key_info) {
|
||||||
|
RSA* key = NULL;
|
||||||
|
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
|
||||||
|
bool success = SerializePrivateKeyInfo(key, private_key_info);
|
||||||
|
RSA_free(key);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||||
|
std::string* rsa_private_key) {
|
||||||
|
RSA* key = NULL;
|
||||||
|
if (DeserializePrivateKeyInfo(private_key_info, &key)) {
|
||||||
|
bool success = SerializeRsaPrivateKey(key, rsa_private_key);
|
||||||
|
RSA_free(key);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
|
||||||
|
const std::string& passphrase,
|
||||||
|
std::string* serialized_private_key) {
|
||||||
|
if (private_key == NULL) {
|
||||||
|
LOG(ERROR) << "Private RSA key is NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (passphrase.empty()) {
|
||||||
|
LOG(ERROR) << "Passphrase for RSA key encryption is empty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (serialized_private_key == NULL) {
|
||||||
|
LOG(ERROR) << "Pointer to hold serialized EncryptedPrivateKeyInfo is NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EVP_PKEY* evp = EVP_PKEY_new();
|
||||||
|
if (evp == NULL) {
|
||||||
|
LOG(ERROR) << "EVP_PKEY_new returned NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
BIO* bio = NULL;
|
||||||
|
if (EVP_PKEY_set1_RSA(evp, const_cast<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.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (i2d_PKCS8PrivateKey_bio(bio, evp, EVP_aes_256_cbc(),
|
||||||
|
const_cast<char*>(passphrase.data()),
|
||||||
|
passphrase.size(), NULL, NULL) == 0) {
|
||||||
|
LOG(ERROR) << "i2d_PKCS8PrivateKey_bio failed.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int serialized_size = BIO_pending(bio);
|
||||||
|
serialized_private_key->assign(serialized_size, 0);
|
||||||
|
if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) !=
|
||||||
|
serialized_size) {
|
||||||
|
LOG(ERROR) << "BIO_read failed.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (bio != NULL) {
|
||||||
|
BIO_free(bio);
|
||||||
|
}
|
||||||
|
EVP_PKEY_free(evp);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below.
|
||||||
|
int get_password(char *buf, int size, int rwflag, void *u) {
|
||||||
|
CHECK(buf);
|
||||||
|
CHECK(u);
|
||||||
|
const std::string* pass(static_cast<const std::string*>(u));
|
||||||
|
if (!pass->empty() && size >= static_cast<int>(pass->size())) {
|
||||||
|
memcpy(buf, pass->data(), pass->size());
|
||||||
|
return pass->size();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key,
|
||||||
|
const std::string& passphrase,
|
||||||
|
RSA** private_key) {
|
||||||
|
if (serialized_private_key.empty()) {
|
||||||
|
LOG(ERROR) << "Serialized RSAEncryptedPrivateKeyInfo is empty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (passphrase.empty()) {
|
||||||
|
LOG(ERROR) << "Passphrase for RSA key decryption is empty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (private_key == NULL) {
|
||||||
|
LOG(ERROR) << "Pointer to hold new RSA private key is NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_private_key.data()),
|
||||||
|
serialized_private_key.size());
|
||||||
|
if (bio == NULL) {
|
||||||
|
LOG(ERROR) << "BIO_new_mem_buf returned NULL";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, NULL, get_password,
|
||||||
|
const_cast<std::string*>(&passphrase));
|
||||||
|
if (evp == NULL) {
|
||||||
|
LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned NULL.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
*private_key = EVP_PKEY_get1_RSA(evp);
|
||||||
|
if (*private_key == NULL) {
|
||||||
|
LOG(ERROR) << "EncryptedPrivateKeyInfo did not contain an RSA key.";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (evp != NULL) {
|
||||||
|
EVP_PKEY_free(evp);
|
||||||
|
}
|
||||||
|
BIO_free(bio);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
|
||||||
|
const std::string& passphrase,
|
||||||
|
std::string* private_key_info) {
|
||||||
|
RSA* key = NULL;
|
||||||
|
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
|
||||||
|
bool success = SerializeEncryptedPrivateKeyInfo(key,
|
||||||
|
passphrase,
|
||||||
|
private_key_info);
|
||||||
|
RSA_free(key);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||||
|
const std::string& passphrase,
|
||||||
|
std::string* rsa_private_key) {
|
||||||
|
RSA* key = NULL;
|
||||||
|
if (DeserializeEncryptedPrivateKeyInfo(private_key_info, passphrase, &key)) {
|
||||||
|
bool success = SerializeRsaPrivateKey(key, rsa_private_key);
|
||||||
|
RSA_free(key);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rsa_util
|
||||||
|
} // namespace widevine
|
||||||
153
common/rsa_util.h
Normal file
153
common/rsa_util.h
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// RSA utility functions for serializing and deserializing RSA keys,
|
||||||
|
// encryption, and signing.
|
||||||
|
|
||||||
|
#ifndef COMMON_RSA_UTIL_H_
|
||||||
|
#define COMMON_RSA_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "openssl/rsa.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace rsa_util {
|
||||||
|
|
||||||
|
// Serialize RSA private key into DER encoded PKCS#1 RSAPrivateKey.
|
||||||
|
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||||
|
// - serialized_private_key is a pointer to the std::string to hold the serialized
|
||||||
|
// PKCS#1 RSAPrivateKey object. Caller retains ownership of the string. This
|
||||||
|
// parameter must not be NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool SerializeRsaPrivateKey(const RSA* private_key,
|
||||||
|
std::string* serialized_private_key);
|
||||||
|
|
||||||
|
// Deserialize RSA private key from DER encoded PKCS#1 RSAPrivateKey.
|
||||||
|
// - serialized_private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
|
||||||
|
// deserialized.
|
||||||
|
// - private_key is a pointer to an RSA structure pointer to point to a newly
|
||||||
|
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||||
|
// which is not allocated if the method fails. This parameter must not be
|
||||||
|
// NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool DeserializeRsaPrivateKey(const std::string& serialized_private_key,
|
||||||
|
RSA** private_key);
|
||||||
|
|
||||||
|
// Serialize RSA key into DER encoded PKCS#1 RSAPublicKey.
|
||||||
|
// - public_key is the RSA key to be serialized, which must not be NULL.
|
||||||
|
// - serialized_public_key is a pointer to the std::string to hold the serialized
|
||||||
|
// PKCS#1 RSAPublicKey object. Caller retains ownership of the string. This
|
||||||
|
// parameter must not be NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool SerializeRsaPublicKey(const RSA* public_key,
|
||||||
|
std::string* serialized_public_key);
|
||||||
|
|
||||||
|
// Deserialize RSA public key from DER encoded PKCS#1 RSAPublicKey.
|
||||||
|
// - serialized_public_key is the DER-encoded PKCS#1 RSAPublicKey to be
|
||||||
|
// deserialized.
|
||||||
|
// - public_key is a pointer to an RSA structure pointer to point to a newly
|
||||||
|
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||||
|
// which is not allocated if the method fails. This parameter must not be
|
||||||
|
// NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
|
||||||
|
RSA** public_key);
|
||||||
|
|
||||||
|
// Serialize RSA private key into DER encoded PKCS#8 PrivateKeyInfo.
|
||||||
|
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||||
|
// - serialized_private_key is a pointer to the std::string to hold the serialized
|
||||||
|
// PKCS#8 PrivateKeyInfo object. Caller retains ownership of the string. This
|
||||||
|
// parameter must not be NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||||
|
std::string* serialized_private_key);
|
||||||
|
|
||||||
|
// Deserialize RSA private key from DER encoded PKCS#8 PrivateKeyInfo.
|
||||||
|
// - serialized_private_key is the DER-encoded PKCS#8 PrivateKeyInfo to be
|
||||||
|
// deserialized.
|
||||||
|
// - private_key is a pointer to an RSA structure pointer to point to a newly
|
||||||
|
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||||
|
// which is not allocated if the method fails. This parameter must not be
|
||||||
|
// NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
|
||||||
|
RSA** private_key);
|
||||||
|
|
||||||
|
// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8
|
||||||
|
// PrivateKeyInfo.
|
||||||
|
// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted.
|
||||||
|
// - private_key_info is a pointer to std::string to hold the PKCS#8 PrivateKeyInfo.
|
||||||
|
// The caller retains ownership of this parameter, which must not be NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
|
||||||
|
std::string* private_key_info);
|
||||||
|
|
||||||
|
// Convert DER-encoded PKCS#8 PrivateKeyInfo to DER-encoded PKCS#1
|
||||||
|
// RSAPrivateKey.
|
||||||
|
// - private_key_info is the PKCS#8 PrivateKeyInfo to be converted.
|
||||||
|
// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey.
|
||||||
|
// The caller retains ownership of this parameter, which must not be NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||||
|
std::string* rsa_private_key);
|
||||||
|
|
||||||
|
|
||||||
|
// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo.
|
||||||
|
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||||
|
// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption.
|
||||||
|
// - serialized_private_key is a pointer to the std::string to hold the serialized
|
||||||
|
// PKCS#8 EncryptedPrivateKeyInfo object. Caller retains ownership of the
|
||||||
|
// string. This parameter must not be NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
|
||||||
|
const std::string& passphrase,
|
||||||
|
std::string* serialized_private_key);
|
||||||
|
|
||||||
|
// Deserialize RSA private key from DER encoded PKCS#8 EncryptedPrivateKeyInfo.
|
||||||
|
// - serialized_private_key is the DER-encoded PKCS#8 EncryptedPrivateKeyInfo to
|
||||||
|
// be deserialized.
|
||||||
|
// - passphrase is the password to use for key decryption.
|
||||||
|
// - private_key is a pointer to an RSA structure pointer to point to a newly
|
||||||
|
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||||
|
// which is not allocated if the method fails. This parameter must not be
|
||||||
|
// NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key,
|
||||||
|
const std::string& passphrase,
|
||||||
|
RSA** private_key);
|
||||||
|
|
||||||
|
// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8
|
||||||
|
// EncryptedPrivateKeyInfo.
|
||||||
|
// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted.
|
||||||
|
// - passphrase is the password to use for PKCS#5 v2.1 AES-256-CBC encryption.
|
||||||
|
// - private_key_info is a pointer to std::string to hold the PKCS#8
|
||||||
|
// EncryptedPrivateKeyInfo.
|
||||||
|
// The caller retains ownership of this parameter, which must not be NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
|
||||||
|
const std::string& passphrase,
|
||||||
|
std::string* private_key_info);
|
||||||
|
|
||||||
|
// Convert DER-encoded PKCS#8 EncryptedPrivateKeyInfo to DER-encoded PKCS#1
|
||||||
|
// RSAPrivateKey.
|
||||||
|
// - private_key_info is the PKCS#8 EncryptedPrivateKeyInfo to be converted.
|
||||||
|
// - passphrase is the password to use for key decryption.
|
||||||
|
// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey.
|
||||||
|
// The caller retains ownership of this parameter, which must not be NULL.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||||
|
const std::string& passphrase,
|
||||||
|
std::string* rsa_private_key);
|
||||||
|
|
||||||
|
} // namespace rsa_util
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_RSA_UTIL_H_
|
||||||
225
common/rsa_util_test.cc
Normal file
225
common/rsa_util_test.cc
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Unit test for rsa_util RSA utilties library.
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "testing/base/public/test_utils.h"
|
||||||
|
#include "common/rsa_test_keys.h"
|
||||||
|
#include "common/rsa_util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace rsa_util {
|
||||||
|
|
||||||
|
class RsaUtilTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
RsaTestKeys test_keys_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(RsaUtilTest, SerializeDeserializePrivateKey) {
|
||||||
|
RSA* private_key;
|
||||||
|
std::string serialized_private_key;
|
||||||
|
// Key 1
|
||||||
|
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||||
|
test_keys_.private_test_key_1_3072_bits(), &private_key));
|
||||||
|
ASSERT_TRUE(private_key != NULL);
|
||||||
|
EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key));
|
||||||
|
EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_1_3072_bits());
|
||||||
|
EXPECT_EQ(RSA_check_key(private_key), 1);
|
||||||
|
RSA_free(private_key);
|
||||||
|
// Key 2
|
||||||
|
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||||
|
test_keys_.private_test_key_2_2048_bits(), &private_key));
|
||||||
|
ASSERT_TRUE(private_key != NULL);
|
||||||
|
EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key));
|
||||||
|
EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_2_2048_bits());
|
||||||
|
EXPECT_EQ(RSA_check_key(private_key), 1);
|
||||||
|
RSA_free(private_key);
|
||||||
|
// Key 3
|
||||||
|
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||||
|
test_keys_.private_test_key_3_2048_bits(), &private_key));
|
||||||
|
ASSERT_TRUE(private_key != NULL);
|
||||||
|
EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key));
|
||||||
|
EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_3_2048_bits());
|
||||||
|
EXPECT_EQ(RSA_check_key(private_key), 1);
|
||||||
|
RSA_free(private_key);
|
||||||
|
// Invalid key
|
||||||
|
EXPECT_FALSE(DeserializeRsaPrivateKey("this is a bad key", &private_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaUtilTest, SerializeDeserializePublicKey) {
|
||||||
|
RSA* public_key;
|
||||||
|
std::string serialized_public_key;
|
||||||
|
// Key 1
|
||||||
|
EXPECT_TRUE(DeserializeRsaPublicKey(
|
||||||
|
test_keys_.public_test_key_1_3072_bits(), &public_key));
|
||||||
|
ASSERT_TRUE(public_key != NULL);
|
||||||
|
EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key));
|
||||||
|
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits());
|
||||||
|
RSA_free(public_key);
|
||||||
|
// Key 2
|
||||||
|
EXPECT_TRUE(DeserializeRsaPublicKey(
|
||||||
|
test_keys_.public_test_key_2_2048_bits(), &public_key));
|
||||||
|
ASSERT_TRUE(public_key != NULL);
|
||||||
|
EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key));
|
||||||
|
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits());
|
||||||
|
RSA_free(public_key);
|
||||||
|
// Key 3
|
||||||
|
EXPECT_TRUE(DeserializeRsaPublicKey(
|
||||||
|
test_keys_.public_test_key_3_2048_bits(), &public_key));
|
||||||
|
ASSERT_TRUE(public_key != NULL);
|
||||||
|
EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key));
|
||||||
|
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits());
|
||||||
|
RSA_free(public_key);
|
||||||
|
// Invalid key
|
||||||
|
EXPECT_FALSE(DeserializeRsaPublicKey("this is a bad key", &public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaUtilTest, PublicKeyExtraction) {
|
||||||
|
RSA* private_key;
|
||||||
|
std::string serialized_public_key;
|
||||||
|
// Key 1
|
||||||
|
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||||
|
test_keys_.private_test_key_1_3072_bits(), &private_key));
|
||||||
|
ASSERT_TRUE(private_key != NULL);
|
||||||
|
EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key));
|
||||||
|
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits());
|
||||||
|
RSA_free(private_key);
|
||||||
|
// Key 2
|
||||||
|
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||||
|
test_keys_.private_test_key_2_2048_bits(), &private_key));
|
||||||
|
ASSERT_TRUE(private_key != NULL);
|
||||||
|
EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key));
|
||||||
|
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits());
|
||||||
|
RSA_free(private_key);
|
||||||
|
// Key 3
|
||||||
|
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||||
|
test_keys_.private_test_key_3_2048_bits(), &private_key));
|
||||||
|
ASSERT_TRUE(private_key != NULL);
|
||||||
|
EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key));
|
||||||
|
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits());
|
||||||
|
RSA_free(private_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaUtilTest, Pkcs8PrivateKeyInfo) {
|
||||||
|
// The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8
|
||||||
|
// serialization/deserialization functionality , so we test those.
|
||||||
|
std::string serialized_pkcs8;
|
||||||
|
std::string serialized_pkcs1;
|
||||||
|
// Key 1
|
||||||
|
EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo(
|
||||||
|
test_keys_.private_test_key_1_3072_bits(), &serialized_pkcs8));
|
||||||
|
EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8,
|
||||||
|
&serialized_pkcs1));
|
||||||
|
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||||
|
EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1);
|
||||||
|
// Key 2
|
||||||
|
EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo(
|
||||||
|
test_keys_.private_test_key_2_2048_bits(), &serialized_pkcs8));
|
||||||
|
EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8,
|
||||||
|
&serialized_pkcs1));
|
||||||
|
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||||
|
EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1);
|
||||||
|
// Key 3
|
||||||
|
EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo(
|
||||||
|
test_keys_.private_test_key_3_2048_bits(), &serialized_pkcs8));
|
||||||
|
EXPECT_TRUE(PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8,
|
||||||
|
&serialized_pkcs1));
|
||||||
|
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||||
|
EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaUtilTest, Pkcs8EncryptedPrivateKeyInfo) {
|
||||||
|
// The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8
|
||||||
|
// serialization/deserialization functionality , so we test those.
|
||||||
|
std::string serialized_pkcs8;
|
||||||
|
std::string serialized_pkcs1;
|
||||||
|
std::string passphrase("passphrase");
|
||||||
|
// Key 1
|
||||||
|
EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||||
|
test_keys_.private_test_key_1_3072_bits(), passphrase,
|
||||||
|
&serialized_pkcs8));
|
||||||
|
EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8,
|
||||||
|
passphrase,
|
||||||
|
&serialized_pkcs1));
|
||||||
|
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||||
|
EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1);
|
||||||
|
// Key 2
|
||||||
|
EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||||
|
test_keys_.private_test_key_2_2048_bits(), passphrase,
|
||||||
|
&serialized_pkcs8));
|
||||||
|
EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8,
|
||||||
|
passphrase,
|
||||||
|
&serialized_pkcs1));
|
||||||
|
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||||
|
EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1);
|
||||||
|
// Key 3
|
||||||
|
EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||||
|
test_keys_.private_test_key_3_2048_bits(), passphrase,
|
||||||
|
&serialized_pkcs8));
|
||||||
|
EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(serialized_pkcs8,
|
||||||
|
passphrase,
|
||||||
|
&serialized_pkcs1));
|
||||||
|
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||||
|
EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaUtilTest, FailOnInvalidParams) {
|
||||||
|
RSA* test_input_key = NULL;
|
||||||
|
RSA* test_output_key = NULL;
|
||||||
|
std::string test_string;
|
||||||
|
std::string pass("password");
|
||||||
|
ASSERT_TRUE(DeserializeRsaPrivateKey(
|
||||||
|
test_keys_.private_test_key_2_2048_bits(), &test_input_key));
|
||||||
|
ASSERT_TRUE(test_input_key != NULL);
|
||||||
|
EXPECT_FALSE(SerializeRsaPrivateKey(NULL, &test_string));
|
||||||
|
EXPECT_FALSE(SerializeRsaPrivateKey(test_input_key, NULL));
|
||||||
|
EXPECT_FALSE(SerializeRsaPublicKey(NULL, &test_string));
|
||||||
|
EXPECT_FALSE(SerializeRsaPublicKey(test_input_key, NULL));
|
||||||
|
EXPECT_FALSE(SerializePrivateKeyInfo(NULL, &test_string));
|
||||||
|
EXPECT_FALSE(SerializePrivateKeyInfo(test_input_key, NULL));
|
||||||
|
EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(NULL, pass, &test_string));
|
||||||
|
EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass, NULL));
|
||||||
|
EXPECT_FALSE(DeserializeRsaPrivateKey("", &test_output_key));
|
||||||
|
EXPECT_FALSE(DeserializeRsaPrivateKey(
|
||||||
|
test_keys_.private_test_key_2_2048_bits(), NULL));
|
||||||
|
EXPECT_FALSE(DeserializeRsaPublicKey("", &test_output_key));
|
||||||
|
EXPECT_FALSE(DeserializeRsaPublicKey(
|
||||||
|
test_keys_.public_test_key_2_2048_bits(), NULL));
|
||||||
|
EXPECT_FALSE(DeserializePrivateKeyInfo("", &test_output_key));
|
||||||
|
EXPECT_FALSE(DeserializePrivateKeyInfo(
|
||||||
|
test_keys_.private_test_key_2_2048_bits(), NULL));
|
||||||
|
EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo("", pass, &test_output_key));
|
||||||
|
EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo(
|
||||||
|
test_keys_.private_test_key_2_2048_bits(), pass, NULL));
|
||||||
|
RSA_free(test_input_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaUtilTest, Pkcs8FailOnInvalidPassword) {
|
||||||
|
RSA* test_input_key = NULL;
|
||||||
|
RSA* test_output_key = NULL;
|
||||||
|
std::string serialized_pkcs8;
|
||||||
|
std::string pass("password");
|
||||||
|
ASSERT_TRUE(DeserializeRsaPrivateKey(
|
||||||
|
test_keys_.private_test_key_2_2048_bits(), &test_input_key));
|
||||||
|
EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(test_input_key, "",
|
||||||
|
&serialized_pkcs8));
|
||||||
|
ASSERT_TRUE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass,
|
||||||
|
&serialized_pkcs8));
|
||||||
|
EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass + "a",
|
||||||
|
&test_output_key));
|
||||||
|
EXPECT_TRUE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass,
|
||||||
|
&test_output_key));
|
||||||
|
RSA_free(test_input_key);
|
||||||
|
RSA_free(test_output_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rsa_util
|
||||||
|
} // namespace widevine
|
||||||
29
common/sha_util.cc
Normal file
29
common/sha_util.cc
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
|
||||||
|
#include "openssl/sha.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
std::string Sha1_Hash(const std::string& message) {
|
||||||
|
return std::string(reinterpret_cast<char*>(
|
||||||
|
SHA1(reinterpret_cast<const uint8_t*>(message.data()),
|
||||||
|
message.size(), nullptr)),
|
||||||
|
SHA_DIGEST_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Sha256_Hash(const std::string& message) {
|
||||||
|
return std::string(reinterpret_cast<char*>(
|
||||||
|
SHA256(reinterpret_cast<const uint8_t*>(message.data()),
|
||||||
|
message.size(), nullptr)),
|
||||||
|
SHA256_DIGEST_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
24
common/sha_util.h
Normal file
24
common/sha_util.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_SHA_UTIL_H_
|
||||||
|
#define COMMON_SHA_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Calculates SHA1 hash.
|
||||||
|
std::string Sha1_Hash(const std::string& message);
|
||||||
|
|
||||||
|
// Calculates SHA256 hash.
|
||||||
|
std::string Sha256_Hash(const std::string& message);
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_SHA_UTIL_H_
|
||||||
51
common/sha_util_test.cc
Normal file
51
common/sha_util_test.cc
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
TEST(ShaUtilTest, Sha1Empty) {
|
||||||
|
const uint8_t kExpected[] = {
|
||||||
|
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
|
||||||
|
0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
|
||||||
|
};
|
||||||
|
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha1_Hash(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ShaUtilTest, Sha256Empty) {
|
||||||
|
const uint8_t kExpected[] = {
|
||||||
|
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
|
||||||
|
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
|
||||||
|
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
||||||
|
};
|
||||||
|
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha256_Hash(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ShaUtilTest, Sha1) {
|
||||||
|
const uint8_t kExpected[] = {
|
||||||
|
0x5f, 0xfd, 0x3b, 0x85, 0x2c, 0xcd, 0xd4, 0x48, 0x80, 0x9a,
|
||||||
|
0xbb, 0x17, 0x2e, 0x19, 0xbb, 0xb9, 0xc0, 0x1a, 0x43, 0xa4,
|
||||||
|
};
|
||||||
|
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||||
|
Sha1_Hash("random std::string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ShaUtilTest, Sha256) {
|
||||||
|
const uint8_t kExpected[] = {
|
||||||
|
0xa8, 0x2a, 0xf4, 0xe9, 0x6b, 0x5b, 0x8d, 0x82, 0x5a, 0x69, 0x10,
|
||||||
|
0xb9, 0x08, 0xec, 0xcd, 0xc4, 0x40, 0x1a, 0xf1, 0xe6, 0x63, 0xdb,
|
||||||
|
0x5e, 0xdf, 0x2d, 0x7d, 0xfb, 0x71, 0x2d, 0xb9, 0x65, 0x29,
|
||||||
|
};
|
||||||
|
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||||
|
Sha256_Hash("random std::string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
40
example/BUILD
Normal file
40
example/BUILD
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Build file for the example code.
|
||||||
|
|
||||||
|
package(
|
||||||
|
default_visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "example_data",
|
||||||
|
srcs = glob(["example_data/*"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "provisioning_example",
|
||||||
|
srcs = ["provisioning_example.cc"],
|
||||||
|
deps = [
|
||||||
|
"//provisioning_sdk/public:libprovisioning_sdk",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "provisioning_message_generator",
|
||||||
|
srcs = ["provisioning_message_generator.cc"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"//common:aes_cbc_util",
|
||||||
|
"//common:file_util",
|
||||||
|
"//common:random_util",
|
||||||
|
"//common:rsa_key",
|
||||||
|
"//protos/public:certificate_provisioning_proto",
|
||||||
|
"//protos/public:client_identification_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
BIN
example/example_data/certificate_list
Normal file
BIN
example/example_data/certificate_list
Normal file
Binary file not shown.
BIN
example/example_data/intermediate.encrypted.private
Normal file
BIN
example/example_data/intermediate.encrypted.private
Normal file
Binary file not shown.
1
example/example_data/intermediate.passphrase
Normal file
1
example/example_data/intermediate.passphrase
Normal file
@@ -0,0 +1 @@
|
|||||||
|
intermediate_passphrase
|
||||||
BIN
example/example_data/intermediate.public
Normal file
BIN
example/example_data/intermediate.public
Normal file
Binary file not shown.
BIN
example/example_data/message
Normal file
BIN
example/example_data/message
Normal file
Binary file not shown.
BIN
example/example_data/provisioner.cert
Normal file
BIN
example/example_data/provisioner.cert
Normal file
Binary file not shown.
BIN
example/example_data/provisioner.encrypted.private
Normal file
BIN
example/example_data/provisioner.encrypted.private
Normal file
Binary file not shown.
1
example/example_data/provisioner.passphrase
Normal file
1
example/example_data/provisioner.passphrase
Normal file
@@ -0,0 +1 @@
|
|||||||
|
provider_passphrase
|
||||||
4
example/example_data/provisioner.spoid_secret
Normal file
4
example/example_data/provisioner.spoid_secret
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Twas bryllyg, and ye slythy toves
|
||||||
|
Did gyre and gymble in ye wabe:
|
||||||
|
All mimsy were ye borogoves;
|
||||||
|
And ye mome raths outgrabe.
|
||||||
BIN
example/example_data/root.private
Normal file
BIN
example/example_data/root.private
Normal file
Binary file not shown.
BIN
example/example_data/service.cert
Normal file
BIN
example/example_data/service.cert
Normal file
Binary file not shown.
BIN
example/example_data/service.encrypted.private
Normal file
BIN
example/example_data/service.encrypted.private
Normal file
Binary file not shown.
1
example/example_data/service.passphrase
Normal file
1
example/example_data/service.passphrase
Normal file
@@ -0,0 +1 @@
|
|||||||
|
service_passphrase
|
||||||
BIN
example/example_data/service.public
Normal file
BIN
example/example_data/service.public
Normal file
Binary file not shown.
BIN
example/example_data/user.private
Normal file
BIN
example/example_data/user.private
Normal file
Binary file not shown.
BIN
example/example_data/user.public
Normal file
BIN
example/example_data/user.public
Normal file
Binary file not shown.
113
example/provisioning_example.cc
Normal file
113
example/provisioning_example.cc
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "provisioning_sdk/public/certificate_type.h"
|
||||||
|
#include "provisioning_sdk/public/provisioning_engine.h"
|
||||||
|
#include "provisioning_sdk/public/provisioning_session.h"
|
||||||
|
#include "provisioning_sdk/public/provisioning_status.h"
|
||||||
|
|
||||||
|
using widevine::OK;
|
||||||
|
using widevine::ProvisioningEngine;
|
||||||
|
using widevine::ProvisioningSession;
|
||||||
|
using widevine::kCertTesting;
|
||||||
|
|
||||||
|
std::string GetContents(const std::string& file_name) {
|
||||||
|
if (file_name.empty()) {
|
||||||
|
std::cout << "File name is empty." << std::endl;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
FILE* file = fopen(file_name.c_str(), "r");
|
||||||
|
if (!file) {
|
||||||
|
std::cout << "Unable to open file " << file_name << std::endl;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string contents;
|
||||||
|
const size_t kReadSize = 0x1000;
|
||||||
|
char buffer[kReadSize];
|
||||||
|
while (true) {
|
||||||
|
size_t size_read = fread(buffer, sizeof(char), kReadSize, file);
|
||||||
|
if (size_read == 0) break;
|
||||||
|
contents.append(buffer, size_read);
|
||||||
|
}
|
||||||
|
if (!feof(file)) std::cout << "Failed to read all file contents.";
|
||||||
|
fclose(file);
|
||||||
|
return contents;;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
ProvisioningEngine engine;
|
||||||
|
|
||||||
|
// Call Initialize to setup the engine.
|
||||||
|
if (engine.Initialize(
|
||||||
|
kCertTesting, GetContents("example_data/service.cert"),
|
||||||
|
GetContents("example_data/service.encrypted.private"),
|
||||||
|
GetContents("example_data/service.passphrase"),
|
||||||
|
GetContents("example_data/provisioner.cert"),
|
||||||
|
GetContents("example_data/provisioner.encrypted.private"),
|
||||||
|
GetContents("example_data/provisioner.passphrase"),
|
||||||
|
GetContents("example_data/provisioner.spoid_secret")) != OK) {
|
||||||
|
std::cout << "Failed to initialize." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certificate status list should be updated periodically. In this example,
|
||||||
|
// we'll just set it once. Note that in practice, the expiration should not be
|
||||||
|
// 10 years long.
|
||||||
|
if (engine.SetCertificateStatusList(
|
||||||
|
GetContents("example_data/certificate_list"),
|
||||||
|
10 * 365 * 24 * 3600 /* 10 years */) != OK) {
|
||||||
|
std::cout << "Failed to set certificate status list." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before being able to process provisioning request for a specific type of
|
||||||
|
// device, we need to generate the intermediate certificate and add it to the
|
||||||
|
// engine first. This only needs to be done once for every new type of device.
|
||||||
|
const int kSystemId = 2001;
|
||||||
|
std::string certificate;
|
||||||
|
if (engine.GenerateDrmIntermediateCertificate(
|
||||||
|
kSystemId, GetContents("example_data/intermediate.public"),
|
||||||
|
&certificate) != OK) {
|
||||||
|
std::cout << "Failed to generate intermediate certificate." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (engine.AddDrmIntermediateCertificate(
|
||||||
|
certificate,
|
||||||
|
GetContents("example_data/intermediate.encrypted.private"),
|
||||||
|
GetContents("example_data/intermediate.passphrase")) != OK) {
|
||||||
|
std::cout << "Failed to add intermediate certificate." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In order to process provisioning request, we need to create a session. The
|
||||||
|
// public/private key pairs should be unique - they cannot be reused if the
|
||||||
|
// message is processed successfully; if ProcessMessage fails, they can be
|
||||||
|
// reused on another session.
|
||||||
|
std::unique_ptr<ProvisioningSession> session;
|
||||||
|
if (engine.NewProvisioningSession(GetContents("example_data/user.public"),
|
||||||
|
GetContents("example_data/user.private"),
|
||||||
|
&session) != OK) {
|
||||||
|
std::cout << "Failed to create session." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string response;
|
||||||
|
bool done;
|
||||||
|
if (session->ProcessMessage(GetContents("example_data/message"), &response,
|
||||||
|
&done) != OK) {
|
||||||
|
std::cout << "Failed to process message." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::cout << "Message processed successfully.";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
95
example/provisioning_message_generator.cc
Normal file
95
example/provisioning_message_generator.cc
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "gflags/gflags.h"
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "common/aes_cbc_util.h"
|
||||||
|
#include "common/file_util.h"
|
||||||
|
#include "common/random_util.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "protos/public/certificate_provisioning.pb.h"
|
||||||
|
#include "protos/public/client_identification.pb.h"
|
||||||
|
|
||||||
|
DEFINE_string(service_public_key_path, "example_data/service.public",
|
||||||
|
"Indicates the file path to service public key. If omitted, the "
|
||||||
|
"token is not encrypted.");
|
||||||
|
DEFINE_string(
|
||||||
|
certificate_path, "",
|
||||||
|
"Indicates the file path to the certificate chain. Should not be empty.");
|
||||||
|
DEFINE_string(certificate_private_key_path, "",
|
||||||
|
"Indicaets the file path to the certificate private key. Should "
|
||||||
|
"not be empty.");
|
||||||
|
DEFINE_string(
|
||||||
|
output_path, "",
|
||||||
|
"Specifies where to write the output message. Should not be empty.");
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
bool GenerateProvisioningMessage(const std::string& service_public_key,
|
||||||
|
const std::string& certificate,
|
||||||
|
const std::string& private_key, std::string* message) {
|
||||||
|
ClientIdentification client_id;
|
||||||
|
client_id.set_token(certificate);
|
||||||
|
client_id.set_type(ClientIdentification::OEM_DEVICE_CERTIFICATE);
|
||||||
|
|
||||||
|
ProvisioningRequest request;
|
||||||
|
if (service_public_key.empty()) {
|
||||||
|
*request.mutable_client_id() = client_id;
|
||||||
|
} else {
|
||||||
|
auto encrypted_id = request.mutable_encrypted_client_id();
|
||||||
|
CHECK(RandomBytes(16, encrypted_id->mutable_encrypted_client_id_iv()));
|
||||||
|
std::string privacy_key;
|
||||||
|
CHECK(RandomBytes(16, &privacy_key));
|
||||||
|
encrypted_id->set_encrypted_client_id(crypto_util::EncryptAesCbc(
|
||||||
|
privacy_key, encrypted_id->encrypted_client_id_iv(),
|
||||||
|
client_id.SerializeAsString()));
|
||||||
|
std::unique_ptr<RsaPublicKey> service_key(
|
||||||
|
RsaPublicKey::Create(service_public_key));
|
||||||
|
if (!service_key) return false;
|
||||||
|
CHECK(service_key->Encrypt(privacy_key,
|
||||||
|
encrypted_id->mutable_encrypted_privacy_key()));
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(RandomBytes(4, request.mutable_nonce()));
|
||||||
|
|
||||||
|
SignedProvisioningMessage signed_message;
|
||||||
|
signed_message.set_message(request.SerializeAsString());
|
||||||
|
std::unique_ptr<RsaPrivateKey> signer(RsaPrivateKey::Create(private_key));
|
||||||
|
CHECK(signer->GenerateSignature(signed_message.message(),
|
||||||
|
signed_message.mutable_signature()));
|
||||||
|
*message = signed_message.SerializeAsString();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
using widevine::GetContents;
|
||||||
|
using widevine::SetContents;
|
||||||
|
using widevine::GenerateProvisioningMessage;
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
std::string service_public_key;
|
||||||
|
if (!FLAGS_service_public_key_path.empty()) {
|
||||||
|
if (!GetContents(FLAGS_service_public_key_path, &service_public_key))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string certificate;
|
||||||
|
if (!GetContents(FLAGS_certificate_path, &certificate)) return 1;
|
||||||
|
std::string private_key;
|
||||||
|
if (!GetContents(FLAGS_certificate_private_key_path, &private_key)) return 1;
|
||||||
|
std::string message;
|
||||||
|
if (!GenerateProvisioningMessage(service_public_key, certificate, private_key,
|
||||||
|
&message))
|
||||||
|
return 1;
|
||||||
|
if (!SetContents(FLAGS_output_path, message)) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
183
glog.BUILD
Normal file
183
glog.BUILD
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Bazel build file for glog.
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
# Common options required by all library targets.
|
||||||
|
glog_copts = [
|
||||||
|
"-Isrc",
|
||||||
|
"-D_START_GOOGLE_NAMESPACE_='namespace google {'",
|
||||||
|
"-D_END_GOOGLE_NAMESPACE_=}",
|
||||||
|
"-DGOOGLE_NAMESPACE=google",
|
||||||
|
"-DGOOGLE_GLOG_DLL_DECL=",
|
||||||
|
"-DHAVE_LIB_GFLAGS",
|
||||||
|
"-DHAVE_PTHREAD",
|
||||||
|
"-DHAVE_RWLOCK",
|
||||||
|
"-DHAVE_PREAD",
|
||||||
|
"-DHAVE_PWRITE",
|
||||||
|
"-DHAVE_SYS_TIME_H",
|
||||||
|
"-DHAVE_SYS_UTSNAME_H",
|
||||||
|
"-DHAVE_UNISTD_H",
|
||||||
|
"-DHAVE_GLOB_H",
|
||||||
|
"-DHAVE___ATTRIBUTE__",
|
||||||
|
"-D__NR_gettid",
|
||||||
|
"-Wno-sign-compare",
|
||||||
|
]
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "glog",
|
||||||
|
srcs = [
|
||||||
|
"src/base/commandlineflags.h",
|
||||||
|
"src/base/googleinit.h",
|
||||||
|
"src/base/mutex.h",
|
||||||
|
"src/demangle.cc",
|
||||||
|
"src/demangle.h",
|
||||||
|
"src/glog/log_severity.h",
|
||||||
|
"src/logging.cc",
|
||||||
|
"src/raw_logging.cc",
|
||||||
|
"src/signalhandler.cc",
|
||||||
|
"src/stacktrace.h",
|
||||||
|
"src/stacktrace_generic-inl.h",
|
||||||
|
"src/stacktrace_libunwind-inl.h",
|
||||||
|
"src/stacktrace_powerpc-inl.h",
|
||||||
|
"src/stacktrace_x86-inl.h",
|
||||||
|
"src/stacktrace_x86_64-inl.h",
|
||||||
|
"src/symbolize.cc",
|
||||||
|
"src/symbolize.h",
|
||||||
|
"src/utilities.cc",
|
||||||
|
"src/utilities.h",
|
||||||
|
"src/vlog_is_on.cc",
|
||||||
|
":config_h",
|
||||||
|
":logging_h",
|
||||||
|
":raw_logging_h",
|
||||||
|
":stl_logging_h",
|
||||||
|
":vlog_is_on_h",
|
||||||
|
],
|
||||||
|
hdrs = glob(["glog/*.h"]),
|
||||||
|
copts = glog_copts,
|
||||||
|
includes = ["src/"],
|
||||||
|
linkopts = ["-pthread"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = ["//external:gflags"],
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "config_h",
|
||||||
|
srcs = ["src/config.h.cmake.in"],
|
||||||
|
outs = ["config.h"],
|
||||||
|
cmd = "awk '{ gsub(/^#cmakedefine/, \"//cmakedefine\"); print; }' $(<) > $(@)",
|
||||||
|
)
|
||||||
|
|
||||||
|
sub_cmd = ("awk '{ " +
|
||||||
|
"gsub(/@ac_google_start_namespace@/, \"namespace google {\"); " +
|
||||||
|
"gsub(/@ac_google_end_namespace@/, \"} // namespace google\"); " +
|
||||||
|
"gsub(/@ac_google_namespace@/, \"google\"); " +
|
||||||
|
("gsub(/@(ac_cv_have_stdint_h|ac_cv_have_uint16_t|" +
|
||||||
|
"ac_cv_have_libgflags|ac_cv_have_unistd_h|" +
|
||||||
|
"ac_cv_have___builtin_expect|" +
|
||||||
|
"ac_cv_cxx_using_operator)@/, \"1\"); ") +
|
||||||
|
"gsub(/@ac_cv___attribute___noreturn@/, \"__attribute__ ((noreturn))\"); " +
|
||||||
|
"gsub(/@ac_cv___attribute___noinline@/, \"__attribute__ ((noinline))\"); " +
|
||||||
|
"gsub(/@(ac_cv___attribute___[a-z0-9_]+)@/, \"\"); " +
|
||||||
|
"gsub(/@([a-z0-9_]+)@/, \"0\"); " +
|
||||||
|
"print; }' $(<) > $(@)")
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "logging_h",
|
||||||
|
srcs = ["src/glog/logging.h.in"],
|
||||||
|
outs = ["glog/logging.h"],
|
||||||
|
cmd = sub_cmd,
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "raw_logging_h",
|
||||||
|
srcs = ["src/glog/raw_logging.h.in"],
|
||||||
|
outs = ["glog/raw_logging.h"],
|
||||||
|
cmd = sub_cmd,
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "stl_logging_h",
|
||||||
|
srcs = ["src/glog/stl_logging.h.in"],
|
||||||
|
outs = ["glog/stl_logging.h"],
|
||||||
|
cmd = sub_cmd,
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "vlog_is_on_h",
|
||||||
|
srcs = ["src/glog/vlog_is_on.h.in"],
|
||||||
|
outs = ["glog/vlog_is_on.h"],
|
||||||
|
cmd = sub_cmd,
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "test_hdrs",
|
||||||
|
hdrs = [
|
||||||
|
"src/config_for_unittests.h",
|
||||||
|
"src/googletest.h",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "demangle_unittest",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["src/demangle_unittest.cc"],
|
||||||
|
copts = glog_copts,
|
||||||
|
data = ["src/demangle_unittest.txt"],
|
||||||
|
deps = [
|
||||||
|
":glog",
|
||||||
|
":test_hdrs",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "logging_unittest",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["src/logging_unittest.cc"],
|
||||||
|
copts = glog_copts,
|
||||||
|
data = ["src/logging_unittest.err"],
|
||||||
|
deps = [
|
||||||
|
":glog",
|
||||||
|
":test_hdrs",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "stacktrace_unittest",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["src/stacktrace_unittest.cc"],
|
||||||
|
copts = glog_copts,
|
||||||
|
deps = [
|
||||||
|
":glog",
|
||||||
|
":test_hdrs",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "symbolize_unittest",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["src/symbolize_unittest.cc"],
|
||||||
|
copts = glog_copts,
|
||||||
|
deps = [
|
||||||
|
":glog",
|
||||||
|
":test_hdrs",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "utilities_unittest",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["src/utilities_unittest.cc"],
|
||||||
|
copts = glog_copts,
|
||||||
|
deps = [
|
||||||
|
":glog",
|
||||||
|
":test_hdrs",
|
||||||
|
],
|
||||||
|
)
|
||||||
37
gtest.BUILD
Normal file
37
gtest.BUILD
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "gtest",
|
||||||
|
srcs = [
|
||||||
|
"googlemock/src/gmock-all.cc",
|
||||||
|
"googletest/src/gtest-all.cc",
|
||||||
|
],
|
||||||
|
hdrs = glob([
|
||||||
|
"googlemock/**/*.h",
|
||||||
|
"googlemock/src/*.cc",
|
||||||
|
"googletest/**/*.h",
|
||||||
|
"googletest/src/*.cc",
|
||||||
|
]),
|
||||||
|
includes = [
|
||||||
|
"googlemock",
|
||||||
|
"googlemock/include",
|
||||||
|
"googletest",
|
||||||
|
"googletest/include",
|
||||||
|
],
|
||||||
|
linkopts = ["-pthread"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "gtest_main",
|
||||||
|
srcs = ["googlemock/src/gmock_main.cc"],
|
||||||
|
linkopts = ["-pthread"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [":gtest"],
|
||||||
|
)
|
||||||
24
oem_certificate_generator/BUILD
Normal file
24
oem_certificate_generator/BUILD
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Build file for OEM certificate generation tool.
|
||||||
|
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
py_binary(
|
||||||
|
name = "oem_certificate",
|
||||||
|
srcs = ["oem_certificate.py"],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_test(
|
||||||
|
name = "oem_certificate_test",
|
||||||
|
srcs = ["oem_certificate_test.py"],
|
||||||
|
deps = [
|
||||||
|
":oem_certificate",
|
||||||
|
],
|
||||||
|
)
|
||||||
492
oem_certificate_generator/oem_certificate.py
Normal file
492
oem_certificate_generator/oem_certificate.py
Normal file
@@ -0,0 +1,492 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""OEM certificate generation tool.
|
||||||
|
|
||||||
|
Supports:
|
||||||
|
- Generating CSR (certificate signing request)
|
||||||
|
- Generating OEM intermediate certificate
|
||||||
|
- Generating OEM leaf certificate chain
|
||||||
|
- Erasing file securely
|
||||||
|
- Getting CSR/certificate/certificate chain information
|
||||||
|
|
||||||
|
Prerequirements (if running the script directly):
|
||||||
|
- Install pip: https://pip.pypa.io/en/stable/installing/
|
||||||
|
- Install python cryptography: https://cryptography.io/en/latest/installation/
|
||||||
|
|
||||||
|
Run 'python oem_certificate.py --help' to see available commands.
|
||||||
|
|
||||||
|
The arguments can be partially or fully loaded from a configuration file, for
|
||||||
|
example, if file "location.cfg" is,
|
||||||
|
|
||||||
|
-C=US
|
||||||
|
-ST=CA
|
||||||
|
-L=Kirkland
|
||||||
|
-O=Some Company
|
||||||
|
-OU=Some Unit
|
||||||
|
|
||||||
|
A command of
|
||||||
|
"python oem_certificate.py generate_csr @location.cfg "
|
||||||
|
"--output_csr_file=csr.pem --output_private_key_file=key.der",
|
||||||
|
is equivalent to
|
||||||
|
"python oem_certificate.py generate_csr -C=US -ST=CA -L=Kirkland "
|
||||||
|
"-O='Some Company' -OU='Some Unit' --output_csr_file=csr.pem "
|
||||||
|
"--output_private_key_file=key.der".
|
||||||
|
|
||||||
|
Note that (1) the arguments in the config file must be one per line; (2) the
|
||||||
|
arguments should not be quoted in the config file.
|
||||||
|
|
||||||
|
The script uses a default configuration file 'oem_certificate.cfg', which will
|
||||||
|
be loaded automatically if exists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from cryptography import utils
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.hazmat import backends
|
||||||
|
from cryptography.hazmat.backends import openssl
|
||||||
|
from cryptography.hazmat.backends.openssl import backend
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
from cryptography.x509 import oid
|
||||||
|
from pyasn1.codec.der import decoder
|
||||||
|
from pyasn1.codec.der import encoder
|
||||||
|
from pyasn1.type import univ
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
|
class WidevineSystemId(x509.UnrecognizedExtension):
|
||||||
|
"""Implement WidevineSystemId x509 extension."""
|
||||||
|
|
||||||
|
# oid of Widevine system id.
|
||||||
|
oid = oid.ObjectIdentifier('1.3.6.1.4.1.11129.4.1.1')
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
"""Inits from raw bytes."""
|
||||||
|
super(WidevineSystemId, self).__init__(WidevineSystemId.oid, value)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_int_value(cls, int_value):
|
||||||
|
"""Construct from integer system id value."""
|
||||||
|
return cls(encoder.encode(univ.Integer(int_value)))
|
||||||
|
|
||||||
|
def int_value(self):
|
||||||
|
"""Return the integer value of the system id."""
|
||||||
|
return int(decoder.decode(self.value)[0])
|
||||||
|
|
||||||
|
|
||||||
|
class X509CertificateChain(object):
|
||||||
|
"""Implement x509 certificate chain object.
|
||||||
|
|
||||||
|
cryptography does not support certificate chain (pkcs7 container) right
|
||||||
|
now, so we have to implement it using low level functions directly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Disable pylint on protected-access in class X509CertificateChain.
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
|
||||||
|
def __init__(self, certificates):
|
||||||
|
"""Inits from certificate list."""
|
||||||
|
self._certificates = certificates
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Iterates through certificates."""
|
||||||
|
return self._certificates.__iter__()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_der(cls, certificate_chain_data):
|
||||||
|
"""Load from DER formatted data."""
|
||||||
|
bio = backend._bytes_to_bio(certificate_chain_data)
|
||||||
|
pkcs7 = backend._lib.d2i_PKCS7_bio(bio.bio, backend._ffi.NULL)
|
||||||
|
if pkcs7 == backend._ffi.NULL:
|
||||||
|
raise ValueError('Unable to load certificate chain')
|
||||||
|
pkcs7 = backend._ffi.gc(pkcs7, backend._lib.PKCS7_free)
|
||||||
|
if not backend._lib.PKCS7_type_is_signed(pkcs7):
|
||||||
|
raise ValueError('Invalid certificate chain')
|
||||||
|
|
||||||
|
x509_stack = pkcs7.d.sign.cert
|
||||||
|
certificates = []
|
||||||
|
for i in xrange(backend._lib.sk_X509_num(x509_stack)):
|
||||||
|
x509_value = backend._ffi.gc(
|
||||||
|
backend._lib.X509_dup(backend._lib.sk_X509_value(x509_stack, i)),
|
||||||
|
backend._lib.X509_free)
|
||||||
|
certificates.append(openssl.x509._Certificate(backend, x509_value))
|
||||||
|
return cls(certificates)
|
||||||
|
|
||||||
|
def der_bytes(self):
|
||||||
|
"""Return DER formatted bytes."""
|
||||||
|
x509_stack = backend._ffi.gc(backend._lib.sk_X509_new_null(),
|
||||||
|
backend._lib.sk_X509_free)
|
||||||
|
for certificate in self._certificates:
|
||||||
|
backend._lib.sk_X509_push(x509_stack, certificate._x509)
|
||||||
|
|
||||||
|
pkcs7_partial = 0x4000
|
||||||
|
p7 = backend._lib.PKCS7_sign(backend._ffi.NULL, backend._ffi.NULL,
|
||||||
|
x509_stack, backend._ffi.NULL, pkcs7_partial)
|
||||||
|
p7 = backend._ffi.gc(p7, backend._lib.PKCS7_free)
|
||||||
|
|
||||||
|
bio = backend._create_mem_bio_gc()
|
||||||
|
backend.openssl_assert(backend._lib.i2d_PKCS7_bio(bio, p7) == 1)
|
||||||
|
return backend._read_mem_bio(bio)
|
||||||
|
|
||||||
|
|
||||||
|
def _multiple_of_1024(key_size_str):
|
||||||
|
"""argparse custom type function for key size."""
|
||||||
|
key_size = int(key_size_str)
|
||||||
|
if key_size % 1024:
|
||||||
|
msg = '%r is not multiple of 1024' % key_size_str
|
||||||
|
raise argparse.ArgumentTypeError(msg)
|
||||||
|
return key_size
|
||||||
|
|
||||||
|
|
||||||
|
def _valid_date(date_str):
|
||||||
|
"""argparse custom type function dates."""
|
||||||
|
return datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||||
|
|
||||||
|
|
||||||
|
def _get_encryption_algorithm(passphrase):
|
||||||
|
"""Convenient function to get the encryption algorithm."""
|
||||||
|
if passphrase:
|
||||||
|
return serialization.BestAvailableEncryption(passphrase)
|
||||||
|
else:
|
||||||
|
return serialization.NoEncryption()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_csr(args):
|
||||||
|
"""Subparser handler for generating certificate signing request."""
|
||||||
|
key = rsa.generate_private_key(
|
||||||
|
public_exponent=65537,
|
||||||
|
key_size=args.key_size,
|
||||||
|
backend=backends.default_backend())
|
||||||
|
args.output_private_key_file.write(
|
||||||
|
key.private_bytes(
|
||||||
|
encoding=serialization.Encoding.DER,
|
||||||
|
format=serialization.PrivateFormat.PKCS8,
|
||||||
|
encryption_algorithm=_get_encryption_algorithm(args.passphrase)))
|
||||||
|
|
||||||
|
csr = x509.CertificateSigningRequestBuilder().subject_name(
|
||||||
|
x509.Name([
|
||||||
|
# Provide various details about who we are.
|
||||||
|
x509.NameAttribute(oid.NameOID.COUNTRY_NAME,
|
||||||
|
six.text_type(args.country_name)),
|
||||||
|
x509.NameAttribute(oid.NameOID.STATE_OR_PROVINCE_NAME,
|
||||||
|
six.text_type(args.state_or_province_name)),
|
||||||
|
x509.NameAttribute(oid.NameOID.LOCALITY_NAME,
|
||||||
|
six.text_type(args.locality_name)),
|
||||||
|
x509.NameAttribute(oid.NameOID.ORGANIZATION_NAME,
|
||||||
|
six.text_type(args.organization_name)),
|
||||||
|
x509.NameAttribute(oid.NameOID.ORGANIZATIONAL_UNIT_NAME,
|
||||||
|
six.text_type(args.organizational_unit_name)),
|
||||||
|
])).sign(key, hashes.SHA256(), backends.default_backend())
|
||||||
|
args.output_csr_file.write(csr.public_bytes(serialization.Encoding.PEM))
|
||||||
|
|
||||||
|
|
||||||
|
def _random_serial_number():
|
||||||
|
"""Utility function to generate random serial number."""
|
||||||
|
return utils.int_from_bytes(os.urandom(16), byteorder='big')
|
||||||
|
|
||||||
|
|
||||||
|
def _build_certificate(subject_name, issuer_name, system_id, not_valid_before,
|
||||||
|
valid_duration, public_key, signing_key, ca):
|
||||||
|
"""Utility function to build certificate."""
|
||||||
|
builder = x509.CertificateBuilder()
|
||||||
|
builder = builder.subject_name(subject_name).issuer_name(issuer_name)
|
||||||
|
|
||||||
|
if ca:
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False)
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.AuthorityKeyIdentifier.from_issuer_public_key(
|
||||||
|
signing_key.public_key()),
|
||||||
|
critical=False)
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.BasicConstraints(True, 0), critical=True)
|
||||||
|
if system_id:
|
||||||
|
builder = builder.add_extension(
|
||||||
|
WidevineSystemId.from_int_value(system_id), critical=False)
|
||||||
|
|
||||||
|
builder = builder.not_valid_before(not_valid_before).not_valid_after(
|
||||||
|
not_valid_before + datetime.timedelta(valid_duration)).serial_number(
|
||||||
|
_random_serial_number()).public_key(public_key)
|
||||||
|
return builder.sign(
|
||||||
|
private_key=signing_key,
|
||||||
|
algorithm=hashes.SHA256(),
|
||||||
|
backend=backends.default_backend())
|
||||||
|
|
||||||
|
|
||||||
|
def generate_intermediate_certificate(args):
|
||||||
|
"""Subparser handler for generating intermediate certificate."""
|
||||||
|
root_cert = x509.load_der_x509_certificate(args.root_certificate_file.read(),
|
||||||
|
backends.default_backend())
|
||||||
|
root_private_key = serialization.load_der_private_key(
|
||||||
|
args.root_private_key_file.read(),
|
||||||
|
password=args.root_private_key_passphrase,
|
||||||
|
backend=backends.default_backend())
|
||||||
|
if (root_private_key.public_key().public_numbers() !=
|
||||||
|
root_cert.public_key().public_numbers()):
|
||||||
|
raise ValueError('Root certificate does not match with root private key')
|
||||||
|
csr = x509.load_pem_x509_csr(args.csr_file.read(), backends.default_backend())
|
||||||
|
|
||||||
|
certificate = _build_certificate(csr.subject, root_cert.subject,
|
||||||
|
args.system_id, args.not_valid_before,
|
||||||
|
args.valid_duration,
|
||||||
|
csr.public_key(), root_private_key, True)
|
||||||
|
args.output_certificate_file.write(
|
||||||
|
certificate.public_bytes(serialization.Encoding.DER))
|
||||||
|
|
||||||
|
|
||||||
|
def generate_leaf_certificate(args):
|
||||||
|
"""Subparser handler for generating leaf certificate."""
|
||||||
|
intermediate_cert_bytes = args.intermediate_certificate_file.read()
|
||||||
|
intermediate_cert = x509.load_der_x509_certificate(intermediate_cert_bytes,
|
||||||
|
backends.default_backend())
|
||||||
|
intermediate_private_key = serialization.load_der_private_key(
|
||||||
|
args.intermediate_private_key_file.read(),
|
||||||
|
password=args.intermediate_private_key_passphrase,
|
||||||
|
backend=backends.default_backend())
|
||||||
|
if (intermediate_private_key.public_key().public_numbers() !=
|
||||||
|
intermediate_cert.public_key().public_numbers()):
|
||||||
|
raise ValueError(
|
||||||
|
'Intermediate certificate does not match with intermediate private key')
|
||||||
|
|
||||||
|
system_id_raw_bytes = intermediate_cert.extensions.get_extension_for_oid(
|
||||||
|
WidevineSystemId.oid).value.value
|
||||||
|
system_id = WidevineSystemId(system_id_raw_bytes).int_value()
|
||||||
|
|
||||||
|
name_attributes = [
|
||||||
|
x509.NameAttribute(oid.NameOID.COMMON_NAME, u'{0}-leaf'.format(system_id))
|
||||||
|
]
|
||||||
|
name_attributes.extend([
|
||||||
|
attribute for attribute in intermediate_cert.subject
|
||||||
|
if attribute.oid != oid.NameOID.COMMON_NAME
|
||||||
|
])
|
||||||
|
subject_name = x509.Name(name_attributes)
|
||||||
|
|
||||||
|
leaf_private_key = rsa.generate_private_key(
|
||||||
|
public_exponent=65537,
|
||||||
|
key_size=args.key_size,
|
||||||
|
backend=backends.default_backend())
|
||||||
|
args.output_private_key_file.write(
|
||||||
|
leaf_private_key.private_bytes(
|
||||||
|
encoding=serialization.Encoding.DER,
|
||||||
|
format=serialization.PrivateFormat.PKCS8,
|
||||||
|
encryption_algorithm=_get_encryption_algorithm(args.passphrase)))
|
||||||
|
|
||||||
|
certificate = _build_certificate(subject_name, intermediate_cert.subject,
|
||||||
|
system_id, args.not_valid_before,
|
||||||
|
args.valid_duration,
|
||||||
|
leaf_private_key.public_key(),
|
||||||
|
intermediate_private_key, False)
|
||||||
|
args.output_certificate_file.write(
|
||||||
|
X509CertificateChain([certificate, intermediate_cert]).der_bytes())
|
||||||
|
|
||||||
|
|
||||||
|
def secure_erase(args):
|
||||||
|
"""Subparser handler for secure erasing of a file."""
|
||||||
|
length = args.file.tell()
|
||||||
|
for _ in xrange(args.passes):
|
||||||
|
args.file.seek(0)
|
||||||
|
for _ in xrange(length):
|
||||||
|
args.file.write(os.urandom(1))
|
||||||
|
args.file.close()
|
||||||
|
os.remove(args.file.name)
|
||||||
|
|
||||||
|
|
||||||
|
def _certificate_as_string(cert):
|
||||||
|
"""Utility function to format certificate as string."""
|
||||||
|
lines = ['Certificate Subject Name:']
|
||||||
|
lines.extend([' {0}'.format(attribute) for attribute in cert.subject])
|
||||||
|
lines.append('Issuer Name:')
|
||||||
|
lines.extend([' {0}'.format(attribute) for attribute in cert.issuer])
|
||||||
|
lines.append('Key Size: {0.key_size}'.format(cert.public_key()))
|
||||||
|
try:
|
||||||
|
system_id_raw_bytes = cert.extensions.get_extension_for_oid(
|
||||||
|
WidevineSystemId.oid).value.value
|
||||||
|
lines.append('Widevine System Id: {0}'.format(
|
||||||
|
WidevineSystemId(system_id_raw_bytes).int_value()))
|
||||||
|
except x509.ExtensionNotFound:
|
||||||
|
pass
|
||||||
|
lines.append('Not valid before: {0.not_valid_before}'.format(cert))
|
||||||
|
lines.append('Not valid after: {0.not_valid_after}'.format(cert))
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def _csr_as_string(csr):
|
||||||
|
"""Utility function to format csr as string."""
|
||||||
|
lines = ['CSR Subject Name:']
|
||||||
|
lines.extend([' {0}'.format(attribute) for attribute in csr.subject])
|
||||||
|
lines.append('Key Size: {0.key_size}'.format(csr.public_key()))
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_csr(data):
|
||||||
|
"""Utility function for get_info to parse csr."""
|
||||||
|
return _csr_as_string(
|
||||||
|
x509.load_pem_x509_csr(data, backends.default_backend()))
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_certificate(data):
|
||||||
|
"""Utility function for get_info to parse certificate."""
|
||||||
|
return _certificate_as_string(
|
||||||
|
x509.load_der_x509_certificate(data, backends.default_backend()))
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_certificate_chain(data):
|
||||||
|
"""Utility function for get_info to parse certificate chain."""
|
||||||
|
return '\n\n'.join([
|
||||||
|
_certificate_as_string(certificate)
|
||||||
|
for certificate in X509CertificateChain.load_der(data)
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def get_info(args, out=sys.stdout):
|
||||||
|
"""Subparser handler to get csr or certificate information."""
|
||||||
|
# The input is either a CSR or a certificate, or a certificate chain.
|
||||||
|
# Loop through the corresponding handlers one by one.
|
||||||
|
data = args.file.read()
|
||||||
|
for handler in [_handle_csr, _handle_certificate, _handle_certificate_chain]:
|
||||||
|
try:
|
||||||
|
out.write(handler(data))
|
||||||
|
return
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
print('Error occurred. The input file is not a valid CSR, nor certificate, '
|
||||||
|
'nor certificate chain.')
|
||||||
|
|
||||||
|
|
||||||
|
def create_parser():
|
||||||
|
"""Command line parsing."""
|
||||||
|
parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
|
||||||
|
subparsers = parser.add_subparsers()
|
||||||
|
|
||||||
|
# Subparser for certificate signing request generation.
|
||||||
|
parser_csr = subparsers.add_parser(
|
||||||
|
'generate_csr', help='generate certificate signing request')
|
||||||
|
parser_csr.add_argument(
|
||||||
|
'--key_size',
|
||||||
|
type=_multiple_of_1024,
|
||||||
|
default=3072,
|
||||||
|
help='specify RSA key size.')
|
||||||
|
parser_csr.add_argument('-C', '--country_name', required=True)
|
||||||
|
parser_csr.add_argument('-ST', '--state_or_province_name', required=True)
|
||||||
|
parser_csr.add_argument('-L', '--locality_name', required=True)
|
||||||
|
parser_csr.add_argument('-O', '--organization_name', required=True)
|
||||||
|
parser_csr.add_argument('-OU', '--organizational_unit_name', required=True)
|
||||||
|
parser_csr.add_argument(
|
||||||
|
'--output_csr_file', type=argparse.FileType('wb'), required=True)
|
||||||
|
parser_csr.add_argument(
|
||||||
|
'--output_private_key_file', type=argparse.FileType('wb'), required=True)
|
||||||
|
parser_csr.add_argument(
|
||||||
|
'--passphrase',
|
||||||
|
help=('specify an optional passphrase to encrypt the private key. The '
|
||||||
|
'private key is not encrypted if omitted.'))
|
||||||
|
parser_csr.set_defaults(func=generate_csr)
|
||||||
|
|
||||||
|
# Subparser for intermediate certificate generation.
|
||||||
|
parser_intermediate_cert = subparsers.add_parser(
|
||||||
|
'generate_intermediate_certificate',
|
||||||
|
help=('generate intermediate certificate from csr. This should only be '
|
||||||
|
'used for testing.'))
|
||||||
|
parser_intermediate_cert.add_argument(
|
||||||
|
'--not_valid_before',
|
||||||
|
type=_valid_date,
|
||||||
|
default=datetime.datetime.today(),
|
||||||
|
help='certificate validity start date - format YYYY-MM-DD')
|
||||||
|
parser_intermediate_cert.add_argument(
|
||||||
|
'--valid_duration',
|
||||||
|
type=int,
|
||||||
|
default=3650,
|
||||||
|
help='the certificate will expire after the specified number of days.')
|
||||||
|
parser_intermediate_cert.add_argument('--system_id', type=int, required=True)
|
||||||
|
parser_intermediate_cert.add_argument(
|
||||||
|
'--csr_file', type=argparse.FileType('rb'), required=True)
|
||||||
|
parser_intermediate_cert.add_argument(
|
||||||
|
'--root_certificate_file', type=argparse.FileType('rb'), required=True)
|
||||||
|
parser_intermediate_cert.add_argument(
|
||||||
|
'--root_private_key_file', type=argparse.FileType('rb'), required=True)
|
||||||
|
parser_intermediate_cert.add_argument('--root_private_key_passphrase')
|
||||||
|
parser_intermediate_cert.add_argument(
|
||||||
|
'--output_certificate_file', type=argparse.FileType('wb'), required=True)
|
||||||
|
parser_intermediate_cert.set_defaults(func=generate_intermediate_certificate)
|
||||||
|
|
||||||
|
# Subparser for leaf certificate generation.
|
||||||
|
parser_leaf_cert = subparsers.add_parser(
|
||||||
|
'generate_leaf_certificate', help='generate leaf certificate')
|
||||||
|
parser_leaf_cert.add_argument(
|
||||||
|
'--key_size',
|
||||||
|
type=_multiple_of_1024,
|
||||||
|
default=3072,
|
||||||
|
help='specify RSA key size.')
|
||||||
|
parser_leaf_cert.add_argument(
|
||||||
|
'--not_valid_before',
|
||||||
|
type=_valid_date,
|
||||||
|
default=datetime.datetime.today(),
|
||||||
|
help='certificate validity start date - format YYYY-MM-DD')
|
||||||
|
parser_leaf_cert.add_argument(
|
||||||
|
'--valid_duration',
|
||||||
|
type=int,
|
||||||
|
default=3650,
|
||||||
|
help='the certificate will expire after the specified number of days.')
|
||||||
|
parser_leaf_cert.add_argument(
|
||||||
|
'--intermediate_certificate_file',
|
||||||
|
type=argparse.FileType('rb'),
|
||||||
|
required=True)
|
||||||
|
parser_leaf_cert.add_argument(
|
||||||
|
'--intermediate_private_key_file',
|
||||||
|
type=argparse.FileType('rb'),
|
||||||
|
required=True)
|
||||||
|
parser_leaf_cert.add_argument('--intermediate_private_key_passphrase')
|
||||||
|
parser_leaf_cert.add_argument(
|
||||||
|
'--output_certificate_file', type=argparse.FileType('wb'), required=True)
|
||||||
|
parser_leaf_cert.add_argument(
|
||||||
|
'--output_private_key_file', type=argparse.FileType('wb'), required=True)
|
||||||
|
parser_leaf_cert.add_argument(
|
||||||
|
'--passphrase',
|
||||||
|
help=('specify an optional passphrase to encrypt the private key. The '
|
||||||
|
'private key is not encrypted if omitted.'))
|
||||||
|
parser_leaf_cert.set_defaults(func=generate_leaf_certificate)
|
||||||
|
|
||||||
|
# Subparser for secure file erase.
|
||||||
|
parser_erase = subparsers.add_parser('erase', help='erase a file securely')
|
||||||
|
parser_erase.add_argument(
|
||||||
|
'-F', '--file', type=argparse.FileType('a'), required=True)
|
||||||
|
parser_erase.add_argument(
|
||||||
|
'--passes',
|
||||||
|
type=int,
|
||||||
|
default=3,
|
||||||
|
help=('the file will be overwritten with random patterns for the '
|
||||||
|
'specified number of passes'))
|
||||||
|
parser_erase.set_defaults(func=secure_erase)
|
||||||
|
|
||||||
|
# Subparser to get CSR or certificate or certificate chain metadata.
|
||||||
|
parser_info = subparsers.add_parser(
|
||||||
|
'info', help='get CSR or certificate metadata')
|
||||||
|
parser_info.add_argument(
|
||||||
|
'-F', '--file', type=argparse.FileType('rb'), required=True)
|
||||||
|
parser_info.set_defaults(func=get_info)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = sys.argv[1:]
|
||||||
|
config_file_name = 'oem_certificate.cfg'
|
||||||
|
if os.path.isfile(config_file_name):
|
||||||
|
print 'Load from args default configuration file: ', config_file_name
|
||||||
|
args.append('@' + config_file_name)
|
||||||
|
parser_args = create_parser().parse_args(args)
|
||||||
|
parser_args.func(parser_args)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
538
oem_certificate_generator/oem_certificate_test.py
Normal file
538
oem_certificate_generator/oem_certificate_test.py
Normal file
@@ -0,0 +1,538 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import StringIO
|
||||||
|
import tempfile
|
||||||
|
import textwrap
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.hazmat import backends
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
from cryptography.x509 import oid
|
||||||
|
|
||||||
|
import oem_certificate
|
||||||
|
|
||||||
|
|
||||||
|
class ArgParseObject(object):
|
||||||
|
"""A convenient object to allow adding arbitrary attribute to it."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OemCertificateTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def _setup_csr_args(self, key_size=4096, passphrase=None):
|
||||||
|
args = ArgParseObject()
|
||||||
|
args.key_size = key_size
|
||||||
|
args.country_name = 'US'
|
||||||
|
args.state_or_province_name = 'WA'
|
||||||
|
args.locality_name = 'Kirkland'
|
||||||
|
args.organization_name = 'CompanyXYZ'
|
||||||
|
args.organizational_unit_name = 'ContentProtection'
|
||||||
|
args.output_csr_file = StringIO.StringIO()
|
||||||
|
args.output_private_key_file = StringIO.StringIO()
|
||||||
|
args.passphrase = passphrase
|
||||||
|
return args
|
||||||
|
|
||||||
|
def test_widevine_system_id(self):
|
||||||
|
system_id = 1234567890123
|
||||||
|
self.assertEqual(
|
||||||
|
oem_certificate.WidevineSystemId.from_int_value(system_id).int_value(),
|
||||||
|
system_id)
|
||||||
|
|
||||||
|
def test_generate_csr(self):
|
||||||
|
args = self._setup_csr_args()
|
||||||
|
oem_certificate.generate_csr(args)
|
||||||
|
# Verify CSR.
|
||||||
|
csr = x509.load_pem_x509_csr(args.output_csr_file.getvalue(),
|
||||||
|
backends.default_backend())
|
||||||
|
subject = csr.subject
|
||||||
|
self.assertEqual(
|
||||||
|
subject.get_attributes_for_oid(oid.NameOID.COUNTRY_NAME)[0].value,
|
||||||
|
args.country_name)
|
||||||
|
self.assertEqual(
|
||||||
|
subject.get_attributes_for_oid(oid.NameOID.STATE_OR_PROVINCE_NAME)[0]
|
||||||
|
.value, args.state_or_province_name)
|
||||||
|
self.assertEqual(
|
||||||
|
subject.get_attributes_for_oid(oid.NameOID.LOCALITY_NAME)[0].value,
|
||||||
|
args.locality_name)
|
||||||
|
self.assertEqual(
|
||||||
|
subject.get_attributes_for_oid(oid.NameOID.ORGANIZATION_NAME)[0].value,
|
||||||
|
args.organization_name)
|
||||||
|
self.assertEqual(
|
||||||
|
subject.get_attributes_for_oid(oid.NameOID.ORGANIZATIONAL_UNIT_NAME)[0]
|
||||||
|
.value, args.organizational_unit_name)
|
||||||
|
|
||||||
|
private_key = serialization.load_der_private_key(
|
||||||
|
args.output_private_key_file.getvalue(),
|
||||||
|
args.passphrase,
|
||||||
|
backend=backends.default_backend())
|
||||||
|
self.assertEqual(private_key.key_size, args.key_size)
|
||||||
|
self.assertEqual(csr.public_key().key_size, args.key_size)
|
||||||
|
# Verify csr and private key match.
|
||||||
|
self.assertEqual(csr.public_key().public_numbers(),
|
||||||
|
private_key.public_key().public_numbers())
|
||||||
|
|
||||||
|
def test_generate_csr_with_keysize4096_and_passphrase(self):
|
||||||
|
args = self._setup_csr_args(key_size=4096, passphrase='passphrase_4096')
|
||||||
|
oem_certificate.generate_csr(args)
|
||||||
|
private_key = serialization.load_der_private_key(
|
||||||
|
args.output_private_key_file.getvalue(),
|
||||||
|
'passphrase_4096',
|
||||||
|
backend=backends.default_backend())
|
||||||
|
csr = x509.load_pem_x509_csr(args.output_csr_file.getvalue(),
|
||||||
|
backends.default_backend())
|
||||||
|
self.assertEqual(private_key.key_size, 4096)
|
||||||
|
self.assertEqual(csr.public_key().key_size, 4096)
|
||||||
|
# Verify csr and private key match.
|
||||||
|
self.assertEqual(csr.public_key().public_numbers(),
|
||||||
|
private_key.public_key().public_numbers())
|
||||||
|
|
||||||
|
def _create_root_certificate_and_key(self):
|
||||||
|
key = rsa.generate_private_key(
|
||||||
|
public_exponent=65537,
|
||||||
|
key_size=3072,
|
||||||
|
backend=backends.default_backend())
|
||||||
|
subject_name = x509.Name(
|
||||||
|
[x509.NameAttribute(oid.NameOID.COMMON_NAME, u'root_cert')])
|
||||||
|
certificate = oem_certificate._build_certificate(
|
||||||
|
subject_name, subject_name, None,
|
||||||
|
datetime.datetime(2001, 8, 9), 1000, key.public_key(), key, True)
|
||||||
|
return (key, certificate)
|
||||||
|
|
||||||
|
def _setup_intermediate_cert_args(self, csr_bytes, root_key,
|
||||||
|
root_certificate):
|
||||||
|
args = ArgParseObject()
|
||||||
|
args.not_valid_before = datetime.datetime(2001, 8, 9)
|
||||||
|
args.valid_duration = 100
|
||||||
|
args.system_id = 1234554321
|
||||||
|
args.csr_file = StringIO.StringIO(csr_bytes)
|
||||||
|
args.root_private_key_passphrase = 'root_passphrase'
|
||||||
|
args.output_certificate_file = StringIO.StringIO()
|
||||||
|
|
||||||
|
serialized_private_key = root_key.private_bytes(
|
||||||
|
serialization.Encoding.DER,
|
||||||
|
format=serialization.PrivateFormat.PKCS8,
|
||||||
|
encryption_algorithm=serialization.BestAvailableEncryption(
|
||||||
|
args.root_private_key_passphrase))
|
||||||
|
serialized_certificate = root_certificate.public_bytes(
|
||||||
|
serialization.Encoding.DER)
|
||||||
|
|
||||||
|
args.root_certificate_file = StringIO.StringIO(serialized_certificate)
|
||||||
|
args.root_private_key_file = StringIO.StringIO(serialized_private_key)
|
||||||
|
return args
|
||||||
|
|
||||||
|
def test_generate_intermediate_certificate(self):
|
||||||
|
csr_args = self._setup_csr_args()
|
||||||
|
oem_certificate.generate_csr(csr_args)
|
||||||
|
csr_bytes = csr_args.output_csr_file.getvalue()
|
||||||
|
csr = x509.load_pem_x509_csr(csr_bytes, backends.default_backend())
|
||||||
|
|
||||||
|
root_key, root_certificate = self._create_root_certificate_and_key()
|
||||||
|
args = self._setup_intermediate_cert_args(csr_bytes, root_key,
|
||||||
|
root_certificate)
|
||||||
|
oem_certificate.generate_intermediate_certificate(args)
|
||||||
|
|
||||||
|
cert = x509.load_der_x509_certificate(
|
||||||
|
args.output_certificate_file.getvalue(), backends.default_backend())
|
||||||
|
self.assertEqual(cert.issuer, root_certificate.subject)
|
||||||
|
self.assertEqual(cert.subject, csr.subject)
|
||||||
|
|
||||||
|
system_id_raw_bytes = cert.extensions.get_extension_for_oid(
|
||||||
|
oem_certificate.WidevineSystemId.oid).value.value
|
||||||
|
self.assertEqual(
|
||||||
|
oem_certificate.WidevineSystemId(system_id_raw_bytes).int_value(),
|
||||||
|
args.system_id)
|
||||||
|
|
||||||
|
self.assertEqual(cert.not_valid_before, datetime.datetime(2001, 8, 9))
|
||||||
|
self.assertEqual(cert.not_valid_after, datetime.datetime(2001, 11, 17))
|
||||||
|
|
||||||
|
root_key.public_key().verify(cert.signature, cert.tbs_certificate_bytes,
|
||||||
|
padding.PKCS1v15(),
|
||||||
|
cert.signature_hash_algorithm)
|
||||||
|
|
||||||
|
def test_generate_intermediate_with_cert_mismatch_root_cert_and_key(self):
|
||||||
|
root_key1, _ = self._create_root_certificate_and_key()
|
||||||
|
_, root_certificate2 = self._create_root_certificate_and_key()
|
||||||
|
args = self._setup_intermediate_cert_args('some csr data', root_key1,
|
||||||
|
root_certificate2)
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
oem_certificate.generate_intermediate_certificate(args)
|
||||||
|
self.assertTrue('certificate does not match' in str(context.exception))
|
||||||
|
|
||||||
|
def _setup_leaf_cert_args(self,
|
||||||
|
intermediate_key_bytes,
|
||||||
|
intermediate_certificate_bytes,
|
||||||
|
key_size=1024,
|
||||||
|
passphrase=None):
|
||||||
|
args = ArgParseObject()
|
||||||
|
args.key_size = key_size
|
||||||
|
args.not_valid_before = datetime.datetime(2001, 8, 9)
|
||||||
|
args.valid_duration = 8000
|
||||||
|
args.intermediate_private_key_passphrase = None
|
||||||
|
args.output_certificate_file = StringIO.StringIO()
|
||||||
|
args.output_private_key_file = StringIO.StringIO()
|
||||||
|
args.passphrase = passphrase
|
||||||
|
|
||||||
|
args.intermediate_private_key_file = StringIO.StringIO(
|
||||||
|
intermediate_key_bytes)
|
||||||
|
args.intermediate_certificate_file = StringIO.StringIO(
|
||||||
|
intermediate_certificate_bytes)
|
||||||
|
return args
|
||||||
|
|
||||||
|
def _create_intermediate_certificate_and_key_bytes(self,
|
||||||
|
key_size=4096,
|
||||||
|
passphrase=None):
|
||||||
|
csr_args = self._setup_csr_args(key_size, passphrase)
|
||||||
|
oem_certificate.generate_csr(csr_args)
|
||||||
|
csr_bytes = csr_args.output_csr_file.getvalue()
|
||||||
|
|
||||||
|
root_key, root_certificate = self._create_root_certificate_and_key()
|
||||||
|
args = self._setup_intermediate_cert_args(csr_bytes, root_key,
|
||||||
|
root_certificate)
|
||||||
|
oem_certificate.generate_intermediate_certificate(args)
|
||||||
|
return (csr_args.output_private_key_file.getvalue(),
|
||||||
|
args.output_certificate_file.getvalue())
|
||||||
|
|
||||||
|
def test_generate_leaf_certificate(self):
|
||||||
|
intermediate_key_bytes, intermediate_certificate_bytes = (
|
||||||
|
self._create_intermediate_certificate_and_key_bytes())
|
||||||
|
args = self._setup_leaf_cert_args(intermediate_key_bytes,
|
||||||
|
intermediate_certificate_bytes)
|
||||||
|
oem_certificate.generate_leaf_certificate(args)
|
||||||
|
|
||||||
|
certificate_chain = oem_certificate.X509CertificateChain.load_der(
|
||||||
|
args.output_certificate_file.getvalue())
|
||||||
|
|
||||||
|
certificates = list(certificate_chain)
|
||||||
|
self.assertEqual(len(certificates), 2)
|
||||||
|
intermediate_cert = certificates[1]
|
||||||
|
leaf_cert = certificates[0]
|
||||||
|
self.assertEqual(
|
||||||
|
intermediate_cert.public_bytes(serialization.Encoding.DER),
|
||||||
|
intermediate_certificate_bytes)
|
||||||
|
intermediate_cert.public_key().verify(leaf_cert.signature,
|
||||||
|
leaf_cert.tbs_certificate_bytes,
|
||||||
|
padding.PKCS1v15(),
|
||||||
|
leaf_cert.signature_hash_algorithm)
|
||||||
|
|
||||||
|
self.assertEqual(leaf_cert.not_valid_before, datetime.datetime(2001, 8, 9))
|
||||||
|
self.assertEqual(leaf_cert.not_valid_after, datetime.datetime(2023, 7, 5))
|
||||||
|
|
||||||
|
system_id_raw_bytes = leaf_cert.extensions.get_extension_for_oid(
|
||||||
|
oem_certificate.WidevineSystemId.oid).value.value
|
||||||
|
self.assertEqual(
|
||||||
|
oem_certificate.WidevineSystemId(system_id_raw_bytes).int_value(),
|
||||||
|
1234554321)
|
||||||
|
|
||||||
|
leaf_key = serialization.load_der_private_key(
|
||||||
|
args.output_private_key_file.getvalue(),
|
||||||
|
args.passphrase,
|
||||||
|
backend=backends.default_backend())
|
||||||
|
self.assertEqual(leaf_key.key_size, args.key_size)
|
||||||
|
self.assertEqual(leaf_cert.public_key().key_size, args.key_size)
|
||||||
|
# Verify cert and private key match.
|
||||||
|
self.assertEqual(leaf_cert.public_key().public_numbers(),
|
||||||
|
leaf_key.public_key().public_numbers())
|
||||||
|
|
||||||
|
def test_generate_leaf_certificate_with_keysize4096_and_passphrase(self):
|
||||||
|
intermediate_key_bytes, intermediate_certificate_bytes = (
|
||||||
|
self._create_intermediate_certificate_and_key_bytes())
|
||||||
|
args = self._setup_leaf_cert_args(
|
||||||
|
intermediate_key_bytes,
|
||||||
|
intermediate_certificate_bytes,
|
||||||
|
key_size=4096,
|
||||||
|
passphrase='leaf passphrase')
|
||||||
|
oem_certificate.generate_leaf_certificate(args)
|
||||||
|
serialization.load_der_private_key(
|
||||||
|
args.output_private_key_file.getvalue(),
|
||||||
|
'leaf passphrase',
|
||||||
|
backend=backends.default_backend())
|
||||||
|
self.assertEqual(4096, args.key_size)
|
||||||
|
|
||||||
|
def test_get_csr_info(self):
|
||||||
|
args = self._setup_csr_args()
|
||||||
|
oem_certificate.generate_csr(args)
|
||||||
|
args.file = StringIO.StringIO(args.output_csr_file.getvalue())
|
||||||
|
output = StringIO.StringIO()
|
||||||
|
oem_certificate.get_info(args, output)
|
||||||
|
expected_info = """\
|
||||||
|
CSR Subject Name:
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value=u'US')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.8, name=stateOrProvinceName)>, value=u'WA')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.7, name=localityName)>, value=u'Kirkland')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.10, name=organizationName)>, value=u'CompanyXYZ')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>, value=u'ContentProtection')>
|
||||||
|
Key Size: 4096"""
|
||||||
|
self.assertEqual(output.getvalue(), textwrap.dedent(expected_info))
|
||||||
|
|
||||||
|
def test_get_certificate_info(self):
|
||||||
|
_, intermediate_certificate_bytes = (
|
||||||
|
self._create_intermediate_certificate_and_key_bytes())
|
||||||
|
args = ArgParseObject()
|
||||||
|
args.file = StringIO.StringIO(intermediate_certificate_bytes)
|
||||||
|
output = StringIO.StringIO()
|
||||||
|
oem_certificate.get_info(args, output)
|
||||||
|
expected_info = """\
|
||||||
|
Certificate Subject Name:
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value=u'US')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.8, name=stateOrProvinceName)>, value=u'WA')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.7, name=localityName)>, value=u'Kirkland')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.10, name=organizationName)>, value=u'CompanyXYZ')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>, value=u'ContentProtection')>
|
||||||
|
Issuer Name:
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'root_cert')>
|
||||||
|
Key Size: 4096
|
||||||
|
Widevine System Id: 1234554321
|
||||||
|
Not valid before: 2001-08-09 00:00:00
|
||||||
|
Not valid after: 2001-11-17 00:00:00"""
|
||||||
|
self.assertEqual(output.getvalue(), textwrap.dedent(expected_info))
|
||||||
|
|
||||||
|
def test_get_certificate_chain_info(self):
|
||||||
|
intermediate_key_bytes, intermediate_certificate_bytes = (
|
||||||
|
self._create_intermediate_certificate_and_key_bytes())
|
||||||
|
args = self._setup_leaf_cert_args(intermediate_key_bytes,
|
||||||
|
intermediate_certificate_bytes)
|
||||||
|
oem_certificate.generate_leaf_certificate(args)
|
||||||
|
args.file = StringIO.StringIO(args.output_certificate_file.getvalue())
|
||||||
|
output = StringIO.StringIO()
|
||||||
|
oem_certificate.get_info(args, output)
|
||||||
|
expected_info = """\
|
||||||
|
Certificate Subject Name:
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'1234554321-leaf')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value=u'US')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.8, name=stateOrProvinceName)>, value=u'WA')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.7, name=localityName)>, value=u'Kirkland')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.10, name=organizationName)>, value=u'CompanyXYZ')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>, value=u'ContentProtection')>
|
||||||
|
Issuer Name:
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value=u'US')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.8, name=stateOrProvinceName)>, value=u'WA')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.7, name=localityName)>, value=u'Kirkland')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.10, name=organizationName)>, value=u'CompanyXYZ')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>, value=u'ContentProtection')>
|
||||||
|
Key Size: 1024
|
||||||
|
Widevine System Id: 1234554321
|
||||||
|
Not valid before: 2001-08-09 00:00:00
|
||||||
|
Not valid after: 2023-07-05 00:00:00
|
||||||
|
|
||||||
|
Certificate Subject Name:
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value=u'US')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.8, name=stateOrProvinceName)>, value=u'WA')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.7, name=localityName)>, value=u'Kirkland')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.10, name=organizationName)>, value=u'CompanyXYZ')>
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>, value=u'ContentProtection')>
|
||||||
|
Issuer Name:
|
||||||
|
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'root_cert')>
|
||||||
|
Key Size: 4096
|
||||||
|
Widevine System Id: 1234554321
|
||||||
|
Not valid before: 2001-08-09 00:00:00
|
||||||
|
Not valid after: 2001-11-17 00:00:00"""
|
||||||
|
self.assertEqual(output.getvalue(), textwrap.dedent(expected_info))
|
||||||
|
|
||||||
|
def test_secure_erase(self):
|
||||||
|
args = ArgParseObject()
|
||||||
|
args.file = tempfile.NamedTemporaryFile(delete=False)
|
||||||
|
args.passes = 2
|
||||||
|
self.assertTrue(os.path.exists(args.file.name))
|
||||||
|
oem_certificate.secure_erase(args)
|
||||||
|
self.assertFalse(os.path.exists(args.file.name))
|
||||||
|
|
||||||
|
|
||||||
|
class OemCertificateArgParseTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.parser = oem_certificate.create_parser()
|
||||||
|
self.test_dir = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
shutil.rmtree(self.test_dir)
|
||||||
|
|
||||||
|
def test_generate_csr(self):
|
||||||
|
cmds = ('generate_csr --key_size 4096 -C USA -ST WA '
|
||||||
|
'-L Kirkland -O Company -OU Widevine').split()
|
||||||
|
output_private_key_file = os.path.join(self.test_dir, 'private_key')
|
||||||
|
output_csr_file = os.path.join(self.test_dir, 'csr')
|
||||||
|
cmds.extend([
|
||||||
|
'--output_csr_file', output_csr_file, '--output_private_key_file',
|
||||||
|
output_private_key_file, '--passphrase', 'pass'
|
||||||
|
])
|
||||||
|
|
||||||
|
args = self.parser.parse_args(cmds)
|
||||||
|
self.assertEqual(args.key_size, 4096)
|
||||||
|
self.assertEqual(args.country_name, 'USA')
|
||||||
|
self.assertEqual(args.state_or_province_name, 'WA')
|
||||||
|
self.assertEqual(args.locality_name, 'Kirkland')
|
||||||
|
self.assertEqual(args.organization_name, 'Company')
|
||||||
|
self.assertEqual(args.organizational_unit_name, 'Widevine')
|
||||||
|
self.assertEqual(args.output_csr_file.name, output_csr_file)
|
||||||
|
self.assertEqual(args.output_csr_file.mode, 'wb')
|
||||||
|
self.assertEqual(args.output_private_key_file.name, output_private_key_file)
|
||||||
|
self.assertEqual(args.output_private_key_file.mode, 'wb')
|
||||||
|
self.assertEqual(args.passphrase, 'pass')
|
||||||
|
self.assertEqual(args.func, oem_certificate.generate_csr)
|
||||||
|
|
||||||
|
def _fill_file_with_dummy_contents(self, file_name):
|
||||||
|
with open(file_name, 'wb') as f:
|
||||||
|
f.write('dummy')
|
||||||
|
|
||||||
|
def test_generate_csr_invalid_key_size(self):
|
||||||
|
cmds = ('generate_csr --key_size unknown -C USA -ST WA '
|
||||||
|
'-L Kirkland -O Company -OU Widevine').split()
|
||||||
|
output_private_key_file = os.path.join(self.test_dir, 'private_key')
|
||||||
|
output_csr_file = os.path.join(self.test_dir, 'csr')
|
||||||
|
cmds.extend([
|
||||||
|
'--output_csr_file', output_csr_file, '--output_private_key_file',
|
||||||
|
output_private_key_file, '--passphrase', 'pass'
|
||||||
|
])
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExit) as context:
|
||||||
|
self.parser.parse_args(cmds)
|
||||||
|
self.assertEqual(context.exception.code, 2)
|
||||||
|
|
||||||
|
def test_generate_intermediate_cert(self):
|
||||||
|
cmds = (
|
||||||
|
'generate_intermediate_certificate --valid_duration 10 --system_id 100'
|
||||||
|
).split()
|
||||||
|
|
||||||
|
csr_file = os.path.join(self.test_dir, 'csr')
|
||||||
|
self._fill_file_with_dummy_contents(csr_file)
|
||||||
|
root_certificate_file = os.path.join(self.test_dir, 'root_cert')
|
||||||
|
self._fill_file_with_dummy_contents(root_certificate_file)
|
||||||
|
root_private_key_file = os.path.join(self.test_dir, 'root_private_key')
|
||||||
|
self._fill_file_with_dummy_contents(root_private_key_file)
|
||||||
|
output_certificate_file = os.path.join(self.test_dir, 'cert')
|
||||||
|
cmds.extend([
|
||||||
|
'--csr_file', csr_file, '--root_certificate_file',
|
||||||
|
root_certificate_file, '--root_private_key_file', root_private_key_file,
|
||||||
|
'--root_private_key_passphrase', 'root_key',
|
||||||
|
'--output_certificate_file', output_certificate_file
|
||||||
|
])
|
||||||
|
|
||||||
|
args = self.parser.parse_args(cmds)
|
||||||
|
self.assertAlmostEqual(
|
||||||
|
args.not_valid_before,
|
||||||
|
datetime.datetime.today(),
|
||||||
|
delta=datetime.timedelta(seconds=60))
|
||||||
|
self.assertEqual(args.valid_duration, 10)
|
||||||
|
self.assertEqual(args.system_id, 100)
|
||||||
|
self.assertEqual(args.csr_file.name, csr_file)
|
||||||
|
self.assertEqual(args.csr_file.mode, 'rb')
|
||||||
|
self.assertEqual(args.root_certificate_file.name, root_certificate_file)
|
||||||
|
self.assertEqual(args.root_certificate_file.mode, 'rb')
|
||||||
|
self.assertEqual(args.root_private_key_file.name, root_private_key_file)
|
||||||
|
self.assertEqual(args.root_private_key_file.mode, 'rb')
|
||||||
|
self.assertEqual(args.root_private_key_passphrase, 'root_key')
|
||||||
|
self.assertEqual(args.output_certificate_file.name, output_certificate_file)
|
||||||
|
self.assertEqual(args.output_certificate_file.mode, 'wb')
|
||||||
|
self.assertEqual(args.func,
|
||||||
|
oem_certificate.generate_intermediate_certificate)
|
||||||
|
|
||||||
|
def test_generate_leaf_cert(self):
|
||||||
|
cmds = ('generate_leaf_certificate --not_valid_before 2016-01-02 '
|
||||||
|
'--valid_duration 10').split()
|
||||||
|
|
||||||
|
intermediate_certificate_file = os.path.join(self.test_dir,
|
||||||
|
'intermediate_cert')
|
||||||
|
self._fill_file_with_dummy_contents(intermediate_certificate_file)
|
||||||
|
intermediate_private_key_file = os.path.join(self.test_dir,
|
||||||
|
'intermediate_private_key')
|
||||||
|
self._fill_file_with_dummy_contents(intermediate_private_key_file)
|
||||||
|
|
||||||
|
output_certificate_file = os.path.join(self.test_dir, 'cert')
|
||||||
|
output_private_key_file = os.path.join(self.test_dir, 'key')
|
||||||
|
cmds.extend([
|
||||||
|
'--intermediate_certificate_file', intermediate_certificate_file,
|
||||||
|
'--intermediate_private_key_file', intermediate_private_key_file,
|
||||||
|
'--intermediate_private_key_passphrase', 'intermediate_key',
|
||||||
|
'--output_certificate_file', output_certificate_file,
|
||||||
|
'--output_private_key_file', output_private_key_file, '--passphrase',
|
||||||
|
'leaf_key'
|
||||||
|
])
|
||||||
|
|
||||||
|
args = self.parser.parse_args(cmds)
|
||||||
|
self.assertEqual(args.not_valid_before, datetime.datetime(2016, 1, 2))
|
||||||
|
self.assertEqual(args.valid_duration, 10)
|
||||||
|
self.assertEqual(args.intermediate_certificate_file.name,
|
||||||
|
intermediate_certificate_file)
|
||||||
|
self.assertEqual(args.intermediate_certificate_file.mode, 'rb')
|
||||||
|
self.assertEqual(args.intermediate_private_key_file.name,
|
||||||
|
intermediate_private_key_file)
|
||||||
|
self.assertEqual(args.intermediate_private_key_file.mode, 'rb')
|
||||||
|
self.assertEqual(args.intermediate_private_key_passphrase,
|
||||||
|
'intermediate_key')
|
||||||
|
self.assertEqual(args.output_certificate_file.name, output_certificate_file)
|
||||||
|
self.assertEqual(args.output_certificate_file.mode, 'wb')
|
||||||
|
self.assertEqual(args.output_private_key_file.name, output_private_key_file)
|
||||||
|
self.assertEqual(args.output_private_key_file.mode, 'wb')
|
||||||
|
self.assertEqual(args.passphrase, 'leaf_key')
|
||||||
|
self.assertEqual(args.func, oem_certificate.generate_leaf_certificate)
|
||||||
|
|
||||||
|
def test_generate_leaf_cert_invalid_date(self):
|
||||||
|
cmds = ('generate_leaf_certificate --not_valid_before invaid-date '
|
||||||
|
'--valid_duration 10').split()
|
||||||
|
|
||||||
|
intermediate_certificate_file = os.path.join(self.test_dir,
|
||||||
|
'intermediate_cert')
|
||||||
|
self._fill_file_with_dummy_contents(intermediate_certificate_file)
|
||||||
|
intermediate_private_key_file = os.path.join(self.test_dir,
|
||||||
|
'intermediate_private_key')
|
||||||
|
self._fill_file_with_dummy_contents(intermediate_private_key_file)
|
||||||
|
|
||||||
|
output_certificate_file = os.path.join(self.test_dir, 'cert')
|
||||||
|
output_private_key_file = os.path.join(self.test_dir, 'key')
|
||||||
|
cmds.extend([
|
||||||
|
'--intermediate_certificate_file', intermediate_certificate_file,
|
||||||
|
'--intermediate_private_key_file', intermediate_private_key_file,
|
||||||
|
'--intermediate_private_key_passphrase', 'intermediate_key',
|
||||||
|
'--output_certificate_file', output_certificate_file,
|
||||||
|
'--output_private_key_file', output_private_key_file, '--passphrase',
|
||||||
|
'leaf_key'
|
||||||
|
])
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExit) as context:
|
||||||
|
self.parser.parse_args(cmds)
|
||||||
|
self.assertEqual(context.exception.code, 2)
|
||||||
|
|
||||||
|
def test_secure_erase(self):
|
||||||
|
file_path = os.path.join(self.test_dir, 'file')
|
||||||
|
self._fill_file_with_dummy_contents(file_path)
|
||||||
|
cmds = ['erase', '-F', file_path, '--passes', '2']
|
||||||
|
args = self.parser.parse_args(cmds)
|
||||||
|
self.assertEqual(args.passes, 2)
|
||||||
|
self.assertEqual(args.file.name, file_path)
|
||||||
|
self.assertEqual(args.file.mode, 'a')
|
||||||
|
self.assertEqual(args.func, oem_certificate.secure_erase)
|
||||||
|
|
||||||
|
def test_get_info(self):
|
||||||
|
file_path = os.path.join(self.test_dir, 'file')
|
||||||
|
self._fill_file_with_dummy_contents(file_path)
|
||||||
|
cmds = ['info', '-F', file_path]
|
||||||
|
args = self.parser.parse_args(cmds)
|
||||||
|
self.assertEqual(args.file.name, file_path)
|
||||||
|
self.assertEqual(args.file.mode, 'rb')
|
||||||
|
self.assertEqual(args.func, oem_certificate.get_info)
|
||||||
|
|
||||||
|
def test_arbitrary_commands(self):
|
||||||
|
with self.assertRaises(SystemExit) as context:
|
||||||
|
self.parser.parse_args(['unsupport', '--commands'])
|
||||||
|
self.assertEqual(context.exception.code, 2)
|
||||||
|
|
||||||
|
def test_no_argument(self):
|
||||||
|
with self.assertRaises(SystemExit) as context:
|
||||||
|
self.parser.parse_args([])
|
||||||
|
self.assertEqual(context.exception.code, 2)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
89
protos/public/BUILD
Normal file
89
protos/public/BUILD
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Description:
|
||||||
|
# Public protocol buffer definitions for Widevine Services.
|
||||||
|
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
load("@protobuf_repo//:protobuf.bzl", "cc_proto_library", "py_proto_library")
|
||||||
|
|
||||||
|
cc_proto_library(
|
||||||
|
name = "certificate_provisioning_proto",
|
||||||
|
srcs = ["certificate_provisioning.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
deps = [":client_identification_proto"],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_proto_library(
|
||||||
|
name = "certificate_provisioning_py_pb2",
|
||||||
|
srcs = ["certificate_provisioning.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf_python",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
deps = [":client_identification_py_pb2"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_proto_library(
|
||||||
|
name = "client_identification_proto",
|
||||||
|
srcs = ["client_identification.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
)
|
||||||
|
|
||||||
|
py_proto_library(
|
||||||
|
name = "client_identification_py_pb2",
|
||||||
|
srcs = ["client_identification.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf_python",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_proto_library(
|
||||||
|
name = "device_certificate_proto",
|
||||||
|
srcs = ["device_certificate.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
deps = [":provisioned_device_info_proto"],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_proto_library(
|
||||||
|
name = "device_certificate_py_pb2",
|
||||||
|
srcs = ["device_certificate.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf_python",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
deps = [":provisioned_device_info_py_pb2"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_proto_library(
|
||||||
|
name = "signed_device_certificate_proto",
|
||||||
|
srcs = ["signed_device_certificate.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
)
|
||||||
|
|
||||||
|
py_proto_library(
|
||||||
|
name = "signed_device_certificate_py_pb2",
|
||||||
|
srcs = ["signed_device_certificate.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf_python",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_proto_library(
|
||||||
|
name = "provisioned_device_info_proto",
|
||||||
|
srcs = ["provisioned_device_info.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
)
|
||||||
|
|
||||||
|
py_proto_library(
|
||||||
|
name = "provisioned_device_info_py_pb2",
|
||||||
|
srcs = ["provisioned_device_info.proto"],
|
||||||
|
default_runtime = "@protobuf_repo//:protobuf_python",
|
||||||
|
protoc = "@protobuf_repo//:protoc",
|
||||||
|
)
|
||||||
98
protos/public/certificate_provisioning.proto
Normal file
98
protos/public/certificate_provisioning.proto
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Public protocol buffer definitions for Widevine Device Certificate
|
||||||
|
// Provisioning protocol.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package widevine;
|
||||||
|
option java_package = "com.google.video.widevine.protos";
|
||||||
|
|
||||||
|
import "protos/public/client_identification.proto";
|
||||||
|
|
||||||
|
// ProvisioningOptions specifies the type of certificate to specify and
|
||||||
|
// in the case of X509 certificates, the certificate authority to use.
|
||||||
|
message ProvisioningOptions {
|
||||||
|
enum CertificateType {
|
||||||
|
WIDEVINE_DRM = 0; // Default. The original certificate type.
|
||||||
|
X509 = 1; // X.509 certificate.
|
||||||
|
}
|
||||||
|
|
||||||
|
optional CertificateType certificate_type = 1 [default = WIDEVINE_DRM];
|
||||||
|
|
||||||
|
// Contains the application-specific name used to identify the certificate
|
||||||
|
// authority for signing the generated certificate. This is required iff the
|
||||||
|
// certificate type is X509.
|
||||||
|
optional string certificate_authority = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provisioning request sent by client devices to provisioning service.
|
||||||
|
message ProvisioningRequest {
|
||||||
|
oneof clear_or_encrypted_client_id {
|
||||||
|
// Device root of trust and other client identification. Required.
|
||||||
|
ClientIdentification client_id = 1;
|
||||||
|
EncryptedClientIdentification encrypted_client_id = 5;
|
||||||
|
}
|
||||||
|
// Nonce value used to prevent replay attacks. Required.
|
||||||
|
optional bytes nonce = 2;
|
||||||
|
// Options for type of certificate to generate. Optional.
|
||||||
|
optional ProvisioningOptions options = 3;
|
||||||
|
oneof spoid_param {
|
||||||
|
// Stable identifier, unique for each device + application (or origin).
|
||||||
|
// To be deprecated.
|
||||||
|
bytes stable_id = 4;
|
||||||
|
// Service provider ID from the service certificate's provider_id field.
|
||||||
|
// Preferred parameter.
|
||||||
|
bytes provider_id = 6;
|
||||||
|
// Client-generated stable per-origin identifier to be copied directly
|
||||||
|
// to the client certificate serial number.
|
||||||
|
bytes spoid = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provisioning response sent by the provisioning server to client devices.
|
||||||
|
// This message is used for both regular Widevine DRM certificates and for
|
||||||
|
// application-specific X.509 certificates.
|
||||||
|
message ProvisioningResponse {
|
||||||
|
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
||||||
|
// Required. For X.509 certificates, the private RSA key may also include
|
||||||
|
// a prefix as specified by private_key_prefix in the X509CertificateMetadata
|
||||||
|
// proto message.
|
||||||
|
optional bytes device_rsa_key = 1;
|
||||||
|
// Initialization vector used to encrypt device_rsa_key. Required.
|
||||||
|
optional bytes device_rsa_key_iv = 2;
|
||||||
|
// For Widevine DRM certificates, this contains the serialized
|
||||||
|
// SignedDrmDeviceCertificate. For X.509 certificates, this contains the PEM
|
||||||
|
// encoded X.509 certificate. Required.
|
||||||
|
optional bytes device_certificate = 3;
|
||||||
|
// Nonce value matching nonce in ProvisioningRequest. Required.
|
||||||
|
optional bytes nonce = 4;
|
||||||
|
// Key used to wrap device_rsa_key when DRM provisioning an OEM factory
|
||||||
|
// provisioned device. Encrypted with the device OEM public key using
|
||||||
|
// RSA-OAEP.
|
||||||
|
optional bytes wrapping_key = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialized ProvisioningRequest or ProvisioningResponse signed with
|
||||||
|
// The message authentication key.
|
||||||
|
message SignedProvisioningMessage {
|
||||||
|
enum ProtocolVersion {
|
||||||
|
VERSION_2 = 2; // Keybox factory-provisioned devices.
|
||||||
|
VERSION_3 = 3; // OEM certificate factory-provisioned devices.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialized ProvisioningRequest or ProvisioningResponse. Required.
|
||||||
|
optional bytes message = 1;
|
||||||
|
// HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required.
|
||||||
|
optional bytes signature = 2;
|
||||||
|
// Version number of provisioning protocol.
|
||||||
|
optional ProtocolVersion protocol_version = 3 [default = VERSION_2];
|
||||||
|
}
|
||||||
101
protos/public/client_identification.proto
Normal file
101
protos/public/client_identification.proto
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// ClientIdentification messages used by provisioning and license protocols.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package widevine;
|
||||||
|
option java_package = "com.google.video.widevine.protos";
|
||||||
|
|
||||||
|
option java_outer_classname = "ClientIdentificationProtos";
|
||||||
|
|
||||||
|
// ClientIdentification message used to authenticate the client device.
|
||||||
|
message ClientIdentification {
|
||||||
|
enum TokenType {
|
||||||
|
KEYBOX = 0;
|
||||||
|
DRM_DEVICE_CERTIFICATE = 1;
|
||||||
|
REMOTE_ATTESTATION_CERTIFICATE = 2;
|
||||||
|
OEM_DEVICE_CERTIFICATE = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NameValue {
|
||||||
|
optional string name = 1;
|
||||||
|
optional string value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capabilities which not all clients may support. Used for the license
|
||||||
|
// exchange protocol only.
|
||||||
|
message ClientCapabilities {
|
||||||
|
enum HdcpVersion {
|
||||||
|
HDCP_NONE = 0;
|
||||||
|
HDCP_V1 = 1;
|
||||||
|
HDCP_V2 = 2;
|
||||||
|
HDCP_V2_1 = 3;
|
||||||
|
HDCP_V2_2 = 4;
|
||||||
|
HDCP_NO_DIGITAL_OUTPUT = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CertificateKeyType {
|
||||||
|
RSA_2048 = 0;
|
||||||
|
RSA_3072 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional bool client_token = 1 [default = false];
|
||||||
|
optional bool session_token = 2 [default = false];
|
||||||
|
optional bool video_resolution_constraints = 3 [default = false];
|
||||||
|
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
|
||||||
|
optional uint32 oem_crypto_api_version = 5;
|
||||||
|
// Client has hardware support for protecting the usage table, such as
|
||||||
|
// storing the generation number in secure memory. For Details, see:
|
||||||
|
// https://docs.google.com/document/d/1Mm8oB51SYAgry62mEuh_2OEkabikBiS61kN7HsDnh9Y/edit#heading=h.xgjl2srtytjt
|
||||||
|
optional bool anti_rollback_usage_table = 6 [default = false];
|
||||||
|
// The client shall report |srm_version| if available.
|
||||||
|
optional uint32 srm_version = 7;
|
||||||
|
// A device may have SRM data, and report a version, but may not be capable
|
||||||
|
// of updating SRM data.
|
||||||
|
optional bool can_update_srm = 8 [default = false];
|
||||||
|
repeated CertificateKeyType supported_certificate_key_type = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of factory-provisioned device root of trust. Optional.
|
||||||
|
optional TokenType type = 1 [default = KEYBOX];
|
||||||
|
// Factory-provisioned device root of trust. Required.
|
||||||
|
optional bytes token = 2;
|
||||||
|
// Optional client information name/value pairs.
|
||||||
|
repeated NameValue client_info = 3;
|
||||||
|
// Client token generated by the content provider. Optional.
|
||||||
|
optional bytes provider_client_token = 4;
|
||||||
|
// Number of licenses received by the client to which the token above belongs.
|
||||||
|
// Only present if client_token is specified.
|
||||||
|
optional uint32 license_counter = 5;
|
||||||
|
// List of non-baseline client capabilities.
|
||||||
|
optional ClientCapabilities client_capabilities = 6;
|
||||||
|
// Serialized VmpData message. Optional.
|
||||||
|
optional bytes vmp_data = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptedClientIdentification message used to hold ClientIdentification
|
||||||
|
// messages encrypted for privacy purposes.
|
||||||
|
message EncryptedClientIdentification {
|
||||||
|
// Provider ID for which the ClientIdentifcation is encrypted (owner of
|
||||||
|
// service certificate).
|
||||||
|
optional string provider_id = 1;
|
||||||
|
// Serial number for the service certificate for which ClientIdentification is
|
||||||
|
// encrypted.
|
||||||
|
optional bytes service_certificate_serial_number = 2;
|
||||||
|
// Serialized ClientIdentification message, encrypted with the privacy key using
|
||||||
|
// AES-128-CBC with PKCS#5 padding.
|
||||||
|
optional bytes encrypted_client_id = 3;
|
||||||
|
// Initialization vector needed to decrypt encrypted_client_id.
|
||||||
|
optional bytes encrypted_client_id_iv = 4;
|
||||||
|
// AES-128 privacy key, encrypted with the service public key using RSA-OAEP.
|
||||||
|
optional bytes encrypted_privacy_key = 5;
|
||||||
|
}
|
||||||
91
protos/public/device_certificate.proto
Normal file
91
protos/public/device_certificate.proto
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Device certificate and certificate status list format definitions.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package widevine;
|
||||||
|
|
||||||
|
option java_outer_classname = "DeviceCertificateProtos";
|
||||||
|
option java_package = "com.google.video.widevine.protos";
|
||||||
|
|
||||||
|
import "protos/public/provisioned_device_info.proto";
|
||||||
|
|
||||||
|
// DRM certificate definition for user devices, intermediate, service, and root
|
||||||
|
// certificates.
|
||||||
|
message DrmDeviceCertificate {
|
||||||
|
enum CertificateType {
|
||||||
|
ROOT = 0;
|
||||||
|
DRM_INTERMEDIATE = 1;
|
||||||
|
DRM_USER_DEVICE = 2;
|
||||||
|
SERVICE = 3;
|
||||||
|
PROVISIONER = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of certificate. Required.
|
||||||
|
optional CertificateType type = 1;
|
||||||
|
// 128-bit globally unique serial number of certificate.
|
||||||
|
// Value is 0 for root certificate. Required.
|
||||||
|
optional bytes serial_number = 2;
|
||||||
|
// POSIX time, in seconds, when the certificate was created. Required.
|
||||||
|
optional uint32 creation_time_seconds = 3;
|
||||||
|
// Device public key. PKCS#1 ASN.1 DER-encoded. Required.
|
||||||
|
optional bytes public_key = 4;
|
||||||
|
// Widevine system ID for the device. Required for intermediate and
|
||||||
|
// user device certificates.
|
||||||
|
optional uint32 system_id = 5;
|
||||||
|
// Deprecated field, which used to indicate whether the device was a test
|
||||||
|
// (non-production) device. The test_device field in ProvisionedDeviceInfo
|
||||||
|
// below should be observed instead.
|
||||||
|
optional bool test_device_deprecated = 6 [deprecated = true];
|
||||||
|
// Service identifier (web origin) for the provider which owns the
|
||||||
|
// certificate. Required for service and provisioner certificates.
|
||||||
|
optional string provider_id = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains DRM and OEM certificate status and device information for a
|
||||||
|
// specific system ID.
|
||||||
|
message DeviceCertificateStatus {
|
||||||
|
enum Status {
|
||||||
|
VALID = 0;
|
||||||
|
REVOKED = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Serial number of the intermediate DrmDeviceCertificate to which this
|
||||||
|
// message refers. Required.
|
||||||
|
optional bytes drm_serial_number = 1;
|
||||||
|
// Status of the certificate. Optional.
|
||||||
|
optional Status status = 2 [default = VALID];
|
||||||
|
// Device model information about the device to which the intermediate
|
||||||
|
// certificate(s) correspond.
|
||||||
|
optional ProvisionedDeviceInfo device_info = 4;
|
||||||
|
// Serial number of the OEM X.509 intermediate certificate for this type
|
||||||
|
// of device. Present only if the device is OEM-provisioned.
|
||||||
|
optional bytes oem_serial_number = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of DeviceCertificateStatus. Used to propagate certificate revocation
|
||||||
|
// status and device information.
|
||||||
|
message DeviceCertificateStatusList {
|
||||||
|
// POSIX time, in seconds, when the list was created. Required.
|
||||||
|
optional uint32 creation_time_seconds = 1;
|
||||||
|
// DeviceCertificateStatus for each system ID.
|
||||||
|
repeated DeviceCertificateStatus certificate_status = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signed CertificateStatusList
|
||||||
|
message SignedCertificateStatusList {
|
||||||
|
// Serialized DeviceCertificateStatusList. Required.
|
||||||
|
optional bytes certificate_status_list = 1;
|
||||||
|
// Signature of certificate_status_list. Signed with root certificate private
|
||||||
|
// key using RSASSA-PSS. Required.
|
||||||
|
optional bytes signature = 2;
|
||||||
|
}
|
||||||
47
protos/public/provisioned_device_info.proto
Normal file
47
protos/public/provisioned_device_info.proto
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Description:
|
||||||
|
// Provisioned device info format definitions.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package widevine;
|
||||||
|
|
||||||
|
option java_package = "com.google.video.widevine.protos";
|
||||||
|
option java_outer_classname = "ProvisionedDeviceInfoProto";
|
||||||
|
|
||||||
|
// Contains device model information for a provisioned device.
|
||||||
|
message ProvisionedDeviceInfo {
|
||||||
|
enum WvSecurityLevel {
|
||||||
|
// Defined in Widevine Security Integration Guide for DASH on Android:
|
||||||
|
// http://doc/1Zum-fcJeoIw6KG1kDP_KepIE5h9gAZg0PaMtemBvk9c/edit#heading=h.1t3h5sf
|
||||||
|
LEVEL_UNSPECIFIED = 0;
|
||||||
|
LEVEL_1 = 1;
|
||||||
|
LEVEL_2 = 2;
|
||||||
|
LEVEL_3 = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widevine system ID for the device. Mandatory.
|
||||||
|
optional uint32 system_id = 1;
|
||||||
|
// Name of system-on-a-chip. Optional.
|
||||||
|
optional string soc = 2;
|
||||||
|
// Name of manufacturer. Optional.
|
||||||
|
optional string manufacturer = 3;
|
||||||
|
// Manufacturer's model name. Matches "brand" in device metadata. Optional.
|
||||||
|
optional string model = 4;
|
||||||
|
// Type of device (Phone, Tablet, TV, etc).
|
||||||
|
optional string device_type = 5;
|
||||||
|
// Device model year. Optional.
|
||||||
|
optional uint32 model_year = 6;
|
||||||
|
// Widevine-defined security level. Optional.
|
||||||
|
optional WvSecurityLevel security_level = 7 [default = LEVEL_UNSPECIFIED];
|
||||||
|
// True if the certificate corresponds to a test (non production) device.
|
||||||
|
// Optional.
|
||||||
|
optional bool test_device = 8 [default = false];
|
||||||
|
}
|
||||||
27
protos/public/signed_device_certificate.proto
Normal file
27
protos/public/signed_device_certificate.proto
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Signed device certificate definition.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package widevine;
|
||||||
|
|
||||||
|
option java_outer_classname = "SignedDeviceCertificateProtos";
|
||||||
|
option java_package = "com.google.video.widevine.protos";
|
||||||
|
|
||||||
|
// DrmDeviceCertificate signed by a higher (CA) DRM certificate.
|
||||||
|
message SignedDrmDeviceCertificate {
|
||||||
|
// Serialized certificate. Required.
|
||||||
|
optional bytes drm_certificate = 1;
|
||||||
|
// Signature of certificate. Signed with root or intermediate
|
||||||
|
// certificate specified below. Required.
|
||||||
|
optional bytes signature = 2;
|
||||||
|
// SignedDrmDeviceCertificate used to sign this certificate.
|
||||||
|
optional SignedDrmDeviceCertificate signer = 3;
|
||||||
|
}
|
||||||
112
provisioning_sdk/internal/BUILD
Normal file
112
provisioning_sdk/internal/BUILD
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Build file for provisioning 3.0 SDK internal library.
|
||||||
|
|
||||||
|
# Only accessible by public provisioning_sdk apis.
|
||||||
|
package_group(
|
||||||
|
name = "internal",
|
||||||
|
packages = [
|
||||||
|
"//provisioning_sdk/...",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
package(
|
||||||
|
default_visibility = [":internal"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "provisioning_engine_impl",
|
||||||
|
srcs = ["provisioning_engine_impl.cc"],
|
||||||
|
hdrs = ["provisioning_engine_impl.h"],
|
||||||
|
deps = [
|
||||||
|
":oem_device_cert",
|
||||||
|
"//base",
|
||||||
|
"//common:random_util",
|
||||||
|
"//common:rsa_key",
|
||||||
|
"//provisioning_sdk/internal/certificates:root_certificates",
|
||||||
|
"//provisioning_sdk/public:certificate_type",
|
||||||
|
"//provisioning_sdk/public:provisioning_status",
|
||||||
|
"//protos/public:device_certificate_proto",
|
||||||
|
"//protos/public:provisioned_device_info_proto",
|
||||||
|
"//protos/public:signed_device_certificate_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "provisioning_engine_impl_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["provisioning_engine_impl_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":provisioning_engine_impl",
|
||||||
|
"//base",
|
||||||
|
"//external:gtest_main",
|
||||||
|
"//common:mock_rsa_key",
|
||||||
|
"//provisioning_sdk/public:certificate_type",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "provisioning_session_impl",
|
||||||
|
srcs = ["provisioning_session_impl.cc"],
|
||||||
|
hdrs = ["provisioning_session_impl.h"],
|
||||||
|
deps = [
|
||||||
|
":oem_device_cert",
|
||||||
|
":provisioning_engine_impl",
|
||||||
|
"//base",
|
||||||
|
"//common:aes_cbc_util",
|
||||||
|
"//common:random_util",
|
||||||
|
"//common:rsa_key",
|
||||||
|
"//common:sha_util",
|
||||||
|
"//provisioning_sdk/public:provisioning_status",
|
||||||
|
"//protos/public:certificate_provisioning_proto",
|
||||||
|
"//protos/public:client_identification_proto",
|
||||||
|
"//protos/public:device_certificate_proto",
|
||||||
|
"//protos/public:provisioned_device_info_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "provisioning_session_impl_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["provisioning_session_impl_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":oem_device_cert",
|
||||||
|
":provisioning_engine_impl",
|
||||||
|
":provisioning_session_impl",
|
||||||
|
"//external:gtest_main",
|
||||||
|
"//common:aes_cbc_util",
|
||||||
|
"//common:mock_rsa_key",
|
||||||
|
"//common:sha_util",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "oem_device_cert",
|
||||||
|
srcs = ["oem_device_cert.cc"],
|
||||||
|
hdrs = ["oem_device_cert.h"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"//external:openssl",
|
||||||
|
"//common:openssl_util",
|
||||||
|
"//common:rsa_key",
|
||||||
|
"//provisioning_sdk/internal/certificates:root_certificates",
|
||||||
|
"//provisioning_sdk/public:certificate_type",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "oem_device_cert_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["oem_device_cert_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":oem_device_cert",
|
||||||
|
"//external:gtest_main",
|
||||||
|
"//provisioning_sdk/internal/certificates:test_certificates",
|
||||||
|
],
|
||||||
|
)
|
||||||
117
provisioning_sdk/internal/certificates/BUILD
Normal file
117
provisioning_sdk/internal/certificates/BUILD
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Build file for test certificates.
|
||||||
|
|
||||||
|
package(
|
||||||
|
default_visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "root_certificates",
|
||||||
|
srcs = [
|
||||||
|
"root_certificates.cc",
|
||||||
|
":drm_ca_root_dev_cert",
|
||||||
|
":drm_ca_root_prod_cert",
|
||||||
|
":drm_ca_root_test_cert",
|
||||||
|
":oem_ca_root_dev_der",
|
||||||
|
":oem_ca_root_prod_der",
|
||||||
|
],
|
||||||
|
hdrs = ["root_certificates.h"],
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "drm_ca_root_test_cert",
|
||||||
|
srcs = ["drm_ca_root_test.cert"],
|
||||||
|
outs = ["drm_ca_root_test_cert.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "drm_ca_root_dev_cert",
|
||||||
|
srcs = ["drm_ca_root_dev.cert"],
|
||||||
|
outs = ["drm_ca_root_dev_cert.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "drm_ca_root_prod_cert",
|
||||||
|
srcs = ["drm_ca_root_prod.cert"],
|
||||||
|
outs = ["drm_ca_root_prod_cert.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "oem_ca_root_dev_der",
|
||||||
|
srcs = ["oem_ca_root_dev_cert.der"],
|
||||||
|
outs = ["oem_ca_root_dev_cert.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "oem_ca_root_prod_der",
|
||||||
|
srcs = ["oem_ca_root_prod_cert.der"],
|
||||||
|
outs = ["oem_ca_root_prod_cert.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "test_certificates",
|
||||||
|
srcs = [
|
||||||
|
"test_certificates.cc",
|
||||||
|
":backwards_chain_der",
|
||||||
|
":expired_2000_chain_der",
|
||||||
|
":invalid_chain_der",
|
||||||
|
":single_cert_chain_der",
|
||||||
|
":sysid_2001_chain_der",
|
||||||
|
":sysid_2001_public_key_der",
|
||||||
|
],
|
||||||
|
hdrs = ["test_certificates.h"],
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "single_cert_chain_der",
|
||||||
|
srcs = ["single-cert-chain.der"],
|
||||||
|
outs = ["single_cert_chain.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "sysid_2001_chain_der",
|
||||||
|
srcs = ["sysid-2001-chain.der"],
|
||||||
|
outs = ["sysid_2001_chain.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "sysid_2001_public_key_der",
|
||||||
|
srcs = ["sysid-2001-public-key.der"],
|
||||||
|
outs = ["sysid_2001_public_key.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "expired_2000_chain_der",
|
||||||
|
srcs = ["expired-2000-chain.der"],
|
||||||
|
outs = ["expired_2000_chain.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "backwards_chain_der",
|
||||||
|
srcs = ["backwards-chain.der"],
|
||||||
|
outs = ["backwards_chain.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "invalid_chain_der",
|
||||||
|
srcs = ["invalid_chain.der"],
|
||||||
|
outs = ["invalid_chain.h"],
|
||||||
|
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
|
||||||
|
)
|
||||||
BIN
provisioning_sdk/internal/certificates/backwards-chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/backwards-chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/drm_ca_root_dev.cert
Normal file
BIN
provisioning_sdk/internal/certificates/drm_ca_root_dev.cert
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/drm_ca_root_prod.cert
Normal file
BIN
provisioning_sdk/internal/certificates/drm_ca_root_prod.cert
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/drm_ca_root_test.cert
Normal file
BIN
provisioning_sdk/internal/certificates/drm_ca_root_test.cert
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/expired-2000-chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/expired-2000-chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/invalid_chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/invalid_chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.der
Normal file
BIN
provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.der
Normal file
BIN
provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.der
Normal file
Binary file not shown.
38
provisioning_sdk/internal/certificates/root_certificates.cc
Normal file
38
provisioning_sdk/internal/certificates/root_certificates.cc
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/certificates/root_certificates.h"
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/certificates/drm_ca_root_dev_cert.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/drm_ca_root_prod_cert.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/drm_ca_root_test_cert.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
RootCertificates::RootCertificates()
|
||||||
|
: drm_root_test_certificate_(
|
||||||
|
drm_ca_root_test_cert,
|
||||||
|
drm_ca_root_test_cert + drm_ca_root_test_cert_len),
|
||||||
|
drm_root_dev_certificate_(
|
||||||
|
drm_ca_root_dev_cert,
|
||||||
|
drm_ca_root_dev_cert + drm_ca_root_dev_cert_len),
|
||||||
|
drm_root_prod_certificate_(
|
||||||
|
drm_ca_root_prod_cert,
|
||||||
|
drm_ca_root_prod_cert + drm_ca_root_prod_cert_len),
|
||||||
|
oem_root_dev_certificate_(
|
||||||
|
oem_ca_root_dev_cert_der,
|
||||||
|
oem_ca_root_dev_cert_der + oem_ca_root_dev_cert_der_len),
|
||||||
|
oem_root_prod_certificate_(
|
||||||
|
oem_ca_root_prod_cert_der,
|
||||||
|
oem_ca_root_prod_cert_der + oem_ca_root_prod_cert_der_len) {}
|
||||||
|
|
||||||
|
RootCertificates::~RootCertificates() {}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
56
provisioning_sdk/internal/certificates/root_certificates.h
Normal file
56
provisioning_sdk/internal/certificates/root_certificates.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_CERTIFICATES_H_
|
||||||
|
#define PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_CERTIFICATES_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// This class contains DRM and OEM root certificates.
|
||||||
|
class RootCertificates {
|
||||||
|
public:
|
||||||
|
RootCertificates();
|
||||||
|
~RootCertificates();
|
||||||
|
|
||||||
|
const std::string& drm_root_test_certificate() const {
|
||||||
|
return drm_root_test_certificate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& drm_root_dev_certificate() const {
|
||||||
|
return drm_root_dev_certificate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& drm_root_prod_certificate() const {
|
||||||
|
return drm_root_prod_certificate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& oem_root_dev_certificate() const {
|
||||||
|
return oem_root_dev_certificate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& oem_root_prod_certificate() const {
|
||||||
|
return oem_root_prod_certificate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RootCertificates(const RootCertificates&) = delete;
|
||||||
|
RootCertificates& operator=(const RootCertificates&) = delete;
|
||||||
|
|
||||||
|
std::string drm_root_test_certificate_;
|
||||||
|
std::string drm_root_dev_certificate_;
|
||||||
|
std::string drm_root_prod_certificate_;
|
||||||
|
std::string oem_root_dev_certificate_;
|
||||||
|
std::string oem_root_prod_certificate_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_CERTIFICATES_H_
|
||||||
|
|
||||||
BIN
provisioning_sdk/internal/certificates/single-cert-chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/single-cert-chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/sysid-2001-chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/sysid-2001-chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/sysid-2001-public-key.der
Normal file
BIN
provisioning_sdk/internal/certificates/sysid-2001-public-key.der
Normal file
Binary file not shown.
40
provisioning_sdk/internal/certificates/test_certificates.cc
Normal file
40
provisioning_sdk/internal/certificates/test_certificates.cc
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/certificates/test_certificates.h"
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/certificates/backwards_chain.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/expired_2000_chain.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/invalid_chain.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/single_cert_chain.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/sysid_2001_chain.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/sysid_2001_public_key.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
TestCertificates::TestCertificates()
|
||||||
|
: single_certificate_chain_der_(
|
||||||
|
single_cert_chain_der,
|
||||||
|
single_cert_chain_der + single_cert_chain_der_len),
|
||||||
|
valid_certificate_chain_der_(
|
||||||
|
sysid_2001_chain_der,
|
||||||
|
sysid_2001_chain_der + sysid_2001_chain_der_len),
|
||||||
|
valid_certificate_public_key_der_(
|
||||||
|
sysid_2001_public_key_der,
|
||||||
|
sysid_2001_public_key_der + sysid_2001_public_key_der_len),
|
||||||
|
expired_certificate_chain_der_(
|
||||||
|
expired_2000_chain_der,
|
||||||
|
expired_2000_chain_der + expired_2000_chain_der_len),
|
||||||
|
backwards_certificate_chain_der_(
|
||||||
|
backwards_chain_der, backwards_chain_der + backwards_chain_der_len),
|
||||||
|
invalid_certificate_chain_der_(
|
||||||
|
invalid_chain_der, invalid_chain_der + invalid_chain_der_len) {}
|
||||||
|
|
||||||
|
TestCertificates::~TestCertificates() {}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
63
provisioning_sdk/internal/certificates/test_certificates.h
Normal file
63
provisioning_sdk/internal/certificates/test_certificates.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_
|
||||||
|
#define PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// This class contains DER-encoded test certificates. The keys for these
|
||||||
|
// certificates came from common/rsa_test_keys.h.
|
||||||
|
class TestCertificates {
|
||||||
|
public:
|
||||||
|
TestCertificates();
|
||||||
|
~TestCertificates();
|
||||||
|
|
||||||
|
const std::string& single_certificate_chain_der() const {
|
||||||
|
return single_certificate_chain_der_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& valid_certificate_chain_der() const {
|
||||||
|
return valid_certificate_chain_der_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& valid_certificate_public_key_der() const {
|
||||||
|
return valid_certificate_public_key_der_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& expired_certificate_chain_der() const {
|
||||||
|
return expired_certificate_chain_der_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& backwards_certificate_chain_der() const {
|
||||||
|
return backwards_certificate_chain_der_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& invalid_certificate_chain_der() const {
|
||||||
|
return invalid_certificate_chain_der_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TestCertificates(const TestCertificates&) = delete;
|
||||||
|
TestCertificates& operator=(const TestCertificates&) = delete;
|
||||||
|
|
||||||
|
std::string single_certificate_chain_der_;
|
||||||
|
// leaf + intermediate certificates.
|
||||||
|
std::string valid_certificate_chain_der_;
|
||||||
|
std::string valid_certificate_public_key_der_;
|
||||||
|
std::string expired_certificate_chain_der_;
|
||||||
|
// intermediate + leaf certificates.
|
||||||
|
std::string backwards_certificate_chain_der_;
|
||||||
|
std::string invalid_certificate_chain_der_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_
|
||||||
210
provisioning_sdk/internal/oem_device_cert.cc
Normal file
210
provisioning_sdk/internal/oem_device_cert.cc
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/oem_device_cert.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "openssl/asn1.h"
|
||||||
|
#include "openssl/x509.h"
|
||||||
|
#include "openssl/x509v3.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/root_certificates.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const int kExtensionOidSize = 64;
|
||||||
|
const char kWidevineSystemIdExtensionOid[] = "1.3.6.1.4.1.11129.4.1.1";
|
||||||
|
|
||||||
|
bool ExtractPublicKey(X509* x509, std::unique_ptr<RsaPublicKey>* public_key) {
|
||||||
|
DCHECK(x509);
|
||||||
|
ScopedPKEY pkey(X509_get_pubkey(x509));
|
||||||
|
if (!pkey) {
|
||||||
|
LOG(WARNING) << "X509_get_pubkey failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public_key->reset(new RsaPublicKey(EVP_PKEY_get1_RSA(pkey.get())));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtractSystemId(X509* x509, uint32_t* system_id) {
|
||||||
|
DCHECK(x509);
|
||||||
|
const int num_of_extensions = X509_get_ext_count(x509);
|
||||||
|
for (int i = 0; i < num_of_extensions; ++i) {
|
||||||
|
X509_EXTENSION* extension = X509_get_ext(x509, i);
|
||||||
|
if (!extension) {
|
||||||
|
LOG(WARNING) << "X509_get_ext failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ASN1_OBJECT* extension_object = X509_EXTENSION_get_object(extension);
|
||||||
|
if (!extension_object) {
|
||||||
|
LOG(WARNING) << "X509_EXTENSION_get_object failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char extension_name[kExtensionOidSize + 1];
|
||||||
|
OBJ_obj2txt(extension_name, kExtensionOidSize, extension_object, 1);
|
||||||
|
if (strcmp(extension_name, kWidevineSystemIdExtensionOid)) continue;
|
||||||
|
|
||||||
|
ASN1_OCTET_STRING* octet_str = X509_EXTENSION_get_data(extension);
|
||||||
|
if (!octet_str) {
|
||||||
|
LOG(WARNING) << "X509_EXTENSION_get_data failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const unsigned char* data = octet_str->data;
|
||||||
|
if (!data) {
|
||||||
|
LOG(WARNING) << "null data in octet string.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ScopedAsn1Integer asn1_integer(
|
||||||
|
d2i_ASN1_INTEGER(nullptr, &data, octet_str->length));
|
||||||
|
if (!asn1_integer) {
|
||||||
|
LOG(WARNING) << "d2i_ASN1_INTEGER failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int64_t system_id_long = ASN1_INTEGER_get(asn1_integer.get());
|
||||||
|
if (system_id_long == -1) {
|
||||||
|
LOG(WARNING) << "ASN1_INTEGER_get failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*system_id = system_id_long;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOG(WARNING) << "Widevine system ID extension not found.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtractSerialNumber(X509* x509, std::string* oem_ca_serial_number) {
|
||||||
|
ASN1_INTEGER* serial = X509_get_serialNumber(x509);
|
||||||
|
if (!serial) {
|
||||||
|
LOG(WARNING) << "X509_get_serialNumber failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint8_t* buffer = ASN1_STRING_data(serial);
|
||||||
|
if (!buffer) {
|
||||||
|
LOG(WARNING) << "ASN1_STRING_data failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int length = ASN1_STRING_length(serial);
|
||||||
|
if (length <= 0) {
|
||||||
|
LOG(WARNING) << "ASN1_STRING_length returns " << length;
|
||||||
|
}
|
||||||
|
oem_ca_serial_number->assign(reinterpret_cast<char*>(buffer), length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
OemDeviceCert::OemDeviceCert() {}
|
||||||
|
|
||||||
|
OemDeviceCert::~OemDeviceCert() {}
|
||||||
|
|
||||||
|
bool OemDeviceCert::Initialize(CertificateType certificate_type) {
|
||||||
|
RootCertificates root_certificates;
|
||||||
|
switch (certificate_type) {
|
||||||
|
case kCertTesting:
|
||||||
|
case kCertDevelopment:
|
||||||
|
return Initialize(root_certificates.oem_root_dev_certificate());
|
||||||
|
case kCertProduction:
|
||||||
|
return Initialize(root_certificates.oem_root_prod_certificate());
|
||||||
|
default:
|
||||||
|
LOG(WARNING) << "Invalid certificate type " << certificate_type;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OemDeviceCert::VerifyCertificateChain(
|
||||||
|
const std::string& certificate_chain,
|
||||||
|
std::unique_ptr<RsaPublicKey>* leaf_public_key, uint32_t* system_id,
|
||||||
|
std::string* oem_ca_serial_number) const {
|
||||||
|
if (certificate_chain.empty()) {
|
||||||
|
LOG(WARNING) << "Empty certificate.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedX509Stack x509_stack(sk_X509_new_null());
|
||||||
|
if (!x509_stack) {
|
||||||
|
LOG(WARNING) << "sk_X509_new_null returned NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CBS pkcs7;
|
||||||
|
CBS_init(&pkcs7, reinterpret_cast<const uint8_t*>(certificate_chain.data()),
|
||||||
|
certificate_chain.size());
|
||||||
|
if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) {
|
||||||
|
LOG(WARNING) << "PKCS7_get_certificates failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sk_X509_num(x509_stack.get()) != 2) {
|
||||||
|
LOG(WARNING) << "Expecting one and only one intermediate certificate.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
X509* leaf_certificate = sk_X509_value(x509_stack.get(), 0);
|
||||||
|
X509* intermediate_certificate = sk_X509_value(x509_stack.get(), 1);
|
||||||
|
DCHECK(leaf_certificate);
|
||||||
|
DCHECK(intermediate_certificate);
|
||||||
|
|
||||||
|
// Verify the cert.
|
||||||
|
// TODO(user): Implement some form of caching mechanism so that we do not
|
||||||
|
// have to verify the same intermediate certs over and over.
|
||||||
|
ScopedX509StoreCtx context(X509_STORE_CTX_new());
|
||||||
|
if (!context) {
|
||||||
|
LOG(WARNING) << "X509_STORE_CTX_new returned NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!X509_STORE_CTX_init(context.get(), store_.get(), leaf_certificate,
|
||||||
|
x509_stack.get())) {
|
||||||
|
LOG(WARNING) << "X509_STORE_CTX_init failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (X509_verify_cert(context.get()) != 1) {
|
||||||
|
LOG(WARNING) << "Verification error: "
|
||||||
|
<< X509_verify_cert_error_string(
|
||||||
|
X509_STORE_CTX_get_error(context.get()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sk_X509_num(context->chain) < 3) {
|
||||||
|
LOG(WARNING) << "The leaf certificate should not be signed by the root "
|
||||||
|
"certificate directly.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract public key from the leaf certificate and system_id extension and
|
||||||
|
// oem serial number from the intermediate certificate.
|
||||||
|
return ExtractPublicKey(leaf_certificate, leaf_public_key) &&
|
||||||
|
ExtractSystemId(intermediate_certificate, system_id) &&
|
||||||
|
ExtractSerialNumber(intermediate_certificate, oem_ca_serial_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OemDeviceCert::Initialize(const std::string& serialized_root_certificate) {
|
||||||
|
if (serialized_root_certificate.empty()) {
|
||||||
|
LOG(WARNING) << "Empty root certificate.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
store_.reset(X509_STORE_new());
|
||||||
|
if (!store_) {
|
||||||
|
LOG(WARNING) << "X509_STORE_new returned NULL";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const char* data = serialized_root_certificate.data();
|
||||||
|
ScopedX509 root_certificate(d2i_X509(nullptr,
|
||||||
|
reinterpret_cast<const uint8_t**>(&data),
|
||||||
|
serialized_root_certificate.size()));
|
||||||
|
if (!root_certificate) {
|
||||||
|
LOG(WARNING) << "d2i_X509 returned NULL.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!X509_STORE_add_cert(store_.get(), root_certificate.get())) {
|
||||||
|
LOG(WARNING) << "X509_STORE_add_cert failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
56
provisioning_sdk/internal/oem_device_cert.h
Normal file
56
provisioning_sdk/internal/oem_device_cert.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef PROVISIONING_SDK_INTERNAL_OEM_DEVICE_CERT_H_
|
||||||
|
#define PROVISIONING_SDK_INTERNAL_OEM_DEVICE_CERT_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/openssl_util.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "provisioning_sdk/public/certificate_type.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Implements a class to handle OEM certificate: verifies the validity of the
|
||||||
|
// certificate and extracts leaf public key and system id.
|
||||||
|
class OemDeviceCert {
|
||||||
|
public:
|
||||||
|
OemDeviceCert();
|
||||||
|
virtual ~OemDeviceCert();
|
||||||
|
|
||||||
|
// Initialize with root certificate.
|
||||||
|
bool Initialize(CertificateType certificate_type);
|
||||||
|
|
||||||
|
// Verify the given certificate chain (in DER encoded pkcs7 format), which
|
||||||
|
// includes the leaf certificate (a device unique certificate containing the
|
||||||
|
// device public OEM key) and the intermediate certificate (OEM model
|
||||||
|
// intermediate CA certificate for a specific device make + model), and
|
||||||
|
// extract public key from the leaf certificate and system id extension and
|
||||||
|
// oem ca serial number from the intermediate certificate.
|
||||||
|
virtual bool VerifyCertificateChain(
|
||||||
|
const std::string& certificate_chain,
|
||||||
|
std::unique_ptr<RsaPublicKey>* leaf_public_key, uint32_t* system_id,
|
||||||
|
std::string* oem_ca_serial_number) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OemDeviceCert(const OemDeviceCert&) = delete;
|
||||||
|
OemDeviceCert& operator=(const OemDeviceCert&) = delete;
|
||||||
|
|
||||||
|
// Internal implementation of Initialize function.
|
||||||
|
bool Initialize(const std::string& serialized_root_certificate);
|
||||||
|
|
||||||
|
ScopedX509Store store_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // PROVISIONING_SDK_INTERNAL_OEM_DEVICE_CERT_H_
|
||||||
95
provisioning_sdk/internal/oem_device_cert_test.cc
Normal file
95
provisioning_sdk/internal/oem_device_cert_test.cc
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/oem_device_cert.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/test_certificates.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class OemDeviceCertTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
ASSERT_TRUE(oem_device_cert_.Initialize(kCertTesting));
|
||||||
|
}
|
||||||
|
|
||||||
|
OemDeviceCert oem_device_cert_;
|
||||||
|
TestCertificates test_certificates_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(OemDeviceCertTest, EmptyCertificateChain) {
|
||||||
|
std::unique_ptr<RsaPublicKey> leaf_public_key;
|
||||||
|
uint32_t system_id;
|
||||||
|
std::string oem_ca_serial_number;
|
||||||
|
EXPECT_FALSE(oem_device_cert_.VerifyCertificateChain(
|
||||||
|
"", &leaf_public_key, &system_id, &oem_ca_serial_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OemDeviceCertTest, InvalidCertificateChain) {
|
||||||
|
std::unique_ptr<RsaPublicKey> leaf_public_key;
|
||||||
|
uint32_t system_id;
|
||||||
|
std::string oem_ca_serial_number;
|
||||||
|
EXPECT_FALSE(oem_device_cert_.VerifyCertificateChain(
|
||||||
|
"invalid certificte chain", &leaf_public_key, &system_id,
|
||||||
|
&oem_ca_serial_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OemDeviceCertTest, OnlyOneCertificateInCertificateChain) {
|
||||||
|
std::unique_ptr<RsaPublicKey> leaf_public_key;
|
||||||
|
uint32_t system_id;
|
||||||
|
std::string oem_ca_serial_number;
|
||||||
|
EXPECT_FALSE(oem_device_cert_.VerifyCertificateChain(
|
||||||
|
test_certificates_.single_certificate_chain_der(), &leaf_public_key,
|
||||||
|
&system_id, &oem_ca_serial_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OemDeviceCertTest, ValidCertificateChain) {
|
||||||
|
std::unique_ptr<RsaPublicKey> leaf_public_key;
|
||||||
|
uint32_t system_id;
|
||||||
|
std::string oem_ca_serial_number;
|
||||||
|
ASSERT_TRUE(oem_device_cert_.VerifyCertificateChain(
|
||||||
|
test_certificates_.valid_certificate_chain_der(), &leaf_public_key,
|
||||||
|
&system_id, &oem_ca_serial_number));
|
||||||
|
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key(RsaPublicKey::Create(
|
||||||
|
test_certificates_.valid_certificate_public_key_der()));
|
||||||
|
ASSERT_TRUE(public_key);
|
||||||
|
EXPECT_TRUE(leaf_public_key->MatchesPublicKey(*public_key));
|
||||||
|
EXPECT_EQ(2001u, system_id);
|
||||||
|
EXPECT_EQ("\x1", oem_ca_serial_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OemDeviceCertTest, ExpiredCertificateChain) {
|
||||||
|
std::unique_ptr<RsaPublicKey> leaf_public_key;
|
||||||
|
uint32_t system_id;
|
||||||
|
std::string oem_ca_serial_number;
|
||||||
|
ASSERT_FALSE(oem_device_cert_.VerifyCertificateChain(
|
||||||
|
test_certificates_.expired_certificate_chain_der(), &leaf_public_key,
|
||||||
|
&system_id, &oem_ca_serial_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OemDeviceCertTest, OutOfOrderCertificateChain) {
|
||||||
|
std::unique_ptr<RsaPublicKey> leaf_public_key;
|
||||||
|
uint32_t system_id;
|
||||||
|
std::string oem_ca_serial_number;
|
||||||
|
ASSERT_FALSE(oem_device_cert_.VerifyCertificateChain(
|
||||||
|
test_certificates_.backwards_certificate_chain_der(), &leaf_public_key,
|
||||||
|
&system_id, &oem_ca_serial_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OemDeviceCertTest, CertificateChainNotSignedByRoot) {
|
||||||
|
std::unique_ptr<RsaPublicKey> leaf_public_key;
|
||||||
|
uint32_t system_id;
|
||||||
|
std::string oem_ca_serial_number;
|
||||||
|
ASSERT_FALSE(oem_device_cert_.VerifyCertificateChain(
|
||||||
|
test_certificates_.invalid_certificate_chain_der(), &leaf_public_key,
|
||||||
|
&system_id, &oem_ca_serial_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
464
provisioning_sdk/internal/provisioning_engine_impl.cc
Normal file
464
provisioning_sdk/internal/provisioning_engine_impl.cc
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "common/random_util.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "provisioning_sdk/internal/certificates/root_certificates.h"
|
||||||
|
#include "provisioning_sdk/public/provisioning_status.h"
|
||||||
|
|
||||||
|
#define LOG_WITH_PROTO(message, proto) \
|
||||||
|
LOG(WARNING) << (message) << " [proto: " << (proto).ShortDebugString() << "]"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Verify that |certificate| is signed by |public_key|. If |public_key| is null,
|
||||||
|
// |certificate| should be self signed.
|
||||||
|
bool VerifyAndExtractCertificate(const RsaPublicKey* public_key,
|
||||||
|
const std::string& certificate,
|
||||||
|
SignedDrmDeviceCertificate* signed_drm_cert,
|
||||||
|
DrmDeviceCertificate* drm_cert) {
|
||||||
|
DCHECK(signed_drm_cert);
|
||||||
|
DCHECK(drm_cert);
|
||||||
|
if (!signed_drm_cert->ParseFromString(certificate)) {
|
||||||
|
LOG(WARNING) << "Failed to parse SignedDrmDeviceCertificate.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (signed_drm_cert->drm_certificate().empty()) {
|
||||||
|
LOG_WITH_PROTO("Missing drm_certificate", *signed_drm_cert);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (signed_drm_cert->signature().empty()) {
|
||||||
|
LOG_WITH_PROTO("Missing signature", *signed_drm_cert);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drm_cert->ParseFromString(signed_drm_cert->drm_certificate())) {
|
||||||
|
LOG_WITH_PROTO("Failed to parse DrmDeviceCertificate", *signed_drm_cert);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (drm_cert->public_key().empty()) {
|
||||||
|
LOG_WITH_PROTO("Missing public_key", *drm_cert);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<RsaPublicKey> local_public_key;
|
||||||
|
if (!public_key) {
|
||||||
|
local_public_key.reset(RsaPublicKey::Create(drm_cert->public_key()));
|
||||||
|
if (!local_public_key) {
|
||||||
|
LOG_WITH_PROTO("Invalid root public key", *drm_cert);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public_key = local_public_key.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!public_key->VerifySignature(signed_drm_cert->drm_certificate(),
|
||||||
|
signed_drm_cert->signature())) {
|
||||||
|
LOG_WITH_PROTO("Signature verification failed", *signed_drm_cert);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GenerateCertificate(DrmDeviceCertificate::CertificateType type,
|
||||||
|
uint32_t system_id,
|
||||||
|
const std::string& provider_id,
|
||||||
|
const std::string& serial_number,
|
||||||
|
const std::string& public_key,
|
||||||
|
const RsaPrivateKey& signing_key,
|
||||||
|
const SignedDrmDeviceCertificate& signer,
|
||||||
|
std::string* certificate) {
|
||||||
|
DCHECK(type == DrmDeviceCertificate::DRM_INTERMEDIATE ||
|
||||||
|
type == DrmDeviceCertificate::DRM_USER_DEVICE);
|
||||||
|
if (serial_number.empty()) {
|
||||||
|
LOG(WARNING) << "Require an non-empty serial number.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrmDeviceCertificate drm_cert;
|
||||||
|
drm_cert.set_type(type);
|
||||||
|
drm_cert.set_system_id(system_id);
|
||||||
|
if (!provider_id.empty()) drm_cert.set_provider_id(provider_id);
|
||||||
|
drm_cert.set_serial_number(serial_number);
|
||||||
|
drm_cert.set_creation_time_seconds(time(nullptr));
|
||||||
|
drm_cert.set_public_key(public_key);
|
||||||
|
|
||||||
|
SignedDrmDeviceCertificate signed_cert;
|
||||||
|
if (!drm_cert.SerializeToString(
|
||||||
|
signed_cert.mutable_drm_certificate())) {
|
||||||
|
LOG(WARNING) << "Error serializing DrmDeviceCertificate.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!signing_key.GenerateSignature(signed_cert.drm_certificate(),
|
||||||
|
signed_cert.mutable_signature())) {
|
||||||
|
LOG(WARNING) << "Failed to generate signature for DrmDeviceCertificate.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*signed_cert.mutable_signer() = signer;
|
||||||
|
if (!signed_cert.SerializeToString(certificate)) {
|
||||||
|
LOG(WARNING) << "Failed to serialize SignedDrmDeviceCertificate to string.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares oem serial number, which is a 16-byte big number.
|
||||||
|
bool IsSerialNumberEq(const std::string& a, const std::string& b) {
|
||||||
|
// An empty serial number indicates that it does not need to be matched.
|
||||||
|
if (a.empty() || b.empty()) return true;
|
||||||
|
int a_index = a.size() - 1;
|
||||||
|
int b_index = b.size() - 1;
|
||||||
|
// Matching a and b backwards.
|
||||||
|
for (; a_index >= 0 && b_index >= 0; --a_index, --b_index)
|
||||||
|
if (a[a_index] != b[b_index]) return false;
|
||||||
|
// The remaining characters should be 0.
|
||||||
|
for (; a_index >= 0; --a_index)
|
||||||
|
if (a[a_index] != 0) return false;
|
||||||
|
for (; b_index >= 0; --b_index)
|
||||||
|
if (b[b_index] != 0) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ProvisioningEngineImpl::ProvisioningEngineImpl()
|
||||||
|
: rsa_key_factory_(new RsaKeyFactory) {}
|
||||||
|
|
||||||
|
ProvisioningEngineImpl::~ProvisioningEngineImpl() {}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngineImpl::Initialize(
|
||||||
|
CertificateType certificate_type, const std::string& drm_service_certificate,
|
||||||
|
const std::string& service_private_key,
|
||||||
|
const std::string& service_private_key_phassphrase,
|
||||||
|
const std::string& provisioning_drm_certificate,
|
||||||
|
const std::string& provisioning_private_key,
|
||||||
|
const std::string& provisioning_private_key_phassphrase,
|
||||||
|
const std::string& secret_spoid_sauce) {
|
||||||
|
if (!LoadDrmRootPublicKey(certificate_type)) return INVALID_CERTIFICATE_TYPE;
|
||||||
|
|
||||||
|
SignedDrmDeviceCertificate signed_drm_cert;
|
||||||
|
DrmDeviceCertificate drm_cert;
|
||||||
|
if (!VerifyAndExtractCertificate(root_public_key_.get(),
|
||||||
|
drm_service_certificate, &signed_drm_cert,
|
||||||
|
&drm_cert)) {
|
||||||
|
return INVALID_SERVICE_DRM_CERTIFICATE;
|
||||||
|
}
|
||||||
|
if (drm_cert.type() != DrmDeviceCertificate::SERVICE) {
|
||||||
|
LOG(WARNING) << "Expecting SERVICE certificate.";
|
||||||
|
return INVALID_SERVICE_DRM_CERTIFICATE;
|
||||||
|
}
|
||||||
|
service_public_key_ =
|
||||||
|
rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key());
|
||||||
|
if (!service_public_key_) return INVALID_SERVICE_DRM_CERTIFICATE;
|
||||||
|
service_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey(
|
||||||
|
service_private_key, service_private_key_phassphrase);
|
||||||
|
if (!service_private_key_) return INVALID_SERVICE_PRIVATE_KEY;
|
||||||
|
if (!service_public_key_->MatchesPrivateKey(*service_private_key_)) {
|
||||||
|
LOG(WARNING) << "Services public key and private key do not match.";
|
||||||
|
return INVALID_SERVICE_PRIVATE_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VerifyAndExtractCertificate(root_public_key_.get(),
|
||||||
|
provisioning_drm_certificate,
|
||||||
|
&signed_provisioning_cert_, &drm_cert)) {
|
||||||
|
return INVALID_PROVISIONER_DRM_CERTIFICATE;
|
||||||
|
}
|
||||||
|
if (drm_cert.type() != DrmDeviceCertificate::ROOT &&
|
||||||
|
drm_cert.type() != DrmDeviceCertificate::PROVISIONER) {
|
||||||
|
LOG(WARNING) << "Expecting ROOT or PROVISIONER certificate.";
|
||||||
|
return INVALID_PROVISIONER_DRM_CERTIFICATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioning_public_key_ =
|
||||||
|
rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key());
|
||||||
|
if (!provisioning_public_key_) return INVALID_PROVISIONER_DRM_CERTIFICATE;
|
||||||
|
provisioning_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey(
|
||||||
|
provisioning_private_key, provisioning_private_key_phassphrase);
|
||||||
|
if (!provisioning_private_key_) return INVALID_PROVISIONER_PRIVATE_KEY;
|
||||||
|
if (!provisioning_public_key_->MatchesPrivateKey(
|
||||||
|
*provisioning_private_key_)) {
|
||||||
|
LOG(WARNING) << "Provisioning public key and private key do not match.";
|
||||||
|
return INVALID_PROVISIONER_PRIVATE_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secret_spoid_sauce.empty()) {
|
||||||
|
LOG(WARNING) << "SPOID secret sauce is empty!";
|
||||||
|
return INVALID_SPOID_SAUCE;
|
||||||
|
}
|
||||||
|
secret_spoid_sauce_ = secret_spoid_sauce;
|
||||||
|
|
||||||
|
if (!oem_device_cert_.Initialize(certificate_type)) return INTERNAL_ERROR;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngineImpl::SetCertificateStatusList(
|
||||||
|
const std::string& certificate_status_list, uint32_t expiration_period_seconds) {
|
||||||
|
if (certificate_status_list.empty()) {
|
||||||
|
LOG(WARNING) << "Empty certificate_status_list.";
|
||||||
|
return INVALID_STATUS_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignedCertificateStatusList signed_cert_status_list;
|
||||||
|
if (!signed_cert_status_list.ParseFromString(certificate_status_list)) {
|
||||||
|
LOG(WARNING) << "Error parsing SignedCertificateStatusList.";
|
||||||
|
return INVALID_STATUS_LIST;
|
||||||
|
}
|
||||||
|
if (!root_public_key_->VerifySignature(
|
||||||
|
signed_cert_status_list.certificate_status_list(),
|
||||||
|
signed_cert_status_list.signature())) {
|
||||||
|
LOG_WITH_PROTO("Signature verification failed", signed_cert_status_list);
|
||||||
|
return INVALID_STATUS_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceCertificateStatusList cert_status_list;
|
||||||
|
if (!cert_status_list.ParseFromString(
|
||||||
|
signed_cert_status_list.certificate_status_list())) {
|
||||||
|
LOG_WITH_PROTO("Error parsing DeviceCertificateStatusList",
|
||||||
|
signed_cert_status_list);
|
||||||
|
return INVALID_STATUS_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriterMutexLock writer_lock(&mutex_);
|
||||||
|
|
||||||
|
if (expiration_period_seconds == 0) {
|
||||||
|
certificate_expiration_seconds_utc_ = std::numeric_limits<uint32_t>::max();
|
||||||
|
} else {
|
||||||
|
certificate_expiration_seconds_utc_ =
|
||||||
|
cert_status_list.creation_time_seconds() + expiration_period_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate_status_map_.clear();
|
||||||
|
for (DeviceCertificateStatus& cert_status :
|
||||||
|
*cert_status_list.mutable_certificate_status()) {
|
||||||
|
const uint32_t system_id = cert_status.device_info().system_id();
|
||||||
|
certificate_status_map_[system_id].Swap(&cert_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove items that are no longer valid.
|
||||||
|
for (auto it = intermediate_certs_info_.begin();
|
||||||
|
it != intermediate_certs_info_.end();) {
|
||||||
|
auto certificate_status_it = certificate_status_map_.find(it->first);
|
||||||
|
if (certificate_status_it == certificate_status_map_.end())
|
||||||
|
intermediate_certs_info_.erase(it++);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
// Set / Update device info.
|
||||||
|
for (const auto& cert_status : certificate_status_map_) {
|
||||||
|
auto device_info = std::make_shared<ProvisionedDeviceInfo>();
|
||||||
|
*device_info = cert_status.second.device_info();
|
||||||
|
intermediate_certs_info_[cert_status.first].device_info = device_info;
|
||||||
|
// intermediate certificate and private key are not changed if exists.
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngineImpl::GenerateDrmIntermediateCertificate(
|
||||||
|
uint32_t system_id, const std::string& public_key, std::string* certificate) const {
|
||||||
|
auto intermediate_public_key =
|
||||||
|
rsa_key_factory_->CreateFromPkcs1PublicKey(public_key);
|
||||||
|
if (!intermediate_public_key) return INVALID_INTERMEDIATE_PUBLIC_KEY;
|
||||||
|
|
||||||
|
const size_t kCertificateSerialNumberSize = 16;
|
||||||
|
std::string serial_number;
|
||||||
|
if (!RandomBytes(kCertificateSerialNumberSize, &serial_number)) {
|
||||||
|
LOG(WARNING) << "Failed to generate serial_number.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
if (!GenerateCertificate(DrmDeviceCertificate::DRM_INTERMEDIATE,
|
||||||
|
system_id, std::string(), serial_number, public_key,
|
||||||
|
*provisioning_private_key_,
|
||||||
|
signed_provisioning_cert_, certificate)) {
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngineImpl::AddDrmIntermediateCertificate(
|
||||||
|
const std::string& intermediate_cert, const std::string& cert_private_key,
|
||||||
|
const std::string& cert_private_key_passphrase) {
|
||||||
|
SignedDrmDeviceCertificate intermediate_signed_cert;
|
||||||
|
DrmDeviceCertificate intermediate_drm_cert;
|
||||||
|
if (!VerifyAndExtractCertificate(provisioning_public_key_.get(),
|
||||||
|
intermediate_cert, &intermediate_signed_cert,
|
||||||
|
&intermediate_drm_cert)) {
|
||||||
|
return INVALID_INTERMEDIATE_DRM_CERTIFICATE;
|
||||||
|
}
|
||||||
|
if (intermediate_drm_cert.type() !=
|
||||||
|
DrmDeviceCertificate::DRM_INTERMEDIATE) {
|
||||||
|
LOG_WITH_PROTO("Invalid device certificate type", intermediate_drm_cert);
|
||||||
|
return INVALID_INTERMEDIATE_DRM_CERTIFICATE;
|
||||||
|
}
|
||||||
|
if (!intermediate_drm_cert.has_system_id()) {
|
||||||
|
LOG_WITH_PROTO("Missing system_id", intermediate_drm_cert);
|
||||||
|
return UNKNOWN_SYSTEM_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string empty_oem_ca_serial_number;
|
||||||
|
ProvisioningStatus status = CheckDeviceStatus(
|
||||||
|
intermediate_drm_cert.system_id(), empty_oem_ca_serial_number);
|
||||||
|
if (status != OK) return status;
|
||||||
|
|
||||||
|
auto intermediate_public_key = rsa_key_factory_->CreateFromPkcs1PublicKey(
|
||||||
|
intermediate_drm_cert.public_key());
|
||||||
|
if (!intermediate_public_key) return INVALID_INTERMEDIATE_DRM_CERTIFICATE;
|
||||||
|
std::unique_ptr<RsaPrivateKey> intermediate_private_key =
|
||||||
|
rsa_key_factory_->CreateFromPkcs8PrivateKey(cert_private_key,
|
||||||
|
cert_private_key_passphrase);
|
||||||
|
if (!intermediate_private_key) return INVALID_INTERMEDIATE_PRIVATE_KEY;
|
||||||
|
if (!intermediate_public_key->MatchesPrivateKey(*intermediate_private_key)) {
|
||||||
|
LOG(WARNING) << "Intermediate public key and private key do not match.";
|
||||||
|
return INVALID_INTERMEDIATE_PRIVATE_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriterMutexLock writer_lock(&mutex_);
|
||||||
|
auto& certificate_info =
|
||||||
|
intermediate_certs_info_[intermediate_drm_cert.system_id()];
|
||||||
|
certificate_info.signed_drm_certificate.Swap(&intermediate_signed_cert);
|
||||||
|
certificate_info.private_key = std::move(intermediate_private_key);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngineImpl::GenerateDeviceDrmCertificate(
|
||||||
|
uint32_t system_id, const std::string& oem_ca_serial_number,
|
||||||
|
const std::string& public_key, const std::string& certificate_serial_number,
|
||||||
|
std::string* certificate) const {
|
||||||
|
// |oem_ca_serial_number| could be empty if it is called directly from
|
||||||
|
// ProvisioningEngine::GenerateDeviceDrmCertificate.
|
||||||
|
DCHECK(!certificate_serial_number.empty());
|
||||||
|
|
||||||
|
return GenerateProviderDeviceDrmCertificate(
|
||||||
|
system_id, oem_ca_serial_number, std::string(), public_key,
|
||||||
|
certificate_serial_number, certificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngineImpl::GenerateProviderDeviceDrmCertificate(
|
||||||
|
uint32_t system_id, const std::string& oem_ca_serial_number,
|
||||||
|
const std::string& provider_id, const std::string& public_key,
|
||||||
|
const std::string& certificate_serial_number, std::string* certificate) const {
|
||||||
|
// |oem_ca_serial_number| could be empty if it is called directly from
|
||||||
|
// ProvisioningEngine::GenerateDeviceDrmCertificate.
|
||||||
|
DCHECK(!certificate_serial_number.empty());
|
||||||
|
|
||||||
|
ProvisioningStatus status =
|
||||||
|
CheckDeviceStatus(system_id, oem_ca_serial_number);
|
||||||
|
if (status != OK) return status;
|
||||||
|
|
||||||
|
std::shared_ptr<RsaPrivateKey> intermediate_private_key;
|
||||||
|
const SignedDrmDeviceCertificate* intermediate_cert = nullptr;
|
||||||
|
{
|
||||||
|
ReaderMutexLock reader_lock(&mutex_);
|
||||||
|
|
||||||
|
auto info_it = intermediate_certs_info_.find(system_id);
|
||||||
|
if (info_it == intermediate_certs_info_.end() ||
|
||||||
|
!info_it->second.private_key) {
|
||||||
|
LOG(WARNING) << "Cannot find the intermediate certificate for system: "
|
||||||
|
<< system_id;
|
||||||
|
return MISSING_DRM_INTERMEDIATE_CERT;
|
||||||
|
}
|
||||||
|
intermediate_private_key = info_it->second.private_key;
|
||||||
|
intermediate_cert = &info_it->second.signed_drm_certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GenerateCertificate(DrmDeviceCertificate::DRM_USER_DEVICE,
|
||||||
|
system_id, provider_id, certificate_serial_number,
|
||||||
|
public_key, *intermediate_private_key,
|
||||||
|
*intermediate_cert, certificate)) {
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ProvisionedDeviceInfo> ProvisioningEngineImpl::GetDeviceInfo(
|
||||||
|
uint32_t system_id) const {
|
||||||
|
ReaderMutexLock reader_lock(&mutex_);
|
||||||
|
auto info_it = intermediate_certs_info_.find(system_id);
|
||||||
|
if (info_it == intermediate_certs_info_.end()) {
|
||||||
|
LOG(WARNING) << "Cannot find the system id in device certificate list: "
|
||||||
|
<< system_id;
|
||||||
|
return std::shared_ptr<ProvisionedDeviceInfo>();
|
||||||
|
}
|
||||||
|
return info_it->second.device_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProvisioningEngineImpl::LoadDrmRootPublicKey(
|
||||||
|
CertificateType certificate_type) {
|
||||||
|
const std::string* root_cert_string = nullptr;
|
||||||
|
RootCertificates root_certificates;
|
||||||
|
switch (certificate_type) {
|
||||||
|
case kCertTesting:
|
||||||
|
root_cert_string = &root_certificates.drm_root_test_certificate();
|
||||||
|
break;
|
||||||
|
case kCertDevelopment:
|
||||||
|
root_cert_string = &root_certificates.drm_root_dev_certificate();
|
||||||
|
break;
|
||||||
|
case kCertProduction:
|
||||||
|
root_cert_string = &root_certificates.drm_root_prod_certificate();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(WARNING) << "Invalid certificate type " << certificate_type;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignedDrmDeviceCertificate signed_root_cert;
|
||||||
|
DrmDeviceCertificate root_cert;
|
||||||
|
if (!VerifyAndExtractCertificate(nullptr /* self signed */, *root_cert_string,
|
||||||
|
&signed_root_cert, &root_cert)) {
|
||||||
|
LOG(WARNING) << "Failed to extract root certificate.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (root_cert.type() != DrmDeviceCertificate::ROOT) {
|
||||||
|
LOG(WARNING) << "Expecting ROOT certificate.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
root_public_key_ =
|
||||||
|
rsa_key_factory_->CreateFromPkcs1PublicKey(root_cert.public_key());
|
||||||
|
CHECK(root_public_key_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngineImpl::CheckDeviceStatus(
|
||||||
|
uint32_t system_id, const std::string& oem_ca_serial_number) const {
|
||||||
|
ReaderMutexLock reader_lock(&mutex_);
|
||||||
|
|
||||||
|
if (certificate_expiration_seconds_utc_ < time(nullptr))
|
||||||
|
return STATUS_LIST_EXPIRED;
|
||||||
|
|
||||||
|
auto certificate_status_it = certificate_status_map_.find(system_id);
|
||||||
|
if (certificate_status_it == certificate_status_map_.end()) {
|
||||||
|
LOG(WARNING) << "Cannot find the system id in device certificate list: "
|
||||||
|
<< system_id;
|
||||||
|
return UNKNOWN_SYSTEM_ID;
|
||||||
|
}
|
||||||
|
if (!IsSerialNumberEq(certificate_status_it->second.oem_serial_number(),
|
||||||
|
oem_ca_serial_number)) {
|
||||||
|
LOG(WARNING) << "Provided serial number does not match with stored serial "
|
||||||
|
"number. It may come from a revoked device. System Id: "
|
||||||
|
<< system_id;
|
||||||
|
return DEVICE_REVOKED;
|
||||||
|
}
|
||||||
|
if (certificate_status_it->second.status() !=
|
||||||
|
DeviceCertificateStatus::VALID) {
|
||||||
|
LOG(WARNING) << "Device revoked " << system_id;
|
||||||
|
return DEVICE_REVOKED;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
189
provisioning_sdk/internal/provisioning_engine_impl.h
Normal file
189
provisioning_sdk/internal/provisioning_engine_impl.h
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// ProvisioningEngine internal implementation. This class is thread-safe.
|
||||||
|
|
||||||
|
#ifndef PROVISIONING_SDK_INTERNAL_PROVISIONING_ENGINE_IMPL_H_
|
||||||
|
#define PROVISIONING_SDK_INTERNAL_PROVISIONING_ENGINE_IMPL_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "base/mutex.h"
|
||||||
|
#include "base/thread_annotations.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "provisioning_sdk/internal/oem_device_cert.h"
|
||||||
|
#include "provisioning_sdk/public/certificate_type.h"
|
||||||
|
#include "provisioning_sdk/public/provisioning_status.h"
|
||||||
|
#include "protos/public/device_certificate.pb.h"
|
||||||
|
#include "protos/public/provisioned_device_info.pb.h"
|
||||||
|
#include "protos/public/signed_device_certificate.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ProvisioningSession;
|
||||||
|
|
||||||
|
class ProvisioningEngineImpl {
|
||||||
|
public:
|
||||||
|
ProvisioningEngineImpl();
|
||||||
|
virtual ~ProvisioningEngineImpl();
|
||||||
|
|
||||||
|
// Initializes the provisioning engine with required credentials.
|
||||||
|
// * |certificate_type| indicates which type of certificate chains will be
|
||||||
|
// used for device provisioning via this engine.
|
||||||
|
// * |drm_service_certificate| is a Google-generated certificate used to
|
||||||
|
// authenticate the service provider for purposes of user privacy.
|
||||||
|
// * |service_private_key| is the encrypted PKCS#8 private RSA key
|
||||||
|
// corresponding to the service certificate.
|
||||||
|
// * |service_private_key_passphrase| is the password required to decrypt
|
||||||
|
// |service_private_key|, if any.
|
||||||
|
// * |provisioning_drm_certificate| is a Google-generated certificate used to
|
||||||
|
// sign intermediate DRM certificates.
|
||||||
|
// * |provisioning_private_key| is the encrypted PKCS#8 private RSA key
|
||||||
|
// corresponding to the provisioning certificate.
|
||||||
|
// * |provisioning_private_key_passphrase| is the password required to
|
||||||
|
// decrypt |provisioning_private_key|, if any.
|
||||||
|
// * |secret_spoid_sauce| is a stable secret used as a factor in the
|
||||||
|
// derivation of Stable Per-Origin IDentifiers.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus Initialize(
|
||||||
|
CertificateType certificate_type,
|
||||||
|
const std::string& drm_service_certificate,
|
||||||
|
const std::string& service_private_key,
|
||||||
|
const std::string& service_private_key_passphrase,
|
||||||
|
const std::string& provisioning_drm_certificate,
|
||||||
|
const std::string& provisioning_private_key,
|
||||||
|
const std::string& provisioning_private_key_passphrase,
|
||||||
|
const std::string& secret_spoid_sauce);
|
||||||
|
|
||||||
|
// Set the certificate status list for this engine.
|
||||||
|
// * |certificate_status_list| is a certificate status list generated by the
|
||||||
|
// Widevine Provisioning Service.
|
||||||
|
// * |expiration_period| is the number of seconds until the
|
||||||
|
// |certificate_status_list| expires after its creation time
|
||||||
|
// (creation_time_seconds). Zero means it will never expire.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus SetCertificateStatusList(
|
||||||
|
const std::string& certificate_status_list,
|
||||||
|
uint32_t expiration_period_seconds);
|
||||||
|
|
||||||
|
// Generate an intermediate DRM certificate.
|
||||||
|
// * |system_id| is the Widevine system ID for the type of device.
|
||||||
|
// * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will
|
||||||
|
// be embedded in the generated certificate.
|
||||||
|
// * |certificate| will contain the new intermediate certificate, upon
|
||||||
|
// successful return.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
// NOTE: The generated certificate and associated private key should be stored
|
||||||
|
// securely to be reused. They should also be propagated to all
|
||||||
|
// engines, including this one, by invoking
|
||||||
|
// |AddIntermediatedrmcertificate| on all active ProvisioningEngine(s).
|
||||||
|
ProvisioningStatus GenerateDrmIntermediateCertificate(
|
||||||
|
uint32_t system_id, const std::string& public_key, std::string* certificate) const;
|
||||||
|
|
||||||
|
// Add an intermediate DRM certificate to the provisioning engine. This is
|
||||||
|
// usually done once for each supported device type.
|
||||||
|
// * |intermediate_cert| is the intermediate DRM certificate to be added.
|
||||||
|
// * |cert_private_key| is a PKCS#8 private key corresponding to
|
||||||
|
// |intermediate_cert|.
|
||||||
|
// * |cert_private_key_passphrase| is the passphrase for cert_private_key,
|
||||||
|
// if any.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus AddDrmIntermediateCertificate(
|
||||||
|
const std::string& intermediate_cert, const std::string& cert_private_key,
|
||||||
|
const std::string& cert_private_key_passphrase);
|
||||||
|
|
||||||
|
// Generate a new device DRM certificate to be provisioned.
|
||||||
|
// * |system_id| is the Widevine system ID for the type of device being
|
||||||
|
// provisioned.
|
||||||
|
// * |oem_ca_serial_number| is the oem serial number for the type of device
|
||||||
|
// being provisioned. ProvisioningEngine uses |system_id| and
|
||||||
|
// |oem_ca_serial_number| to determine if the device is valid, e.g. whether
|
||||||
|
// it has been revoked. |oem_ca_serial_number| can be empty if we do not
|
||||||
|
// care about serial number matches or not.
|
||||||
|
// * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will
|
||||||
|
// be embedded in the generated certificate.
|
||||||
|
// * |certificate_serial_number| is a binary std::string to be used as the
|
||||||
|
// generated DRM certificate serial number.
|
||||||
|
// * |certificate| will contain, upon successful return the generated
|
||||||
|
// certificate.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
// Virtual for mocking.
|
||||||
|
virtual ProvisioningStatus GenerateDeviceDrmCertificate(
|
||||||
|
uint32_t system_id, const std::string& oem_ca_serial_number,
|
||||||
|
const std::string& public_key, const std::string& certificate_serial_number,
|
||||||
|
std::string* certificate) const;
|
||||||
|
|
||||||
|
// Internal version of the method above. Allows specifying |provider_id|.
|
||||||
|
virtual ProvisioningStatus GenerateProviderDeviceDrmCertificate(
|
||||||
|
uint32_t system_id, const std::string& oem_ca_serial_number,
|
||||||
|
const std::string& provider_id, const std::string& public_key,
|
||||||
|
const std::string& certificate_serial_number, std::string* certificate) const;
|
||||||
|
|
||||||
|
// Get the device info for the given |system_id|.
|
||||||
|
std::shared_ptr<ProvisionedDeviceInfo> GetDeviceInfo(
|
||||||
|
uint32_t system_id) const;
|
||||||
|
|
||||||
|
// Returns the service private key.
|
||||||
|
const RsaPrivateKey* service_private_key() const {
|
||||||
|
return service_private_key_.get();
|
||||||
|
}
|
||||||
|
const OemDeviceCert& oem_device_cert() const { return oem_device_cert_; }
|
||||||
|
const std::string& secret_spoid_sauce() const { return secret_spoid_sauce_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ProvisioningEngineImplTest;
|
||||||
|
|
||||||
|
ProvisioningEngineImpl(const ProvisioningEngineImpl&) = delete;
|
||||||
|
ProvisioningEngineImpl& operator=(const ProvisioningEngineImpl&) = delete;
|
||||||
|
|
||||||
|
// Load DRM root public key with type |certificate_type|.
|
||||||
|
bool LoadDrmRootPublicKey(CertificateType certificate_type);
|
||||||
|
|
||||||
|
// Check device status.
|
||||||
|
// If |oem_ca_serial_number| is empty, we do not care whether serial number
|
||||||
|
// matches or not.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus CheckDeviceStatus(
|
||||||
|
uint32_t system_id, const std::string& oem_ca_serial_number) const;
|
||||||
|
|
||||||
|
// Inject rsa_key_factory for testing.
|
||||||
|
void set_rsa_key_factory(std::unique_ptr<RsaKeyFactory> rsa_key_factory) {
|
||||||
|
rsa_key_factory_ = std::move(rsa_key_factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<RsaKeyFactory> rsa_key_factory_;
|
||||||
|
std::unique_ptr<RsaPublicKey> root_public_key_;
|
||||||
|
std::unique_ptr<RsaPublicKey> service_public_key_;
|
||||||
|
std::unique_ptr<RsaPrivateKey> service_private_key_;
|
||||||
|
SignedDrmDeviceCertificate signed_provisioning_cert_;
|
||||||
|
std::unique_ptr<RsaPublicKey> provisioning_public_key_;
|
||||||
|
std::unique_ptr<RsaPrivateKey> provisioning_private_key_;
|
||||||
|
std::string secret_spoid_sauce_;
|
||||||
|
OemDeviceCert oem_device_cert_;
|
||||||
|
|
||||||
|
mutable Mutex mutex_;
|
||||||
|
// POSIX time, in seconds, when the list would be expired.
|
||||||
|
uint32_t certificate_expiration_seconds_utc_ GUARDED_BY(mutex_) = 0;
|
||||||
|
// Maps with system_id as the key.
|
||||||
|
std::map<uint32_t, DeviceCertificateStatus> certificate_status_map_
|
||||||
|
GUARDED_BY(mutex_);
|
||||||
|
struct IntermediateCertificateInfo {
|
||||||
|
SignedDrmDeviceCertificate signed_drm_certificate;
|
||||||
|
std::shared_ptr<ProvisionedDeviceInfo> device_info;
|
||||||
|
std::shared_ptr<RsaPrivateKey> private_key;
|
||||||
|
};
|
||||||
|
std::map<uint32_t, IntermediateCertificateInfo> intermediate_certs_info_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // PROVISIONING_SDK_INTERNAL_PROVISIONING_ENGINE_IMPL_H_
|
||||||
783
provisioning_sdk/internal/provisioning_engine_impl_test.cc
Normal file
783
provisioning_sdk/internal/provisioning_engine_impl_test.cc
Normal file
@@ -0,0 +1,783 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "common/mock_rsa_key.h"
|
||||||
|
#include "provisioning_sdk/public/certificate_type.h"
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::ByMove;
|
||||||
|
using ::testing::DoAll;
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::SaveArg;
|
||||||
|
using ::testing::SetArgPointee;
|
||||||
|
using ::testing::StrEq;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const int kSystemId = 100;
|
||||||
|
const int kExpirationPeriodSeconds = 3600;
|
||||||
|
const int kInfiniteExpirationPeriodSeconds = 0;
|
||||||
|
const char kEmptyOemSerialNumber[] = "";
|
||||||
|
const char kSecretSauce[] = "Twas bryllyg, and ye slythy toves";
|
||||||
|
const char kCertificateSerialNumber[] = "certificate_serial_number";
|
||||||
|
const char kOemSerialNumber0[] = "oem_serial_number_0";
|
||||||
|
const char kOemSerialNumber1[] = "oem_serial_number_1";
|
||||||
|
const char kDevicePublicKey[] = "device_public_key";
|
||||||
|
const char kSignature[] = "mock_signature";
|
||||||
|
const char kIntermediatePrivateKey[] = "intermediate_private_key";
|
||||||
|
const char kIntermediatePrivateKeyPassphrase[] =
|
||||||
|
"intermediate_private_key_passphrase";
|
||||||
|
const char kIntermediatePublicKey[] = "intermediate_public_key";
|
||||||
|
const char kServicePrivateKey[] = "service_private_key";
|
||||||
|
const char kServicePrivateKeyPassphrase[] = "service_private_key_phassphrase";
|
||||||
|
const char kServiceDrmCertificate[] = "service_drm_certificate";
|
||||||
|
const char kProvisioningDrmCertificate[] = "provisioning_drm_certificate";
|
||||||
|
const char kProvisioningPrivateKey[] = "provisioning_private_key";
|
||||||
|
const char kProvisioningPrivateKeyPassphrase[] =
|
||||||
|
"provisioning_private_key_phassphrase";
|
||||||
|
const char kProvisioningPublicKey[] = "provisioning_public_key";
|
||||||
|
const char kProvisioningSignature[] = "provisioning_signature";
|
||||||
|
|
||||||
|
// A simple std::string concatenation function. This assumes i within [0, 9].
|
||||||
|
std::string StrCat(const std::string& in, int i) {
|
||||||
|
DCHECK_LE(i, 9);
|
||||||
|
DCHECK_GE(i, 0);
|
||||||
|
std::string out = in + "0";
|
||||||
|
out[out.size() - 1] += i;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ProvisioningEngineImplTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
ProvisioningEngineImplTest() {
|
||||||
|
mock_rsa_key_factory_ = new MockRsaKeyFactory;
|
||||||
|
engine_impl_.set_rsa_key_factory(
|
||||||
|
std::unique_ptr<RsaKeyFactory>(mock_rsa_key_factory_));
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus CheckDeviceStatus(uint32_t system_id,
|
||||||
|
const std::string& oem_ca_serial_number) {
|
||||||
|
return engine_impl_.CheckDeviceStatus(system_id, oem_ca_serial_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningEngineImpl engine_impl_;
|
||||||
|
MockRsaKeyFactory* mock_rsa_key_factory_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplTest, InvalidCertType) {
|
||||||
|
CertificateType invalid_certificate = static_cast<CertificateType>(100);
|
||||||
|
EXPECT_EQ(
|
||||||
|
INVALID_CERTIFICATE_TYPE,
|
||||||
|
engine_impl_.Initialize(
|
||||||
|
invalid_certificate, kServiceDrmCertificate, kServicePrivateKey,
|
||||||
|
kServicePrivateKeyPassphrase, kProvisioningDrmCertificate,
|
||||||
|
kProvisioningPrivateKey, kProvisioningPrivateKeyPassphrase,
|
||||||
|
kSecretSauce));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProvisioningEngineImplServiceTest : public ProvisioningEngineImplTest {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
mock_root_public_key_ = new MockRsaPublicKey();
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_, CreateFromPkcs1PublicKey(_))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPublicKey>(mock_root_public_key_))));
|
||||||
|
|
||||||
|
service_cert_.set_public_key("service_public_key");
|
||||||
|
service_cert_.set_type(DrmDeviceCertificate::SERVICE);
|
||||||
|
signed_service_cert_.set_drm_certificate(service_cert_.SerializeAsString());
|
||||||
|
signed_service_cert_.set_signature("service_signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus Initialize(const std::string& service_drm_certificate) {
|
||||||
|
return engine_impl_.Initialize(
|
||||||
|
kCertTesting, service_drm_certificate, kServicePrivateKey,
|
||||||
|
kServicePrivateKeyPassphrase, kProvisioningDrmCertificate,
|
||||||
|
kProvisioningPrivateKey, kProvisioningPrivateKeyPassphrase,
|
||||||
|
kSecretSauce);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrmDeviceCertificate service_cert_;
|
||||||
|
SignedDrmDeviceCertificate signed_service_cert_;
|
||||||
|
MockRsaPublicKey* mock_root_public_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplServiceTest, Empty) {
|
||||||
|
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, Initialize(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplServiceTest, MissingDeviceCert) {
|
||||||
|
signed_service_cert_.clear_drm_certificate();
|
||||||
|
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_service_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplServiceTest, MissingSignature) {
|
||||||
|
signed_service_cert_.clear_signature();
|
||||||
|
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_service_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplServiceTest, SignatureVerificationFailure) {
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(StrEq(service_cert_.SerializeAsString()),
|
||||||
|
"service_signature"))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_service_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplServiceTest, InvalidDeviceCertType) {
|
||||||
|
service_cert_.set_type(DrmDeviceCertificate::PROVISIONER);
|
||||||
|
signed_service_cert_.set_drm_certificate(service_cert_.SerializeAsString());
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(StrEq(service_cert_.SerializeAsString()),
|
||||||
|
"service_signature"))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_service_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplServiceTest, InvaidPublicKey) {
|
||||||
|
EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature"))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey("service_public_key"))
|
||||||
|
.WillOnce(Return(ByMove(nullptr)));
|
||||||
|
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_service_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplServiceTest, InvalidPrivateKey) {
|
||||||
|
EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature"))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey("service_public_key"))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kServicePrivateKey,
|
||||||
|
kServicePrivateKeyPassphrase))
|
||||||
|
.WillOnce(Return(ByMove(nullptr)));
|
||||||
|
EXPECT_EQ(INVALID_SERVICE_PRIVATE_KEY,
|
||||||
|
Initialize(signed_service_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplServiceTest, MismatchPublicKeyPrivateKey) {
|
||||||
|
EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature"))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey("service_public_key"))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
|
||||||
|
MockRsaPrivateKey* mock_rsa_private_key = new MockRsaPrivateKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kServicePrivateKey,
|
||||||
|
kServicePrivateKeyPassphrase))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPrivateKey>(mock_rsa_private_key))));
|
||||||
|
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
EXPECT_EQ(INVALID_SERVICE_PRIVATE_KEY,
|
||||||
|
Initialize(signed_service_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProvisioningEngineImplProvTest
|
||||||
|
: public ProvisioningEngineImplServiceTest {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
ProvisioningEngineImplServiceTest::SetUp();
|
||||||
|
|
||||||
|
// Service certificate expectations.
|
||||||
|
EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature"))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
mock_service_public_key_ = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey("service_public_key"))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPublicKey>(mock_service_public_key_))));
|
||||||
|
mock_service_private_key_ = new MockRsaPrivateKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kServicePrivateKey,
|
||||||
|
kServicePrivateKeyPassphrase))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPrivateKey>(mock_service_private_key_))));
|
||||||
|
EXPECT_CALL(*mock_service_public_key_, MatchesPrivateKey(_))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
prov_cert_.set_public_key(kProvisioningPublicKey);
|
||||||
|
prov_cert_.set_type(DrmDeviceCertificate::PROVISIONER);
|
||||||
|
signed_prov_cert_.set_drm_certificate(prov_cert_.SerializeAsString());
|
||||||
|
signed_prov_cert_.set_signature(kProvisioningSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus Initialize(const std::string& provisioning_drm_certificate) {
|
||||||
|
return engine_impl_.Initialize(
|
||||||
|
kCertTesting, signed_service_cert_.SerializeAsString(),
|
||||||
|
kServicePrivateKey, kServicePrivateKeyPassphrase,
|
||||||
|
provisioning_drm_certificate, kProvisioningPrivateKey,
|
||||||
|
kProvisioningPrivateKeyPassphrase, "spoid_secret_sauce");
|
||||||
|
}
|
||||||
|
|
||||||
|
DrmDeviceCertificate prov_cert_;
|
||||||
|
SignedDrmDeviceCertificate signed_prov_cert_;
|
||||||
|
MockRsaPublicKey* mock_service_public_key_ = nullptr;
|
||||||
|
MockRsaPrivateKey* mock_service_private_key_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplProvTest, Empty) {
|
||||||
|
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE, Initialize(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplProvTest, MissingDeviceCert) {
|
||||||
|
signed_prov_cert_.clear_drm_certificate();
|
||||||
|
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_prov_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplProvTest, MissingSignature) {
|
||||||
|
signed_prov_cert_.clear_signature();
|
||||||
|
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_prov_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplProvTest, SignatureVerificationFailure) {
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(StrEq(prov_cert_.SerializeAsString()),
|
||||||
|
kProvisioningSignature))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_prov_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplProvTest, InvalidDeviceCertType) {
|
||||||
|
prov_cert_.set_type(DrmDeviceCertificate::SERVICE);
|
||||||
|
signed_prov_cert_.set_drm_certificate(prov_cert_.SerializeAsString());
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(StrEq(prov_cert_.SerializeAsString()),
|
||||||
|
kProvisioningSignature))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_prov_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplProvTest, InvaidPublicKey) {
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(_, kProvisioningSignature))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kProvisioningPublicKey))
|
||||||
|
.WillOnce(Return(ByMove(nullptr)));
|
||||||
|
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
|
||||||
|
Initialize(signed_prov_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplProvTest, InvalidPrivateKey) {
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(_, kProvisioningSignature))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kProvisioningPublicKey))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kProvisioningPrivateKey,
|
||||||
|
kProvisioningPrivateKeyPassphrase))
|
||||||
|
.WillOnce(Return(ByMove(nullptr)));
|
||||||
|
EXPECT_EQ(INVALID_PROVISIONER_PRIVATE_KEY,
|
||||||
|
Initialize(signed_prov_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplProvTest, MismatchPublicKeyPrivateKey) {
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(_, kProvisioningSignature))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kProvisioningPublicKey))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
|
||||||
|
MockRsaPrivateKey* mock_rsa_private_key = new MockRsaPrivateKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kProvisioningPrivateKey,
|
||||||
|
kProvisioningPrivateKeyPassphrase))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPrivateKey>(mock_rsa_private_key))));
|
||||||
|
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
EXPECT_EQ(INVALID_PROVISIONER_PRIVATE_KEY,
|
||||||
|
Initialize(signed_prov_cert_.SerializeAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProvisioningEngineImplGeneralTest
|
||||||
|
: public ProvisioningEngineImplProvTest {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
ProvisioningEngineImplProvTest::SetUp();
|
||||||
|
|
||||||
|
// Provisioning certificate expectations.
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(_, kProvisioningSignature))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
mock_prov_public_key_ = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kProvisioningPublicKey))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPublicKey>(mock_prov_public_key_))));
|
||||||
|
mock_prov_private_key_ = new MockRsaPrivateKey;
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kProvisioningPrivateKey,
|
||||||
|
kProvisioningPrivateKeyPassphrase))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPrivateKey>(mock_prov_private_key_))));
|
||||||
|
EXPECT_CALL(*mock_prov_public_key_, MatchesPrivateKey(_))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
ASSERT_EQ(OK, ProvisioningEngineImplProvTest::Initialize(
|
||||||
|
signed_prov_cert_.SerializeAsString()));
|
||||||
|
|
||||||
|
// Setup certificate status list.
|
||||||
|
cert_status_list_.set_creation_time_seconds(time(nullptr));
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
DeviceCertificateStatus* cert_status =
|
||||||
|
cert_status_list_.add_certificate_status();
|
||||||
|
cert_status->set_oem_serial_number(StrCat("oem_serial_number_", i));
|
||||||
|
ProvisionedDeviceInfo* device_info = cert_status->mutable_device_info();
|
||||||
|
device_info->set_system_id(kSystemId + i);
|
||||||
|
device_info->set_model(StrCat("model_", i));
|
||||||
|
}
|
||||||
|
cert_status_list_.mutable_certificate_status(0)->set_status(
|
||||||
|
DeviceCertificateStatus::VALID);
|
||||||
|
cert_status_list_.mutable_certificate_status(1)->set_status(
|
||||||
|
DeviceCertificateStatus::REVOKED);
|
||||||
|
|
||||||
|
SignedCertificateStatusList signed_cert_status_list;
|
||||||
|
signed_cert_status_list.set_certificate_status_list(
|
||||||
|
cert_status_list_.SerializeAsString());
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(
|
||||||
|
StrEq(signed_cert_status_list.certificate_status_list()),
|
||||||
|
"cert_status_list_signature"))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
signed_cert_status_list.set_signature("cert_status_list_signature");
|
||||||
|
ASSERT_EQ(OK, engine_impl_.SetCertificateStatusList(
|
||||||
|
signed_cert_status_list.SerializeAsString(),
|
||||||
|
kExpirationPeriodSeconds));
|
||||||
|
|
||||||
|
// Setup a DrmDeviceCertificate to be used later.
|
||||||
|
intermediate_cert_.set_type(DrmDeviceCertificate::DRM_INTERMEDIATE);
|
||||||
|
intermediate_cert_.set_system_id(kSystemId);
|
||||||
|
intermediate_cert_.set_public_key(kIntermediatePublicKey);
|
||||||
|
signed_intermediate_cert_.set_drm_certificate(
|
||||||
|
intermediate_cert_.SerializeAsString());
|
||||||
|
signed_intermediate_cert_.set_signature(kSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
MockRsaPublicKey* mock_prov_public_key_ = nullptr;
|
||||||
|
MockRsaPrivateKey* mock_prov_private_key_ = nullptr;
|
||||||
|
DeviceCertificateStatusList cert_status_list_;
|
||||||
|
DrmDeviceCertificate intermediate_cert_;
|
||||||
|
SignedDrmDeviceCertificate signed_intermediate_cert_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest, InvalidCertificateStatusList) {
|
||||||
|
EXPECT_EQ(INVALID_STATUS_LIST, engine_impl_.SetCertificateStatusList(
|
||||||
|
"", kExpirationPeriodSeconds));
|
||||||
|
EXPECT_EQ(INVALID_STATUS_LIST,
|
||||||
|
engine_impl_.SetCertificateStatusList(
|
||||||
|
"invalid_certificate_status_list", kExpirationPeriodSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
CertificateStatusListIncorrectSignature) {
|
||||||
|
SignedCertificateStatusList signed_cert_status_list;
|
||||||
|
signed_cert_status_list.set_certificate_status_list(
|
||||||
|
cert_status_list_.SerializeAsString());
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(_, "cert_status_list_signature"))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
signed_cert_status_list.set_signature("cert_status_list_signature");
|
||||||
|
ASSERT_EQ(INVALID_STATUS_LIST,
|
||||||
|
engine_impl_.SetCertificateStatusList(
|
||||||
|
signed_cert_status_list.SerializeAsString(),
|
||||||
|
kExpirationPeriodSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest, GetDeviceInfoAndCheckDeviceStatus) {
|
||||||
|
EXPECT_EQ(OK, CheckDeviceStatus(kSystemId, kEmptyOemSerialNumber));
|
||||||
|
auto device_info = engine_impl_.GetDeviceInfo(kSystemId);
|
||||||
|
ASSERT_NE(nullptr, device_info);
|
||||||
|
EXPECT_EQ("model_0", device_info->model());
|
||||||
|
|
||||||
|
EXPECT_EQ(DEVICE_REVOKED,
|
||||||
|
CheckDeviceStatus(kSystemId + 1, kEmptyOemSerialNumber));
|
||||||
|
// We can still query device info for revoked device.
|
||||||
|
device_info = engine_impl_.GetDeviceInfo(kSystemId + 1);
|
||||||
|
ASSERT_NE(nullptr, device_info);
|
||||||
|
EXPECT_EQ("model_1", device_info->model());
|
||||||
|
|
||||||
|
EXPECT_EQ(UNKNOWN_SYSTEM_ID,
|
||||||
|
CheckDeviceStatus(kSystemId + 2, kEmptyOemSerialNumber));
|
||||||
|
EXPECT_EQ(nullptr, engine_impl_.GetDeviceInfo(kSystemId + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest, UpdateCertificateStatusList) {
|
||||||
|
cert_status_list_.mutable_certificate_status(0)->set_status(
|
||||||
|
DeviceCertificateStatus::REVOKED);
|
||||||
|
|
||||||
|
DeviceCertificateStatus* cert_status =
|
||||||
|
cert_status_list_.add_certificate_status();
|
||||||
|
ProvisionedDeviceInfo* device_info = cert_status->mutable_device_info();
|
||||||
|
device_info->set_system_id(kSystemId + 2);
|
||||||
|
device_info->set_model("model_2");
|
||||||
|
|
||||||
|
SignedCertificateStatusList signed_cert_status_list;
|
||||||
|
signed_cert_status_list.set_certificate_status_list(
|
||||||
|
cert_status_list_.SerializeAsString());
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(_, "cert_status_list_signature"))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
signed_cert_status_list.set_signature("cert_status_list_signature");
|
||||||
|
ASSERT_EQ(OK, engine_impl_.SetCertificateStatusList(
|
||||||
|
signed_cert_status_list.SerializeAsString(),
|
||||||
|
kInfiniteExpirationPeriodSeconds));
|
||||||
|
|
||||||
|
// Now device with system id = kSystemId is revoked.
|
||||||
|
EXPECT_EQ(DEVICE_REVOKED,
|
||||||
|
CheckDeviceStatus(kSystemId, kEmptyOemSerialNumber));
|
||||||
|
EXPECT_EQ("model_0", engine_impl_.GetDeviceInfo(kSystemId)->model());
|
||||||
|
EXPECT_EQ(DEVICE_REVOKED,
|
||||||
|
CheckDeviceStatus(kSystemId + 1, kEmptyOemSerialNumber));
|
||||||
|
EXPECT_EQ("model_1", engine_impl_.GetDeviceInfo(kSystemId + 1)->model());
|
||||||
|
EXPECT_EQ(OK, CheckDeviceStatus(kSystemId + 2, kEmptyOemSerialNumber));
|
||||||
|
EXPECT_EQ("model_2", engine_impl_.GetDeviceInfo(kSystemId + 2)->model());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
GenerateDrmIntermediateCertificateInvalidPublicKey) {
|
||||||
|
std::string drm_certificate;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
|
||||||
|
.WillOnce(Return(ByMove(nullptr)));
|
||||||
|
std::string certificate;
|
||||||
|
ASSERT_EQ(INVALID_INTERMEDIATE_PUBLIC_KEY,
|
||||||
|
engine_impl_.GenerateDrmIntermediateCertificate(
|
||||||
|
kSystemId, kIntermediatePublicKey, &certificate));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest, GenerateDrmIntermediateCertificate) {
|
||||||
|
std::string drm_certificate;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
|
||||||
|
EXPECT_CALL(*mock_prov_private_key_, GenerateSignature(_, _))
|
||||||
|
.WillOnce(DoAll(SaveArg<0>(&drm_certificate),
|
||||||
|
SetArgPointee<1>(kSignature), Return(true)));
|
||||||
|
std::string certificate;
|
||||||
|
ASSERT_EQ(OK, engine_impl_.GenerateDrmIntermediateCertificate(
|
||||||
|
kSystemId, kIntermediatePublicKey, &certificate));
|
||||||
|
|
||||||
|
SignedDrmDeviceCertificate signed_drm_cert_proto;
|
||||||
|
ASSERT_TRUE(signed_drm_cert_proto.ParseFromString(certificate));
|
||||||
|
EXPECT_EQ(drm_certificate, signed_drm_cert_proto.drm_certificate());
|
||||||
|
EXPECT_EQ(kSignature, signed_drm_cert_proto.signature());
|
||||||
|
EXPECT_EQ(signed_prov_cert_.SerializeAsString(),
|
||||||
|
signed_drm_cert_proto.signer().SerializeAsString());
|
||||||
|
|
||||||
|
DrmDeviceCertificate drm_cert_proto;
|
||||||
|
ASSERT_TRUE(drm_cert_proto.ParseFromString(drm_certificate));
|
||||||
|
EXPECT_EQ(DrmDeviceCertificate::DRM_INTERMEDIATE, drm_cert_proto.type());
|
||||||
|
EXPECT_NE("", drm_cert_proto.serial_number());
|
||||||
|
EXPECT_EQ(kSystemId, drm_cert_proto.system_id());
|
||||||
|
EXPECT_EQ(kIntermediatePublicKey, drm_cert_proto.public_key());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
AddDrmIntermediateCertificateInvalidCert) {
|
||||||
|
EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE,
|
||||||
|
engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
"", kIntermediatePrivateKey,
|
||||||
|
kIntermediatePrivateKeyPassphrase));
|
||||||
|
EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE,
|
||||||
|
engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
"invalid_intermediate_cert", kIntermediatePrivateKey,
|
||||||
|
kIntermediatePrivateKeyPassphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
AddDrmIntermediateCertificateIncorrectCertType) {
|
||||||
|
intermediate_cert_.set_type(DrmDeviceCertificate::DRM_USER_DEVICE);
|
||||||
|
signed_intermediate_cert_.set_drm_certificate(
|
||||||
|
intermediate_cert_.SerializeAsString());
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_prov_public_key_,
|
||||||
|
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
|
||||||
|
StrEq(signed_intermediate_cert_.signature())))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_EQ(
|
||||||
|
INVALID_INTERMEDIATE_DRM_CERTIFICATE,
|
||||||
|
engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
AddDrmIntermediateCertificateMissingSystemId) {
|
||||||
|
intermediate_cert_.clear_system_id();
|
||||||
|
signed_intermediate_cert_.set_drm_certificate(
|
||||||
|
intermediate_cert_.SerializeAsString());
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_prov_public_key_,
|
||||||
|
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
|
||||||
|
StrEq(signed_intermediate_cert_.signature())))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_EQ(
|
||||||
|
UNKNOWN_SYSTEM_ID,
|
||||||
|
engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
AddDrmIntermediateCertificateRevokedCert) {
|
||||||
|
intermediate_cert_.set_system_id(kSystemId + 1);
|
||||||
|
signed_intermediate_cert_.set_drm_certificate(
|
||||||
|
intermediate_cert_.SerializeAsString());
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_prov_public_key_,
|
||||||
|
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
|
||||||
|
StrEq(signed_intermediate_cert_.signature())))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_EQ(DEVICE_REVOKED, engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
kIntermediatePrivateKey,
|
||||||
|
kIntermediatePrivateKeyPassphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
AddDrmIntermediateCertificateInvalidPublicKey) {
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_prov_public_key_,
|
||||||
|
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
|
||||||
|
StrEq(signed_intermediate_cert_.signature())))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
|
||||||
|
.WillOnce(Return(ByMove(nullptr)));
|
||||||
|
EXPECT_EQ(
|
||||||
|
INVALID_INTERMEDIATE_DRM_CERTIFICATE,
|
||||||
|
engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
AddDrmIntermediateCertificateInvalidPrivateKey) {
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_prov_public_key_,
|
||||||
|
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
|
||||||
|
StrEq(signed_intermediate_cert_.signature())))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kIntermediatePrivateKey,
|
||||||
|
kIntermediatePrivateKeyPassphrase))
|
||||||
|
.WillOnce(Return(ByMove(nullptr)));
|
||||||
|
|
||||||
|
EXPECT_EQ(
|
||||||
|
INVALID_INTERMEDIATE_PRIVATE_KEY,
|
||||||
|
engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
AddDrmIntermediateCertificateMismatchPublicPrivateKey) {
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_prov_public_key_,
|
||||||
|
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
|
||||||
|
StrEq(signed_intermediate_cert_.signature())))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPublicKey>(mock_intermediate_public_key))));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kIntermediatePrivateKey,
|
||||||
|
kIntermediatePrivateKeyPassphrase))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
|
||||||
|
EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
|
||||||
|
EXPECT_EQ(
|
||||||
|
INVALID_INTERMEDIATE_PRIVATE_KEY,
|
||||||
|
engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
AddDrmIntermediateCertificateSuccess) {
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_prov_public_key_,
|
||||||
|
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
|
||||||
|
StrEq(signed_intermediate_cert_.signature())))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPublicKey>(mock_intermediate_public_key))));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kIntermediatePrivateKey,
|
||||||
|
kIntermediatePrivateKeyPassphrase))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
|
||||||
|
EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
EXPECT_EQ(OK, engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
kIntermediatePrivateKey,
|
||||||
|
kIntermediatePrivateKeyPassphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest, ExpiredCertificateStatusList) {
|
||||||
|
cert_status_list_.set_creation_time_seconds(time(nullptr) -
|
||||||
|
kExpirationPeriodSeconds - 1);
|
||||||
|
|
||||||
|
SignedCertificateStatusList signed_cert_status_list;
|
||||||
|
signed_cert_status_list.set_certificate_status_list(
|
||||||
|
cert_status_list_.SerializeAsString());
|
||||||
|
EXPECT_CALL(*mock_root_public_key_,
|
||||||
|
VerifySignature(_, "cert_status_list_signature"))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
signed_cert_status_list.set_signature("cert_status_list_signature");
|
||||||
|
ASSERT_EQ(OK, engine_impl_.SetCertificateStatusList(
|
||||||
|
signed_cert_status_list.SerializeAsString(),
|
||||||
|
kExpirationPeriodSeconds));
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_prov_public_key_,
|
||||||
|
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
|
||||||
|
StrEq(signed_intermediate_cert_.signature())))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_EQ(
|
||||||
|
STATUS_LIST_EXPIRED,
|
||||||
|
engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
GenerateDeviceDrmCertificateRevokedDevice) {
|
||||||
|
std::string certificate;
|
||||||
|
EXPECT_EQ(DEVICE_REVOKED,
|
||||||
|
engine_impl_.GenerateDeviceDrmCertificate(
|
||||||
|
kSystemId + 1, kOemSerialNumber1, kDevicePublicKey,
|
||||||
|
kCertificateSerialNumber, &certificate));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
GenerateDeviceDrmCertificateWithMismatchingOemSerialNumber) {
|
||||||
|
std::string certificate;
|
||||||
|
// If oem serial number does not match, consider as revoked.
|
||||||
|
EXPECT_EQ(DEVICE_REVOKED,
|
||||||
|
engine_impl_.GenerateDeviceDrmCertificate(
|
||||||
|
kSystemId, kOemSerialNumber1, kDevicePublicKey,
|
||||||
|
kCertificateSerialNumber, &certificate));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
GenerateDeviceDrmCertificateWithoutIntermediateCert) {
|
||||||
|
std::string certificate;
|
||||||
|
EXPECT_EQ(MISSING_DRM_INTERMEDIATE_CERT,
|
||||||
|
engine_impl_.GenerateDeviceDrmCertificate(
|
||||||
|
kSystemId, kOemSerialNumber0, kDevicePublicKey,
|
||||||
|
kCertificateSerialNumber, &certificate));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineImplGeneralTest,
|
||||||
|
GenerateDeviceDrmCertificate) {
|
||||||
|
// Add Intermediate certificate.
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_prov_public_key_,
|
||||||
|
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
|
||||||
|
StrEq(signed_intermediate_cert_.signature())))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPublicKey>(mock_intermediate_public_key))));
|
||||||
|
MockRsaPrivateKey* mock_intermediate_private_key = new MockRsaPrivateKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs8PrivateKey(kIntermediatePrivateKey,
|
||||||
|
kIntermediatePrivateKeyPassphrase))
|
||||||
|
.WillOnce(Return(ByMove(
|
||||||
|
std::unique_ptr<RsaPrivateKey>(mock_intermediate_private_key))));
|
||||||
|
EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
EXPECT_EQ(OK, engine_impl_.AddDrmIntermediateCertificate(
|
||||||
|
signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
kIntermediatePrivateKey,
|
||||||
|
kIntermediatePrivateKeyPassphrase));
|
||||||
|
|
||||||
|
// Intermediate private key expectation.
|
||||||
|
std::string drm_certificate;
|
||||||
|
EXPECT_CALL(*mock_intermediate_private_key, GenerateSignature(_, _))
|
||||||
|
.WillOnce(DoAll(SaveArg<0>(&drm_certificate),
|
||||||
|
SetArgPointee<1>(kSignature), Return(true)));
|
||||||
|
std::string certificate;
|
||||||
|
EXPECT_EQ(OK, engine_impl_.GenerateDeviceDrmCertificate(
|
||||||
|
kSystemId, kOemSerialNumber0, kDevicePublicKey,
|
||||||
|
kCertificateSerialNumber, &certificate));
|
||||||
|
|
||||||
|
SignedDrmDeviceCertificate signed_drm_cert_proto;
|
||||||
|
ASSERT_TRUE(signed_drm_cert_proto.ParseFromString(certificate));
|
||||||
|
EXPECT_EQ(drm_certificate, signed_drm_cert_proto.drm_certificate());
|
||||||
|
EXPECT_EQ(kSignature, signed_drm_cert_proto.signature());
|
||||||
|
EXPECT_THAT(signed_intermediate_cert_.SerializeAsString(),
|
||||||
|
signed_drm_cert_proto.signer().SerializeAsString());
|
||||||
|
|
||||||
|
DrmDeviceCertificate drm_cert_proto;
|
||||||
|
ASSERT_TRUE(drm_cert_proto.ParseFromString(drm_certificate));
|
||||||
|
EXPECT_EQ(DrmDeviceCertificate::DRM_USER_DEVICE, drm_cert_proto.type());
|
||||||
|
EXPECT_EQ(kCertificateSerialNumber, drm_cert_proto.serial_number());
|
||||||
|
EXPECT_EQ(kSystemId, drm_cert_proto.system_id());
|
||||||
|
EXPECT_EQ(kDevicePublicKey, drm_cert_proto.public_key());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
256
provisioning_sdk/internal/provisioning_session_impl.cc
Normal file
256
provisioning_sdk/internal/provisioning_session_impl.cc
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/provisioning_session_impl.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "gflags/gflags.h"
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "common/aes_cbc_util.h"
|
||||||
|
#include "common/random_util.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
#include "provisioning_sdk/public/provisioning_status.h"
|
||||||
|
|
||||||
|
DEFINE_int32(provisioning_log_every_n, 1,
|
||||||
|
"parameter for LOG_EVERY_N to help abate log spamming.");
|
||||||
|
|
||||||
|
#define LOG_EVERY_N_WITH_PROTO(message, proto) \
|
||||||
|
LOG_EVERY_N(WARNING, FLAGS_provisioning_log_every_n) \
|
||||||
|
<< (message) << " [proto: " << (proto).ShortDebugString() << "]"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
ProvisioningSessionImpl::ProvisioningSessionImpl(
|
||||||
|
const ProvisioningEngineImpl& engine, const OemDeviceCert& oem_device_cert,
|
||||||
|
const RsaPrivateKey& service_private_key)
|
||||||
|
: engine_(engine),
|
||||||
|
oem_device_cert_(oem_device_cert),
|
||||||
|
service_private_key_(service_private_key),
|
||||||
|
rsa_key_factory_(new RsaKeyFactory) {}
|
||||||
|
|
||||||
|
ProvisioningSessionImpl::~ProvisioningSessionImpl() {}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningSessionImpl::Initialize(
|
||||||
|
const std::string& device_public_key, const std::string& device_private_key) {
|
||||||
|
auto rsa_public_key =
|
||||||
|
rsa_key_factory_->CreateFromPkcs1PublicKey(device_public_key);
|
||||||
|
if (!rsa_public_key) return INVALID_DEVICE_PUBLIC_KEY;
|
||||||
|
auto rsa_private_key =
|
||||||
|
rsa_key_factory_->CreateFromPkcs1PrivateKey(device_private_key);
|
||||||
|
if (!rsa_private_key) return INVALID_DEVICE_PRIVATE_KEY;
|
||||||
|
if (!rsa_public_key->MatchesPrivateKey(*rsa_private_key)) {
|
||||||
|
LOG(WARNING) << "Device public key and private key do not match.";
|
||||||
|
return INVALID_DEVICE_PRIVATE_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_public_key_ = device_public_key;
|
||||||
|
device_private_key_ = device_private_key;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningSessionImpl::ProcessMessage(
|
||||||
|
const std::string& message, std::string* response) {
|
||||||
|
SignedProvisioningMessage signed_request;
|
||||||
|
ProvisioningRequest request;
|
||||||
|
if (!ValidateAndDeserializeRequest(message, &signed_request, &request))
|
||||||
|
return INVALID_REQUEST_MESSAGE;
|
||||||
|
|
||||||
|
ClientIdentification client_id;
|
||||||
|
if (request.has_encrypted_client_id()) {
|
||||||
|
if (!DecryptClientIdentification(request.encrypted_client_id(), &client_id))
|
||||||
|
return INVALID_REQUEST_MESSAGE;
|
||||||
|
} else {
|
||||||
|
DCHECK(request.has_client_id());
|
||||||
|
client_id.Swap(request.mutable_client_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client_id.type() != ClientIdentification::OEM_DEVICE_CERTIFICATE) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Invalid client_id type", client_id);
|
||||||
|
return INVALID_REQUEST_MESSAGE;
|
||||||
|
}
|
||||||
|
if (client_id.token().empty()) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Missing client_id.token", client_id);
|
||||||
|
return INVALID_REQUEST_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<RsaPublicKey> cert_public_key;
|
||||||
|
uint32_t system_id;
|
||||||
|
std::string oem_ca_serial_number;
|
||||||
|
if (!oem_device_cert_.VerifyCertificateChain(client_id.token(),
|
||||||
|
&cert_public_key, &system_id,
|
||||||
|
&oem_ca_serial_number)) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Invalid token", client_id);
|
||||||
|
return INVALID_REQUEST_MESSAGE;
|
||||||
|
}
|
||||||
|
if (!cert_public_key->VerifySignature(signed_request.message(),
|
||||||
|
signed_request.signature())) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Signature verification failed", client_id);
|
||||||
|
return INVALID_REQUEST_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save device_info for query later.
|
||||||
|
device_info_ = engine_.GetDeviceInfo(system_id);
|
||||||
|
|
||||||
|
std::string certificate_serial_number;
|
||||||
|
if (request.has_spoid()) {
|
||||||
|
certificate_serial_number = request.spoid();
|
||||||
|
} else {
|
||||||
|
// Generate stable serial number.
|
||||||
|
const std::string stable_data(client_id.token() + request.stable_id() +
|
||||||
|
request.provider_id() +
|
||||||
|
engine_.secret_spoid_sauce());
|
||||||
|
const std::string hash = Sha256_Hash(stable_data);
|
||||||
|
const size_t kCertificateSerialNumberSize = 16;
|
||||||
|
certificate_serial_number = hash.substr(0, kCertificateSerialNumberSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningResponse provisioning_response;
|
||||||
|
ProvisioningStatus status = GenerateProvisioningResponse(
|
||||||
|
system_id, oem_ca_serial_number, request.provider_id(),
|
||||||
|
certificate_serial_number, *cert_public_key, &provisioning_response);
|
||||||
|
if (status != OK) return status;
|
||||||
|
provisioning_response.set_nonce(request.nonce());
|
||||||
|
|
||||||
|
// Sign the response.
|
||||||
|
SignedProvisioningMessage signed_message;
|
||||||
|
if (!provisioning_response.SerializeToString(
|
||||||
|
signed_message.mutable_message())) {
|
||||||
|
LOG(WARNING) << "Error serializing ProvisioningResponse.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
if (!service_private_key_.GenerateSignature(
|
||||||
|
signed_message.message(), signed_message.mutable_signature())) {
|
||||||
|
LOG(WARNING) << "Failed to sign ProvisioningResponse.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
if (!signed_message.SerializeToString(response)) {
|
||||||
|
LOG(WARNING) << "Error serializing SignedProvisioningMessage.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProvisioningSessionImpl::ValidateAndDeserializeRequest(
|
||||||
|
const std::string& message, SignedProvisioningMessage* signed_request,
|
||||||
|
ProvisioningRequest* request) const {
|
||||||
|
if (!signed_request->ParseFromString(message)) {
|
||||||
|
LOG_EVERY_N(WARNING, FLAGS_provisioning_log_every_n)
|
||||||
|
<< "Failed to parse SignedProvisioningMessage.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
VLOG(1) << "signed_request: " << signed_request->ShortDebugString();
|
||||||
|
|
||||||
|
if (signed_request->message().empty()) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Missing message", *signed_request);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (signed_request->signature().empty()) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Missing signature", *signed_request);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!request->ParseFromString(signed_request->message())) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Failed to parse ProvisioningRequest",
|
||||||
|
*signed_request);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request->has_encrypted_client_id()) {
|
||||||
|
const EncryptedClientIdentification& encrypted_client_id =
|
||||||
|
request->encrypted_client_id();
|
||||||
|
if (encrypted_client_id.encrypted_client_id().empty()) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id",
|
||||||
|
encrypted_client_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id_iv",
|
||||||
|
encrypted_client_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (encrypted_client_id.encrypted_privacy_key().empty()) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Missing encrypted_privacy_key",
|
||||||
|
encrypted_client_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!request->has_client_id()) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Missing clear_or_encrypted_client_id", *request);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t kMinimumRequiredNonceLength = 4;
|
||||||
|
if (request->nonce().size() < kMinimumRequiredNonceLength) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Missing or invalid nonce", *request);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProvisioningSessionImpl::DecryptClientIdentification(
|
||||||
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
|
ClientIdentification* client_id) {
|
||||||
|
std::string privacy_key;
|
||||||
|
if (!service_private_key_.Decrypt(encrypted_client_id.encrypted_privacy_key(),
|
||||||
|
&privacy_key)) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Failed to decrypt encrypted_privacy_key",
|
||||||
|
encrypted_client_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
||||||
|
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
||||||
|
encrypted_client_id.encrypted_client_id()));
|
||||||
|
if (serialized_client_id.empty()) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Failed to decrypt client_id", encrypted_client_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!client_id->ParseFromString(serialized_client_id)) {
|
||||||
|
LOG_EVERY_N_WITH_PROTO("Failed to parse client_id", encrypted_client_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningSessionImpl::GenerateProvisioningResponse(
|
||||||
|
uint32_t system_id, const std::string& oem_ca_serial_number,
|
||||||
|
const std::string& provider_id, const std::string& certificate_serial_number,
|
||||||
|
const RsaPublicKey& cert_public_key, ProvisioningResponse* response) {
|
||||||
|
ProvisioningStatus status = engine_.GenerateProviderDeviceDrmCertificate(
|
||||||
|
system_id, oem_ca_serial_number, provider_id, device_public_key_,
|
||||||
|
certificate_serial_number, response->mutable_device_certificate());
|
||||||
|
if (status != OK) return status;
|
||||||
|
|
||||||
|
const size_t kAesKeySize = 16;
|
||||||
|
const size_t kIvSize = 16;
|
||||||
|
|
||||||
|
// Encrypt private key.
|
||||||
|
std::string message_key;
|
||||||
|
if (!RandomBytes(kAesKeySize, &message_key)) {
|
||||||
|
LOG(WARNING) << "Failed to generate message_key.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
std::string iv;
|
||||||
|
if (!RandomBytes(kIvSize, &iv)) {
|
||||||
|
LOG(WARNING) << "Failed to generate iv.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
response->set_device_rsa_key_iv(iv);
|
||||||
|
response->set_device_rsa_key(
|
||||||
|
crypto_util::EncryptAesCbc(message_key, iv, device_private_key_));
|
||||||
|
if (response->device_rsa_key().empty()) {
|
||||||
|
LOG(WARNING) << "Failed to encrypt device_rsa_key";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
if (!cert_public_key.Encrypt(message_key, response->mutable_wrapping_key())) {
|
||||||
|
LOG(WARNING) << "Failed to encrypt wrapping_key";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
88
provisioning_sdk/internal/provisioning_session_impl.h
Normal file
88
provisioning_sdk/internal/provisioning_session_impl.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// ProvisioningSession internal implementation.
|
||||||
|
|
||||||
|
#ifndef PROVISIONING_SDK_INTERNAL_PROVISIONING_SESSION_IMPL_H_
|
||||||
|
#define PROVISIONING_SDK_INTERNAL_PROVISIONING_SESSION_IMPL_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "provisioning_sdk/internal/oem_device_cert.h"
|
||||||
|
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
|
||||||
|
#include "provisioning_sdk/public/provisioning_status.h"
|
||||||
|
#include "protos/public/certificate_provisioning.pb.h"
|
||||||
|
#include "protos/public/client_identification.pb.h"
|
||||||
|
#include "protos/public/device_certificate.pb.h"
|
||||||
|
#include "protos/public/provisioned_device_info.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ProvisioningSessionImpl {
|
||||||
|
public:
|
||||||
|
ProvisioningSessionImpl(const ProvisioningEngineImpl& engine,
|
||||||
|
const OemDeviceCert& oem_device_cert,
|
||||||
|
const RsaPrivateKey& service_private_key);
|
||||||
|
~ProvisioningSessionImpl();
|
||||||
|
|
||||||
|
// Initialize provisioning session with given public key and private key.
|
||||||
|
ProvisioningStatus Initialize(const std::string& device_public_key,
|
||||||
|
const std::string& device_private_key);
|
||||||
|
|
||||||
|
// Process a message from the client device.
|
||||||
|
// * |message| is the message received from the client device.
|
||||||
|
// * |response| will contain, upon successful return, a message to be sent
|
||||||
|
// back to the client device as a response to |message|.
|
||||||
|
// Returns OK if successful, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus ProcessMessage(const std::string& message, std::string* response);
|
||||||
|
|
||||||
|
// * Returns a ProvisioneddeviceInfo message containing information about the
|
||||||
|
// type of device being provisioned. May return nullptr.
|
||||||
|
const ProvisionedDeviceInfo* GetDeviceInfo() const {
|
||||||
|
return device_info_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ProvisioningSessionImplTest;
|
||||||
|
|
||||||
|
ProvisioningSessionImpl(const ProvisioningSessionImpl&) = delete;
|
||||||
|
ProvisioningSessionImpl& operator=(const ProvisioningSessionImpl&) = delete;
|
||||||
|
|
||||||
|
bool ValidateAndDeserializeRequest(const std::string& message,
|
||||||
|
SignedProvisioningMessage* signed_request,
|
||||||
|
ProvisioningRequest* request) const;
|
||||||
|
bool DecryptClientIdentification(
|
||||||
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
|
ClientIdentification* client_id);
|
||||||
|
ProvisioningStatus GenerateProvisioningResponse(
|
||||||
|
uint32_t system_id, const std::string& oem_ca_serial_number,
|
||||||
|
const std::string& provider_id, const std::string& certificate_serial_number,
|
||||||
|
const RsaPublicKey& cert_public_key, ProvisioningResponse* response);
|
||||||
|
|
||||||
|
// Inject rsa_key_factory for testing.
|
||||||
|
void set_rsa_key_factory(std::unique_ptr<RsaKeyFactory> rsa_key_factory) {
|
||||||
|
rsa_key_factory_ = std::move(rsa_key_factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProvisioningEngineImpl& engine_;
|
||||||
|
const OemDeviceCert& oem_device_cert_;
|
||||||
|
const RsaPrivateKey& service_private_key_;
|
||||||
|
|
||||||
|
std::unique_ptr<RsaKeyFactory> rsa_key_factory_;
|
||||||
|
std::string device_public_key_;
|
||||||
|
std::string device_private_key_;
|
||||||
|
std::shared_ptr<ProvisionedDeviceInfo> device_info_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // PROVISIONING_SDK_INTERNAL_PROVISIONING_SESSION_IMPL_H_
|
||||||
426
provisioning_sdk/internal/provisioning_session_impl_test.cc
Normal file
426
provisioning_sdk/internal/provisioning_session_impl_test.cc
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/internal/provisioning_session_impl.h"
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "common/aes_cbc_util.h"
|
||||||
|
#include "common/mock_rsa_key.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
#include "provisioning_sdk/internal/oem_device_cert.h"
|
||||||
|
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::ByMove;
|
||||||
|
using ::testing::DoAll;
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::SaveArg;
|
||||||
|
using ::testing::SetArgPointee;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const char kEncryptedClientIdIv[] = "sixteen_bytes_iv";
|
||||||
|
const char kPrivacyKey[] = "privacy_key_16B_";
|
||||||
|
const char kProviderId[] = "testing_provider";
|
||||||
|
const char kClientToken[] = "client_id_token";
|
||||||
|
const char kDevicePublicKey[] = "device_public_key";
|
||||||
|
const char kEncryptedPrivacyKey[] = "encrypted_privacy_key";
|
||||||
|
const char kDevicePrivateKey[] = "device_private_key";
|
||||||
|
const char kWrappingKey[] = "wrapping_key";
|
||||||
|
const char kDeviceCertificate[] = "device_certificate";
|
||||||
|
const char kNonce[] = "testing_nonce";
|
||||||
|
const char kSignature[] = "generated_signature";
|
||||||
|
|
||||||
|
// Derives Stable Per-Origin IDentifiers.
|
||||||
|
std::string DeriveSpoid(const std::string& client_token,
|
||||||
|
const std::string& provider_id,
|
||||||
|
const std::string& secret_sauce) {
|
||||||
|
return widevine::Sha256_Hash(client_token + provider_id + secret_sauce)
|
||||||
|
.substr(0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class MockProvisioningEngineImpl : public ProvisioningEngineImpl {
|
||||||
|
public:
|
||||||
|
MOCK_CONST_METHOD6(GenerateProviderDeviceDrmCertificate,
|
||||||
|
ProvisioningStatus(uint32_t system_id,
|
||||||
|
const std::string& oem_ca_serial_number,
|
||||||
|
const std::string& provider_id,
|
||||||
|
const std::string& public_key,
|
||||||
|
const std::string& certificate_serial_number,
|
||||||
|
std::string* certificate));
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockOemDeviceCert : public OemDeviceCert {
|
||||||
|
public:
|
||||||
|
// gmock does not support SetArgPointee on std::unique_ptr, so we have to
|
||||||
|
// workaround it with a trick.
|
||||||
|
MOCK_CONST_METHOD4(DoVerifyCertificateChain,
|
||||||
|
bool(const std::string& certificate_chain,
|
||||||
|
RsaPublicKey** leaf_public_key, uint32_t* system_id,
|
||||||
|
std::string* oem_ca_serial_number));
|
||||||
|
bool VerifyCertificateChain(const std::string& certificate_chain,
|
||||||
|
std::unique_ptr<RsaPublicKey>* leaf_public_key,
|
||||||
|
uint32_t* system_id,
|
||||||
|
std::string* oem_ca_serial_number) const override {
|
||||||
|
RsaPublicKey* raw_leaf_public_key = nullptr;
|
||||||
|
if (!DoVerifyCertificateChain(certificate_chain, &raw_leaf_public_key,
|
||||||
|
system_id, oem_ca_serial_number)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*leaf_public_key = std::unique_ptr<RsaPublicKey>(raw_leaf_public_key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProvisioningSessionImplTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
ProvisioningSessionImplTest()
|
||||||
|
: session_impl_(mock_engine_impl_, mock_oem_device_cert_,
|
||||||
|
mock_service_private_key_) {
|
||||||
|
mock_rsa_key_factory_ = new MockRsaKeyFactory;
|
||||||
|
session_impl_.set_rsa_key_factory(
|
||||||
|
std::unique_ptr<RsaKeyFactory>(mock_rsa_key_factory_));
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningSessionImpl session_impl_;
|
||||||
|
MockRsaKeyFactory* mock_rsa_key_factory_ = nullptr;
|
||||||
|
MockProvisioningEngineImpl mock_engine_impl_;
|
||||||
|
MockOemDeviceCert mock_oem_device_cert_;
|
||||||
|
MockRsaPrivateKey mock_service_private_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplTest, InitializeWithInvalidPublicKey) {
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kDevicePublicKey))
|
||||||
|
.WillOnce(Return(ByMove(nullptr)));
|
||||||
|
EXPECT_EQ(
|
||||||
|
INVALID_DEVICE_PUBLIC_KEY,
|
||||||
|
session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplTest, InitializeWithInvalidPrivateKey) {
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kDevicePublicKey))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PrivateKey(kDevicePrivateKey))
|
||||||
|
.WillOnce(Return(ByMove(nullptr)));
|
||||||
|
EXPECT_EQ(
|
||||||
|
INVALID_DEVICE_PRIVATE_KEY,
|
||||||
|
session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplTest, InitializeWithMismatchPublicPrivateKey) {
|
||||||
|
MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kDevicePublicKey))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PrivateKey(kDevicePrivateKey))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
|
||||||
|
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
EXPECT_EQ(
|
||||||
|
INVALID_DEVICE_PRIVATE_KEY,
|
||||||
|
session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProvisioningSessionImplProcessTest : public ProvisioningSessionImplTest {
|
||||||
|
public:
|
||||||
|
void SetUp() override {
|
||||||
|
MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PublicKey(kDevicePublicKey))
|
||||||
|
.WillOnce(
|
||||||
|
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
|
||||||
|
EXPECT_CALL(*mock_rsa_key_factory_,
|
||||||
|
CreateFromPkcs1PrivateKey(kDevicePrivateKey))
|
||||||
|
.WillOnce(Return(
|
||||||
|
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
|
||||||
|
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
ASSERT_EQ(OK, session_impl_.Initialize(kDevicePublicKey,
|
||||||
|
kDevicePrivateKey));
|
||||||
|
|
||||||
|
// Setup Provisioning Message.
|
||||||
|
client_id_.set_type(ClientIdentification::OEM_DEVICE_CERTIFICATE);
|
||||||
|
client_id_.set_token(kClientToken);
|
||||||
|
|
||||||
|
EncryptedClientIdentification* encrypted_client_id =
|
||||||
|
prov_request_.mutable_encrypted_client_id();
|
||||||
|
encrypted_client_id->set_encrypted_client_id(crypto_util::EncryptAesCbc(
|
||||||
|
kPrivacyKey, kEncryptedClientIdIv, client_id_.SerializeAsString()));
|
||||||
|
encrypted_client_id->set_encrypted_client_id_iv(kEncryptedClientIdIv);
|
||||||
|
encrypted_client_id->set_encrypted_privacy_key(kEncryptedPrivacyKey);
|
||||||
|
prov_request_.set_provider_id(kProviderId);
|
||||||
|
prov_request_.set_nonce(kNonce);
|
||||||
|
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
signed_prov_message_.set_signature("testing_signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientIdentification client_id_;
|
||||||
|
ProvisioningRequest prov_request_;
|
||||||
|
SignedProvisioningMessage signed_prov_message_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, InvalidMessage) {
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage("invalid_message", &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, EmptyMessage) {
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage("", &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, MissingMessage) {
|
||||||
|
signed_prov_message_.clear_message();
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, MissingSignature) {
|
||||||
|
signed_prov_message_.clear_signature();
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, MissingClientId) {
|
||||||
|
prov_request_.clear_encrypted_client_id();
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, MissingEncryptedClientId) {
|
||||||
|
prov_request_.mutable_encrypted_client_id()->clear_encrypted_client_id();
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, MissingEncryptedClientIdIv) {
|
||||||
|
prov_request_.mutable_encrypted_client_id()->clear_encrypted_client_id_iv();
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, MissingEncryptedPrivacyKey) {
|
||||||
|
prov_request_.mutable_encrypted_client_id()->clear_encrypted_privacy_key();
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, InvalidNonce) {
|
||||||
|
// Nonce should be at least 4 buytes.
|
||||||
|
const char kNonceWithLessThanFourBytes[] = "xx";
|
||||||
|
prov_request_.set_nonce(kNonceWithLessThanFourBytes);
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, PrivacyKeyDecryptionFailed) {
|
||||||
|
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, InvalidEncryptedClientId) {
|
||||||
|
prov_request_.mutable_encrypted_client_id()->set_encrypted_client_id(
|
||||||
|
"invalid_encrypted_client_id");
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, VerifyCertificateChainFailed) {
|
||||||
|
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
|
||||||
|
EXPECT_CALL(mock_oem_device_cert_,
|
||||||
|
DoVerifyCertificateChain(kClientToken, _, _, _))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest,
|
||||||
|
ClearClientIdVerifyCertificateChainFailed) {
|
||||||
|
*prov_request_.mutable_client_id() = client_id_;
|
||||||
|
prov_request_.clear_encrypted_client_id();
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_oem_device_cert_,
|
||||||
|
DoVerifyCertificateChain(kClientToken, _, _, _))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, ClearClientIdInvalidClientIdType) {
|
||||||
|
client_id_.set_type(ClientIdentification::KEYBOX);
|
||||||
|
*prov_request_.mutable_client_id() = client_id_;
|
||||||
|
prov_request_.clear_encrypted_client_id();
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, ClearClientIdMissingToken) {
|
||||||
|
client_id_.clear_token();
|
||||||
|
*prov_request_.mutable_client_id() = client_id_;
|
||||||
|
prov_request_.clear_encrypted_client_id();
|
||||||
|
signed_prov_message_.set_message(prov_request_.SerializeAsString());
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, VerifySignatureFailed) {
|
||||||
|
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
|
||||||
|
MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(mock_oem_device_cert_,
|
||||||
|
DoVerifyCertificateChain(kClientToken, _, _, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(mock_cert_public_key), Return(true)));
|
||||||
|
EXPECT_CALL(*mock_cert_public_key,
|
||||||
|
VerifySignature(signed_prov_message_.message(),
|
||||||
|
signed_prov_message_.signature()))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, GenerateDeviceCertificateFailed) {
|
||||||
|
const uint32_t kSystemId = 1234;
|
||||||
|
const char kExpectedOemSerialNumber[] = "test_oem_serial_number";
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
|
||||||
|
MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(mock_oem_device_cert_,
|
||||||
|
DoVerifyCertificateChain(kClientToken, _, _, _))
|
||||||
|
.WillOnce(DoAll(
|
||||||
|
SetArgPointee<1>(mock_cert_public_key), SetArgPointee<2>(kSystemId),
|
||||||
|
SetArgPointee<3>(kExpectedOemSerialNumber), Return(true)));
|
||||||
|
EXPECT_CALL(*mock_cert_public_key,
|
||||||
|
VerifySignature(signed_prov_message_.message(),
|
||||||
|
signed_prov_message_.signature()))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
mock_engine_impl_,
|
||||||
|
GenerateProviderDeviceDrmCertificate(
|
||||||
|
kSystemId, kExpectedOemSerialNumber, kProviderId, kDevicePublicKey,
|
||||||
|
DeriveSpoid(kClientToken, kProviderId, ""), _))
|
||||||
|
.WillOnce(Return(INTERNAL_ERROR));
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
EXPECT_EQ(INTERNAL_ERROR,
|
||||||
|
session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningSessionImplProcessTest, Success) {
|
||||||
|
const uint32_t kSystemId = 1234;
|
||||||
|
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
|
||||||
|
MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey;
|
||||||
|
EXPECT_CALL(mock_oem_device_cert_,
|
||||||
|
DoVerifyCertificateChain(kClientToken, _, _, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(mock_cert_public_key),
|
||||||
|
SetArgPointee<2>(kSystemId), Return(true)));
|
||||||
|
EXPECT_CALL(*mock_cert_public_key,
|
||||||
|
VerifySignature(signed_prov_message_.message(),
|
||||||
|
signed_prov_message_.signature()))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_engine_impl_,
|
||||||
|
GenerateProviderDeviceDrmCertificate(kSystemId, _, _,
|
||||||
|
kDevicePublicKey, _, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<5>(kDeviceCertificate), Return(OK)));
|
||||||
|
|
||||||
|
std::string message_key;
|
||||||
|
EXPECT_CALL(*mock_cert_public_key, Encrypt(_, _))
|
||||||
|
.WillOnce(DoAll(SaveArg<0>(&message_key),
|
||||||
|
SetArgPointee<1>(kWrappingKey), Return(true)));
|
||||||
|
std::string message;
|
||||||
|
EXPECT_CALL(mock_service_private_key_, GenerateSignature(_, _))
|
||||||
|
.WillOnce(DoAll(SaveArg<0>(&message),
|
||||||
|
SetArgPointee<1>(kSignature), Return(true)));
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
ASSERT_EQ(OK, session_impl_.ProcessMessage(
|
||||||
|
signed_prov_message_.SerializeAsString(), &response));
|
||||||
|
|
||||||
|
// Verify the response.
|
||||||
|
SignedProvisioningMessage signed_prov_message;
|
||||||
|
ASSERT_TRUE(signed_prov_message.ParseFromString(response));
|
||||||
|
EXPECT_EQ(message, signed_prov_message.message());
|
||||||
|
EXPECT_EQ(kSignature, signed_prov_message.signature());
|
||||||
|
|
||||||
|
ProvisioningResponse prov_response;
|
||||||
|
ASSERT_TRUE(prov_response.ParseFromString(message));
|
||||||
|
EXPECT_EQ(
|
||||||
|
kDevicePrivateKey,
|
||||||
|
crypto_util::DecryptAesCbc(message_key, prov_response.device_rsa_key_iv(),
|
||||||
|
prov_response.device_rsa_key()));
|
||||||
|
EXPECT_EQ(kDeviceCertificate, prov_response.device_certificate());
|
||||||
|
EXPECT_EQ(kNonce, prov_response.nonce());
|
||||||
|
EXPECT_EQ(kWrappingKey, prov_response.wrapping_key());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
84
provisioning_sdk/public/BUILD
Normal file
84
provisioning_sdk/public/BUILD
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Build file for provisioning 3.0 SDK.
|
||||||
|
|
||||||
|
package(
|
||||||
|
default_visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Defines the common copts for cc_library targets. The main option here is
|
||||||
|
# -fvisibility=default, which exports symbols from the public APIs in the shared
|
||||||
|
# library.
|
||||||
|
# Note that the shared library should be built with -fvisibility=hidden.
|
||||||
|
PUBLIC_COPTS = ["-fvisibility=default"]
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "libprovisioning_sdk.so",
|
||||||
|
linkshared = 1,
|
||||||
|
deps = [":provisioning_engine"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "libprovisioning_sdk",
|
||||||
|
srcs = [":libprovisioning_sdk.so"],
|
||||||
|
hdrs = glob(["*.h"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "provisioning_engine",
|
||||||
|
srcs = ["provisioning_engine.cc"],
|
||||||
|
hdrs = ["provisioning_engine.h"],
|
||||||
|
copts = PUBLIC_COPTS,
|
||||||
|
deps = [
|
||||||
|
":certificate_type",
|
||||||
|
":provisioning_session",
|
||||||
|
":provisioning_status",
|
||||||
|
"//base",
|
||||||
|
"//common:rsa_key",
|
||||||
|
"//provisioning_sdk/internal:provisioning_engine_impl",
|
||||||
|
"//provisioning_sdk/internal:provisioning_session_impl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "provisioning_engine_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["provisioning_engine_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":provisioning_engine",
|
||||||
|
"//external:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "provisioning_session",
|
||||||
|
srcs = ["provisioning_session.cc"],
|
||||||
|
hdrs = ["provisioning_session.h"],
|
||||||
|
copts = PUBLIC_COPTS,
|
||||||
|
deps = [
|
||||||
|
":provisioning_status",
|
||||||
|
"//base",
|
||||||
|
"//provisioning_sdk/internal:provisioning_session_impl",
|
||||||
|
"//protos/public:device_certificate_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "provisioning_status",
|
||||||
|
srcs = ["provisioning_status.cc"],
|
||||||
|
hdrs = ["provisioning_status.h"],
|
||||||
|
copts = PUBLIC_COPTS,
|
||||||
|
deps = ["//base"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "certificate_type",
|
||||||
|
hdrs = ["certificate_type.h"],
|
||||||
|
copts = PUBLIC_COPTS,
|
||||||
|
)
|
||||||
22
provisioning_sdk/public/certificate_type.h
Normal file
22
provisioning_sdk/public/certificate_type.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_
|
||||||
|
#define PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
enum CertificateType {
|
||||||
|
kCertTesting = 0,
|
||||||
|
kCertDevelopment,
|
||||||
|
kCertProduction,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_
|
||||||
118
provisioning_sdk/public/provisioning_engine.cc
Normal file
118
provisioning_sdk/public/provisioning_engine.cc
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/public/provisioning_engine.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
|
||||||
|
#include "provisioning_sdk/internal/provisioning_session_impl.h"
|
||||||
|
#include "provisioning_sdk/public/provisioning_session.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
ProvisioningEngine::ProvisioningEngine() {}
|
||||||
|
|
||||||
|
ProvisioningEngine::~ProvisioningEngine() {}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngine::Initialize(
|
||||||
|
CertificateType certificate_type, const std::string& service_drm_certificate,
|
||||||
|
const std::string& service_private_key,
|
||||||
|
const std::string& service_private_key_passphrase,
|
||||||
|
const std::string& provisioning_drm_certificate,
|
||||||
|
const std::string& provisioning_private_key,
|
||||||
|
const std::string& provisioning_private_key_passphrase,
|
||||||
|
const std::string& secret_spoid_sauce) {
|
||||||
|
if (impl_) {
|
||||||
|
LOG(WARNING) << "ProvisioningEngine is already initialized.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ProvisioningEngineImpl> impl(new ProvisioningEngineImpl);
|
||||||
|
ProvisioningStatus status = impl->Initialize(
|
||||||
|
certificate_type, service_drm_certificate, service_private_key,
|
||||||
|
service_private_key_passphrase, provisioning_drm_certificate,
|
||||||
|
provisioning_private_key, provisioning_private_key_passphrase,
|
||||||
|
secret_spoid_sauce);
|
||||||
|
if (status != OK) return status;
|
||||||
|
impl_ = std::move(impl);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngine::SetCertificateStatusList(
|
||||||
|
const std::string& certificate_status_list, uint32_t expiration_period_seconds) {
|
||||||
|
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
|
||||||
|
return impl_->SetCertificateStatusList(certificate_status_list,
|
||||||
|
expiration_period_seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngine::GenerateDrmIntermediateCertificate(
|
||||||
|
uint32_t system_id, const std::string& public_key, std::string* certificate) const {
|
||||||
|
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
|
||||||
|
if (!certificate) {
|
||||||
|
LOG(WARNING) << "|certificate| should not be a nullptr.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return impl_->GenerateDrmIntermediateCertificate(system_id, public_key,
|
||||||
|
certificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngine::AddDrmIntermediateCertificate(
|
||||||
|
const std::string& intermediate_cert, const std::string& cert_private_key,
|
||||||
|
const std::string& cert_private_key_passphrase) {
|
||||||
|
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
|
||||||
|
return impl_->AddDrmIntermediateCertificate(
|
||||||
|
intermediate_cert, cert_private_key, cert_private_key_passphrase);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngine::NewProvisioningSession(
|
||||||
|
const std::string& device_public_key, const std::string& device_private_key,
|
||||||
|
std::unique_ptr<ProvisioningSession>* new_session) const {
|
||||||
|
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
|
||||||
|
if (!new_session) {
|
||||||
|
LOG(WARNING) << "|new_session| should not be a nullptr.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ProvisioningSessionImpl> session_impl(
|
||||||
|
new ProvisioningSessionImpl(*impl_, impl_->oem_device_cert(),
|
||||||
|
*impl_->service_private_key()));
|
||||||
|
ProvisioningStatus status =
|
||||||
|
session_impl->Initialize(device_public_key, device_private_key);
|
||||||
|
if (status != OK) return status;
|
||||||
|
new_session->reset(new ProvisioningSession(std::move(session_impl)));
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningEngine::GenerateDeviceDrmCertificate(
|
||||||
|
uint32_t system_id, const std::string& public_key, const std::string& serial_number,
|
||||||
|
std::string* certificate) const {
|
||||||
|
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
|
||||||
|
if (!certificate) {
|
||||||
|
LOG(WARNING) << "|certificate| should not be a nullptr.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate public key. This validation is done here instead of inside
|
||||||
|
// |impl_->GenerateDeviceDrmCertificate| is because the latter is an internal
|
||||||
|
// function which expect the input to be already validated.
|
||||||
|
std::unique_ptr<RsaPublicKey> rsa_public_key(
|
||||||
|
RsaPublicKey::Create(public_key));
|
||||||
|
if (!rsa_public_key) return INVALID_DEVICE_PUBLIC_KEY;
|
||||||
|
if (serial_number.empty()) return INVALID_SERIAL_NUMBER;
|
||||||
|
|
||||||
|
const std::string empty_oem_ca_serial_number;
|
||||||
|
return impl_->GenerateDeviceDrmCertificate(
|
||||||
|
system_id, empty_oem_ca_serial_number, public_key, serial_number,
|
||||||
|
certificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
153
provisioning_sdk/public/provisioning_engine.h
Normal file
153
provisioning_sdk/public/provisioning_engine.h
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef PROVISIONING_SDK_PUBLIC_PROVISIONING_ENGINE_H_
|
||||||
|
#define PROVISIONING_SDK_PUBLIC_PROVISIONING_ENGINE_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "provisioning_sdk/public/certificate_type.h"
|
||||||
|
#include "provisioning_sdk/public/provisioning_status.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ProvisioningEngineImpl;
|
||||||
|
class ProvisioningSession;
|
||||||
|
|
||||||
|
// Class which is used to implement a Widevine DRM device provisioning engine.
|
||||||
|
// There should be only one instance of ProvisioningEngine. The engine should
|
||||||
|
// be "Initialized" before being used. ProvisioningEngine::Initialize is the
|
||||||
|
// only method that is not thread-safe. After initializing the engine, it can
|
||||||
|
// be safely used in different threads.
|
||||||
|
class ProvisioningEngine {
|
||||||
|
public:
|
||||||
|
ProvisioningEngine();
|
||||||
|
~ProvisioningEngine();
|
||||||
|
|
||||||
|
// Initializes the provisioning engine with required credentials.
|
||||||
|
// * |certificate_type| indicates which type of certificate chains will be
|
||||||
|
// used for device provisioning via this engine.
|
||||||
|
// * |service_drm_certificate| is a Google-generated certificate used to
|
||||||
|
// authenticate the service provider for purposes of user privacy.
|
||||||
|
// * |service_private_key| is the encrypted PKCS#8 private RSA key
|
||||||
|
// corresponding to the service certificate.
|
||||||
|
// * |service_private_key_passphrase| is the password required to decrypt
|
||||||
|
// |service_private_key|, if any.
|
||||||
|
// * |provisioning_drm_certificate| is a Google-generated certificate used to
|
||||||
|
// sign intermediate DRM certificates.
|
||||||
|
// * |provisioning_private_key| is the encrypted PKCS#8 private RSA key
|
||||||
|
// corresponding to the provisioning certificate.
|
||||||
|
// * |provisioning_private_key_passphrase| is the password required to
|
||||||
|
// decrypt |provisioning_private_key|, if any.
|
||||||
|
// * |secret_spoid_sauce| is a stable secret used as a factor in the
|
||||||
|
// derivation of Stable Per-Origin IDentifiers.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus Initialize(
|
||||||
|
CertificateType certificate_type,
|
||||||
|
const std::string& service_drm_certificate,
|
||||||
|
const std::string& service_private_key,
|
||||||
|
const std::string& service_private_key_passphrase,
|
||||||
|
const std::string& provisioning_drm_certificate,
|
||||||
|
const std::string& provisioning_private_key,
|
||||||
|
const std::string& provisioning_private_key_passphrase,
|
||||||
|
const std::string& secret_spoid_sauce);
|
||||||
|
|
||||||
|
// Set the certificate status list for this engine.
|
||||||
|
// * |certificate_status_list| is a certificate status list generated by the
|
||||||
|
// Widevine Provisioning Service.
|
||||||
|
// * |expiration_period| is the number of seconds until the
|
||||||
|
// |certificate_status_list| expires after its creation time
|
||||||
|
// (creation_time_seconds). Zero means it will never expire.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus SetCertificateStatusList(
|
||||||
|
const std::string& certificate_status_list,
|
||||||
|
uint32_t expiration_period_seconds);
|
||||||
|
|
||||||
|
// Generate an intermediate DRM certificate.
|
||||||
|
// * |system_id| is the Widevine system ID for the type of device.
|
||||||
|
// * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will
|
||||||
|
// be embedded in the generated certificate.
|
||||||
|
// * |certificate| will contain the new intermediate certificate, upon
|
||||||
|
// successful return.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
// NOTE: The generated certificate and associated private key should be stored
|
||||||
|
// securely to be reused. They should also be propagated to all
|
||||||
|
// engines, including this one, by invoking
|
||||||
|
// |AddIntermediatedrmcertificate| on all active ProvisioningEngine(s).
|
||||||
|
ProvisioningStatus GenerateDrmIntermediateCertificate(
|
||||||
|
uint32_t system_id,
|
||||||
|
const std::string& public_key,
|
||||||
|
std::string* certificate) const;
|
||||||
|
|
||||||
|
// Add an intermediate DRM certificate to the provisioning engine. This is
|
||||||
|
// usually done once for each supported device type.
|
||||||
|
// * |intermediate_cert| is the intermediate DRM certificate to be added.
|
||||||
|
// * |cert_private_key| is a PKCS#8 private key corresponding to
|
||||||
|
// |intermediate_cert|.
|
||||||
|
// * |cert_private_key_passphrase| is the passphrase for cert_private_key,
|
||||||
|
// if any.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus AddDrmIntermediateCertificate(
|
||||||
|
const std::string& intermediate_cert,
|
||||||
|
const std::string& cert_private_key,
|
||||||
|
const std::string& cert_private_key_passphrase);
|
||||||
|
|
||||||
|
// Create a session to handle a provisioning exchange between a client device
|
||||||
|
// and the provisioning server.
|
||||||
|
// * |device_public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which
|
||||||
|
// will used to create the DRM certificate to be provisioned onto the
|
||||||
|
// device.
|
||||||
|
// * |device_private_key| is a DER-encoded PKCS#8 PrivateKeyInfo message
|
||||||
|
// which contains the private key matching |device_public_key|.
|
||||||
|
// * |new_session| will point, on successful return, to the newly created
|
||||||
|
// ProvisioningSession.
|
||||||
|
// * Returns OK if successful, or an appropriate error status code otherwise.
|
||||||
|
// The key pairs can be re-used if the created session failed to process the
|
||||||
|
// message.
|
||||||
|
// NOTE: All ProvisioningSession objects must be deleted before the
|
||||||
|
// ProvisioningEngine which created them.
|
||||||
|
ProvisioningStatus NewProvisioningSession(
|
||||||
|
const std::string& device_public_key,
|
||||||
|
const std::string& device_private_key,
|
||||||
|
std::unique_ptr<ProvisioningSession>* new_session) const;
|
||||||
|
|
||||||
|
// Generate a new device DRM certificate to be provisioned by means other than
|
||||||
|
// the Widevine provisioning protocol.
|
||||||
|
// NOTE: This API should only be used to provision devices which were
|
||||||
|
// manufactured without Widevine DRM support. It is meant to be used as
|
||||||
|
// an exception, and not the norm. Most devices should be provisioned
|
||||||
|
// by means of a ProvisioningSession.
|
||||||
|
// * |system_id| is the Widevine system ID for the type of device being
|
||||||
|
// provisioned.
|
||||||
|
// * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will
|
||||||
|
// be embedded in the generated certificate.
|
||||||
|
// * |serial_number| is a binary std::string to be used as the generated DRM
|
||||||
|
// certificate serial number.
|
||||||
|
// * |certificate| will contain, upon successful return the generated
|
||||||
|
// certificate.
|
||||||
|
// * Returns OK on success, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus GenerateDeviceDrmCertificate(
|
||||||
|
uint32_t system_id,
|
||||||
|
const std::string& public_key,
|
||||||
|
const std::string& serial_number,
|
||||||
|
std::string* certificate) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifndef SWIGPYTHON
|
||||||
|
ProvisioningEngine(const ProvisioningEngine&) = delete;
|
||||||
|
ProvisioningEngine& operator=(const ProvisioningEngine&) = delete;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::unique_ptr<ProvisioningEngineImpl> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // PROVISIONING_SDK_PUBLIC_PROVISIONING_ENGINE_H_
|
||||||
58
provisioning_sdk/public/provisioning_engine_test.cc
Normal file
58
provisioning_sdk/public/provisioning_engine_test.cc
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/public/provisioning_engine.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const int kSystemId = 100;
|
||||||
|
const int kExpirationPeriodSeconds = 3600;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ProvisioningEngineTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
ProvisioningEngine engine_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineTest, SetCertificateStatusListWithoutInit) {
|
||||||
|
EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED,
|
||||||
|
engine_.SetCertificateStatusList("certificate_status_list",
|
||||||
|
kExpirationPeriodSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineTest, GenerateDrmIntermediateCertificateWithoutInit) {
|
||||||
|
std::string certificate;
|
||||||
|
EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED,
|
||||||
|
engine_.GenerateDrmIntermediateCertificate(
|
||||||
|
kSystemId, "intermediate_public_key", &certificate));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineTest, AddDrmIntermediateCertificateWithoutInit) {
|
||||||
|
std::string certificate;
|
||||||
|
EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED,
|
||||||
|
engine_.AddDrmIntermediateCertificate(
|
||||||
|
"intermediate_certificate", "intermediate_private_key",
|
||||||
|
"intermediate_private_key_passphrase"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProvisioningEngineTest, GenerateDeviceDrmCertificateWithoutInit) {
|
||||||
|
std::string certificate;
|
||||||
|
EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED,
|
||||||
|
engine_.GenerateDeviceDrmCertificate(kSystemId, "device_public_key",
|
||||||
|
"device_serial_number",
|
||||||
|
&certificate));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisioningEngine is basically a wrapper of ProvisioningEngineImpl, so there
|
||||||
|
// is no need to test the functionalities as ProvisioningEngineImpl is already
|
||||||
|
// tested.
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
48
provisioning_sdk/public/provisioning_session.cc
Normal file
48
provisioning_sdk/public/provisioning_session.cc
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/public/provisioning_session.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "provisioning_sdk/internal/provisioning_session_impl.h"
|
||||||
|
#include "protos/public/device_certificate.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
ProvisioningSession::~ProvisioningSession() {}
|
||||||
|
|
||||||
|
ProvisioningStatus ProvisioningSession::ProcessMessage(const std::string& message,
|
||||||
|
std::string* response,
|
||||||
|
bool* done) {
|
||||||
|
if (!response) {
|
||||||
|
LOG(WARNING) << "|response| should not be a nullptr.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
if (!done) {
|
||||||
|
LOG(WARNING) << "|done| should not be a nullptr.";
|
||||||
|
return INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningStatus status = impl_->ProcessMessage(message, response);
|
||||||
|
*done = true;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProvisionedDeviceInfo* ProvisioningSession::GetDeviceInfo() const {
|
||||||
|
return impl_->GetDeviceInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvisioningSession::ProvisioningSession(
|
||||||
|
std::unique_ptr<ProvisioningSessionImpl> impl)
|
||||||
|
: impl_(std::move(impl)) {
|
||||||
|
DCHECK(impl_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
56
provisioning_sdk/public/provisioning_session.h
Normal file
56
provisioning_sdk/public/provisioning_session.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef PROVISIONING_SDK_PUBLIC_PROVISIONING_SESSION_H_
|
||||||
|
#define PROVISIONING_SDK_PUBLIC_PROVISIONING_SESSION_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "provisioning_sdk/public/provisioning_status.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ProvisionedDeviceInfo;
|
||||||
|
class ProvisioningSessionImpl;
|
||||||
|
|
||||||
|
// Class which is used to implement the provisioning session state machine.
|
||||||
|
class ProvisioningSession {
|
||||||
|
public:
|
||||||
|
~ProvisioningSession();
|
||||||
|
|
||||||
|
// Process a message from the client device.
|
||||||
|
// * |message| is the message received from the client device.
|
||||||
|
// * |response| will contain, upon successful return, a message to be sent
|
||||||
|
// back to the client device as a response to |message|.
|
||||||
|
// * |done| will indicate, upon successful return, whether the provisioning
|
||||||
|
// exchange is complete, and the ProvisioningSession can be deleted.
|
||||||
|
// Returns OK if successful, or an appropriate error status code otherwise.
|
||||||
|
ProvisioningStatus ProcessMessage(const std::string& message,
|
||||||
|
std::string* response,
|
||||||
|
bool* done);
|
||||||
|
|
||||||
|
// * Returns a ProvisioneddeviceInfo message containing information about the
|
||||||
|
// type of device being provisioned. May return nullptr.
|
||||||
|
const ProvisionedDeviceInfo* GetDeviceInfo() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifndef SWIGPYTHON
|
||||||
|
friend class ProvisioningEngine;
|
||||||
|
|
||||||
|
ProvisioningSession(const ProvisioningSession&) = delete;
|
||||||
|
ProvisioningSession& operator=(const ProvisioningSession&) = delete;
|
||||||
|
#endif
|
||||||
|
explicit ProvisioningSession(std::unique_ptr<ProvisioningSessionImpl> impl);
|
||||||
|
|
||||||
|
std::unique_ptr<ProvisioningSessionImpl> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // PROVISIONING_SDK_PUBLIC_PROVISIONING_SESSION_H_
|
||||||
48
provisioning_sdk/public/provisioning_status.cc
Normal file
48
provisioning_sdk/public/provisioning_status.cc
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "provisioning_sdk/public/provisioning_status.h"
|
||||||
|
|
||||||
|
#include "base/macros.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
static const char* kProvisioningStatusMessage[] = {
|
||||||
|
"OK",
|
||||||
|
"Invalid certificate type",
|
||||||
|
"Provisioning engine uninitialized",
|
||||||
|
"Invalid service drm certificate",
|
||||||
|
"Invalid service private key",
|
||||||
|
"Invalid provisioning drm certificate",
|
||||||
|
"Invalid provisioning private key",
|
||||||
|
"Invalid intermediate drm certificate",
|
||||||
|
"Invalid intermediate public key",
|
||||||
|
"Invalid intermediate private key",
|
||||||
|
"Invalid status list",
|
||||||
|
"Status list expired",
|
||||||
|
"Unknown system id",
|
||||||
|
"Invalid device public key",
|
||||||
|
"Invalid device private key",
|
||||||
|
"Invalid request message",
|
||||||
|
"Invalid MAC",
|
||||||
|
"Missing DRM intermediate certificate",
|
||||||
|
"DRM device certificate not set",
|
||||||
|
"Device revoked",
|
||||||
|
"Invalid serial number",
|
||||||
|
"Internal error",
|
||||||
|
"Invalid SPOID secret sauce"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* GetProvisioningStatusMessage(ProvisioningStatus status) {
|
||||||
|
static_assert(
|
||||||
|
arraysize(kProvisioningStatusMessage) == NUM_PROVISIONING_STATUS,
|
||||||
|
"mismatching provisioning status message and provisioning status.");
|
||||||
|
return kProvisioningStatusMessage[status];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
49
provisioning_sdk/public/provisioning_status.h
Normal file
49
provisioning_sdk/public/provisioning_status.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_
|
||||||
|
#define PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
enum ProvisioningStatus {
|
||||||
|
OK = 0,
|
||||||
|
INVALID_CERTIFICATE_TYPE = 1,
|
||||||
|
PROVISIONING_ENGINE_UNINITIALIZED = 2,
|
||||||
|
INVALID_SERVICE_DRM_CERTIFICATE = 3,
|
||||||
|
// Invalid service private key or private key passphrase.
|
||||||
|
INVALID_SERVICE_PRIVATE_KEY = 4,
|
||||||
|
INVALID_PROVISIONER_DRM_CERTIFICATE = 5,
|
||||||
|
// Invalid provisioner private key or private key passphrase.
|
||||||
|
INVALID_PROVISIONER_PRIVATE_KEY = 6,
|
||||||
|
INVALID_INTERMEDIATE_DRM_CERTIFICATE = 7,
|
||||||
|
INVALID_INTERMEDIATE_PUBLIC_KEY = 8,
|
||||||
|
// Invalid intermediate private key or private key passphrase.
|
||||||
|
INVALID_INTERMEDIATE_PRIVATE_KEY = 9,
|
||||||
|
INVALID_STATUS_LIST = 10,
|
||||||
|
STATUS_LIST_EXPIRED = 11,
|
||||||
|
UNKNOWN_SYSTEM_ID = 12,
|
||||||
|
INVALID_DEVICE_PUBLIC_KEY = 13,
|
||||||
|
INVALID_DEVICE_PRIVATE_KEY = 14,
|
||||||
|
INVALID_REQUEST_MESSAGE = 15,
|
||||||
|
INVALID_MAC = 16,
|
||||||
|
MISSING_DRM_INTERMEDIATE_CERT = 17,
|
||||||
|
DRM_DEVICE_CERTIFICATE_NOT_SET = 18,
|
||||||
|
DEVICE_REVOKED = 19,
|
||||||
|
INVALID_SERIAL_NUMBER = 20,
|
||||||
|
INTERNAL_ERROR = 21,
|
||||||
|
INVALID_SPOID_SAUCE = 22,
|
||||||
|
NUM_PROVISIONING_STATUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the message std::string for the given ProvisioningStatus.
|
||||||
|
const char* GetProvisioningStatusMessage(ProvisioningStatus status);
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_
|
||||||
73
provisioning_sdk/public/python/BUILD
Normal file
73
provisioning_sdk/public/python/BUILD
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
py_library(
|
||||||
|
name = "test_data_utility",
|
||||||
|
srcs = ["test_data_utility.py"],
|
||||||
|
data = ["//example:example_data"],
|
||||||
|
deps = [
|
||||||
|
"//protos/public:certificate_provisioning_py_pb2",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_library(
|
||||||
|
name = "crypto_utility",
|
||||||
|
srcs = ["crypto_utility.py"],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_test(
|
||||||
|
name = "init_engine_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["init_engine_test.py"],
|
||||||
|
deps = [
|
||||||
|
":test_data_utility",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_test(
|
||||||
|
name = "set_certificate_status_list_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["set_certificate_status_list_test.py"],
|
||||||
|
deps = [
|
||||||
|
":test_data_utility",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_test(
|
||||||
|
name = "drm_intermediate_certificate_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["drm_intermediate_certificate_test.py"],
|
||||||
|
deps = [
|
||||||
|
":test_data_utility",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_test(
|
||||||
|
name = "engine_generate_certificate_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["engine_generate_certificate_test.py"],
|
||||||
|
deps = [
|
||||||
|
":crypto_utility",
|
||||||
|
":test_data_utility",
|
||||||
|
"//protos/public:signed_device_certificate_py_pb2",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_test(
|
||||||
|
name = "new_session_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["new_session_test.py"],
|
||||||
|
deps = [
|
||||||
|
":crypto_utility",
|
||||||
|
":test_data_utility",
|
||||||
|
"//protos/public:certificate_provisioning_py_pb2",
|
||||||
|
"//protos/public:signed_device_certificate_py_pb2",
|
||||||
|
],
|
||||||
|
)
|
||||||
27
provisioning_sdk/public/python/base.i
Normal file
27
provisioning_sdk/public/python/base.i
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
%include "std_string.i"
|
||||||
|
%include "typemaps.i"
|
||||||
|
|
||||||
|
%define %ignoreall %ignore ""; %enddef
|
||||||
|
%define %unignore %rename("%s") %enddef
|
||||||
|
%define %unignoreall %rename("%s") ""; %enddef
|
||||||
|
|
||||||
|
%define COPY_TYPEMAPS(oldtype, newtype)
|
||||||
|
typedef oldtype newtype;
|
||||||
|
%apply oldtype * OUTPUT { newtype * OUTPUT };
|
||||||
|
%apply oldtype & OUTPUT { newtype & OUTPUT };
|
||||||
|
%apply oldtype * INPUT { newtype * INPUT };
|
||||||
|
%apply oldtype & INPUT { newtype & INPUT };
|
||||||
|
%apply oldtype * INOUT { newtype * INOUT };
|
||||||
|
%apply oldtype & INOUT { newtype & INOUT };
|
||||||
|
%enddef
|
||||||
|
|
||||||
|
COPY_TYPEMAPS(int, int32);
|
||||||
|
COPY_TYPEMAPS(unsigned int, uint32);
|
||||||
28
provisioning_sdk/public/python/certificate_type.i
Normal file
28
provisioning_sdk/public/python/certificate_type.i
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2016 Google Inc.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Swig file to generate a Python library for:
|
||||||
|
// provisioning_sdk/public/certificate_type.h
|
||||||
|
|
||||||
|
%module pywrapcertificate_type
|
||||||
|
|
||||||
|
%include "base.i"
|
||||||
|
|
||||||
|
%{
|
||||||
|
#include "provisioning_sdk/public/certificate_type.h"
|
||||||
|
%}
|
||||||
|
|
||||||
|
%ignoreall
|
||||||
|
|
||||||
|
%unignore widevine;
|
||||||
|
%unignore widevine::CertificateType;
|
||||||
|
%unignore widevine::kCertTesting;
|
||||||
|
%unignore widevine::kCertDevelopment;
|
||||||
|
%include "provisioning_sdk/public/certificate_type.h"
|
||||||
|
|
||||||
|
%unignoreall
|
||||||
25
provisioning_sdk/public/python/crypto_utility.py
Normal file
25
provisioning_sdk/public/python/crypto_utility.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""Utility functions for cryptography."""
|
||||||
|
|
||||||
|
from cryptography.hazmat import backends
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
|
|
||||||
|
def VerifySignature(public_key, signature, data):
|
||||||
|
hash_algorithm = hashes.SHA1()
|
||||||
|
salt_len = 20
|
||||||
|
|
||||||
|
key = serialization.load_der_public_key(
|
||||||
|
public_key, backend=backends.default_backend())
|
||||||
|
key.verify(signature, data,
|
||||||
|
padding.PSS(padding.MGF1(hash_algorithm), salt_len),
|
||||||
|
hash_algorithm)
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import pywrapprovisioning_engine
|
||||||
|
import pywrapprovisioning_status
|
||||||
|
import test_data_utility
|
||||||
|
|
||||||
|
|
||||||
|
class AddDrmIntermediateTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self._engine = pywrapprovisioning_engine.ProvisioningEngine()
|
||||||
|
test_data_utility.InitProvisionEngineWithTestData(
|
||||||
|
self._engine, verify_success=True)
|
||||||
|
|
||||||
|
def testGenerateDrmIntermediateCertificateWithValidExpirationPeriod(self):
|
||||||
|
test_data_utility.SetCertificateStatusListWithTestData(
|
||||||
|
self._engine, 0, verify_success=True)
|
||||||
|
|
||||||
|
test_data_utility.AddDrmIntermediateCertificateWithTestData(
|
||||||
|
self._engine, 2001, verify_success=True)
|
||||||
|
|
||||||
|
def testSetCertificateStatusListInvalid(self):
|
||||||
|
set_cert_status_list = self._engine.SetCertificateStatusList(
|
||||||
|
'INVALID_STATUS_LIST', 0)
|
||||||
|
self.assertEqual(pywrapprovisioning_status.INVALID_STATUS_LIST,
|
||||||
|
set_cert_status_list)
|
||||||
|
|
||||||
|
def testAddDrmIntermediateCertificateWithoutCertificateStatusList(self):
|
||||||
|
# Users should not be able to add DRM certificate without having
|
||||||
|
# certificate status list.
|
||||||
|
status = test_data_utility.AddDrmIntermediateCertificateWithTestData(
|
||||||
|
self._engine, 2001)
|
||||||
|
self.assertEqual(pywrapprovisioning_status.STATUS_LIST_EXPIRED, status)
|
||||||
|
|
||||||
|
def testAddDrmIntermediateCertificateSystemIdInvalid(self):
|
||||||
|
test_data_utility.SetCertificateStatusListWithTestData(
|
||||||
|
self._engine, 0, verify_success=True)
|
||||||
|
|
||||||
|
# system_id 9999 is not in the sample certificate status list
|
||||||
|
add_ca_status = test_data_utility.AddDrmIntermediateCertificateWithTestData(
|
||||||
|
self._engine, 9999)
|
||||||
|
self.assertEqual(pywrapprovisioning_status.UNKNOWN_SYSTEM_ID, add_ca_status)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
################################################################################
|
||||||
|
# Copyright 2016 Google Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
|
# widevine-licensing@google.com.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import crypto_utility
|
||||||
|
import pywrapprovisioning_engine
|
||||||
|
import pywrapprovisioning_status
|
||||||
|
import test_data_utility
|
||||||
|
from protos.public import signed_device_certificate_pb2
|
||||||
|
|
||||||
|
|
||||||
|
class EngineGenerateCertificateTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self._engine = pywrapprovisioning_engine.ProvisioningEngine()
|
||||||
|
test_data_utility.InitProvisionEngineWithTestData(
|
||||||
|
self._engine, verify_success=True)
|
||||||
|
test_data_utility.SetCertificateStatusListWithTestData(
|
||||||
|
self._engine, 0, verify_success=True)
|
||||||
|
test_data_utility.AddDrmIntermediateCertificateWithTestData(
|
||||||
|
self._engine, 2001, verify_success=True)
|
||||||
|
|
||||||
|
def testSuccess(self):
|
||||||
|
status, signed_cert_string = self._engine.GenerateDeviceDrmCertificate(
|
||||||
|
2001, test_data_utility.DEVICE_PUBLIC_KEY, 'DEVICE_SERIAL_NUMBER')
|
||||||
|
self.assertEqual(pywrapprovisioning_status.OK, status)
|
||||||
|
|
||||||
|
signed_cert = signed_device_certificate_pb2.SignedDrmDeviceCertificate()
|
||||||
|
signed_cert.ParseFromString(signed_cert_string)
|
||||||
|
crypto_utility.VerifySignature(test_data_utility.CA_PUBLIC_KEY,
|
||||||
|
signed_cert.signature,
|
||||||
|
signed_cert.drm_certificate)
|
||||||
|
|
||||||
|
def testEmptySerialNumber(self):
|
||||||
|
status, _ = self._engine.GenerateDeviceDrmCertificate(
|
||||||
|
2001, test_data_utility.DEVICE_PUBLIC_KEY, '')
|
||||||
|
self.assertEqual(pywrapprovisioning_status.INVALID_SERIAL_NUMBER, status)
|
||||||
|
|
||||||
|
def testEmptyPublicKey(self):
|
||||||
|
status, _ = self._engine.GenerateDeviceDrmCertificate(
|
||||||
|
2001, '', 'DEVICE_SERIAL_NUMBER')
|
||||||
|
self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY,
|
||||||
|
status)
|
||||||
|
|
||||||
|
def testInvalidPublicKey(self):
|
||||||
|
status, _ = self._engine.GenerateDeviceDrmCertificate(
|
||||||
|
2001, 'PUBLIC_KEY_MUST_BE_IN_DER_ENCODED_PKCS1_FORMAT',
|
||||||
|
'DEVICE_SERIAL_NUMBER')
|
||||||
|
self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY,
|
||||||
|
status)
|
||||||
|
|
||||||
|
def testMissingIntermediateCertificate(self):
|
||||||
|
status, _ = self._engine.GenerateDeviceDrmCertificate(
|
||||||
|
2002, test_data_utility.DEVICE_PUBLIC_KEY, 'DEVICE_SERIAL_NUMBER')
|
||||||
|
self.assertEqual(pywrapprovisioning_status.DEVICE_REVOKED, status)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user