Restructure Project

To make it easier to have separate implementations, we have
structured the repo so that there are three Bazel workspaces:

  - The API (and reference)
  - The vendor implementation for dev
  - The vendor implementation for prod

This allows the vendor implementation to be separated from
the API, while it makes little difference in this repo. While
it makes little difference for this repo, it makes managing versions
much easier internally. We do it here to better reflect our internal
structure to partners.

A vendor implementation has been stubbed in (BUILD file and directory
structure) to provide vendors with some scaffolding to organize their
implementation.
This commit is contained in:
Aaron Vaage
2020-11-17 10:27:42 -08:00
parent 69ea909ff5
commit 6b00ecfb33
124 changed files with 1545 additions and 85 deletions

172
whitebox/.clang-format Normal file
View File

@@ -0,0 +1,172 @@
---
Language: Cpp
# BasedOnStyle: Chromium
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: false
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
- Regex: '^<.*'
Priority: 2
SortPriority: 0
- Regex: '.*'
Priority: 3
SortPriority: 0
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: true
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Auto
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
...

150
whitebox/README.md Normal file
View File

@@ -0,0 +1,150 @@
# White-box API
## Summary
This repo contains the [Widevine](https://www.widevine.com) white-box API and
reference implementation, allowing for all CDMs to share a single white-box
implementation abstracted behind an API tailored for Widevine's use-cases.
This repo focuses on implementing the Widevine API, dependencies on any actual
white-box implementations should be treated as an external dependency and not
stored in this repo (e.g. any source or libraries should not be checked into
this repo).
## Building and Dependencies
In this repo we use [Bazel](https://bazel.build). This was done as Bazel is
open-source and widely available, making it easier for us to collaborate with
external partners, and develop independent of any final integration. It is not
expected nor required that integrators use Bazel.
The Bazel workspace is set to pull in any and all external dependencies, making
the project as self-contained as possible. To make the reference implementation
easy to integrate into our first target (Alcatraz) paths to dependencies have
been aliased to match Chromium paths.
To build the full repo and run all tests, from within or below the repo root
(the directory containing the `WORKSPACE` file), run:
```bash
bazel build "//..."
bazel test "//..."
```
## API
The API is defined in the `//api` directory. There are two white-box APIs
defined, each focused on different use-cases.
__License White-box__
The license white-box is designed to handle content keys. It is designed
to consume a Widevine license, extract the context keys, load them into the
underlying white-box, and only use them when the content policy allows (e.g.
`SW_SECURE_DECODE` can only be used by `WB_License_MaskedDecrypt()`).
__Aead White-box__
The Aead white-box is designed to encrypt/decrypt local data using
authenticated encryption, allowing for offline licenses to be locked-per-device
and avoid sensitive data from sitting exposed in runtime memory.
## Testing
Each white-box comes with a collection of conformance tests used to verify an
implementation's adherence to the API. The tests are defined in `//api` and
are designed so that by providing an implementation for `//api/test_data.h`,
the tests can be shared across multiple implementations. See "Creating A
Custom Implementation" for more information on how to do this.
## Reference Implementation
A reference implementation has been provided in `//impl/reference`. It uses
[boringssl](https://boringssl.googlesource.com/boringssl) to implement all the
crypto operations and uses
[Protocol Buffers](https://developers.google.com/protocol-buffers) to parse the
license request and license response.
This reference implementation was created for the sole purpose of developing the
conformance tests and to act as a mock in test. It should only be used in
test/debug scenarios.
## Creating A Custom Implementation
To create a custom implementation, create a new directory in `//impl`. Create
a `BUILD` file and define these two targets:
```python
cc_library(
name = "aead_whitebox",
srcs = [],
visibility = ["//visibility:public"],
deps = [
"//api:aead_whitebox",
"//api:result",
],
)
cc_library(
name = "license_whitebox",
srcs = [],
visibility = ["//visibility:public"],
deps = [
"//api:license_whitebox",
"//api:result",
],
)
```
You'll need to update the `aead_whitebox` and `license_whitebox` targets with
the source files you use (`srcs`) and the targets your depend on (`deps`).
To build your implementation, from within or below the repo root (the directory
containing the `WORKSPACE` file), run:
```bash
bazel build "//impl/your-implementation"
```
To link the conformance tests against your implementation, you'll need to
implement `//api/test_data.h` and define the following targets in your `BUILD`
file:
```python
cc_library(
name = "test_data",
src = [
"test_data.cc",
],
deps = [
"//api:test_data",
],
)
cc_test(
name = "aead_whitebox_test",
size = "small",
deps = [
":aead_whitebox",
":test_data",
"//api:aead_whitebox_test",
],
)
cc_test(
name = "license_whitebox_test",
size = "small",
deps = [
":license_whitebox",
":test_data",
"//api:license_whitebox_test",
],
)
```
To run the tests, from within or below the repo root (the directory containing
the `WORKSPACE` file), run:
```bash
bazel test "//impl/your-implementation"
```

105
whitebox/WORKSPACE Normal file
View File

@@ -0,0 +1,105 @@
# Copyright 2020 Google LLC. All Rights Reserved.
workspace(name = "whitebox")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
git_repository(
name = "glog_repo",
commit = "3ba8976592274bc1f907c402ce22558011d6fc5e", # 2020-02-16
remote = "https://github.com/google/glog.git",
)
git_repository(
name = "com_github_gflags_gflags",
commit = "2e227c3daae2ea8899f49858a23f3d318ea39b57", # 2020-01-15
remote = "https://github.com/gflags/gflags.git",
)
git_repository(
name = "abseil_repo",
commit = "fcb104594b0bb4b8ac306cb2f55ecdad40974683", # 2018-12-04
remote = "https://github.com/abseil/abseil-cpp.git",
)
git_repository(
name = "boringssl_repo",
commit = "14164f6fef47b7ebd97cdb0cea1624eabd6fe6b8", # 2018-11-26
remote = "https://github.com/google/boringssl.git",
)
git_repository(
name = "googletest_repo",
commit = "b6cd405286ed8635ece71c72f118e659f4ade3fb", # 2019-01-04
remote = "https://github.com/google/googletest.git",
)
# We use "com_google_protobuf" instead of "protobuf_repo" because Bazel's proto
# rules implicitly depend on @com_google_protobuf. See
# https://bazel.build/blog/2017/02/27/protocol-buffers.html.
git_repository(
name = "com_google_protobuf",
remote = "https://github.com/google/protobuf.git",
tag = "v3.8.0",
)
# bazel_skylib is required by google protobuf.
git_repository(
name = "bazel_skylib",
remote = "https://github.com/bazelbuild/bazel-skylib.git",
tag = "1.0.2",
)
# Protobuf library support. Not included in the recent protobuf release.
http_archive(
name = "zlib",
build_file = "@com_google_protobuf//:third_party/zlib.BUILD",
sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1",
strip_prefix = "zlib-1.2.11",
urls = ["https://zlib.net/zlib-1.2.11.tar.gz"],
)
# ODK
new_git_repository(
name = "odk_repo",
build_file = "//external:odk.BUILD",
commit = "bee799748752fac84d9c3ecd549aa54f72c88d02",
remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git",
)
bind(
name = "glog",
actual = "@glog_repo//:glog",
)
bind(
name = "gflags",
actual = "@com_github_gflags_gflags//:gflags",
)
bind(
name = "boringssl",
actual = "@boringssl_repo//:crypto",
)
bind(
name = "gtest",
actual = "@googletest_repo//:gtest",
)
bind(
name = "gtest_main",
actual = "@googletest_repo//:gtest_main",
)
bind(
name = "protobuf",
actual = "@com_google_protobuf//:protobuf",
)
bind(
name = "odk",
actual = "@odk_repo//:odk",
)

185
whitebox/api/BUILD Normal file
View File

@@ -0,0 +1,185 @@
# Copyright 2020 Google LLC. All Rights Reserved.
package(default_visibility = ["//visibility:private"])
cc_library(
name = "result",
hdrs = [
"result.h",
],
visibility = ["//visibility:public"],
)
cc_library(
name = "export",
hdrs = ["export.h"],
visibility = ["//visibility:private"],
)
cc_library(
name = "aead_whitebox",
hdrs = [
"aead_whitebox.h",
],
visibility = ["//visibility:public"],
deps = [
":export",
":result",
],
)
cc_library(
name = "license_whitebox",
hdrs = [
"license_whitebox.h",
],
visibility = ["//visibility:public"],
deps = [
":export",
":result",
],
)
cc_library(
name = "test_data",
testonly = True,
hdrs = [
"test_data.h",
],
visibility = ["//visibility:public"],
)
cc_library(
name = "golden_data",
testonly = True,
srcs = [
"golden_data.cc",
],
hdrs = [
"golden_data.h",
],
deps = [
"//chromium_deps/cdm/protos:license_protocol_proto",
],
)
cc_library(
name = "test_license_builder",
testonly = True,
srcs = ["test_license_builder.cc"],
hdrs = ["test_license_builder.h"],
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/cdm/keys:dev_certs",
"//chromium_deps/cdm/protos:license_protocol_proto",
"//crypto_utils:aes_cbc_encryptor",
"//crypto_utils:crypto_util",
"//crypto_utils:rsa_key",
"//external:odk",
],
)
cc_library(
name = "aead_whitebox_test",
testonly = True,
srcs = [
"aead_whitebox_create_test.cc",
"aead_whitebox_cross_instance_test.cc",
"aead_whitebox_decrypt_test.cc",
"aead_whitebox_encrypt_test.cc",
],
visibility = ["//visibility:public"],
deps = [
":aead_whitebox",
":test_data",
"//chromium_deps/testing",
],
)
cc_library(
name = "aead_whitebox_benchmark",
testonly = True,
srcs = [
"aead_whitebox_benchmark.cc",
],
visibility = ["//visibility:public"],
deps = [
":aead_whitebox",
":test_data",
"//benchmarking:data_source",
"//benchmarking:measurements",
"//chromium_deps/base:glog",
"//chromium_deps/testing",
],
)
cc_library(
name = "license_whitebox_test",
testonly = True,
srcs = [
"license_whitebox_chromeos_test.cc",
"license_whitebox_create_test.cc",
"license_whitebox_decrypt_test.cc",
"license_whitebox_get_secret_string_test.cc",
"license_whitebox_masked_decrypt_test.cc",
"license_whitebox_process_license_response_core_message_test.cc",
"license_whitebox_process_license_response_test.cc",
"license_whitebox_sign_license_request_test.cc",
"license_whitebox_sign_renewal_request_test.cc",
"license_whitebox_test_base.cc",
"license_whitebox_verify_renewal_response_test.cc",
],
hdrs = [
"license_whitebox_test_base.h",
],
visibility = ["//visibility:public"],
deps = [
":golden_data",
":license_whitebox",
":test_data",
":test_license_builder",
"//chromium_deps/cdm/keys:api",
"//chromium_deps/cdm/protos:license_protocol_proto",
"//chromium_deps/testing",
"//crypto_utils:rsa_key",
],
)
cc_library(
name = "license_whitebox_benchmark",
testonly = True,
srcs = [
"license_whitebox_benchmark.cc",
"license_whitebox_decrypt_benchmark.cc",
"license_whitebox_process_license_response_benchmark.cc",
"license_whitebox_sign_benchmark.cc",
"license_whitebox_verify_benchmark.cc",
],
hdrs = [
"license_whitebox_benchmark.h",
],
visibility = ["//visibility:public"],
deps = [
":license_whitebox",
":test_data",
":test_license_builder",
"//benchmarking:data_source",
"//benchmarking:measurements",
"//chromium_deps/base:glog",
"//chromium_deps/testing",
"//crypto_utils:crypto_util",
],
)
cc_library(
name = "license_whitebox_golden_data",
testonly = True,
srcs = [
"license_whitebox_golden_data_test.cc",
],
visibility = ["//visibility:public"],
deps = [
":license_whitebox",
"//chromium_deps/testing",
],
)

View File

@@ -0,0 +1,133 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_API_AEAD_WHITEBOX_H_
#define WHITEBOX_API_AEAD_WHITEBOX_H_
#include <stddef.h>
#include <stdint.h>
#include "api/export.h"
#include "api/result.h"
#ifdef __cplusplus
extern "C" {
#endif
// The opaque type representing a single AEAD white-box instance.
typedef struct WB_Aead_Whitebox WB_Aead_Whitebox;
// Creates a new white-box instance using |whitebox_init_data|. A pointer to the
// white-box instance will be returned via |whitebox|.
//
// Args:
// whitebox_init_data (in): The implementation-specific initialization data
// needed to initialize a new white-box instance. It contains the white-box
// representation of an AES key.
//
// whitebox_init_data_size (in) : The number of bytes in |whitebox_init_data|.
//
// context (in) : The device-specific data to bind the ciphertexts to the
// device. It is used to derive a context specific key from the initial master
// key.
//
// context_size (in) : The number of bytes in |context|.
//
// whitebox (out) : The output parameter used to return the new white-box
// instance.
//
// Returns:
// WB_RESULT_OK if the white-box instance was successfully created.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox_init_data| was null, if
// |whitebox_init_data| was invalid, if |whitebox_init_data_size| was zero, if
// |context| was null, if |context_size| was zero, or if |whitebox| was null.
//
// WB_RESULT_OUT_OF_MEMORY if the necessary memory could not be allocated.
WB_API WB_Result WB_Aead_Create(const uint8_t* whitebox_init_data,
size_t whitebox_init_data_size,
const uint8_t* context,
size_t context_size,
WB_Aead_Whitebox** whitebox);
// Releases all resources used by the white-box instance pointed to by
// |whitebox|.
//
// Args:
// whitebox (in) : A pointer to a white-box instance. Passing in null will
// result in a no-op.
WB_API void WB_Aead_Delete(WB_Aead_Whitebox* whitebox);
// Encrypts |input_data| and writes the cipher data, nonce and the data
// verification tag to |output_data|. The implementation should generate and use
// a random nonce internally to randomize the operation. |output_data_size|
// should be at least |input_data_size| + 22 to allow room for the nonce and the
// verification tag.
//
// Args:
// whitebox (in) : The white-box containing the keys for encryption.
//
// input_data (in) : The plaintext.
//
// input_data_size (in) : The number of bytes in |input_data|.
//
// output_data (out) : The output parameter for the ciphertext, nonce, and
// verification tag.
//
// output_data_size (in/out) : As input, this contains the max number of bytes
// that can be written to |output_data|. As output, |output_data_size| is set
// to the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
//
// Returns:
// WB_RESULT_OK if |input_data| was successfully encrypted.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |input_data| was
// null, if |input_data_size| was zero, if |output_data| was null, or if
// |output_data_size| was null.
//
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
// the required size.
WB_API WB_Result WB_Aead_Encrypt(const WB_Aead_Whitebox* whitebox,
const uint8_t* input_data,
size_t input_data_size,
uint8_t* output_data,
size_t* output_data_size);
// Decrypts |input_data| and writes the plaintext to |output_data|. |input_data|
// must have been encrypted using WB_Aead_Encrypt() with the same |whitebox|.
//
// Args:
// whitebox (in) : The white-box containing the keys for decryption.
//
// input_data (in) : The ciphertext, nonce and verification tag.
//
// input_data_size (in) : The number of bytes in |input_data|.
//
// output_data (out) : The output parameter for the plaintext.
//
// output_data_size (in/out) : As input, this contains the max number of bytes
// that can be written to |output_data|. As output, |output_data_size| is set
// to the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
//
// Returns:
// WB_RESULT_OK if |input_data| was successfully decrypted.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |input_data| was
// null, if |input_data_size| was invalid, if |output_data| was null, or if
// |output_data_size| was null.
//
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
// the required size.
//
// WB_RESULT_DATA_VERIFICATION_ERROR if |input_data| failed data verification.
// The state of |output_data| is undefined.
WB_API WB_Result WB_Aead_Decrypt(const WB_Aead_Whitebox* whitebox,
const uint8_t* input_data,
size_t input_data_size,
uint8_t* output_data,
size_t* output_data_size);
#ifdef __cplusplus
}
#endif
#endif // WHITEBOX_API_AEAD_WHITEBOX_H_

View File

@@ -0,0 +1,157 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <stddef.h>
#include <stdint.h>
#include <vector>
#include "api/aead_whitebox.h"
#include "api/test_data.h"
#include "benchmarking/data_source.h"
#include "benchmarking/measurements.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
namespace {
constexpr size_t kSafeOverhead = 128;
} // namespace
// Param:
// - The number of bytes that should be pushed through Decrypt() each time.
// - The number of iterations to run.
class AeadWhiteboxBenchmark
: public ::testing::Test,
public testing::WithParamInterface<std::tuple<size_t, size_t>> {
protected:
void SetUp() override {
const std::vector<uint8_t> context = data_source_.Get(16);
const std::vector<uint8_t> init_data = GetValidAeadInitData();
ASSERT_EQ(WB_RESULT_OK,
WB_Aead_Create(init_data.data(), init_data.size(), context.data(),
context.size(), &whitebox_));
std::tie(plaintext_size_, iterations_) = GetParam();
ciphertext_size_ = plaintext_size_ + kSafeOverhead;
}
void TearDown() override { WB_Aead_Delete(whitebox_); }
void InitWithCiphertextAsInput() {
// Make sure both buffers are the correct size for the test now that we know
// that the ciphertext is the input and the plaintext is the output.
input_.resize(ciphertext_size_);
output_.resize(plaintext_size_);
const auto plaintext = data_source_.Get(plaintext_size_);
size_t size = ciphertext_size_;
ASSERT_EQ(WB_RESULT_OK,
WB_Aead_Encrypt(whitebox_, plaintext.data(), plaintext.size(),
input_.data(), &size));
input_.resize(size);
}
void InitWithPlaintextAsInput() {
// Make sure both buffers are the correct size for the test now that we know
// that the plaintext is the input and the ciphertext is the output.
input_ = data_source_.Get(plaintext_size_);
output_.resize(ciphertext_size_);
}
WB_Aead_Whitebox* whitebox_ = nullptr;
DataSource data_source_;
size_t plaintext_size_;
size_t ciphertext_size_;
std::vector<uint8_t> input_;
std::vector<uint8_t> output_;
size_t iterations_;
};
TEST_P(AeadWhiteboxBenchmark, EncryptDuration) {
InitWithPlaintextAsInput();
Timer timer;
Sampler sampler;
for (size_t i = 0; i < iterations_; i++) {
timer.Reset();
size_t output_size = output_.size();
ASSERT_EQ(WB_RESULT_OK,
WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(),
output_.data(), &output_size));
sampler.Push(timer.Get());
}
PrettyPrint("AEAD Encrypt Duration", sampler, input_.size());
}
TEST_P(AeadWhiteboxBenchmark, EncryptThroughput) {
InitWithPlaintextAsInput();
Timer timer;
timer.Reset();
for (size_t i = 0; i < iterations_; i++) {
size_t output_size = output_.size();
ASSERT_EQ(WB_RESULT_OK,
WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(),
output_.data(), &output_size));
}
Throughput throughput(timer.Get(), input_.size() * iterations_);
PrettyPrint("AEAD Encrypt Throughput", throughput, input_.size());
}
TEST_P(AeadWhiteboxBenchmark, DecryptDuration) {
InitWithCiphertextAsInput();
Timer timer;
Sampler sampler;
for (size_t i = 0; i < iterations_; i++) {
timer.Reset();
size_t output_size = output_.size();
ASSERT_EQ(WB_RESULT_OK,
WB_Aead_Decrypt(whitebox_, input_.data(), input_.size(),
output_.data(), &output_size));
sampler.Push(timer.Get());
}
PrettyPrint("AEAD Decrypt Duration", sampler, input_.size());
}
TEST_P(AeadWhiteboxBenchmark, DecryptThroughput) {
InitWithCiphertextAsInput();
Timer timer;
timer.Reset();
for (size_t i = 0; i < iterations_; i++) {
size_t output_size = output_.size();
ASSERT_EQ(WB_RESULT_OK,
WB_Aead_Decrypt(whitebox_, input_.data(), input_.size(),
output_.data(), &output_size));
}
Throughput throughput(timer.Get(), iterations_ * input_.size());
PrettyPrint("AEAD Encrypt Throughput", throughput, input_.size());
}
// Run the test will different sizes of input-per-call. The values we have
// picked are divided into three groups, "bytes" and "kilobytes". The services
// team says that a license will normally by 1KB as a base size and then 50 to
// 100 bytes per key.
INSTANTIATE_TEST_SUITE_P(DifferentBytesPerCall,
AeadWhiteboxBenchmark,
::testing::Values(std::make_tuple(1024, 100),
std::make_tuple(16 * 1024, 50),
std::make_tuple(256 * 1024, 25)));
} // namespace widevine

View File

@@ -0,0 +1,75 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <vector>
#include "api/aead_whitebox.h"
#include "api/test_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class AeadWhiteboxCreateTest : public ::testing::Test {
protected:
void TearDown() override { WB_Aead_Delete(whitebox_); }
WB_Aead_Whitebox* whitebox_ = nullptr;
const std::vector<uint8_t> init_data_ = GetValidAeadInitData();
const std::vector<uint8_t> context_ = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
};
TEST_F(AeadWhiteboxCreateTest, Success) {
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
context_.data(), context_.size(), &whitebox_),
WB_RESULT_OK);
ASSERT_TRUE(whitebox_);
}
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForNullInitData) {
ASSERT_EQ(WB_Aead_Create(nullptr, init_data_.size(), context_.data(),
context_.size(), &whitebox_),
WB_RESULT_INVALID_PARAMETER);
ASSERT_FALSE(whitebox_);
}
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForInvalidInitData) {
const std::vector<uint8_t> invalid_init_data = GetInvalidAeadInitData();
ASSERT_EQ(WB_Aead_Create(invalid_init_data.data(), invalid_init_data.size(),
context_.data(), context_.size(), &whitebox_),
WB_RESULT_INVALID_PARAMETER);
ASSERT_FALSE(whitebox_);
}
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForZeroInitDataSize) {
ASSERT_EQ(WB_Aead_Create(init_data_.data(), 0, context_.data(),
context_.size(), &whitebox_),
WB_RESULT_INVALID_PARAMETER);
ASSERT_FALSE(whitebox_);
}
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForNullContext) {
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(), nullptr,
context_.size(), &whitebox_),
WB_RESULT_INVALID_PARAMETER);
ASSERT_FALSE(whitebox_);
}
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForZeroContextSize) {
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
context_.data(), 0, &whitebox_),
WB_RESULT_INVALID_PARAMETER);
ASSERT_FALSE(whitebox_);
}
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForNullWhitebox) {
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
context_.data(), context_.size(), nullptr),
WB_RESULT_INVALID_PARAMETER);
ASSERT_FALSE(whitebox_);
}
} // namespace

View File

@@ -0,0 +1,125 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <vector>
#include "api/aead_whitebox.h"
#include "api/test_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// These tests focus on functionality that should and should not exist between
// different Aead White-box instances. They assume that all other functionality
// of the white-box has already been verified.
class AeadWhiteboxCrossInstanceTest : public ::testing::Test {
protected:
void SetUp() override { init_data_ = GetValidAeadInitData(); }
void TearDown() override {
// Even if the pointer is null, as per the API, it should be safe to call
// WB_Aead_Delete().
WB_Aead_Delete(whitebox_a_);
WB_Aead_Delete(whitebox_b_);
}
WB_Aead_Whitebox* whitebox_a_ = nullptr;
WB_Aead_Whitebox* whitebox_b_ = nullptr;
// Save a copy of the init data so that it is easier to create instances. It
// would be nice if this could be const, but we need to set it in SetUp().
std::vector<uint8_t> init_data_;
// We need two different contexts so that we can have two unique instances.
// Each context was generated using a random number generator. There is no
// meaning to the bytes, the only condition is that they be different from
// each other.
const std::vector<uint8_t> context_a_ = {
0xa0, 0xaf, 0x75, 0xbf, 0x6b, 0xff, 0x2f, 0x76,
0x90, 0x47, 0xc4, 0x21, 0xb5, 0xf2, 0xb9, 0x62,
};
const std::vector<uint8_t> context_b_ = {
0xfe, 0x6d, 0x01, 0x45, 0xbd, 0xa9, 0xcc, 0xd4,
0x74, 0xaf, 0xca, 0xf8, 0xf9, 0x79, 0x2d, 0xf4,
};
// Both instances will always use the same plaintext. Since we are going to
// verify decryption, we need the plaintext to be recognizable after it is
// encrypted and then decrypted. To make it easier to read, we also avoided
// random data.
const std::vector<uint8_t> common_plaintext_ = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
};
// We don't know how big the ciphertext will be, so we don't know how much
// memory to allocate for our buffers. Since our plaintext is small, 256
// should be large enough.
const size_t safe_buffer_size_ = 256;
};
// Create two instances, each with unique contexts. Since they have different
// contexts, they should not be able to decrypt each other's data.
TEST_F(AeadWhiteboxCrossInstanceTest,
DataVerificationErrorForDifferentContext) {
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
context_a_.data(), context_a_.size(), &whitebox_a_),
WB_RESULT_OK);
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
context_b_.data(), context_b_.size(), &whitebox_b_),
WB_RESULT_OK);
size_t ciphertext_size = safe_buffer_size_;
std::vector<uint8_t> ciphertext(ciphertext_size);
size_t plaintext_size = safe_buffer_size_;
std::vector<uint8_t> plaintext(plaintext_size);
// The flow looks like:
// |common plaintext| --> [WB A] --> ciphertext --> [WB B] --> plaintext
//
// But it should fail after white-box tries to decrypt it, so |plaintext|
// should never get written to.
ASSERT_EQ(WB_Aead_Encrypt(whitebox_a_, common_plaintext_.data(),
common_plaintext_.size(), ciphertext.data(),
&ciphertext_size),
WB_RESULT_OK);
ciphertext.resize(ciphertext_size);
ASSERT_EQ(WB_Aead_Decrypt(whitebox_b_, ciphertext.data(), ciphertext.size(),
plaintext.data(), &plaintext_size),
WB_RESULT_DATA_VERIFICATION_ERROR);
plaintext.resize(plaintext_size);
}
// Create two instances, each using the same contexts. Since they have the same
// context, they should be able to decrypt each other's data.
TEST_F(AeadWhiteboxCrossInstanceTest, DataVerificationErrorForCommonContext) {
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
context_a_.data(), context_a_.size(), &whitebox_a_),
WB_RESULT_OK);
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
context_a_.data(), context_a_.size(), &whitebox_b_),
WB_RESULT_OK);
size_t ciphertext_size = safe_buffer_size_;
std::vector<uint8_t> ciphertext(ciphertext_size);
size_t plaintext_size = safe_buffer_size_;
std::vector<uint8_t> plaintext(plaintext_size);
// The flow looks like:
// |common plaintext| --> [WB A] --> ciphertext --> [WB B] --> plaintext
ASSERT_EQ(WB_Aead_Encrypt(whitebox_a_, common_plaintext_.data(),
common_plaintext_.size(), ciphertext.data(),
&ciphertext_size),
WB_RESULT_OK);
ciphertext.resize(ciphertext_size);
ASSERT_EQ(WB_Aead_Decrypt(whitebox_b_, ciphertext.data(), ciphertext.size(),
plaintext.data(), &plaintext_size),
WB_RESULT_OK);
plaintext.resize(plaintext_size);
ASSERT_EQ(plaintext, common_plaintext_);
}
} // namespace

View File

@@ -0,0 +1,156 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <vector>
#include "api/aead_whitebox.h"
#include "api/test_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// For our decrypt tests, we assume that WB_Aead_Create() and WB_Aead_Encrypt()
// work and each test will have valid encrypted data.
class AeadWhiteboxDecryptTest : public ::testing::Test {
protected:
void SetUp() override {
const std::vector<uint8_t> init_data = GetValidAeadInitData();
const std::vector<uint8_t> context = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
ASSERT_EQ(WB_Aead_Create(init_data.data(), init_data.size(), context.data(),
context.size(), &whitebox_),
WB_RESULT_OK);
// Regardless of implementation, we need to have enough room in our cipher
// text buffer to hold the additional information added by
// WB_Aead_Encrypt(). So this number just needs to be reasonably big
// compared to the input.
size_t ciphertext_size = 256;
ciphertext_.resize(ciphertext_size);
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, plaintext_.data(), plaintext_.size(),
ciphertext_.data(), &ciphertext_size),
WB_RESULT_OK);
ciphertext_.resize(ciphertext_size);
}
void TearDown() override { WB_Aead_Delete(whitebox_); }
WB_Aead_Whitebox* whitebox_ = nullptr;
// Since we are going to verify decryption, we need the plaintext to be
// recognizable after it is encrypted and then decrypted.
const std::vector<uint8_t> plaintext_ = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
};
// Because the ciphertext will be implementation specific, we can't use
// golden data for it. |ciphertext_| will be initialized in SetUp().
std::vector<uint8_t> ciphertext_;
};
TEST_F(AeadWhiteboxDecryptTest, Success) {
// The plaintext should be smaller than the input, but we use the input so
// that we will always have enough.
size_t plaintext_size = ciphertext_.size();
std::vector<uint8_t> plaintext(plaintext_size);
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
plaintext.data(), &plaintext_size),
WB_RESULT_OK);
plaintext.resize(plaintext_size);
ASSERT_EQ(plaintext, plaintext_);
}
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForNullWhitebox) {
// The plaintext should be smaller than the input, but we use the input so
// that we will always have enough.
size_t plaintext_size = ciphertext_.size();
std::vector<uint8_t> plaintext(plaintext_size);
ASSERT_EQ(WB_Aead_Decrypt(nullptr, ciphertext_.data(), ciphertext_.size(),
plaintext.data(), &plaintext_size),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForNullInputData) {
// The plaintext should be smaller than the input, but we use the input so
// that we will always have enough.
size_t plaintext_size = ciphertext_.size();
std::vector<uint8_t> plaintext(plaintext_size);
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, nullptr, ciphertext_.size(),
plaintext.data(), &plaintext_size),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForInvalidInputDataSize) {
// The plaintext should be smaller than the input, but we use the input so
// that we will always have enough.
size_t plaintext_size = ciphertext_.size();
std::vector<uint8_t> plaintext(plaintext_size);
// Because we don't know how big the authentication tag will be, we need to
// keep the number small. However, we don't use zero as "no data" is different
// than "invalid data".
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), 4, plaintext.data(),
&plaintext_size),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForNullOutputData) {
// The plaintext should be smaller than the input, but we use the input so
// that we will always have enough.
size_t plaintext_size = ciphertext_.size();
std::vector<uint8_t> plaintext(plaintext_size);
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
nullptr, &plaintext_size),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForNullOutputDataSize) {
// The plaintext should be smaller than the input, but we use the input so
// that we will always have enough.
size_t plaintext_size = ciphertext_.size();
std::vector<uint8_t> plaintext(plaintext_size);
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
plaintext.data(), nullptr),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxDecryptTest, BufferTooSmall) {
// We know what the plaintext should be, so if we make the plaintext buffer
// just a little too small, it should fail without the test needing to know
// anything out the implementation.
size_t plaintext_size = plaintext_.size() - 1;
std::vector<uint8_t> plaintext(plaintext_size);
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
plaintext.data(), &plaintext_size),
WB_RESULT_BUFFER_TOO_SMALL);
ASSERT_EQ(plaintext_size, plaintext_.size());
}
TEST_F(AeadWhiteboxDecryptTest, DataVerificationError) {
// The plaintext should be smaller than the input, but we use the input so
// that we will always have enough.
size_t plaintext_size = ciphertext_.size();
std::vector<uint8_t> plaintext(plaintext_size);
// Create invalid input by flipping one byte. Use bitwise-not so that, no
// matter the original value, it will be different (we don't know what the
// cipher text will actually be).
std::vector<uint8_t> invalid_input = ciphertext_;
invalid_input[0] = ~invalid_input[0];
ASSERT_EQ(
WB_Aead_Decrypt(whitebox_, invalid_input.data(), invalid_input.size(),
plaintext.data(), &plaintext_size),
WB_RESULT_DATA_VERIFICATION_ERROR);
}
} // namespace

View File

@@ -0,0 +1,119 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <vector>
#include "api/aead_whitebox.h"
#include "api/test_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Regardless of implementation, we need to have enough room in our output
// buffer to hold the additional information added by WB_Aead_Encrypt(). So
// this number just needs to be reasonably big compared to the input.
constexpr size_t kDefaultOutputSize = 256;
// For our encrypt tests, we assume that WB_Aead_Create() works and each test
// will have a functional instance. In these tests, we will not be checking the
// encrypt and decrypt flow, just the return codes of encrypt. For checking the
// encrypt-decrypt flow, see the decrypt tests as they will assume encryption
// works.
class AeadWhiteboxEncryptTest : public ::testing::Test {
protected:
void SetUp() override {
const std::vector<uint8_t> init_data = GetValidAeadInitData();
const std::vector<uint8_t> context = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
ASSERT_EQ(WB_Aead_Create(init_data.data(), init_data.size(), context.data(),
context.size(), &whitebox_),
WB_RESULT_OK);
}
void TearDown() override { WB_Aead_Delete(whitebox_); }
WB_Aead_Whitebox* whitebox_ = nullptr;
// Because we are not going to verify the encryption in these tests, we don't
// need the input to be recognizable.
const std::vector<uint8_t> input_ = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
};
TEST_F(AeadWhiteboxEncryptTest, Success) {
size_t output_size = kDefaultOutputSize;
std::vector<uint8_t> output(output_size);
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(),
output.data(), &output_size),
WB_RESULT_OK);
// Additional information should have been added to the output (compared to
// the input).
ASSERT_GT(output_size, input_.size());
}
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForNullWhitebox) {
size_t output_size = kDefaultOutputSize;
std::vector<uint8_t> output(output_size);
ASSERT_EQ(WB_Aead_Encrypt(nullptr, input_.data(), input_.size(),
output.data(), &output_size),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForNullInputData) {
size_t output_size = kDefaultOutputSize;
std::vector<uint8_t> output(output_size);
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, nullptr, input_.size(), output.data(),
&output_size),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForZeroInputSize) {
size_t output_size = kDefaultOutputSize;
std::vector<uint8_t> output(output_size);
ASSERT_EQ(
WB_Aead_Encrypt(whitebox_, input_.data(), 0, output.data(), &output_size),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForNullOutputData) {
size_t output_size = kDefaultOutputSize;
std::vector<uint8_t> output(output_size);
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(), nullptr,
&output_size),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForNullOutputDataSize) {
size_t output_size = kDefaultOutputSize;
std::vector<uint8_t> output(output_size);
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(),
output.data(), nullptr),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(AeadWhiteboxEncryptTest, BufferTooSmallForSmallOutputBuffer) {
// Because WB_Aead_Encrypt() will add more data, having the output be the
// same size as the input will make it by definition too small.
size_t output_size = input_.size();
std::vector<uint8_t> output(output_size);
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(),
output.data(), &output_size),
WB_RESULT_BUFFER_TOO_SMALL);
// Additional room should be needed. We don't know how much more, so we can
// only check that |output_size| is larger than |input_.size()|.
ASSERT_GT(output_size, input_.size());
}
} // namespace

12
whitebox/api/export.h Normal file
View File

@@ -0,0 +1,12 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_API_EXPORT_H_
#define WHITEBOX_API_EXPORT_H_
#if defined(_WIN32)
#define WB_API _declspec(dllexport)
#else
#define WB_API __attribute__((visibility("default")))
#endif
#endif // WHITEBOX_API_EXPORT_H_

View File

@@ -0,0 +1,92 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/golden_data.h"
namespace widevine {
GoldenData::GoldenData() {
// Content generated with:
// openssl aes-128-cbc -e -in data.txt
// -K EBDD62F16814D27B68EF122AFCE4AE3C
// -iv 30313233343536373839303132333435 | xxd -i
// Extra padding was stripped off.
cbc_content_ = {
/* plaintext */ {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e',
' ', 'p', 'l', 'a', 'i', 'n', 't', 'e', 'x', 't', ' ',
':', ' ', '3', '2', ' ', 'b', 'y', 't', 'e', 's'},
/* ciphertext */
{0x5e, 0x60, 0x0d, 0x3c, 0x29, 0xb9, 0x49, 0x4c, 0x65, 0x67, 0x7e,
0x87, 0x82, 0x9d, 0x47, 0x58, 0xb9, 0x86, 0xd0, 0x39, 0x6a, 0x67,
0x2c, 0x53, 0xe9, 0xbc, 0x99, 0x5b, 0x23, 0x34, 0x9f, 0xf8},
/* key */
{0xEB, 0xDD, 0x62, 0xF1, 0x68, 0x14, 0xD2, 0x7B, 0x68, 0xEF, 0x12, 0x2A,
0xFC, 0xE4, 0xAE, 0x3C},
/* iv */
{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31,
0x32, 0x33, 0x34, 0x35},
};
cbc_crypto_key_ = {
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
{0xFF, 0, 0, 0},
&cbc_content_,
};
cbc_decode_key_ = {
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE,
{0xFF, 1, 0, 0},
&cbc_content_,
};
cbc_hardware_key_ = {
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO,
{0xFF, 2, 0, 0},
&cbc_content_,
};
// Content generated with:
// openssl aes-128-ctr -e -in data.txt
// -K dd3c6cd4ea73b99d55f2e0357e1f560f
// -iv d50c08b31fc09e9e748431ca972334e6 | xxd -i
ctr_content_ = {
/* plaintext */ {'T', 'h', 'i', 'r', 't', 'y', '-', 't', 'w', 'o', ' ',
'b', 'y', 't', 'e', 's', ' ', 'o', 'f', ' ', 'r', 'a',
'n', 'd', 'o', 'm', ' ', 'd', 'a', 't', 'a', '.'},
/* ciphertext */
{0x5d, 0x83, 0xdd, 0xb9, 0xed, 0x18, 0x2f, 0x10, 0xbf, 0x6f, 0x4d,
0xb0, 0xb3, 0xeb, 0x0d, 0x20, 0xd7, 0x7e, 0x9a, 0x3a, 0xc4, 0x41,
0xcf, 0x0a, 0xb3, 0xae, 0x02, 0x01, 0x0a, 0xf2, 0x72, 0x72},
/* key */
{0xdd, 0x3c, 0x6c, 0xd4, 0xea, 0x73, 0xb9, 0x9d, 0x55, 0xf2, 0xe0, 0x35,
0x7e, 0x1f, 0x56, 0x0f},
/* iv */
{0xd5, 0x0c, 0x08, 0xb3, 0x1f, 0xc0, 0x9e, 0x9e, 0x74, 0x84, 0x31, 0xca,
0x97, 0x23, 0x34, 0xe6},
};
ctr_crypto_key_ = {
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
{0xFF, 3, 0, 0},
&ctr_content_,
};
ctr_decode_key_ = {
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE,
{0xFF, 4, 0, 0},
&ctr_content_,
};
ctr_hardware_key_ = {
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO,
{0xFF, 5, 0, 0},
&ctr_content_,
};
}
void GoldenData::MakeKeyIdDifferent(std::vector<uint8_t>* key_id) const {
// All our internal key ids start with 0xFF, so pushing something that is not
// 0xFF to the front will ensure that they don't collide.
key_id->insert(key_id->begin(), 0xAB);
}
} // namespace widevine

View File

@@ -0,0 +1,61 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_API_GOLDEN_DATA_H_
#define WHITEBOX_API_GOLDEN_DATA_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "cdm/protos/license_protocol.pb.h"
namespace widevine {
class GoldenData {
public:
struct Content {
std::vector<uint8_t> plaintext;
std::vector<uint8_t> ciphertext;
std::vector<uint8_t> key;
std::vector<uint8_t> iv;
};
struct Key {
video_widevine::License_KeyContainer_SecurityLevel level;
std::vector<uint8_t> id;
const Content* content;
};
GoldenData();
const Content& CBCContent() const { return cbc_content_; }
const Key& CBCCryptoKey() const { return cbc_crypto_key_; }
const Key& CBCDecodeKey() const { return cbc_decode_key_; }
const Key& CBCHardwareKey() const { return cbc_hardware_key_; }
const Content& CTRContent() const { return ctr_content_; }
const Key& CTRCryptoKey() const { return ctr_crypto_key_; }
const Key& CTRDecodeKey() const { return ctr_decode_key_; }
const Key& CTRHardwareKey() const { return ctr_hardware_key_; }
// When a test needs to define a key id that does not conflict with any key
// ids defined in the golden data, it should use this to update their key id
// by prepending a single byte to ensure it won't collide with any of the
// internal key ids.
void MakeKeyIdDifferent(std::vector<uint8_t>* key_id) const;
private:
Content cbc_content_;
Key cbc_crypto_key_;
Key cbc_decode_key_;
Key cbc_hardware_key_;
Content ctr_content_;
Key ctr_crypto_key_;
Key ctr_decode_key_;
Key ctr_hardware_key_;
};
} // namespace widevine
#endif // WHITEBOX_API_GOLDEN_DATA_H_

View File

@@ -0,0 +1,431 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_API_LICENSE_WHITEBOX_H_
#define WHITEBOX_API_LICENSE_WHITEBOX_H_
#include <stddef.h>
#include <stdint.h>
#include "api/export.h"
#include "api/result.h"
#ifdef __cplusplus
extern "C" {
#endif
// The opaque type representing a single white-box instance.
typedef struct WB_License_Whitebox WB_License_Whitebox;
typedef enum {
WB_CIPHER_MODE_CTR,
WB_CIPHER_MODE_CBC,
} WB_CipherMode;
// Creates a new white-box instance using |whitebox_init_data|. A pointer to the
// white-box instance will be returned via |whitebox|.
//
// Args:
// whitebox_init_data (in) : The implementation-specific initialization data
// needed to initialize a new white-box instance.
//
// whitebox_init_data_size (in) : The number of bytes in |whitebox_init_data|.
//
// whitebox (out) : The output parameter used to return the new white-box
// instance.
//
// Returns:
// WB_RESULT_OK if the white-box instance was successfully created.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox_init_data| was null or invalid.
//
// WB_RESULT_OUT_OF_MEMORY if the necessary memory could not be allocated.
WB_API WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
size_t whitebox_init_data_size,
WB_License_Whitebox** whitebox);
// Releases all resources used by the white-box instance pointed to by
// |whitebox|.
//
// Args:
// whitebox (in) : A pointer to a white-box instance. Passing in null will
// result in a no-op.
WB_API void WB_License_Delete(WB_License_Whitebox* whitebox);
// Signs a license request using the CDM's private signing key.
//
// Args:
// whitebox (in) : A pointer to the current white-box instance.
//
// license_request (in) : The license request in serialized form.
//
// license_request_size (in) : The number of bytes in the license_request.
//
// signature (out) : The generated signature for |license_request|.
//
// signature_size (in/out) : As input, this contains the max number of bytes
// that can be written to |signature|. As output, |signature_size| is set to
// the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
//
// Returns:
// WB_RESULT_OK if the signature was successfully generated.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |license_request| was
// null, if |license_request_size| was zero, if |signature| was null, or if
// |signature_size| was null.
//
// WB_RESULT_BUFFER_TOO_SMALL if |signature_size| (as input) was less than the
// required size.
WB_API WB_Result
WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
const uint8_t* license_request,
size_t license_request_size,
uint8_t* signature,
size_t* signature_size);
// Verifies a license response using HMAC and the server signing key.
//
// Extracts and loads content and signing keys for use. Any content keys that
// exceed the security levels permitted by the instance, will be thrown away,
// but the key ids are retained (see WB_RESULT_INSUFFICIENT_SECURITY_LEVEL). All
// non-content keys and non-signing keys will be thrown away.
//
// This function can only be called once per white-box instance. To parse a new
// license response, a new white-box instance must be used.
//
// Args:
// whitebox (in/out) : The white-box instance that will load the keys.
//
// core_message (in) : Serialized information communicating the structure of
// |message|. Signature verification should be done on |core_message| +
// |message|.
//
// core_message_size (in) : The number of bytes in |core_message|. If this is
// zero, it means that there was no meta message provided for the message.
//
// message (in) : The message field of the license response.
//
// message_size (in) : The number of bytes in |message|.
//
// signature (in) : The signature field of the license response.
//
// signature_size (in) : The number of bytes in |signature|.
//
// session_key (in) : The session key field of the license response.
//
// session_key_size (in) : The number of bytes in |session_key|.
//
// license_request (in) : The license request that was sent in order to get the
// license response.
//
// license_request_size (in) : The number of bytes in |license_request|.
//
// Returns:
// WB_RESULT_OK if the response was verified and the keys were loaded into
// |whitebox|.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |core_message| was
// null, if |message| was null, if |message_size| was zero, if |message| did
// not conform to the expected format, if |signature| was null, if
// |signature_size| was incorrect, if |session_key| was null, if
// |session_key_size| was incorrect, if |license_request| was null, or if
// |license_request_size| was zero.
//
// WB_RESULT_INVALID_SIGNATURE if |message|'s signature does not match
// |signature| or if |session_key| could not be unwrapped correctly (and
// interferes with verification).
//
// WB_RESULT_INVALID_STATE if a license has already been loaded.
//
// Notes:
// We allow a modified session key to be used. Using it will cause message
// verification to fail (therefore we return WB_RESULT_INVALID_SIGNATURE).
// Doing this was found to allow for better hardening of the license processing
// code.
WB_API WB_Result
WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
const uint8_t* core_message,
size_t core_message_size,
const uint8_t* message,
size_t message_size,
const uint8_t* signature,
size_t signature_size,
const uint8_t* session_key,
size_t session_key_size,
const uint8_t* license_request,
size_t license_request_size);
// Signs |message| and return the signature via |signature| using HMAC and the
// client renewal signing key
//
// Args:
// whitebox (in) : The white-box instance containing the signing key.
//
// message (in) : The message that should be signed.
//
// message_size (in) : The number of bytes in |message|.
//
// signature (out) : The output parameter used to return the signature.
//
// signature_size (in/out) : As input, this contains the max number of bytes
// that can be written to |signature|. As output, |signature_size| is set to
// the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
//
// Returns:
// WB_RESULT_OK if |message| was successfully signed.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |message| was null,
// if |message_size| was zero, if |signature| was null, or if |signature_size|
// was null.
//
// WB_RESULT_BUFFER_TOO_SMALL if |signature_size| (as input) was less than the
// required size.
//
// WB_RESULT_INVALID_STATE if |whitebox| had no signing keys.
WB_API WB_Result
WB_License_SignRenewalRequest(const WB_License_Whitebox* whitebox,
const uint8_t* message,
size_t message_size,
uint8_t* signature,
size_t* signature_size);
// Verifies the renewal response using HMAC and the server signing key.
//
// Args:
// whitebox (in) : The white-box containing the server signing key.
//
// message (in) : The message that needs to be verified.
//
// message_size (in) : The number of bytes in |message|.
//
// signature (in) : The expected signature for |message|.
//
// signature_size (in) : The number of bytes in |signature|.
//
// Returns:
// WB_RESULT_OK if |message|'s signature matches |signature|.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |message| was null,
// if |message_size| was zero, if |signature| was null, of if |signature_size|
// was incorrect.
//
// WB_RESULT_INVALID_SIGNATURE if |message|'s signature did not match
// |signature|.
//
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
WB_API WB_Result
WB_License_VerifyRenewalResponse(const WB_License_Whitebox* whitebox,
const uint8_t* message,
size_t message_size,
const uint8_t* signature,
size_t signature_size);
// Gets the secret string needed by WB_License_Unmask() in order to unmask the
// masked decrypted content returned by WB_License_MaskedDecrypt().
//
// Args:
// whitebox (in) : The white-box instance that will be used for the calls to
// WB_License_MaskedDecrypt().
//
// mode (in) : The AES decryption mode that will be used for the calls to
// WB_License_MaskedDecrypt().
//
// key_id (in) : The key id that will be used for the call to
// WB_License_MaskedDecrypt(). The key id must match a key loaded in
// |whitebox|.
//
// key_id_size (in) : The number of bytes in |key_id|.
//
// secret_string (out) : The output parameter used to return the secret string
// that can be passed to WB_License_Unmask().
//
// secret_string_size (in/out) : As input, this contains the max number of
// bytes that can be written to |secret_string|. As output,
// |secret_string_size| is set to the required size on WB_RESULT_OK and
// WB_RESULT_BUFFER_TOO_SMALL.
//
// Returns:
// WB_RESULT_OK if |key_id| matches a key from the license response and the
// secret string was written to |secret_string|.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |mode| was invalid,
// if |key_id| was null, if |key_id_size| was zero, if |secret_string| was
// null, or if |secret_string_size| was null.
//
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key from
// the license, but the |whitebox| was not allowed to use it.
//
// WB_RESULT_KEY_UNAVAILABLE if |key_id| did not match a content key from the
// loaded license.
//
// WB_RESULT_BUFFER_TOO_SMALL if |secret_string_size| (as input) was less than
// the required size.
//
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
WB_API WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
WB_CipherMode mode,
const uint8_t* key_id,
size_t key_id_size,
uint8_t* secret_string,
size_t* secret_string_size);
// Decrypts |input_data| and writes the plaintext to |output_data|.
//
// Args:
// whitebox (in) : The white-box containing the keys needed to decrypt
// |input_data|.
//
// mode (in) : The decryption algorithm that should be used to decrypt
// |input_data|.
//
// key_id (in) : The identifier for which key in |whitebox| to use to decrypt
// |input_data|.
//
// key_id_size (in) : The number of bytes in |key_id|.
//
// input_data (in) : The ciphertext.
//
// input_data_size (in) : The number of bytes in |input_data|. If |mode| is set
// to CBC, then |input_data_size| must be a multiple of 16. Regardless of mode,
// this |input_data_size| must be greater than zero.
//
// iv (in) : The iv.
//
// iv_size (in) : The number of bytes in |iv|. This must be 16.
//
// output_data (out) : The output parameter for the plaintext.
//
// output_data_size (in/out) : As input, this contains the max number of bytes
// that can be written to |output_data|. As output, |output_data_size| is set
// to the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
//
// Returns:
// WB_RESULT_OK if |input_data| was successfully decrypted.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |mode| was invalid,
// if |key_id| was null, if |key_id_size| was zero, if |input_data| was null,
// if |input_data_size| was invalid, if |iv| was null, if |iv_size| was
// invalid, if |output_data| was null, or if |output_data_size| was null.
//
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key from
// the license, but the |whitebox| was not allowed to use it.
//
// WB_RESULT_KEY_UNAVAILABLE if |key_id| did not match a content key from the
// loaded license.
//
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
// the required size.
//
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
WB_API WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox,
WB_CipherMode mode,
const uint8_t* key_id,
size_t key_id_size,
const uint8_t* input_data,
size_t input_data_size,
const uint8_t* iv,
size_t iv_size,
uint8_t* output_data,
size_t* output_data_size);
// Decrypts |input_data| and write the obfuscated plaintext to
// |masked_output_data|. The obfuscated plaintext can be deobfuscated using
// WB_License_GetSecretString() and WB_License_Unmask().
//
// Args:
// whitebox (in) : The white-box containing the keys needed to decrypt
// |input_data|.
//
// mode (in) : The decryption algorithm that should be used to decrypt
// |input_data|.
//
// key_id (in) : The identifier for which key in |whitebox| to use to decrypt
// |input_data|.
//
// key_id_size (in) : The number of bytes in |key_id|.
//
// input_data (in) : The ciphertext.
//
// input_data_size (in) : The number of bytes in |input_data|. If |mode| is set
// to CBC, then |input_data_size| must be a multiple of 16. Regardless of mode,
// this |input_data_size| must be greater than zero.
//
// iv (in) : The iv.
//
// iv_size (in) : The number of bytes in |iv|. This must be 16.
//
// masked_output_data (out) : The output parameter for the obfuscated
// plaintext.
//
// masked_output_data_size (in/out) : As input, this contains the max number of
// bytes that can be written to |masked_output_data|. As output,
// |masked_output_data_size| is set to the required size on WB_RESULT_OK and
// WB_RESULT_BUFFER_TOO_SMALL.
//
// Returns:
// WB_RESULT_OK if |input_data| was successfully decrypted.
//
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |mode| was invalid,
// if |key_id| was null, if |key_id_size| was zero, if |input_data| was null,
// if |input_data_size| was invalid, if |iv| was null, if |iv_size| was
// invalid, if |masked_output_data| was null, or if |masked_output_data_size|
// was null.
//
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key from
// the license, but the |whitebox| was not allowed to use it.
//
// WB_RESULT_KEY_UNAVAILABLE if |key_id| did not match a content key from the
// loaded license.
//
// WB_RESULT_BUFFER_TOO_SMALL if |masked_output_data_size| (as input) was less
// than the required size.
//
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
WB_API WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
WB_CipherMode mode,
const uint8_t* key_id,
size_t key_id_size,
const uint8_t* input_data,
size_t input_data_size,
const uint8_t* iv,
size_t iv_size,
uint8_t* masked_output_data,
size_t* masked_output_data_size);
// Unmasks a subset of the data in |masked_data| using |secret_string| and
// writes it to |unmasked_data|.
//
// The subset is denoted as |offset| (inclusive) to |offset + size| (exclusive).
// It is assumed that |offset| and |offset + size - 1| are both valid indexes
// into |masked_data|.
//
// It is assumed that indexes between 0 and |size - 1| (inclusive) are all valid
// indexes into |unmasked_data|.
//
// The memory range used for |masked_data| must not overlap with
// |unmasked_data|.
//
// Args:
// masked_data (in) : The masked data to read from.
//
// offset (in) : The index into |masked_data| from where to start reading data.
//
// size (in) : The number of bytes from |masked_data| to unmask and copy into
// |unmasked_data|.
//
// secret_string (in) : The auxiliary data for unmasking |masked_data|.
//
// secret_string_size (in) : The number of bytes in |secret_string|.
//
// unmasked_data (out) : The output buffer to write the unmasked data to.
WB_API void WB_License_Unmask(const uint8_t* masked_data,
size_t offset,
size_t size,
const uint8_t* secret_string,
size_t secret_string_size,
uint8_t* unmasked_data);
#ifdef __cplusplus
}
#endif
#endif // WHITEBOX_API_LICENSE_WHITEBOX_H_

View File

@@ -0,0 +1,63 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox_benchmark.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "api/license_whitebox.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "benchmarking/data_source.h"
#include "crypto_utils/crypto_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
namespace {
constexpr size_t kBlockSize = 16; // This must align with the AES block size.
} // namespace
void LicenseWhiteboxBenchmark::SetUp() {
key_id_ = data_source_.Get(8); // The id size is not meaningful.
key_ = data_source_.Get(kBlockSize);
iv_ = data_source_.Get(kBlockSize);
const auto public_key_data = GetMatchingLicensePublicKey();
public_key_.reset(widevine::RsaPublicKey::Create(
std::string(public_key_data.begin(), public_key_data.end())));
ASSERT_TRUE(public_key_);
}
License LicenseWhiteboxBenchmark::CreateLicense() const {
widevine::TestLicenseBuilder license_builder;
license_builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
// Use secure crypto as it will work with both Decrypt() and
// MaskedDecrypt().
license_builder.AddContentKey(
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
key_id_, key_);
widevine::License license;
license_builder.Build(*public_key_, &license);
return license;
}
std::vector<uint8_t> LicenseWhiteboxBenchmark::SignAsServer(
const std::vector<uint8_t>& message) const {
// The server key is the first half of the signing key.
const auto key = TestLicenseBuilder::DefaultSigningKey();
const std::string server_key(key.begin(),
key.begin() + crypto_util::kSigningKeySizeBytes);
// crypto util uses strings, so we will need to convert the result back to a
// vector before we return it.
const auto signature = crypto_util::CreateSignatureHmacSha256(
server_key, std::string(message.begin(), message.end()));
return std::vector<uint8_t>(signature.begin(), signature.end());
}
} // namespace widevine

View File

@@ -0,0 +1,47 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_API_LICENSE_WHITEBOX_BENCHMARK_H_
#define WHITEBOX_API_LICENSE_WHITEBOX_BENCHMARK_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "api/license_whitebox.h"
#include "api/test_license_builder.h"
#include "benchmarking/data_source.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxBenchmark : public ::testing::Test {
protected:
virtual void SetUp() override;
License CreateLicense() const;
std::vector<uint8_t> SignAsServer(const std::vector<uint8_t>& message) const;
DataSource& Data() { return data_source_; }
const RsaPublicKey* PublicKey() const { return public_key_.get(); }
const std::vector<uint8_t>& ContentKeyId() const { return key_id_; }
const std::vector<uint8_t>& ContentKey() const { return key_; }
const std::vector<uint8_t>& ContentIV() const { return iv_; }
private:
DataSource data_source_;
std::unique_ptr<RsaPublicKey> public_key_;
std::vector<uint8_t> key_id_;
std::vector<uint8_t> key_;
std::vector<uint8_t> iv_;
};
} // namespace widevine
#endif // WHITEBOX_API_LICENSE_WHITEBOX_BENCHMARK_H_

View File

@@ -0,0 +1,207 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <vector>
#include "api/golden_data.h"
#include "api/license_whitebox_test_base.h"
#include "api/result.h"
#include "api/test_license_builder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
namespace {
using RemoteAttestation = TestLicenseBuilder::RemoteAttestation;
using VerificationStatus = TestLicenseBuilder::VerificationStatus;
// We can't use the actual keys with TEST_P, so define an enum that we can use
// to communicate which key to use.
enum class Key {
kCrypto,
kDecode,
kHardware,
};
} // namespace
class LicenseWhiteboxChromeOSTest
: public LicenseWhiteboxTestBase,
public testing::WithParamInterface<
std::tuple<Key, RemoteAttestation, VerificationStatus>> {
protected:
void SetUp() override {
LicenseWhiteboxTestBase::SetUp();
std::tie(key_, remote_attestation_, verification_status_) = GetParam();
LoadLicense();
// We know that the plaintext will be smaller than the ciphertext, so we
// use that to ensure the buffer is large enough.
plaintext_size_ = golden_data_.CBCContent().ciphertext.size();
plaintext_.resize(plaintext_size_);
// We make the assumption that the secret string can never be longer than
// the ciphertext.
secret_string_size_ = golden_data_.CBCContent().ciphertext.size();
secret_string_.resize(secret_string_size_);
}
// This requires that the remote attestation and verification status to be
// set before being called.
void LoadLicense() {
TestLicenseBuilder builder;
// Only use CBC keys so that we can always use the CBC content.
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
golden_data_.CBCCryptoKey().id,
golden_data_.CBCCryptoKey().content->key);
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
golden_data_.CBCDecodeKey().id,
golden_data_.CBCDecodeKey().content->key);
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
golden_data_.CBCHardwareKey().id,
golden_data_.CBCHardwareKey().content->key);
builder.SetRemoteAttestation(remote_attestation_);
builder.SetVerificationStatus(verification_status_);
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
// This is the strictest level of enforcement. It will be the last one
// checked. Any basic overrides, for example a crypto key in decrypt,
// will short-circuit it.
WB_Result GetExpectedPlatformVerificationResult() {
if (remote_attestation_ == RemoteAttestation::kUnverified) {
return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL;
}
if (verification_status_ == VerificationStatus::kOther) {
return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL;
}
if (remote_attestation_ == RemoteAttestation::kVerified) {
return WB_RESULT_OK;
}
if (verification_status_ == VerificationStatus::kHardwareVerified) {
return WB_RESULT_OK;
}
return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL;
}
WB_Result GetExpectedDecryptResult() {
if (key_ == Key::kCrypto) {
return WB_RESULT_OK;
}
return GetExpectedPlatformVerificationResult();
}
// Since MaskedDecrypt() and GetSecretString() go hand-in-hand. This function
// will be used for both.
WB_Result GetExpectedMaskedDecryptResult() {
if (key_ == Key::kCrypto || key_ == Key::kDecode) {
return WB_RESULT_OK;
}
return GetExpectedPlatformVerificationResult();
}
const GoldenData::Key* GetContentKey() const {
switch (key_) {
case Key::kCrypto:
return &golden_data_.CBCCryptoKey();
case Key::kDecode:
return &golden_data_.CBCDecodeKey();
case Key::kHardware:
return &golden_data_.CBCHardwareKey();
}
return nullptr;
}
Key key_;
RemoteAttestation remote_attestation_;
VerificationStatus verification_status_;
// This is the buffer used to store the output of each decrypt and unmask
// call.
size_t plaintext_size_;
std::vector<uint8_t> plaintext_;
// This is the buffer used to store the secret string used to demask the
// result of WB_License_MaskedDecrypt().
size_t secret_string_size_;
std::vector<uint8_t> secret_string_;
};
TEST_P(LicenseWhiteboxChromeOSTest, Decrypt) {
const WB_Result expected_result = GetExpectedDecryptResult();
const GoldenData::Key* key = GetContentKey();
ASSERT_NE(key, nullptr);
const auto* content = key->content;
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, key->id.data(),
key->id.size(), content->ciphertext.data(),
content->plaintext.size(), content->iv.data(),
content->iv.size(), plaintext_.data(),
&plaintext_size_),
expected_result);
}
TEST_P(LicenseWhiteboxChromeOSTest, MaskedDecrypt) {
const WB_Result expected_result = GetExpectedMaskedDecryptResult();
const GoldenData::Key* key = GetContentKey();
ASSERT_NE(key, nullptr);
const auto* content = key->content;
ASSERT_EQ(WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, key->id.data(), key->id.size(),
content->ciphertext.data(), content->plaintext.size(),
content->iv.data(), content->iv.size(), plaintext_.data(),
&plaintext_size_),
expected_result);
}
TEST_P(LicenseWhiteboxChromeOSTest, GetSecretString) {
const WB_Result expected_result = GetExpectedMaskedDecryptResult();
const GoldenData::Key* key = GetContentKey();
ASSERT_NE(key, nullptr);
ASSERT_EQ(WB_License_GetSecretString(
whitebox_, WB_CIPHER_MODE_CBC, key->id.data(), key->id.size(),
secret_string_.data(), &secret_string_size_),
expected_result);
}
INSTANTIATE_TEST_SUITE_P(
AllCombinations,
LicenseWhiteboxChromeOSTest,
::testing::Combine(
::testing::Values(Key::kCrypto, Key::kDecode, Key::kHardware),
::testing::Values(RemoteAttestation::kUnavailable,
RemoteAttestation::kUnverified,
RemoteAttestation::kVerified),
::testing::Values(VerificationStatus::kUnavailable,
VerificationStatus::kOther,
VerificationStatus::kHardwareVerified)));
} // namespace widevine

View File

@@ -0,0 +1,57 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <vector>
#include "api/test_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class LicenseWhiteboxCreateTest : public ::testing::Test {
protected:
void SetUp() override {
init_data_ = GetLicenseInitData();
ASSERT_GT(init_data_.size(), 0u);
}
void TearDown() override { WB_License_Delete(whitebox_); }
WB_License_Whitebox* whitebox_ = nullptr;
std::vector<uint8_t> init_data_;
};
TEST_F(LicenseWhiteboxCreateTest, Succeeds) {
ASSERT_EQ(WB_License_Create(init_data_.data(), init_data_.size(), &whitebox_),
WB_RESULT_OK);
ASSERT_TRUE(whitebox_);
}
TEST_F(LicenseWhiteboxCreateTest, InvalidParameterForNullInitData) {
ASSERT_EQ(WB_License_Create(nullptr, init_data_.size(), &whitebox_),
WB_RESULT_INVALID_PARAMETER);
ASSERT_FALSE(whitebox_);
}
TEST_F(LicenseWhiteboxCreateTest, InvalidParameterForZeroInitDataSize) {
ASSERT_EQ(WB_License_Create(init_data_.data(), 0, &whitebox_),
WB_RESULT_INVALID_PARAMETER);
ASSERT_FALSE(whitebox_);
}
TEST_F(LicenseWhiteboxCreateTest, InvalidParameterForInvalidInitData) {
const std::vector<uint8_t> invalid_init_data = GetInvalidLicenseInitData();
ASSERT_EQ(WB_License_Create(invalid_init_data.data(),
invalid_init_data.size(), &whitebox_),
WB_RESULT_INVALID_PARAMETER);
ASSERT_FALSE(whitebox_);
}
TEST_F(LicenseWhiteboxCreateTest, InvalidParameterForNullWhitebox) {
ASSERT_EQ(WB_License_Create(init_data_.data(), init_data_.size(), nullptr),
WB_RESULT_INVALID_PARAMETER);
}
} // namespace

View File

@@ -0,0 +1,183 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "api/license_whitebox.h"
#include "api/license_whitebox_benchmark.h"
#include "api/result.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "benchmarking/data_source.h"
#include "benchmarking/measurements.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
namespace {
// The mask size can be any size (depends on the implementation), so 256 should
// be more than enough for any implementation.
constexpr size_t kMaskSize = 256;
} // namespace
// Test Parameter:
// - The number of blocks given to each decrypt call. Each block will be 16
// bytes.
// - The number of iterations to run.
class LicenseWhiteboxDecryptBenchmark
: public LicenseWhiteboxBenchmark,
public testing::WithParamInterface<std::tuple<size_t, size_t>> {
protected:
void SetUp() override {
LicenseWhiteboxBenchmark::SetUp();
// Extract all parameters.
size_t blocks_per_call;
std::tie(blocks_per_call, iterations_) = GetParam();
// We are using AES with no padding, the input and output will be the same
// size.
const size_t bytes_per_call = blocks_per_call * 16;
ciphertext_ = Data().Get(bytes_per_call);
masked_text_.resize(bytes_per_call);
plaintext_.resize(bytes_per_call);
const auto init_data = GetLicenseInitData();
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
WB_RESULT_OK);
const auto license = CreateLicense();
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
void TearDown() override { WB_License_Delete(whitebox_); }
WB_License_Whitebox* whitebox_;
std::vector<uint8_t> ciphertext_;
std::vector<uint8_t> masked_text_;
std::vector<uint8_t> plaintext_;
size_t iterations_;
};
TEST_P(LicenseWhiteboxDecryptBenchmark, DecryptCBCThroughput) {
Timer timer;
timer.Reset();
for (size_t i = 0; i < iterations_; i++) {
size_t plaintext_size = plaintext_.size();
ASSERT_EQ(WB_RESULT_OK,
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
ContentKeyId().data(), ContentKeyId().size(),
ciphertext_.data(), ciphertext_.size(),
ContentIV().data(), ContentIV().size(),
plaintext_.data(), &plaintext_size));
}
Throughput throughput(timer.Get(), iterations_ * ciphertext_.size());
PrettyPrint("License Decrypt CBC Throughput", throughput, ciphertext_.size());
}
TEST_P(LicenseWhiteboxDecryptBenchmark, DecryptCTRThroughput) {
Timer timer;
timer.Reset();
for (size_t i = 0; i < iterations_; i++) {
size_t plaintext_size = plaintext_.size();
ASSERT_EQ(WB_RESULT_OK,
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR,
ContentKeyId().data(), ContentKeyId().size(),
ciphertext_.data(), ciphertext_.size(),
ContentIV().data(), ContentIV().size(),
plaintext_.data(), &plaintext_size));
}
Throughput throughput(timer.Get(), iterations_ * ciphertext_.size());
PrettyPrint("License Decrypt CTR Throughput", throughput, ciphertext_.size());
}
TEST_P(LicenseWhiteboxDecryptBenchmark, MaskedDecryptCBCThroughput) {
std::vector<uint8_t> mask(kMaskSize);
size_t mask_size = mask.size();
ASSERT_EQ(WB_RESULT_OK,
WB_License_GetSecretString(
whitebox_, WB_CIPHER_MODE_CBC, ContentKeyId().data(),
ContentKeyId().size(), mask.data(), &mask_size));
mask.resize(mask_size);
Timer timer;
timer.Reset();
for (size_t i = 0; i < iterations_; i++) {
// Include the unmask in the timing as it will be part of the flow from
// encrypted to consumption.
size_t plaintext_size = plaintext_.size();
ASSERT_EQ(WB_RESULT_OK,
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, ContentKeyId().data(),
ContentKeyId().size(), ciphertext_.data(), ciphertext_.size(),
ContentIV().data(), ContentIV().size(), masked_text_.data(),
&plaintext_size));
WB_License_Unmask(masked_text_.data(), 0, plaintext_size, mask.data(),
mask.size(), plaintext_.data());
}
Throughput throughput(timer.Get(), iterations_ * ciphertext_.size());
PrettyPrint("License Masked Decrypt CBC Throughput", throughput,
ciphertext_.size());
}
TEST_P(LicenseWhiteboxDecryptBenchmark, MaskedDecryptCTRThroughput) {
std::vector<uint8_t> mask(kMaskSize);
size_t mask_size = mask.size();
ASSERT_EQ(WB_RESULT_OK,
WB_License_GetSecretString(
whitebox_, WB_CIPHER_MODE_CTR, ContentKeyId().data(),
ContentKeyId().size(), mask.data(), &mask_size));
mask.resize(mask_size);
Timer timer;
timer.Reset();
for (size_t i = 0; i < iterations_; i++) {
// Include the unmask in the timing as it will be part of the flow from
// encrypted to consumption.
size_t plaintext_size = plaintext_.size();
ASSERT_EQ(WB_RESULT_OK,
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CTR, ContentKeyId().data(),
ContentKeyId().size(), ciphertext_.data(), ciphertext_.size(),
ContentIV().data(), ContentIV().size(), masked_text_.data(),
&plaintext_size));
WB_License_Unmask(masked_text_.data(), 0, plaintext_size, mask.data(),
mask.size(), plaintext_.data());
}
Throughput throughput(timer.Get(), iterations_ * ciphertext_.size());
PrettyPrint("License Masked Decrypt CTR Throughput", throughput,
ciphertext_.size());
}
// The first value is the number of blocks. Each block is 16 bytes.
INSTANTIATE_TEST_SUITE_P(
DifferentBytesPerCall,
LicenseWhiteboxDecryptBenchmark,
::testing::Values(std::make_tuple(1, 100), // 16 B
std::make_tuple(16, 100), // 256 B
std::make_tuple(64, 100), // 1 KB
std::make_tuple(1024, 100), // 16 KB
std::make_tuple(16 * 1024, 25), // 256 KB
std::make_tuple(64 * 1024, 10))); // 1 MB
} // namespace widevine

View File

@@ -0,0 +1,494 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <memory>
#include <string>
#include <vector>
#include "api/golden_data.h"
#include "api/license_whitebox_test_base.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase {
protected:
void SetUp() override {
LicenseWhiteboxTestBase::SetUp();
// Because we are using the same buffer for both CTR and CBC, need to make
// sure that it is large enough for either one.
plaintext_size_ = std::max(golden_data_.CBCContent().ciphertext.size(),
golden_data_.CTRContent().ciphertext.size());
plaintext_.resize(plaintext_size_);
golden_data_.MakeKeyIdDifferent(&non_content_key_id_);
golden_data_.MakeKeyIdDifferent(&missing_key_id_);
}
void LoadLicense(const std::vector<uint8_t>& padding) {
TestLicenseBuilder builder;
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
golden_data_.CBCCryptoKey().id,
golden_data_.CBCCryptoKey().content->key, padding);
builder.AddContentKey(golden_data_.CTRCryptoKey().level,
golden_data_.CTRCryptoKey().id,
golden_data_.CTRCryptoKey().content->key, padding);
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
golden_data_.CBCDecodeKey().id,
golden_data_.CBCDecodeKey().content->key, padding);
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
golden_data_.CBCHardwareKey().id,
golden_data_.CBCHardwareKey().content->key, padding);
builder.AddOperatorSessionKey(non_content_key_id_);
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
// We need two special keys for this test, one that will be used for a
// non-content key and one that will never be in the license.
std::vector<uint8_t> non_content_key_id_ = {0, 0, 0};
std::vector<uint8_t> missing_key_id_ = {1, 0, 0};
// This is the buffer used to store the output of each decrypt call.
size_t plaintext_size_;
std::vector<uint8_t> plaintext_;
};
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCbcMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_OK);
plaintext_.resize(plaintext_size_);
ASSERT_EQ(plaintext_, golden_data_.CBCCryptoKey().content->plaintext);
}
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCtrMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
golden_data_.CTRCryptoKey().content->ciphertext.data(),
golden_data_.CTRCryptoKey().content->ciphertext.size(),
golden_data_.CTRCryptoKey().content->iv.data(),
golden_data_.CTRCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_OK);
plaintext_.resize(plaintext_size_);
ASSERT_EQ(plaintext_, golden_data_.CTRCryptoKey().content->plaintext);
}
// We try to decrypt CBC encrypted data in CTR mode. All operations should be
// successful, but the resulting plaintext should not match.
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCtrMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_OK);
plaintext_.resize(plaintext_size_);
ASSERT_NE(plaintext_, golden_data_.CBCCryptoKey().content->plaintext);
}
// We try to decrypt CTR encrypted data in CBC mode. All operations should be
// successful, but the resulting plaintext should not match.
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCbcMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
golden_data_.CTRCryptoKey().content->ciphertext.data(),
golden_data_.CTRCryptoKey().content->ciphertext.size(),
golden_data_.CTRCryptoKey().content->iv.data(),
golden_data_.CTRCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_OK);
plaintext_.resize(plaintext_size_);
ASSERT_NE(plaintext_, golden_data_.CTRCryptoKey().content->plaintext);
}
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) {
LoadLicense(TestLicenseBuilder::PKSC8Padding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_OK);
plaintext_.resize(plaintext_size_);
ASSERT_EQ(plaintext_, golden_data_.CBCCryptoKey().content->plaintext);
}
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) {
LoadLicense(TestLicenseBuilder::PKSC8Padding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
golden_data_.CTRCryptoKey().content->ciphertext.data(),
golden_data_.CTRCryptoKey().content->ciphertext.size(),
golden_data_.CTRCryptoKey().content->iv.data(),
golden_data_.CTRCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_OK);
plaintext_.resize(plaintext_size_);
ASSERT_EQ(plaintext_, golden_data_.CTRCryptoKey().content->plaintext);
}
// Try decrypting two different sets of content to make sure that two
// different keys can be used at the same time.
TEST_F(LicenseWhiteboxDecryptTest, SuccessWithMultipleKeys) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_OK);
plaintext_.resize(plaintext_size_);
ASSERT_EQ(plaintext_, golden_data_.CBCCryptoKey().content->plaintext);
// Reset our output buffer.
plaintext_.clear();
plaintext_size_ = golden_data_.CTRDecodeKey().content->plaintext.size();
plaintext_.resize(plaintext_size_);
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
golden_data_.CTRCryptoKey().content->ciphertext.data(),
golden_data_.CTRCryptoKey().content->ciphertext.size(),
golden_data_.CTRCryptoKey().content->iv.data(),
golden_data_.CTRCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_OK);
plaintext_.resize(plaintext_size_);
ASSERT_EQ(plaintext_, golden_data_.CTRCryptoKey().content->plaintext);
}
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(nullptr, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCipherMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
// In order to trick the compiler into letting us pass an invalid enum value
// to WB__License_Decrypt(), we need to cast it. If we don't do this, the
// compiler tries to save us.
const WB_CipherMode invalid_mode = static_cast<WB_CipherMode>(0xFF);
ASSERT_EQ(WB_License_Decrypt(
whitebox_, invalid_mode, golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, nullptr,
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroKeyIdSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(), 0,
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(), nullptr,
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
// AES CBC requires that the input be block aligned (multiple of 16). CTR does
// not care.
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCBCInputDataSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
14, golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
// The white-box (using any cipher mode) should reject input with size zero.
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroInputDataSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
0, golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(), nullptr,
golden_data_.CBCCryptoKey().content->iv.size(), plaintext_.data(),
&plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
// IV size should be 16. Any number other than 16 should fail.
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(), 9,
plaintext_.data(), &plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
nullptr, &plaintext_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), nullptr),
WB_RESULT_INVALID_PARAMETER);
}
// For this test, "missing key id" specifically means a key id that was never
// in the license to start with. This is different than "non content key"
// and "dropped content key", as those keys were in the license but ignored.
TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForMissingKeyId) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(),
missing_key_id_.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_KEY_UNAVAILABLE);
}
TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForNonContentKey) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
non_content_key_id_.data(), non_content_key_id_.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_KEY_UNAVAILABLE);
}
// Under normal circumstances, a hardware key should be dropped. The exception
// to this rule is on ChromeOS with a special license.
TEST_F(LicenseWhiteboxDecryptTest,
InsufficientSecurityLevelForHardwareContentKey) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_Decrypt(
whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCHardwareKey().id.data(),
golden_data_.CBCHardwareKey().id.size(),
golden_data_.CBCHardwareKey().content->ciphertext.data(),
golden_data_.CBCHardwareKey().content->ciphertext.size(),
golden_data_.CBCHardwareKey().content->iv.data(),
golden_data_.CBCHardwareKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL);
}
TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) {
LoadLicense(TestLicenseBuilder::NoPadding());
// Use the software decode key as they are limited to
// WB_License_Decrypt().
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL);
}
TEST_F(LicenseWhiteboxDecryptTest, BufferTooSmall) {
LoadLicense(TestLicenseBuilder::NoPadding());
// Our ciphertext will be large enough that we should not need to worry about
// using a constant here.
plaintext_size_ = 8;
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_BUFFER_TOO_SMALL);
// We don't use padding so the reported plaintext size should be the same as
// the cipher text size.
ASSERT_EQ(plaintext_size_,
golden_data_.CBCCryptoKey().content->ciphertext.size());
}
TEST_F(LicenseWhiteboxDecryptTest, InvalidState) {
// Unlike the other tests, we do not call LoadLicense() as the criteria for
// WB_RESULT_INVALID_STATE is that no key can be found and keys are provided
// via a license.
ASSERT_EQ(
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(),
plaintext_.data(), &plaintext_size_),
WB_RESULT_INVALID_STATE);
}
} // namespace widevine

View File

@@ -0,0 +1,257 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <string>
#include <vector>
#include "api/golden_data.h"
#include "api/license_whitebox_test_base.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxGetSecretStringTest : public LicenseWhiteboxTestBase {
protected:
void SetUp() override {
LicenseWhiteboxTestBase::SetUp();
// The secret string size is implementation specific, so this number just
// needs to be reasonably large to accommodate different implementations.
secret_string_size_ = 256;
secret_string_.resize(secret_string_size_);
golden_data_.MakeKeyIdDifferent(&non_content_key_id_);
golden_data_.MakeKeyIdDifferent(&missing_key_id_);
}
void LoadLicense() {
TestLicenseBuilder builder;
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
golden_data_.CBCCryptoKey().id,
golden_data_.CBCCryptoKey().content->key);
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
golden_data_.CBCDecodeKey().id,
golden_data_.CBCDecodeKey().content->key);
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
golden_data_.CBCHardwareKey().id,
golden_data_.CBCHardwareKey().content->key);
builder.AddOperatorSessionKey(non_content_key_id_);
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
// We need two special keys for this test, one that will be used for a
// non-content key and one that will never be in the license.
std::vector<uint8_t> non_content_key_id_ = {0, 0, 0};
std::vector<uint8_t> missing_key_id_ = {1, 0, 0};
size_t secret_string_size_;
std::vector<uint8_t> secret_string_;
};
TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCBCWithCryptoKey) {
LoadLicense();
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
ASSERT_GT(secret_string_size_, 0u);
}
TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCTRWithCryptoKey) {
LoadLicense();
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
ASSERT_GT(secret_string_size_, 0u);
}
TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCBCWithDecodeKey) {
LoadLicense();
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
ASSERT_GT(secret_string_size_, 0u);
}
TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCTRWithDecodeKey) {
LoadLicense();
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
ASSERT_GT(secret_string_size_, 0u);
}
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullWhitebox) {
LoadLicense();
ASSERT_EQ(
WB_License_GetSecretString(nullptr, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxGetSecretStringTest,
InvalidParameterForInvalidCipherMode) {
LoadLicense();
// In order to trick the compiler into letting us pass an invalid enum value
// to WB__License_Decrypt(), we need to cast it. If we don't do this, the
// compiler tries to save us.
const WB_CipherMode invalid_mode = static_cast<WB_CipherMode>(0xFF);
ASSERT_EQ(WB_License_GetSecretString(
whitebox_, invalid_mode, golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(), secret_string_.data(),
&secret_string_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullKeyId) {
LoadLicense();
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, nullptr,
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForZeroKeyIdSize) {
LoadLicense();
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(), 0,
secret_string_.data(), &secret_string_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxGetSecretStringTest,
InvalidParameterForNullSecretString) {
LoadLicense();
ASSERT_EQ(WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
nullptr, &secret_string_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxGetSecretStringTest,
InvalidParameterForNullSecretStringSize) {
LoadLicense();
ASSERT_EQ(WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), nullptr),
WB_RESULT_INVALID_PARAMETER);
}
// For this test, "missing key id" specifically means a key id that was never
// in the license to start with. This is different than "non content key"
// and "dropped content key", as those keys were in the license but ignored.
TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForMissingKeyId) {
LoadLicense();
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
missing_key_id_.data(), missing_key_id_.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_KEY_UNAVAILABLE);
}
TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForNonContentKey) {
LoadLicense();
ASSERT_EQ(WB_License_GetSecretString(
whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(),
non_content_key_id_.size(), secret_string_.data(),
&secret_string_size_),
WB_RESULT_KEY_UNAVAILABLE);
}
// Under normal circumstances, a hardware key should be dropped. The exception
// to this rule is on ChromeOS with a special license.
TEST_F(LicenseWhiteboxGetSecretStringTest,
InsufficientSecurityLevelForHardwareContentKey) {
LoadLicense();
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCHardwareKey().id.data(),
golden_data_.CBCHardwareKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL);
}
TEST_F(LicenseWhiteboxGetSecretStringTest, BufferTooSmall) {
LoadLicense();
// Since the secret string is implementation specific, we don't want to make
// any big assumptions about what would be too small. Using 1 is fairly safe
// as it would not introduce enough variation to be effective. We avoid using
// zero here so that we can verify that we are not just checking for zero.
secret_string_size_ = 1;
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_BUFFER_TOO_SMALL);
// Make sure that the output included the required size. We don't know what
// it is, so we rely on checking that it is just bigger than the "too small"
// size.
ASSERT_GT(secret_string_size_, 1u);
}
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidState) {
// Purposely do not load a license so that we won't have any keys, causing
// use to be in an invalid state.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_INVALID_STATE);
}
} // namespace widevine

View File

@@ -0,0 +1,343 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <cstddef>
#include <cstdint>
#include <vector>
#include "api/license_whitebox.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
// This must be defined by the implementation as they are implementation
// specific. They must be defined so that they work with the test key used to
// generate the license request.
extern const uint8_t kLicenseWhiteboxInitData[];
extern const size_t kLicenseWhiteboxInitDataSize;
namespace {
const uint8_t kMessage[] = {
0x0a, 0x2a, 0x0a, 0x10, 0x53, 0xbc, 0x88, 0x54, 0x04, 0xfc, 0x85, 0xbd,
0x75, 0xce, 0x15, 0x2a, 0x06, 0xb5, 0x1f, 0xc5, 0x12, 0x10, 0x53, 0xbc,
0x88, 0x54, 0x04, 0xfc, 0x85, 0xbd, 0x75, 0xce, 0x15, 0x2a, 0x06, 0xb5,
0x1f, 0xc5, 0x1a, 0x00, 0x20, 0x01, 0x28, 0x00, 0x12, 0x12, 0x08, 0x01,
0x10, 0x01, 0x18, 0x01, 0x20, 0x80, 0xa3, 0x05, 0x28, 0x80, 0xa3, 0x05,
0x30, 0x80, 0xa3, 0x05, 0x1a, 0x66, 0x12, 0x10, 0x94, 0x40, 0xfa, 0xa2,
0xd0, 0xe2, 0xa4, 0x80, 0xb4, 0x74, 0x68, 0x91, 0x0a, 0xb9, 0xe4, 0x6a,
0x1a, 0x50, 0x30, 0xe2, 0xcb, 0x71, 0xa1, 0x92, 0xb7, 0xc8, 0x3f, 0xd9,
0x00, 0x2e, 0xf0, 0xf8, 0xb1, 0xe6, 0x9e, 0x62, 0xca, 0x27, 0xb3, 0xa7,
0x26, 0x19, 0x73, 0x9e, 0xf8, 0xc2, 0x8e, 0x6f, 0xea, 0xb3, 0x5b, 0x01,
0xa6, 0xe8, 0x9b, 0x84, 0x1b, 0x9e, 0xaf, 0xe2, 0xa1, 0xff, 0x4a, 0xca,
0xcf, 0x84, 0x3f, 0x36, 0xaa, 0x17, 0xa6, 0x0a, 0x0a, 0xe2, 0xcc, 0x12,
0xb1, 0x9f, 0xac, 0x05, 0xdf, 0xe7, 0x00, 0xad, 0xc4, 0xa5, 0xd4, 0x4d,
0x46, 0x01, 0xbb, 0xcc, 0x18, 0x7e, 0x4d, 0xb5, 0x05, 0x2a, 0x20, 0x01,
0x1a, 0x52, 0x0a, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x12, 0x10, 0xf1, 0xed,
0x57, 0xf4, 0xad, 0x5d, 0x0e, 0x2c, 0xf9, 0x78, 0xc9, 0xe4, 0x4d, 0x2e,
0xcc, 0x68, 0x1a, 0x20, 0xd7, 0xf5, 0x15, 0x85, 0x3b, 0x33, 0xea, 0x8e,
0x40, 0xef, 0x2a, 0xd0, 0x8f, 0x96, 0x69, 0xb6, 0xf9, 0xdd, 0x63, 0x8f,
0x80, 0x7e, 0x68, 0x02, 0xc7, 0x74, 0x55, 0x5d, 0xd4, 0x17, 0x45, 0x09,
0x20, 0x02, 0x28, 0x01, 0x32, 0x00, 0x3a, 0x00, 0x62, 0x02, 0x48, 0x44,
0x1a, 0x52, 0x0a, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x12, 0x10, 0xdb, 0x38,
0x5d, 0x4a, 0x0d, 0x80, 0x37, 0x42, 0x10, 0x60, 0x0d, 0x24, 0x03, 0xde,
0xd0, 0x3c, 0x1a, 0x20, 0x22, 0xab, 0x33, 0xb9, 0xe5, 0x16, 0xa2, 0x6d,
0x5a, 0x42, 0x45, 0xd4, 0x0e, 0x47, 0x0c, 0xf3, 0x6c, 0x1f, 0x20, 0xd9,
0x0e, 0x79, 0xd7, 0xb7, 0x34, 0xc1, 0xbe, 0xd2, 0x6b, 0x01, 0xff, 0x73,
0x20, 0x02, 0x28, 0x01, 0x32, 0x00, 0x3a, 0x00, 0x62, 0x02, 0x48, 0x44,
0x1a, 0x55, 0x0a, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x12, 0x10, 0xdd, 0xfb,
0x7a, 0x27, 0x34, 0xd4, 0xec, 0xcf, 0x01, 0x9c, 0xbd, 0x84, 0xe3, 0xf0,
0xbd, 0xc6, 0x1a, 0x20, 0x11, 0x0c, 0xa2, 0xc3, 0xb8, 0x95, 0xaf, 0x48,
0x80, 0x57, 0xd1, 0x39, 0xec, 0xd7, 0x09, 0x5a, 0x95, 0x31, 0xf8, 0x3f,
0xa0, 0x6d, 0x9c, 0xad, 0x10, 0x70, 0xa4, 0x6b, 0x6a, 0x5c, 0xca, 0x73,
0x20, 0x02, 0x28, 0x01, 0x32, 0x00, 0x3a, 0x00, 0x62, 0x05, 0x41, 0x55,
0x44, 0x49, 0x4f, 0x1a, 0x52, 0x0a, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x37, 0x12,
0x10, 0xcb, 0xac, 0x50, 0x57, 0x81, 0xcd, 0x04, 0xf6, 0x71, 0xb2, 0xc2,
0x08, 0x86, 0x12, 0x78, 0x58, 0x1a, 0x20, 0x71, 0x0e, 0xd7, 0xa2, 0x8c,
0x64, 0x75, 0x0b, 0x11, 0x9e, 0x48, 0x47, 0x0c, 0x2d, 0x78, 0x48, 0x1c,
0x98, 0x2f, 0x14, 0x51, 0x15, 0x2a, 0x05, 0xe8, 0x84, 0x6e, 0x61, 0xc2,
0x26, 0x82, 0xe6, 0x20, 0x02, 0x28, 0x01, 0x32, 0x00, 0x3a, 0x00, 0x62,
0x02, 0x48, 0x44, 0x1a, 0x52, 0x0a, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x12,
0x10, 0xa6, 0xd4, 0xb8, 0x0a, 0x37, 0x46, 0xf6, 0xc8, 0x59, 0x5a, 0x30,
0x96, 0x39, 0x16, 0x29, 0x1e, 0x1a, 0x20, 0xff, 0xe7, 0xb2, 0x9e, 0x98,
0x40, 0x33, 0xce, 0x9e, 0xcf, 0x85, 0x60, 0x52, 0x43, 0x38, 0xe6, 0x1b,
0x07, 0x10, 0xfa, 0x71, 0xa8, 0x66, 0x0f, 0xd6, 0x9a, 0x09, 0xac, 0xb1,
0x47, 0xe4, 0x19, 0x20, 0x02, 0x28, 0x01, 0x32, 0x00, 0x3a, 0x00, 0x62,
0x02, 0x53, 0x44, 0x1a, 0x52, 0x0a, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x12,
0x10, 0x57, 0x43, 0x24, 0x10, 0x4d, 0xd3, 0xcf, 0x6e, 0xef, 0x37, 0xce,
0x5b, 0xba, 0xd1, 0x61, 0x62, 0x1a, 0x20, 0x54, 0xec, 0xbb, 0xc7, 0x4b,
0xa6, 0x3f, 0x17, 0xcc, 0x5e, 0xb8, 0x20, 0x41, 0x9e, 0xcf, 0xe2, 0x9f,
0x03, 0x9d, 0x79, 0xd0, 0x06, 0x0b, 0x0e, 0x6e, 0xf4, 0x9e, 0x93, 0x92,
0x50, 0x19, 0xbd, 0x20, 0x02, 0x28, 0x01, 0x32, 0x00, 0x3a, 0x00, 0x62,
0x02, 0x53, 0x44, 0x1a, 0x52, 0x0a, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x35, 0x12,
0x10, 0xd7, 0x93, 0x22, 0x74, 0xf1, 0x0b, 0x61, 0x26, 0x7e, 0x16, 0xd5,
0x88, 0x5f, 0x70, 0x2b, 0x3d, 0x1a, 0x20, 0xb2, 0x21, 0xc6, 0xb4, 0x65,
0xf1, 0x00, 0x07, 0x11, 0x0e, 0xe4, 0x67, 0x3c, 0xb4, 0x4c, 0xc9, 0xe9,
0x82, 0x5e, 0xbc, 0x2a, 0x2a, 0xf0, 0x68, 0x45, 0xfe, 0xd2, 0xce, 0xea,
0xae, 0xfb, 0x9a, 0x20, 0x02, 0x28, 0x01, 0x32, 0x00, 0x3a, 0x00, 0x62,
0x02, 0x53, 0x44, 0x1a, 0x52, 0x0a, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x36, 0x12,
0x10, 0x96, 0x9e, 0xc6, 0xcb, 0xc0, 0xbf, 0xf5, 0x83, 0xef, 0xc9, 0xe7,
0xbb, 0x2a, 0x1a, 0xce, 0x0b, 0x1a, 0x20, 0xe0, 0x23, 0x16, 0x1e, 0x00,
0x05, 0xc0, 0xfe, 0x97, 0x47, 0x5f, 0x75, 0x69, 0x8c, 0x09, 0x8b, 0x3e,
0x93, 0x1b, 0xa4, 0x84, 0x4b, 0xd3, 0xb1, 0x77, 0xd4, 0x42, 0xee, 0x7e,
0xf5, 0xe4, 0x0c, 0x20, 0x02, 0x28, 0x01, 0x32, 0x00, 0x3a, 0x00, 0x62,
0x02, 0x53, 0x44, 0x20, 0xb2, 0x88, 0xc6, 0xfa, 0x05, 0x38, 0x00,
};
const uint8_t kSignature[] = {
0x34, 0x7e, 0xc1, 0xa9, 0x3e, 0x1d, 0x66, 0x77, 0x5d, 0xac, 0xe8,
0x8b, 0xc0, 0x37, 0x3e, 0x4c, 0x9b, 0xe5, 0x9c, 0x25, 0x04, 0xed,
0x3f, 0x66, 0x6e, 0x6c, 0x51, 0x42, 0x57, 0x7f, 0x6c, 0x53,
};
const uint8_t kSessionKey[] = {
0x21, 0xd8, 0x3b, 0x58, 0xda, 0xa9, 0x3a, 0x1c, 0xdb, 0xfb, 0x54, 0xff,
0x0d, 0x30, 0xcd, 0x00, 0xff, 0x52, 0xc4, 0xed, 0x5c, 0xf9, 0xc9, 0x1c,
0x7b, 0x41, 0x48, 0xc0, 0x98, 0xa9, 0x74, 0x99, 0x79, 0xa0, 0x75, 0xc3,
0x50, 0xf0, 0xac, 0xfa, 0xec, 0xc4, 0xc0, 0x34, 0x06, 0x5b, 0xb9, 0xaa,
0x6d, 0xcf, 0x71, 0x69, 0xf0, 0x5e, 0x8f, 0x6b, 0x50, 0x9a, 0x16, 0x54,
0xa7, 0x72, 0x11, 0x50, 0xf9, 0xec, 0x8d, 0xcc, 0x3a, 0x23, 0x16, 0x84,
0x78, 0xea, 0xaa, 0xbd, 0x6f, 0xd9, 0x88, 0x5f, 0xa8, 0x82, 0x3c, 0x05,
0x53, 0xe7, 0x76, 0x5f, 0xe3, 0x7a, 0x4a, 0xe5, 0xeb, 0x45, 0x9d, 0x04,
0x45, 0xe8, 0x9a, 0x4a, 0x10, 0x02, 0x63, 0x97, 0x35, 0x68, 0x87, 0xc4,
0xe3, 0xaa, 0xbd, 0x1b, 0xe7, 0xa2, 0xd4, 0x52, 0xb0, 0xea, 0x2d, 0x2f,
0x59, 0x62, 0x7e, 0xb4, 0x6b, 0xe0, 0x06, 0x7a, 0xee, 0x98, 0x21, 0x74,
0xec, 0x99, 0x61, 0x55, 0x9b, 0x1d, 0xf9, 0x5d, 0x6f, 0x80, 0x87, 0xdd,
0xf3, 0x7b, 0x5d, 0x98, 0xb6, 0x37, 0x6a, 0x0a, 0x83, 0x5e, 0x36, 0x08,
0x8a, 0xf4, 0xcd, 0x9e, 0x5c, 0x7d, 0xec, 0xf2, 0x4e, 0x5b, 0x4d, 0xac,
0x9a, 0xab, 0xb1, 0x80, 0xc9, 0x58, 0xa0, 0x1d, 0x19, 0x16, 0x0b, 0xd5,
0xea, 0xe7, 0xfc, 0xfd, 0x4e, 0x04, 0x98, 0xa1, 0x8d, 0x79, 0xb0, 0xa7,
0xb5, 0x44, 0xfc, 0x12, 0xd2, 0x04, 0x7d, 0x53, 0x02, 0xfd, 0x70, 0x21,
0xef, 0xfd, 0x64, 0x11, 0x48, 0x03, 0xc4, 0x66, 0x7a, 0x29, 0xc3, 0x83,
0xfc, 0xee, 0xad, 0x9a, 0x4c, 0x97, 0x64, 0x0a, 0x9e, 0x72, 0x42, 0xc1,
0x1f, 0x32, 0x29, 0x80, 0xcf, 0xb9, 0xdc, 0x1d, 0x81, 0x92, 0x13, 0xfe,
0x85, 0x50, 0x53, 0x4a, 0x0a, 0xa2, 0x24, 0x52, 0x26, 0x9a, 0x65, 0x28,
0x13, 0x26, 0xae, 0xf3,
};
const uint8_t kLicenseRequest[] = {
0x0a, 0x88, 0x0b, 0x08, 0x01, 0x12, 0xed, 0x09, 0x0a, 0xb0, 0x02, 0x08,
0x02, 0x12, 0x10, 0x0c, 0x36, 0x48, 0x86, 0x75, 0xa1, 0x84, 0x53, 0x4d,
0xbc, 0x55, 0x96, 0xc9, 0xf4, 0xaf, 0x0a, 0x18, 0x97, 0xe7, 0xdd, 0xf8,
0x05, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
0x00, 0xae, 0xa6, 0xa8, 0xea, 0xdd, 0xac, 0x6f, 0xb4, 0x41, 0x47, 0xcb,
0x18, 0x81, 0xeb, 0xdf, 0x4f, 0x17, 0xf7, 0x17, 0xc0, 0xab, 0x6f, 0x47,
0x50, 0xbf, 0xe4, 0x8c, 0xc9, 0x45, 0x24, 0x5e, 0x1c, 0x2e, 0x86, 0x9f,
0xcb, 0x47, 0x05, 0xf9, 0xfe, 0x91, 0x90, 0xaf, 0xbd, 0x22, 0x14, 0x47,
0xa7, 0x34, 0x39, 0x79, 0x44, 0xe9, 0x92, 0x83, 0x4a, 0x80, 0xa8, 0x2a,
0xe6, 0x9f, 0x2b, 0xb7, 0xda, 0x2a, 0xd2, 0xae, 0x57, 0x5b, 0xfa, 0xb6,
0xdf, 0xca, 0x3e, 0xb1, 0xb8, 0x42, 0x0b, 0xde, 0x46, 0x36, 0xdb, 0x42,
0x33, 0x8b, 0xda, 0x5c, 0x60, 0x44, 0x7c, 0x99, 0xb4, 0x98, 0xb4, 0x1e,
0xd8, 0x25, 0x6d, 0x67, 0x84, 0xc9, 0x67, 0xde, 0x05, 0xe6, 0x06, 0xb5,
0xb5, 0xca, 0xbc, 0xfa, 0xb0, 0xa7, 0x46, 0x29, 0x3f, 0x63, 0x47, 0x9d,
0x70, 0x8d, 0xa2, 0x8d, 0x22, 0xc6, 0xeb, 0x06, 0xd4, 0x5c, 0x3b, 0x62,
0x98, 0xc7, 0xda, 0x16, 0x8f, 0x17, 0x59, 0xd5, 0xcb, 0xd1, 0x5d, 0xe3,
0xe1, 0x07, 0xe6, 0x97, 0x87, 0xf4, 0x22, 0x53, 0xfa, 0xf9, 0xa9, 0xf5,
0xeb, 0xd7, 0x55, 0xdf, 0x32, 0x2d, 0x4e, 0x07, 0x86, 0x25, 0x44, 0x93,
0xd6, 0xf7, 0xc6, 0xf9, 0x78, 0x91, 0x24, 0x1e, 0xd4, 0x6b, 0xe3, 0x4a,
0xff, 0x4a, 0x3a, 0xb9, 0x89, 0x90, 0x61, 0x87, 0xb9, 0x41, 0x45, 0x02,
0xfd, 0xd0, 0xc5, 0x5a, 0x98, 0x41, 0x88, 0xa4, 0xe3, 0xe2, 0xa2, 0x9d,
0x9a, 0x90, 0x3f, 0x44, 0x8e, 0x3a, 0xe1, 0xd1, 0xe9, 0x79, 0x9a, 0xc6,
0xe2, 0x7c, 0x8e, 0x9c, 0x3d, 0xb2, 0xe0, 0x26, 0x5a, 0x46, 0x13, 0xc8,
0x43, 0x9f, 0xf7, 0x51, 0x7e, 0xbb, 0x55, 0x6d, 0xcc, 0x97, 0x38, 0xdb,
0xa4, 0x4b, 0x96, 0x40, 0xe5, 0x2d, 0x8f, 0x43, 0xe3, 0x21, 0x15, 0xda,
0x34, 0x97, 0x7a, 0x9e, 0xbb, 0x02, 0x03, 0x01, 0x00, 0x01, 0x28, 0xe8,
0x3d, 0x48, 0x01, 0x12, 0x80, 0x02, 0xa2, 0x05, 0x30, 0x91, 0xed, 0xc2,
0x17, 0xfd, 0x92, 0x5c, 0x69, 0x1f, 0xb4, 0x82, 0x35, 0x75, 0xcb, 0x48,
0x22, 0x07, 0x54, 0x72, 0x05, 0x86, 0x2e, 0xa1, 0x98, 0xc9, 0x46, 0x07,
0xc9, 0x26, 0x6b, 0x05, 0x56, 0xa2, 0xa6, 0x5b, 0xe3, 0xba, 0x80, 0xd0,
0x01, 0xb3, 0xbd, 0x9f, 0x7b, 0x15, 0xe3, 0xe6, 0xad, 0x85, 0xe6, 0x92,
0x35, 0xec, 0x9b, 0x18, 0x7e, 0x1d, 0x39, 0x93, 0xfb, 0x0b, 0x2c, 0xe8,
0xa0, 0xee, 0x6e, 0x27, 0x8f, 0x08, 0x99, 0xc7, 0xd6, 0x08, 0x1f, 0xe5,
0xd4, 0x9b, 0x30, 0x83, 0x31, 0xf8, 0x49, 0x05, 0x9b, 0xa0, 0x30, 0xaa,
0xc1, 0x61, 0xd4, 0xec, 0x81, 0x28, 0xe1, 0x80, 0x17, 0x09, 0xdc, 0x35,
0xb8, 0xdc, 0xd3, 0x46, 0x7b, 0xa6, 0xad, 0x44, 0x90, 0x17, 0x99, 0x8e,
0x43, 0xd4, 0x35, 0x00, 0x98, 0x6c, 0xe5, 0xc3, 0x2e, 0xad, 0x80, 0xa6,
0x35, 0x3e, 0xed, 0xb2, 0x22, 0xe0, 0xad, 0x8a, 0xd0, 0x1a, 0x5e, 0x27,
0x29, 0xe2, 0xd9, 0x01, 0x2d, 0xf3, 0xe8, 0xb8, 0xa5, 0x6d, 0x8b, 0xfb,
0x1c, 0x8e, 0x77, 0xa1, 0xe0, 0x8e, 0x64, 0x1b, 0xfb, 0x3a, 0x05, 0x2f,
0x96, 0x6f, 0xe7, 0x86, 0xff, 0x75, 0x8f, 0xdf, 0x3d, 0xf4, 0x2d, 0x7a,
0xfc, 0x76, 0x0d, 0xf1, 0xde, 0x60, 0xe7, 0x4b, 0xc1, 0x87, 0xb0, 0x3f,
0xa3, 0xe6, 0x37, 0xf1, 0xf0, 0x0a, 0x9e, 0x99, 0xe2, 0x7f, 0x6a, 0x99,
0xe1, 0xde, 0x2c, 0x23, 0x8d, 0xc6, 0x2d, 0xe8, 0x4d, 0x4a, 0x2b, 0x02,
0xf2, 0xc1, 0xea, 0xae, 0x9d, 0x2f, 0xb3, 0xee, 0x84, 0xe9, 0xb8, 0xe4,
0x7b, 0x62, 0x47, 0x9c, 0xf0, 0xed, 0x80, 0x37, 0xcf, 0x92, 0xc5, 0xae,
0xbb, 0x32, 0x31, 0xb4, 0xd7, 0xc4, 0xce, 0xa3, 0x4e, 0xb6, 0xd6, 0xe9,
0x72, 0x5f, 0xd1, 0xe5, 0xa0, 0xf1, 0xfb, 0x79, 0xb1, 0xa8, 0x1a, 0xb4,
0x05, 0x0a, 0xae, 0x02, 0x08, 0x01, 0x12, 0x10, 0x65, 0x80, 0x2c, 0x9b,
0x62, 0x5e, 0x5a, 0x31, 0x9c, 0x33, 0xdc, 0x1c, 0xb7, 0xc3, 0xc6, 0xd4,
0x18, 0xe3, 0xa5, 0xbd, 0xd0, 0x05, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01,
0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb8, 0x05, 0x02, 0x04, 0x3c, 0x2a,
0x8a, 0x0f, 0xd8, 0xd2, 0x5c, 0x61, 0x3e, 0x1e, 0x3e, 0x3b, 0x5e, 0x34,
0x9f, 0x33, 0x2f, 0x04, 0x51, 0x6a, 0x75, 0x10, 0xd3, 0x80, 0x21, 0xa5,
0x62, 0x9b, 0x9a, 0xa0, 0x27, 0xae, 0xad, 0x3c, 0x75, 0x9b, 0x7a, 0xfe,
0x70, 0xbe, 0xd6, 0x5f, 0x3d, 0xf6, 0x86, 0x0f, 0xf5, 0xeb, 0x60, 0xb9,
0x83, 0xa3, 0xff, 0xa3, 0x3f, 0xde, 0x06, 0xf3, 0xb7, 0x30, 0x14, 0xdf,
0xc8, 0x45, 0xab, 0x37, 0x1c, 0x66, 0x00, 0x56, 0x2e, 0x9d, 0x90, 0x4f,
0x84, 0x2b, 0x8b, 0xa4, 0xa5, 0xd9, 0x20, 0x0f, 0xfa, 0x3e, 0xd4, 0x5d,
0x70, 0x55, 0x20, 0xa5, 0xc3, 0x72, 0xa8, 0x89, 0xf9, 0xe3, 0x14, 0x38,
0x62, 0x34, 0xc6, 0x89, 0x7a, 0xe6, 0x55, 0x85, 0x1f, 0xcd, 0x9a, 0xdb,
0x4e, 0xf9, 0x12, 0x6c, 0x78, 0x38, 0x6e, 0xa9, 0x3b, 0xcb, 0x25, 0xba,
0x3e, 0xc4, 0x75, 0xc5, 0x5c, 0x60, 0x8e, 0x77, 0x1c, 0x76, 0x3a, 0xb0,
0x25, 0x06, 0xf9, 0xb0, 0x72, 0x52, 0xd6, 0xab, 0xf7, 0xea, 0x64, 0xb1,
0xeb, 0xde, 0x7b, 0x95, 0xc6, 0x40, 0x76, 0x90, 0x53, 0x3b, 0xd6, 0x89,
0x0b, 0x92, 0x74, 0xc1, 0x60, 0x66, 0xf7, 0x4f, 0xc4, 0x01, 0xea, 0x35,
0x5f, 0x0a, 0x02, 0x10, 0x68, 0x14, 0xd4, 0x9b, 0xf0, 0xc8, 0x9e, 0x6e,
0x1f, 0x8d, 0xb2, 0xa4, 0x78, 0x41, 0xcd, 0x0d, 0xad, 0x79, 0x32, 0x96,
0xa1, 0x07, 0xc3, 0x62, 0x23, 0x40, 0x4f, 0x2b, 0xf1, 0xfc, 0xa1, 0x6f,
0xd0, 0xa4, 0xb9, 0x82, 0x63, 0x4d, 0xb6, 0x24, 0x07, 0xf8, 0xf1, 0x4a,
0xca, 0xe3, 0xb0, 0x5a, 0x03, 0x8b, 0xd3, 0xe4, 0xbb, 0xba, 0xe4, 0x39,
0x1b, 0xbf, 0xa7, 0xa4, 0x7f, 0xb9, 0xd0, 0x1d, 0xe8, 0x57, 0xea, 0x88,
0xe5, 0xe3, 0x6e, 0xe3, 0x6e, 0x24, 0x58, 0x59, 0xfc, 0x0f, 0x02, 0x03,
0x01, 0x00, 0x01, 0x28, 0xe8, 0x3d, 0x12, 0x80, 0x03, 0x7e, 0x06, 0x58,
0x1a, 0x01, 0x91, 0x84, 0xab, 0x57, 0x2a, 0xfd, 0xca, 0xdd, 0xd0, 0x3f,
0x16, 0x1c, 0xe6, 0x82, 0x00, 0xf8, 0xe6, 0xf8, 0xad, 0x16, 0x19, 0x47,
0x36, 0x0b, 0xc8, 0xd4, 0x9c, 0x0d, 0x68, 0x00, 0x9b, 0x1c, 0x46, 0x44,
0xf9, 0xb3, 0xf3, 0xfb, 0x6d, 0xdf, 0xd9, 0x2e, 0xf9, 0x2d, 0xe6, 0x2d,
0x41, 0xd4, 0x59, 0xd2, 0x9d, 0x81, 0xbf, 0xae, 0xf3, 0x97, 0x0a, 0x3a,
0x39, 0xd2, 0x5b, 0x26, 0x62, 0xec, 0xb0, 0x3b, 0x2d, 0xa7, 0xb6, 0x83,
0x02, 0xfa, 0xa6, 0xdd, 0x98, 0xd9, 0x5a, 0x14, 0x3c, 0xc8, 0xc1, 0xcb,
0x6a, 0xdd, 0xa7, 0x6d, 0x2e, 0xe9, 0xc3, 0x72, 0x3f, 0xaf, 0x95, 0xa2,
0x9c, 0xdc, 0x3e, 0x96, 0x8b, 0x68, 0x21, 0xa9, 0x1c, 0x05, 0x1c, 0xa2,
0x80, 0xa8, 0x66, 0x69, 0x71, 0x0a, 0x1a, 0xd7, 0xa4, 0x4b, 0xf9, 0x21,
0x80, 0x27, 0x46, 0x0d, 0xf6, 0x94, 0xe2, 0xe9, 0x27, 0x03, 0x96, 0xdf,
0x22, 0x19, 0x63, 0xf2, 0x1e, 0xe6, 0xaa, 0x22, 0x0a, 0x5e, 0xe4, 0xa4,
0xd0, 0xfe, 0xb3, 0xd5, 0x3e, 0xb5, 0x73, 0x2f, 0x8f, 0x91, 0xe9, 0xa9,
0x6b, 0x3b, 0x8b, 0xe2, 0x84, 0xc5, 0x13, 0x39, 0xea, 0x28, 0x4d, 0x4d,
0x0e, 0xdd, 0x55, 0xb6, 0xad, 0x56, 0xf7, 0x41, 0x64, 0x20, 0xe0, 0x5e,
0x05, 0x9f, 0x97, 0x34, 0xa9, 0x6b, 0xe2, 0x5a, 0xa4, 0x45, 0x60, 0xdb,
0xa8, 0xc3, 0x87, 0x55, 0xa4, 0x2a, 0x82, 0xbd, 0x7f, 0x88, 0xed, 0xd1,
0x9d, 0xf3, 0x46, 0xa6, 0x67, 0xb3, 0x3b, 0x81, 0x14, 0xc7, 0x6a, 0x88,
0x38, 0xc4, 0x23, 0xd8, 0x24, 0xa5, 0x0b, 0x23, 0x25, 0x1a, 0x08, 0x81,
0x36, 0xd6, 0xe8, 0xf4, 0x75, 0x29, 0x9d, 0x2a, 0xfd, 0x46, 0xce, 0xa5,
0x1b, 0x5c, 0xbd, 0xf7, 0x89, 0xa5, 0x72, 0x12, 0x5c, 0xd2, 0x4f, 0xbb,
0x81, 0x3b, 0x38, 0x7a, 0x10, 0xcd, 0x2a, 0x30, 0xe3, 0x44, 0x76, 0x34,
0xab, 0x34, 0x08, 0xf9, 0x6b, 0x9c, 0xf3, 0xd9, 0x88, 0x96, 0xd4, 0x05,
0xf3, 0xf5, 0x40, 0xd9, 0xc5, 0x79, 0x62, 0x76, 0x0f, 0xcd, 0x17, 0x7c,
0xdd, 0x10, 0x1e, 0xb8, 0xa4, 0x14, 0x8b, 0x9c, 0x29, 0xce, 0xd5, 0xea,
0xd6, 0x45, 0xa9, 0x5b, 0x69, 0x8f, 0x1c, 0xdc, 0x6e, 0x1d, 0xb6, 0x67,
0x8b, 0x85, 0x07, 0x41, 0x86, 0x08, 0x0d, 0x68, 0xd1, 0x3c, 0xd3, 0x7e,
0x07, 0xb1, 0x6d, 0xe3, 0x70, 0xcd, 0x9a, 0xfb, 0x9b, 0x25, 0x56, 0x4a,
0x73, 0xa3, 0x0e, 0x2a, 0xf8, 0x08, 0x5e, 0xa3, 0x7d, 0x31, 0x0c, 0x47,
0x4f, 0x0e, 0x67, 0xac, 0x00, 0xca, 0x99, 0x2a, 0x52, 0x96, 0xfa, 0xed,
0xad, 0x7a, 0xa0, 0x6e, 0xcd, 0x79, 0x0f, 0x1e, 0x3d, 0x42, 0x65, 0x58,
0xfa, 0x98, 0x38, 0x3e, 0x3c, 0xd2, 0xed, 0x48, 0x30, 0x1a, 0x1b, 0x0a,
0x11, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72,
0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x06, 0x78, 0x38, 0x36, 0x2d,
0x36, 0x34, 0x1a, 0x16, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e,
0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x06, 0x47, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x1a, 0x17, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x09, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
0x43, 0x44, 0x4d, 0x1a, 0x16, 0x0a, 0x0d, 0x70, 0x6c, 0x61, 0x74, 0x66,
0x6f, 0x72, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x05, 0x4c, 0x69,
0x6e, 0x75, 0x78, 0x1a, 0x24, 0x0a, 0x14, 0x77, 0x69, 0x64, 0x65, 0x76,
0x69, 0x6e, 0x65, 0x5f, 0x63, 0x64, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x0c, 0x34, 0x2e, 0x31, 0x30, 0x2e, 0x31, 0x36,
0x38, 0x36, 0x2e, 0x39, 0x36, 0x32, 0x08, 0x08, 0x00, 0x10, 0x00, 0x18,
0x01, 0x20, 0x00, 0x12, 0x3f, 0x0a, 0x3d, 0x0a, 0x27, 0x08, 0x01, 0x12,
0x01, 0x30, 0x1a, 0x0d, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65,
0x5f, 0x74, 0x65, 0x73, 0x74, 0x22, 0x0a, 0x32, 0x30, 0x31, 0x35, 0x5f,
0x74, 0x65, 0x61, 0x72, 0x73, 0x2a, 0x05, 0x41, 0x55, 0x44, 0x49, 0x4f,
0x10, 0x01, 0x1a, 0x10, 0x53, 0xbc, 0x88, 0x54, 0x04, 0xfc, 0x85, 0xbd,
0x75, 0xce, 0x15, 0x2a, 0x06, 0xb5, 0x1f, 0xc5, 0x18, 0x01, 0x20, 0xb2,
0x88, 0xc6, 0xfa, 0x05, 0x30, 0x15,
};
const size_t kMessageSize = sizeof(kMessage);
const size_t kLicenseRequestSize = sizeof(kLicenseRequest);
const size_t kSignatureSize = sizeof(kSignature);
const size_t kSessionKeySize = sizeof(kSessionKey);
// The pointer for core message must be non-null, but the length needs to be
// zero.
const uint8_t kCoreMessage[] = {0};
const size_t kCoreMessageSize = 0;
const uint8_t kPlaintext[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const size_t kPlaintextSize = sizeof(kPlaintext);
// Generated with:
// openssl aes-128-cbc -e -in clean -K 78a1dc0646119707e903514d8a00735f -iv
// 00000000000000000000000000000000 | xxd -i
const uint8_t kCiphertext[] = {
0xef, 0x21, 0x57, 0x2d, 0xe1, 0x08, 0xc6, 0x3c,
0x54, 0x7d, 0x3d, 0xde, 0x92, 0x2e, 0x5c, 0xe0,
0x79, 0x52, 0xb5, 0xc1, 0x76, 0x3e, 0xdf, 0x73,
0x5b, 0x05, 0xe8, 0x20, 0xe6, 0xa3, 0x22, 0x49,
0xc7, 0xa5, 0x9f, 0xd5, 0x76, 0x6b, 0x20, 0xd3,
0x21, 0x52, 0xe6, 0xc6, 0x9b, 0x22, 0xb0, 0xb3,
0xf8, 0x0d, 0x58, 0xdd, 0xb2, 0x37, 0x7d, 0x7e,
0xe3, 0xaf, 0xaf, 0x82, 0x7b, 0xb8, 0x69, 0x12,
// Drop the padding at the end of the ciphertext.
// 0x40, 0x03, 0x23, 0x83, 0x2f, 0x65, 0xda, 0x81,
// 0x78, 0x46, 0xa6, 0xa8, 0xa4, 0x2c, 0x86, 0x75,
};
const size_t kCiphertextSize = sizeof(kCiphertext);
// The key associated with this id is: 78a1dc0646119707e903514d8a00735f
const uint8_t kKeyId[] = {
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31,
};
const size_t kKeyIdSize = sizeof(kKeyId);
const uint8_t kIv[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const size_t kIvSize = sizeof(kIv);
} // namespace
class LicenseWhiteboxDecryptGoldenData : public ::testing::Test {
protected:
void TearDown() override { WB_License_Delete(whitebox_); }
WB_License_Whitebox* whitebox_ = nullptr;
};
TEST_F(LicenseWhiteboxDecryptGoldenData, CryptoKeyWithCbcDataInCbcMode) {
ASSERT_EQ(WB_License_Create(kLicenseWhiteboxInitData,
kLicenseWhiteboxInitDataSize, &whitebox_),
WB_RESULT_OK);
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, kCoreMessage, kCoreMessageSize, kMessage,
kMessageSize, kSignature, kSignatureSize, kSessionKey,
kSessionKeySize, kLicenseRequest, kLicenseRequestSize),
WB_RESULT_OK);
size_t plaintext_size = kCiphertextSize;
std::vector<uint8_t> plaintext(plaintext_size);
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, kKeyId,
kKeyIdSize, kCiphertext, kCiphertextSize, kIv,
kIvSize, plaintext.data(), &plaintext_size),
WB_RESULT_OK);
plaintext.resize(plaintext_size);
const std::vector<uint8_t> golden_plaintext(kPlaintext,
kPlaintext + kPlaintextSize);
ASSERT_EQ(golden_plaintext, plaintext);
}
} // namespace widevine

View File

@@ -0,0 +1,855 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include "api/golden_data.h"
#include "api/license_whitebox_test_base.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "crypto_utils/crypto_util.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase {
protected:
void SetUp() override {
LicenseWhiteboxTestBase::SetUp();
// Because we are going to use the same buffer for both tests, make sure it
// will be large enough for either.
masked_text_size_ = std::max(golden_data_.CBCContent().ciphertext.size(),
golden_data_.CTRContent().ciphertext.size());
masked_text_.resize(masked_text_size_);
// We have no idea how big the secret string will be, but it should be safe
// to assume it won't be larger than the plaintext.
secret_string_size_ = masked_text_size_;
secret_string_.resize(secret_string_size_);
golden_data_.MakeKeyIdDifferent(&non_content_key_id_);
golden_data_.MakeKeyIdDifferent(&missing_key_id_);
}
void LoadLicense(const std::vector<uint8_t>& padding) {
TestLicenseBuilder builder;
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
golden_data_.CBCCryptoKey().id,
golden_data_.CBCCryptoKey().content->key);
builder.AddContentKey(golden_data_.CTRCryptoKey().level,
golden_data_.CTRCryptoKey().id,
golden_data_.CTRCryptoKey().content->key);
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
golden_data_.CBCDecodeKey().id,
golden_data_.CBCDecodeKey().content->key);
builder.AddContentKey(golden_data_.CTRDecodeKey().level,
golden_data_.CTRDecodeKey().id,
golden_data_.CTRDecodeKey().content->key);
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
golden_data_.CBCHardwareKey().id,
golden_data_.CBCHardwareKey().content->key);
builder.AddOperatorSessionKey(non_content_key_id_);
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
// We need two special keys for this test, one that will be used for a
// non-content key and one that will never be in the license.
std::vector<uint8_t> non_content_key_id_ = {0, 0, 0};
std::vector<uint8_t> missing_key_id_ = {1, 0, 0};
size_t secret_string_size_;
std::vector<uint8_t> secret_string_;
size_t masked_text_size_;
std::vector<uint8_t> masked_text_;
std::vector<uint8_t> plaintext_;
};
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Returned data is masked, so it should be the correct size but not
// match the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CBCDecodeKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CBCDecodeKey().content->plaintext);
// Now unmask the data.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_EQ(plaintext_, golden_data_.CBCDecodeKey().content->plaintext);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CTR, golden_data_.CTRDecodeKey().id.data(),
golden_data_.CTRDecodeKey().id.size(),
golden_data_.CTRDecodeKey().content->ciphertext.data(),
golden_data_.CTRDecodeKey().content->ciphertext.size(),
golden_data_.CTRDecodeKey().content->iv.data(),
golden_data_.CTRDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Returned data is masked, so it should be the correct size but not
// match the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CTRDecodeKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CTRDecodeKey().content->plaintext);
// Now unmask the data.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CTRDecodeKey().id.data(),
golden_data_.CTRDecodeKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_EQ(plaintext_, golden_data_.CTRDecodeKey().content->plaintext);
}
// We try to decrypt CBC encrypted data in CTR mode. All operations should be
// successful, but the resulting plaintext should not match.
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CTR, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Whatever is returned must not be the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CBCDecodeKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CBCDecodeKey().content->plaintext);
// Now unmask the data. Still should not match.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_NE(masked_text_, golden_data_.CBCDecodeKey().content->plaintext);
}
// We try to decrypt CTR encrypted data in CBC mode. All operations should be
// successful, but the resulting plaintext should not match.
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CTRDecodeKey().id.data(),
golden_data_.CTRDecodeKey().id.size(),
golden_data_.CTRDecodeKey().content->ciphertext.data(),
golden_data_.CTRDecodeKey().content->ciphertext.size(),
golden_data_.CTRDecodeKey().content->iv.data(),
golden_data_.CTRDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Whatever is returned must not be the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CTRDecodeKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CTRDecodeKey().content->plaintext);
// Now unmask the data. Still should not match.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CTRDecodeKey().id.data(),
golden_data_.CTRDecodeKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_NE(masked_text_, golden_data_.CTRDecodeKey().content->plaintext);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Returned data is masked, so it should be the correct size but not
// match the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CBCDecodeKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CBCDecodeKey().content->plaintext);
// Now unmask the data.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_EQ(plaintext_, golden_data_.CBCDecodeKey().content->plaintext);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CTR, golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
golden_data_.CTRDecodeKey().content->ciphertext.data(),
golden_data_.CTRDecodeKey().content->ciphertext.size(),
golden_data_.CTRDecodeKey().content->iv.data(),
golden_data_.CTRDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Returned data is masked, so it should be the correct size but not
// match the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CTRDecodeKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CTRDecodeKey().content->plaintext);
// Now unmask the data.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_EQ(plaintext_, golden_data_.CTRDecodeKey().content->plaintext);
}
// We try to decrypt CBC encrypted data in CTR mode. All operations should be
// successful, but the resulting plaintext should not match.
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CTR, golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Whatever is returned must not be the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CBCDecodeKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CBCDecodeKey().content->plaintext);
// Now unmask the data. Still should not match.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_NE(masked_text_, golden_data_.CBCDecodeKey().content->plaintext);
}
// We try to decrypt CTR encrypted data in CBC mode. All operations should be
// successful, but the resulting plaintext should not match.
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
golden_data_.CTRCryptoKey().content->ciphertext.data(),
golden_data_.CTRCryptoKey().content->ciphertext.size(),
golden_data_.CTRCryptoKey().content->iv.data(),
golden_data_.CTRCryptoKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Whatever is returned must not be the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CTRCryptoKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CTRCryptoKey().content->plaintext);
// Now unmask the data. Still should not match.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_NE(masked_text_, golden_data_.CTRCryptoKey().content->plaintext);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) {
LoadLicense(TestLicenseBuilder::PKSC8Padding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
golden_data_.CBCCryptoKey().content->ciphertext.data(),
golden_data_.CBCCryptoKey().content->ciphertext.size(),
golden_data_.CBCCryptoKey().content->iv.data(),
golden_data_.CBCCryptoKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Returned data is masked, so it should be the correct size but not
// match the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CBCCryptoKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CBCCryptoKey().content->plaintext);
// Now unmask the data.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCCryptoKey().id.data(),
golden_data_.CBCCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_EQ(plaintext_, golden_data_.CBCCryptoKey().content->plaintext);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) {
LoadLicense(TestLicenseBuilder::PKSC8Padding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CTR, golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
golden_data_.CTRCryptoKey().content->ciphertext.data(),
golden_data_.CTRCryptoKey().content->ciphertext.size(),
golden_data_.CTRCryptoKey().content->iv.data(),
golden_data_.CTRCryptoKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Returned data is masked, so it should be the correct size but not
// match the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CTRCryptoKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CTRCryptoKey().content->plaintext);
// Now unmask the data.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CTRCryptoKey().id.data(),
golden_data_.CTRCryptoKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_EQ(plaintext_, golden_data_.CTRCryptoKey().content->plaintext);
}
// Try decrypting two different sets of content to make sure that two
// different keys can be used at the same time.
//
// Since we have two CBC keys, try using the decode key and then the crypto
// key.
TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Returned data is masked, so it should be the correct size but not
// match the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CBCDecodeKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CBCDecodeKey().content->plaintext);
// Now unmask the data.
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_EQ(plaintext_, golden_data_.CBCDecodeKey().content->plaintext);
// Reset our output buffer.
masked_text_.clear();
masked_text_size_ = golden_data_.CTRDecodeKey().content->plaintext.size();
masked_text_.resize(masked_text_size_);
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CTR, golden_data_.CTRDecodeKey().id.data(),
golden_data_.CTRDecodeKey().id.size(),
golden_data_.CTRDecodeKey().content->ciphertext.data(),
golden_data_.CTRDecodeKey().content->ciphertext.size(),
golden_data_.CTRDecodeKey().content->iv.data(),
golden_data_.CTRDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
masked_text_.resize(masked_text_size_);
// Returned data is masked, so it should be the correct size but not
// match the original text.
ASSERT_EQ(masked_text_.size(),
golden_data_.CTRDecodeKey().content->plaintext.size());
ASSERT_NE(masked_text_, golden_data_.CTRDecodeKey().content->plaintext);
// Now unmask the data.
secret_string_.clear();
secret_string_size_ = masked_text_.size();
secret_string_.resize(secret_string_size_);
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR,
golden_data_.CTRDecodeKey().id.data(),
golden_data_.CTRDecodeKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
plaintext_.resize(masked_text_size_);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
plaintext_.data());
ASSERT_EQ(plaintext_, golden_data_.CTRDecodeKey().content->plaintext);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
nullptr, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCipherMode) {
LoadLicense(TestLicenseBuilder::NoPadding());
// In order to trick the compiler into letting us pass an invalid enum value
// to WB__License_MaskedDecrypt(), we need to cast it. If we don't do this,
// the compiler tries to save us.
const WB_CipherMode invalid_mode = static_cast<WB_CipherMode>(0xFF);
ASSERT_EQ(WB_License_MaskedDecrypt(
whitebox_, invalid_mode, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(),
masked_text_.data(), &masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, nullptr,
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(),
masked_text_.data(), &masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
0, golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(), nullptr,
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
// AES CBC requires that the input be block aligned (multiple of 16). CTR does
// not care.
TEST_F(LicenseWhiteboxMaskedDecryptTest,
InvalidParameterForInvalidCBCInputDataSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(), 14,
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
// The white-box (using any cipher mode) should reject input with size zero.
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(), 0,
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(), nullptr,
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
// IV size should be 16. Any number other than 16 should fail.
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(), 9,
masked_text_.data(), &masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), nullptr,
&masked_text_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
nullptr),
WB_RESULT_INVALID_PARAMETER);
}
// For this test, "missing key id" specifically means a key id that was never
// in the license to start with. This is different than "non content key"
// and "dropped content key", as those keys were in the license but ignored.
TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(),
missing_key_id_.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(),
masked_text_.data(), &masked_text_size_),
WB_RESULT_KEY_UNAVAILABLE);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(),
non_content_key_id_.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(),
masked_text_.data(), &masked_text_size_),
WB_RESULT_KEY_UNAVAILABLE);
}
// Under normal circumstances, a hardware key should be dropped. The exception
// to this rule is on ChromeOS with a special license.
TEST_F(LicenseWhiteboxMaskedDecryptTest,
InsufficientSecurityLevelForHardwareContentKey) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCHardwareKey().id.data(),
golden_data_.CBCHardwareKey().id.size(),
golden_data_.CBCHardwareKey().content->ciphertext.data(),
golden_data_.CBCHardwareKey().content->ciphertext.size(),
golden_data_.CBCHardwareKey().content->iv.data(),
golden_data_.CBCHardwareKey().content->iv.size(),
masked_text_.data(), &masked_text_size_),
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL);
}
// Unlike the other tests, we do not call LoadLicense() as the criteria for
// WB_RESULT_INVALID_STATE is that no key can be found and keys are provided
// via a license.
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidState) {
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_INVALID_STATE);
}
TEST_F(LicenseWhiteboxMaskedDecryptTest, BufferTooSmall) {
LoadLicense(TestLicenseBuilder::NoPadding());
// Our ciphertext will be large enough that we should not need to worry about
// using a constant here.
masked_text_size_ = 8;
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_BUFFER_TOO_SMALL);
// We don't use padding so the reported plaintext size should be the same as
// the cipher text size.
ASSERT_EQ(masked_text_size_,
golden_data_.CBCDecodeKey().content->ciphertext.size());
}
// Check that the result of unmasking only a small portion of the data is the
// same as when we unmask the whole buffer.
TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessForSubRangeUnmask) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_MaskedDecrypt(
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
golden_data_.CBCDecodeKey().content->ciphertext.data(),
golden_data_.CBCDecodeKey().content->ciphertext.size(),
golden_data_.CBCDecodeKey().content->iv.data(),
golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(),
&masked_text_size_),
WB_RESULT_OK);
ASSERT_EQ(
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
golden_data_.CBCDecodeKey().id.data(),
golden_data_.CBCDecodeKey().id.size(),
secret_string_.data(), &secret_string_size_),
WB_RESULT_OK);
secret_string_.resize(secret_string_size_);
std::vector<uint8_t> full_unmask(masked_text_size_);
std::vector<uint8_t> partial_unmask(3);
WB_License_Unmask(masked_text_.data(), 0, masked_text_size_,
secret_string_.data(), secret_string_.size(),
full_unmask.data());
WB_License_Unmask(masked_text_.data(), 4, partial_unmask.size(),
secret_string_.data(), secret_string_.size(),
partial_unmask.data());
ASSERT_EQ(full_unmask[4], partial_unmask[0]);
ASSERT_EQ(full_unmask[5], partial_unmask[1]);
ASSERT_EQ(full_unmask[6], partial_unmask[2]);
}
} // namespace widevine

View File

@@ -0,0 +1,138 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <stddef.h>
#include <stdint.h>
#include "api/license_whitebox.h"
#include "api/license_whitebox_benchmark.h"
#include "api/result.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "benchmarking/measurements.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
namespace {
// The white-box implementation is slow, so the build bots keep timing out. To
// work around this, use a fixed number of iterations.
constexpr size_t kIterations = 100;
} // namespace
class LicenseWhiteboxProcessLicenseResponseBenchmark
: public LicenseWhiteboxBenchmark {
protected:
void SetUp() override {
LicenseWhiteboxBenchmark::SetUp();
license_ = CreateLicense();
}
void TearDown() override { WB_License_Delete(whitebox_); }
WB_License_Whitebox* whitebox_;
License license_;
};
TEST_F(LicenseWhiteboxProcessLicenseResponseBenchmark,
CreateAndProcessLicenseResponseAndDelete) {
Timer timer;
Sampler sampler;
for (size_t i = 0; i < kIterations; i++) {
const auto init_data = GetLicenseInitData();
// We can only call ProcessLicenseResponse() once per whitebox instance. So
// we need to create a new instance each time. Collect samples for create +
// process license + delete so that we know the cost of getting a new
// license.
timer.Reset();
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
WB_RESULT_OK);
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), license_.request.data(),
license_.request.size()),
WB_RESULT_OK);
WB_License_Delete(whitebox_);
sampler.Push(timer.Get());
whitebox_ = nullptr;
}
PrettyPrint("License White-box Create + Process License + Delete Duration",
sampler, license_.message.size());
}
TEST_F(LicenseWhiteboxProcessLicenseResponseBenchmark,
CreateAndProcessLicenseResponse) {
Timer timer;
Sampler sampler;
for (size_t i = 0; i < kIterations; i++) {
const auto init_data = GetLicenseInitData();
// We can only call ProcessLicenseResponse() once per whitebox instance. So
// we need to create a new instance each time. Collect samples for create +
// process license so that we know the true start-up cost.
timer.Reset();
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
WB_RESULT_OK);
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), license_.request.data(),
license_.request.size()),
WB_RESULT_OK);
sampler.Push(timer.Get());
WB_License_Delete(whitebox_);
whitebox_ = nullptr;
}
PrettyPrint("License White-box Create + Process License Duration", sampler,
license_.message.size());
}
TEST_F(LicenseWhiteboxProcessLicenseResponseBenchmark, ProcessLicenseResponse) {
Timer timer;
Sampler sampler;
for (size_t i = 0; i < kIterations; i++) {
// We can only call ProcessLicenseResponse() once per whitebox instance. So
// we need to create a new instance each time. Do this before we reset the
// timer so that we are not counting it in the execution time.
const auto init_data = GetLicenseInitData();
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
WB_RESULT_OK);
timer.Reset();
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), license_.request.data(),
license_.request.size()),
WB_RESULT_OK);
sampler.Push(timer.Get());
WB_License_Delete(whitebox_);
whitebox_ = nullptr;
}
PrettyPrint("License White-box Process License Duration", sampler,
license_.message.size());
}
} // namespace widevine

View File

@@ -0,0 +1,153 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <vector>
#include "api/license_whitebox_test_base.h"
#include "api/test_license_builder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest
: public LicenseWhiteboxTestBase {
protected:
void UseLicenseWithoutSigningKey(bool use_odk) {
TestLicenseBuilder builder;
builder.AddStubbedContentKey();
builder.SetUseODK(use_odk);
builder.Build(*public_key_, &license_);
}
void UseLicenseWithSigningKey(const std::vector<uint8_t>& padding,
bool use_odk) {
TestLicenseBuilder builder;
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), padding);
builder.AddStubbedContentKey();
builder.SetUseODK(use_odk);
builder.Build(*public_key_, &license_);
}
License license_;
};
// Success tests below test with and without the ODK.
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
SuccessWithoutOdkAndWithoutSigningKey) {
UseLicenseWithoutSigningKey(/* use_odk = */ false);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
SuccessWithOdkAndWithoutSigningKey) {
UseLicenseWithoutSigningKey(/* use_odk = */ true);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
SuccessWithoutOdkAndWithSigningKeyNoPadding) {
UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding(),
/* use_odk = */ false);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
SuccessWithOdkAndWithSigningKeyNoPadding) {
UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding(),
/* use_odk = */ true);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
SuccessWithoutOdkAndWithSigningKeyPKSC8Padding) {
UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding(),
/* use_odk = */ false);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
SuccessWithOdkAndWithSigningKeyPKSC8Padding) {
UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding(),
/* use_odk = */ true);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
InvalidParameterForNullCoreMessage) {
UseLicenseWithoutSigningKey(/* use_odk = */ true);
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, nullptr, license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
InvalidSignatureWithZeroCoreMessageSize) {
UseLicenseWithoutSigningKey(/* use_odk = */ true);
// |core_message_size| = 0 means the core message is not provided, so
// no parameter check will be done. However, since the license was created
// with one, signature checking will fail as there is no core message
// to be included when checking the signature.
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), 0,
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_SIGNATURE);
}
} // namespace widevine

View File

@@ -0,0 +1,357 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <string>
#include <vector>
#include "api/golden_data.h"
#include "api/license_whitebox_test_base.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "crypto_utils/crypto_util.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxProcessLicenseResponseTest
: public LicenseWhiteboxTestBase {
protected:
// No content keys. No signing keys.
void UseLicenseWithNoKeys() {
TestLicenseBuilder builder;
builder.Build(*public_key_, &license_);
}
void UseLicenseWithoutSigningKey() {
TestLicenseBuilder builder;
builder.AddStubbedContentKey();
builder.Build(*public_key_, &license_);
}
void UseLicenseWithSigningKey(const std::vector<uint8_t>& padding) {
TestLicenseBuilder builder;
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), padding);
builder.AddStubbedContentKey();
builder.Build(*public_key_, &license_);
}
License license_;
};
TEST_F(LicenseWhiteboxProcessLicenseResponseTest, SuccessWithoutSigningKey) {
UseLicenseWithoutSigningKey();
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
SuccessWithSigningKeyNoPadding) {
UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
SuccessWithSigningKeyPKSC8Padding) {
UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding());
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
InvalidParameterWithMultipleSigningKeys) {
TestLicenseBuilder builder;
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(),
TestLicenseBuilder::PKSC8Padding());
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(),
TestLicenseBuilder::PKSC8Padding());
builder.AddStubbedContentKey();
builder.Build(*public_key_, &license_);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
InvalidParameterWithNoKeys) {
UseLicenseWithNoKeys();
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
class LicenseWhiteboxProcessLicenseResponseErrorTest
: public LicenseWhiteboxProcessLicenseResponseTest {
protected:
void SetUp() override {
LicenseWhiteboxProcessLicenseResponseTest::SetUp();
// For these tests, we don't care what license we use, it just needs to be
// a valid license.
UseLicenseWithoutSigningKey();
}
};
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidSignatureForModifedMessage) {
Modify(&license_.message);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_SIGNATURE);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidSignatureForModifedSignature) {
Modify(&license_.signature);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_SIGNATURE);
}
// The license request is used to derive the signing key. If the request was
// modified, then the wrong signing key should be generated.
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidSignatureForModifedLicenseRequest) {
Modify(&license_.request);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_SIGNATURE);
}
// If the session key is modified, unwrapping it will fail. Therefore, we will
// know that the parameter is invalid (compared to a modified license request).
// However, it was found that allowing the error to propagate was a better
// implementation solution, so we allow "invalid signature" to be returned.
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidSignatureForModifedSessionKey) {
Modify(&license_.session_key);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_SIGNATURE);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullWhitebox) {
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
nullptr, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullMessage) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), nullptr, license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForZeroMessageSize) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(), 0,
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullSignature) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), nullptr, license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForInvalidSignatureSize) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(), 5,
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullSessionKey) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), nullptr, license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForInvalidSessionKeySize) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(), 5,
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullLicenseRequest) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), nullptr, license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForZeroLienseRequestSize) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), license_.request.data(), 0),
WB_RESULT_INVALID_PARAMETER);
}
class LicenseWhiteboxMultiLicenseTest
: public LicenseWhiteboxProcessLicenseResponseTest {
protected:
void SetUp() override {
LicenseWhiteboxProcessLicenseResponseTest::SetUp();
// For these tests, we don't care what license we use, it just needs to be
// a valid license.
UseLicenseWithoutSigningKey();
}
};
// A whitebox can only process a license once. If it has loaded a license
// (successfully) it should reject later calls with WB_RESULT_INVALID_STATE.
TEST_F(LicenseWhiteboxMultiLicenseTest, InvalidState) {
// Load the first license. This one is expected to succeed as the whitebox has
// not loaded a license yet.
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
// Attempt to load the same license again. This should fail as it already has
// a license (even though it is the same license).
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_STATE);
}
// Even though a whitebox can only load a license once, if it fails to load a
// license, it should still be able to try again.
TEST_F(LicenseWhiteboxMultiLicenseTest, SuccessAfterFailure) {
// Force this one to fail my changing the request, this will cause an error
// in key derivation which is a later step of license parsing.
std::vector<uint8_t> bad_request = license_.request;
Modify(&bad_request);
ASSERT_NE(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
bad_request.data(), bad_request.size()),
WB_RESULT_OK);
// Attempt to load the license again, but use the correct (unmodified)
// request.
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
} // namespace widevine

View File

@@ -0,0 +1,93 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "api/license_whitebox.h"
#include "api/license_whitebox_benchmark.h"
#include "api/result.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "benchmarking/data_source.h"
#include "benchmarking/measurements.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
namespace {
constexpr size_t kMessageSize = 4 * 1024;
constexpr size_t kSignatureSize = 256;
constexpr size_t kIterations = 100;
} // namespace
class LicenseWhiteboxSignBenchmark : public LicenseWhiteboxBenchmark {
protected:
void SetUp() override {
LicenseWhiteboxBenchmark::SetUp();
message_ = Data().Get(kMessageSize);
signature_.resize(kSignatureSize);
const auto init_data = GetLicenseInitData();
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
WB_RESULT_OK);
const auto license = CreateLicense();
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
void TearDown() override { WB_License_Delete(whitebox_); }
WB_License_Whitebox* whitebox_;
std::vector<uint8_t> message_;
std::vector<uint8_t> signature_;
};
TEST_F(LicenseWhiteboxSignBenchmark, SignLicenseRequest) {
Timer timer;
Sampler sampler;
for (size_t i = 0; i < kIterations; i++) {
size_t signature_size = signature_.size();
timer.Reset();
ASSERT_EQ(WB_RESULT_OK, WB_License_SignLicenseRequest(
whitebox_, message_.data(), message_.size(),
signature_.data(), &signature_size));
sampler.Push(timer.Get());
}
PrettyPrint("License White-box Sign License Request Duration", sampler,
message_.size());
}
TEST_F(LicenseWhiteboxSignBenchmark, SignRenewalRequest) {
Timer timer;
Sampler sampler;
for (size_t i = 0; i < kIterations; i++) {
size_t signature_size = signature_.size();
timer.Reset();
ASSERT_EQ(WB_RESULT_OK, WB_License_SignRenewalRequest(
whitebox_, message_.data(), message_.size(),
signature_.data(), &signature_size));
sampler.Push(timer.Get());
}
PrettyPrint("License White-box Sign Renewal Request Duration", sampler,
message_.size());
}
} // namespace widevine

View File

@@ -0,0 +1,128 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <memory>
#include <string>
#include <vector>
#include "api/license_whitebox_test_base.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxSignLicenseRequestTest : public LicenseWhiteboxTestBase {
protected:
void SetUp() override {
LicenseWhiteboxTestBase::SetUp();
TestLicenseBuilder builder;
builder.Build(*public_key_, &license_);
// We must make the default size large to hold the signature returned by
// WB_License_SignLicenseRequest().
signature_size_ = 256;
signature_.resize(signature_size_);
}
std::vector<uint8_t> invalid_license_request_ = {
0x1e, 0x70, 0xbd, 0xeb, 0x24, 0xf2, 0x9d, 0x05, 0xc5, 0xb5,
0xf4, 0xca, 0xe6, 0x1d, 0x01, 0x97, 0x29, 0xf4, 0xe0, 0x7c,
0xfd, 0xcc, 0x97, 0x8d, 0xc2, 0xbb, 0x2d, 0x9b, 0x6b, 0x45,
0x06, 0xbd, 0x2c, 0x66, 0x10, 0x42, 0x73, 0x8d, 0x88, 0x9b,
0x18, 0xcc, 0xcb, 0x7e, 0x43, 0x23, 0x06, 0xe9, 0x8f, 0x8f,
};
License license_;
// These will be the output from each test case.
size_t signature_size_;
std::vector<uint8_t> signature_;
};
TEST_F(LicenseWhiteboxSignLicenseRequestTest, SuccessForInvalidLicenseRequest) {
ASSERT_EQ(
WB_License_SignLicenseRequest(whitebox_, invalid_license_request_.data(),
invalid_license_request_.size(),
signature_.data(), &signature_size_),
WB_RESULT_OK);
signature_.resize(signature_size_);
ASSERT_TRUE(public_key_->VerifySignature(
std::string(invalid_license_request_.begin(),
invalid_license_request_.end()),
std::string(signature_.begin(), signature_.end())));
}
TEST_F(LicenseWhiteboxSignLicenseRequestTest, SuccessForValidLicenseRequest) {
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, license_.request.data(),
license_.request.size(),
signature_.data(), &signature_size_),
WB_RESULT_OK);
signature_.resize(signature_size_);
ASSERT_TRUE(public_key_->VerifySignature(
std::string(license_.request.begin(), license_.request.end()),
std::string(signature_.begin(), signature_.end())));
}
TEST_F(LicenseWhiteboxSignLicenseRequestTest, InvalidParameterForNullWhitebox) {
ASSERT_EQ(WB_License_SignLicenseRequest(nullptr, license_.request.data(),
license_.request.size(),
signature_.data(), &signature_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignLicenseRequestTest,
InvalidParameterForNullLicenseRequest) {
ASSERT_EQ(
WB_License_SignLicenseRequest(whitebox_, nullptr, license_.request.size(),
signature_.data(), &signature_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignLicenseRequestTest,
InvalidParameterForZeroLicenseRequestSize) {
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, license_.request.data(), 0,
signature_.data(), &signature_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignLicenseRequestTest,
InvalidParameterForNullSignature) {
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, license_.request.data(),
license_.request.size(), nullptr,
&signature_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignLicenseRequestTest,
InvalidParameterForNullSignatureSize) {
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, license_.request.data(),
license_.request.size(),
signature_.data(), nullptr),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignLicenseRequestTest, BufferTooSmall) {
// We could test zero, but zero is just a subset of "smaller" and we want to
// make sure that they zero is not the only check. So use something larger
// than zero.
signature_size_ = 1;
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, license_.request.data(),
license_.request.size(),
signature_.data(), &signature_size_),
WB_RESULT_BUFFER_TOO_SMALL);
// When WB_RESULT_BUFFER_TOO_SMALL is returned, the required buffer size
// should be returned via |signature_size|. Since we don't know what it is, we
// must rely on it being larger than the original "too small" size.
ASSERT_GT(signature_size_, 1u);
}
} // namespace widevine

View File

@@ -0,0 +1,206 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <memory>
#include <string>
#include <vector>
#include "api/license_whitebox_test_base.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "crypto_utils/crypto_util.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase {
protected:
void SetUp() override {
LicenseWhiteboxTestBase::SetUp();
// We don't know the actual signature size, so make the buffer large enough
// that it should accomidate any change in signature size.
signature_size_ = 256;
signature_.resize(signature_size_);
}
void LoadLicense(const std::vector<uint8_t>& padding) {
TestLicenseBuilder builder;
builder.AddSigningKey(signing_key_, padding);
// Add a throw away key. We just need a key in the license since a license
// should always have a content key.
builder.AddStubbedContentKey();
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
// Get the expected signature for |message|. By returning the message, this
// will allow us to assert in the test, giving us better output in the case of
// a failure.
std::vector<uint8_t> GetSignature(const std::vector<uint8_t>& message) {
// The client key is the second half of the signing key.
std::string key_str(signing_key_.begin(), signing_key_.end());
key_str.erase(0, crypto_util::kSigningKeySizeBytes);
const std::string signature = crypto_util::CreateSignatureHmacSha256(
key_str, std::string(message.begin(), message.end()));
return std::vector<uint8_t>(signature.begin(), signature.end());
}
const std::string session_key_ = "0123456789ABCDEF";
const std::vector<uint8_t> signing_key_ =
TestLicenseBuilder::DefaultSigningKey();
size_t signature_size_;
std::vector<uint8_t> signature_;
const std::vector<uint8_t> garbage_request_ = {
0x1e, 0x70, 0xbd, 0xeb, 0x24, 0xf2, 0x9d, 0x05, 0xc5, 0xb5,
0xf4, 0xca, 0xe6, 0x1d, 0x01, 0x97, 0x29, 0xf4, 0xe0, 0x7c,
0xfd, 0xcc, 0x97, 0x8d, 0xc2, 0xbb, 0x2d, 0x9b, 0x6b, 0x45,
0x06, 0xbd, 0x2c, 0x66, 0x10, 0x42, 0x73, 0x8d, 0x88, 0x9b,
0x18, 0xcc, 0xcb, 0x7e, 0x43, 0x23, 0x06, 0xe9, 0x8f, 0x8f,
};
};
TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithInvalidRequest) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
garbage_request_.size(),
signature_.data(), &signature_size_),
WB_RESULT_OK);
signature_.resize(signature_size_);
ASSERT_EQ(signature_, GetSignature(garbage_request_));
}
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
SuccessWithSigningKeyPKSC8Padding) {
LoadLicense(TestLicenseBuilder::PKSC8Padding());
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
garbage_request_.size(),
signature_.data(), &signature_size_),
WB_RESULT_OK);
signature_.resize(signature_size_);
ASSERT_EQ(signature_, GetSignature(garbage_request_));
}
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_SignRenewalRequest(nullptr, garbage_request_.data(),
garbage_request_.size(),
signature_.data(), &signature_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_SignRenewalRequest(whitebox_, nullptr, garbage_request_.size(),
signature_.data(), &signature_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
InvalidParameterForZeroMessageSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), 0,
signature_.data(), &signature_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
InvalidParameterForNullSignature) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
garbage_request_.size(), nullptr,
&signature_size_),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
InvalidParameterForNullSignatureSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
garbage_request_.size(),
signature_.data(), nullptr),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxSignRenewalRequestTest, BufferTooSmall) {
LoadLicense(TestLicenseBuilder::NoPadding());
// We need the signature to be too small. While it would be possible to use
// zero, using a non-zero value ensures that we are not combining "empty" and
// "too small".
signature_size_ = 1;
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
garbage_request_.size(),
signature_.data(), &signature_size_),
WB_RESULT_BUFFER_TOO_SMALL);
// Since the API does not limit the signature size, we can't specify the
// actual expected size, however, it should at least be greater than our "too
// small" size.
ASSERT_GT(signature_size_, 1u);
}
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidStateForNoLicense) {
// Unlike the other tests, we do not call LoadLicense() because we need to
// have no license loaded in order to have no renewal key, which is the
// criteria WB_RESULT_INVALID_STATE.
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
garbage_request_.size(),
signature_.data(), &signature_size_),
WB_RESULT_INVALID_STATE);
}
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidStateForNoSigningKey) {
// Make a license with no signing key but has a content key. Every license
// must have a content key.
TestLicenseBuilder builder;
builder.AddStubbedContentKey();
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
garbage_request_.size(),
signature_.data(), &signature_size_),
WB_RESULT_INVALID_STATE);
}
} // namespace widevine

View File

@@ -0,0 +1,36 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox_test_base.h"
#include <string>
#include "api/test_data.h"
namespace widevine {
void LicenseWhiteboxTestBase::SetUp() {
const std::vector<uint8_t> init_data = GetLicenseInitData();
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
WB_RESULT_OK);
const auto public_key_data = GetMatchingLicensePublicKey();
public_key_.reset(RsaPublicKey::Create(
std::string(public_key_data.begin(), public_key_data.end())));
ASSERT_TRUE(public_key_);
}
void LicenseWhiteboxTestBase::TearDown() {
WB_License_Delete(whitebox_);
}
void LicenseWhiteboxTestBase::Modify(std::vector<uint8_t>* data) const {
ASSERT_TRUE(data);
ASSERT_GT(data->size(), 0u);
// Bitwise-not the first byte so that we are guaranteed to have at least one
// byte different from the original data.
data->data()[0] = ~data->data()[0];
}
} // namespace widevine

View File

@@ -0,0 +1,34 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_API_LICENSE_WHITEBOX_TEST_BASE_H_
#define WHITEBOX_API_LICENSE_WHITEBOX_TEST_BASE_H_
#include <memory>
#include "api/golden_data.h"
#include "api/license_whitebox.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxTestBase : public ::testing::Test {
protected:
void SetUp() override;
void TearDown() override;
// Modify a buffer so that it won't be the exact same as it was before. This
// to make it easier to invalidate signatures.
void Modify(std::vector<uint8_t>* data) const;
std::unique_ptr<RsaPublicKey> public_key_;
GoldenData golden_data_;
WB_License_Whitebox* whitebox_;
};
} // namespace widevine
#endif // WHITEBOX_API_LICENSE_WHITEBOX_TEST_BASE_H_

View File

@@ -0,0 +1,71 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "api/license_whitebox.h"
#include "api/license_whitebox_benchmark.h"
#include "api/result.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "benchmarking/data_source.h"
#include "benchmarking/measurements.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
namespace {
constexpr size_t kMessageSize = 4 * 1024; // 4 KB license response.
}
class LicenseWhiteboxVerifyBenchmark : public LicenseWhiteboxBenchmark {
protected:
void SetUp() override {
LicenseWhiteboxBenchmark::SetUp();
message_ = Data().Get(kMessageSize);
signature_ = SignAsServer(message_);
const auto init_data = GetLicenseInitData();
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
WB_RESULT_OK);
const auto license = CreateLicense();
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
void TearDown() override { WB_License_Delete(whitebox_); }
WB_License_Whitebox* whitebox_;
std::vector<uint8_t> message_;
std::vector<uint8_t> signature_;
};
TEST_F(LicenseWhiteboxVerifyBenchmark, VerifyRenewalResponse) {
constexpr size_t kIterations = 100;
Timer timer;
Sampler sampler;
for (size_t i = 0; i < kIterations; i++) {
timer.Reset();
ASSERT_EQ(WB_RESULT_OK, WB_License_VerifyRenewalResponse(
whitebox_, message_.data(), message_.size(),
signature_.data(), signature_.size()));
sampler.Push(timer.Get());
}
PrettyPrint("License White-box Verify Renewal Response Duration", sampler,
message_.size());
}
} // namespace widevine

View File

@@ -0,0 +1,232 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/license_whitebox.h"
#include <memory>
#include <string>
#include <vector>
#include "api/license_whitebox_test_base.h"
#include "api/test_data.h"
#include "api/test_license_builder.h"
#include "crypto_utils/crypto_util.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxVerifyRenewalResponseTest
: public LicenseWhiteboxTestBase {
protected:
void SetUp() override {
LicenseWhiteboxTestBase::SetUp();
garbage_renewal_signature_ = Sign(garbage_renewal_message_);
}
void LoadLicense(const std::vector<uint8_t>& padding) {
const auto signing_key = TestLicenseBuilder::DefaultSigningKey();
// We need a license so that we can always have a valid signature for our
// message(s), but don't load the license as some test will need no
// license loaded.
TestLicenseBuilder builder;
builder.AddSigningKey(signing_key, padding);
builder.AddStubbedContentKey();
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
std::vector<uint8_t> Sign(const std::vector<uint8_t>& message) {
const auto key = TestLicenseBuilder::DefaultSigningKey();
// The server signing key is the first half of the signing key.
std::string server_key = std::string(
key.begin(), key.begin() + crypto_util::kSigningKeySizeBytes);
const auto signature = crypto_util::CreateSignatureHmacSha256(
server_key, std::string(message.begin(), message.end()));
return std::vector<uint8_t>(signature.begin(), signature.end());
}
// Allow this to be mutable so that a test can corrupt it. This data is random
// and has no meaning.
std::vector<uint8_t> garbage_renewal_message_ = {
0xf1, 0x5c, 0xf1, 0x92, 0x73, 0x0c, 0xf9, 0x5d, 0x2b, 0x1e, 0x3f, 0x51,
0xb2, 0x75, 0xa1, 0xb3, 0xd3, 0xa8, 0x16, 0x83, 0x08, 0xf1, 0xe2, 0x47,
0x7b, 0x80, 0x37, 0xed, 0xf8, 0x8b, 0x1d, 0x79, 0x7f, 0xb0, 0xa1, 0xde,
0xcd, 0xba, 0xd4, 0x8f, 0xb7, 0x3c, 0x1a, 0x3f, 0x3e, 0x3a, 0xb4, 0xea,
0xd8, 0xd7, 0xa4, 0x65, 0xa1, 0x40, 0x87, 0xf6, 0xaa, 0xf4, 0xb1, 0x24,
0x17, 0xed, 0xf4, 0xca, 0x18, 0x51, 0x4a, 0x54, 0x3c, 0x73, 0xca, 0x45,
0x3e, 0xef, 0x39, 0x49, 0x65, 0xdd, 0x62, 0x11, 0x99, 0x13, 0x40, 0x67,
0x7f, 0xfb, 0x07, 0x09, 0x1e, 0xfe, 0x0e, 0xdc, 0xda, 0x0a, 0x85, 0x91,
0x15, 0x40, 0xa8, 0x7a, 0x0e, 0x76, 0xf6, 0xbe, 0x94, 0x2c, 0x70, 0xe9,
0x07, 0xea, 0xf8, 0x7a, 0xc3, 0x48, 0xe1, 0xcf, 0xf4, 0x7b, 0xd6, 0x27,
0xd7, 0x30, 0x6f, 0x18, 0xb3, 0x2d, 0x6a, 0x23,
};
// Allow this to be mutable so that we can initialize it in SetUp() but also
// so a test can corrupt it.
std::vector<uint8_t> garbage_renewal_signature_;
};
// TODO: Implement a test that uses a real serialized response. Once we have a
// real serialized response, we should update all the tests - except the
// SuccessForGarbageMessage - to use the real serialized response.
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessForGarbageMessage) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
SuccessWithSigningKeyPKSC8Padding) {
LoadLicense(TestLicenseBuilder::PKSC8Padding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForNullWhitebox) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(
WB_License_VerifyRenewalResponse(nullptr, garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForNullMessage) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, nullptr,
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForZeroMessageSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(), 0,
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForNullSignature) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(
whitebox_, garbage_renewal_message_.data(),
garbage_renewal_message_.size(), nullptr,
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForInvalidSignatureSize) {
LoadLicense(TestLicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(
whitebox_, garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(), 14),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidSignatureForModifiedMessage) {
LoadLicense(TestLicenseBuilder::NoPadding());
Modify(&garbage_renewal_message_);
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_SIGNATURE);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidSignatureForModifiedSignature) {
LoadLicense(TestLicenseBuilder::NoPadding());
Modify(&garbage_renewal_signature_);
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_SIGNATURE);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidStateForNoLicense) {
// Unlike the other tests, we do not call LoadLicense() as the criteria for
// WB_RESULT_INVALID_STATE is that no key can be found and keys are provided
// via a license.
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_STATE);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidStateForNoSigningKey) {
// Create a license with no signing key and one content key (every license
// must have a content key).
widevine::TestLicenseBuilder builder;
builder.AddStubbedContentKey();
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, license.core_message.data(), license.core_message.size(),
license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_STATE);
}
} // namespace widevine

48
whitebox/api/result.h Normal file
View File

@@ -0,0 +1,48 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_API_RESULT_H_
#define WHITEBOX_API_RESULT_H_
#ifdef __cplusplus
extern "C" {
#endif
// Error codes returned by all the white-box API functions. See the function
// returning the result for additional details.
typedef enum {
// The operation completed successfully.
WB_RESULT_OK = 0,
// The operation failed because memory could not be allocated.
WB_RESULT_OUT_OF_MEMORY = 1,
// The state of white-box does not allow execution of this operation. For
// example, another API function may need to be called first.
WB_RESULT_INVALID_STATE = 2,
// The output buffer length is too small to contain the operation's output.
WB_RESULT_BUFFER_TOO_SMALL = 3,
// There is an issue with one or more parameters.
WB_RESULT_INVALID_PARAMETER = 4,
// The message and signature did not pass signature verification.
WB_RESULT_INVALID_SIGNATURE = 5,
// The request key is not available for the operations. For example, no key
// exists for the given key id.
WB_RESULT_KEY_UNAVAILABLE = 6,
// The requested key's security level is not sufficient for the operation.
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL = 7,
// The input data failed to be verified. This may happen if the data was
// corrupted or tampered.
WB_RESULT_DATA_VERIFICATION_ERROR = 8,
} WB_Result;
#ifdef __cplusplus
}
#endif
#endif // WHITEBOX_API_WB_RESULT_H_

27
whitebox/api/test_data.h Normal file
View File

@@ -0,0 +1,27 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_API_TEST_DATA_H_
#define WHITEBOX_API_TEST_DATA_H_
#include <stdint.h>
#include <vector>
// Returns init data that the aead white-box will accept.
std::vector<uint8_t> GetValidAeadInitData();
// Returns init data that the aead white-box will reject.
std::vector<uint8_t> GetInvalidAeadInitData();
// Returns valid init_data needed by the license whitebox tests when calling
// WB_License_Create().
std::vector<uint8_t> GetLicenseInitData();
// Returns the matching public key for the data returned by
// GetLicenseInitData(). The format is a DER encoded PKCS#1 RSAPublicKey.
std::vector<uint8_t> GetMatchingLicensePublicKey();
// Returns invalid non-empty init_data that should be rejected by
// WB_License_Create().
std::vector<uint8_t> GetInvalidLicenseInitData();
#endif // WHITEBOX_API_TEST_DATA_H_

View File

@@ -0,0 +1,374 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "api/test_license_builder.h"
#include <ctime>
#include "base/check.h"
#include "base/check_op.h"
#include "cdm/keys/certs.h"
#include "crypto_utils/aes_cbc_encryptor.h"
#include "crypto_utils/crypto_util.h"
#include "crypto_utils/sha_util.h"
#include "oemcrypto/odk/include/core_message_deserialize.h"
#include "oemcrypto/odk/include/core_message_serialize_proto.h"
#include "oemcrypto/odk/include/odk.h"
#include "oemcrypto/odk/include/odk_structs.h"
namespace widevine {
namespace {
void InitializeRequest(video_widevine::LicenseRequest* request) {
request->set_request_time(std::time(nullptr)); // Use time=now.
auto* client_id = request->mutable_client_id();
auto* capabilities = client_id->mutable_client_capabilities();
capabilities->set_video_resolution_constraints(true);
capabilities->set_client_token(true);
capabilities->set_session_token(false);
capabilities->set_max_hdcp_version(
video_widevine::ClientIdentification::ClientCapabilities::HDCP_V1);
auto* client_info = client_id->add_client_info();
client_info->set_name("architecture_name");
client_info->set_value("x86-64");
client_info = client_id->add_client_info();
client_info->set_name("company_name");
client_info->set_value("Google");
client_info = client_id->add_client_info();
client_info->set_name("model_name");
client_info->set_value("ChromeCDM");
client_info = client_id->add_client_info();
client_info->set_name("platform_name");
client_info->set_value("Windows");
client_info = client_id->add_client_info();
client_info->set_name("widevine_cdm_version");
client_info->set_value("4.10.1686.29");
client_id->set_type(
video_widevine::ClientIdentification_TokenType_DRM_DEVICE_CERTIFICATE);
client_id->set_token(wvcdm::kRsaDrmCertificate,
wvcdm::kRsaDrmCertificateSize);
auto* content_id = request->mutable_content_id();
auto* webm_key_id = content_id->mutable_webm_key_id();
webm_key_id->set_license_type(video_widevine::STREAMING);
webm_key_id->set_request_id("REQUEST_ID");
webm_key_id->set_header("01234567890123456");
request->set_protocol_version(video_widevine::VERSION_2_1);
request->set_type(video_widevine::LicenseRequest::NEW);
}
void InitializeResponse(const video_widevine::LicenseRequest& request,
video_widevine::License* response) {
auto* id = response->mutable_id();
id->set_request_id("REQUEST_ID");
id->set_session_id("SESSION_ID");
id->set_type(video_widevine::STREAMING);
id->set_version(0);
auto* policy = response->mutable_policy();
policy->set_can_play(true);
policy->set_can_persist(false);
policy->set_can_renew(true);
policy->set_license_duration_seconds(600);
policy->set_renewal_delay_seconds(30);
policy->set_renewal_retry_interval_seconds(10);
policy->set_renew_with_usage(false);
response->set_license_start_time(request.request_time());
response->set_remote_attestation_verified(false);
response->set_platform_verification_status(
video_widevine::PlatformVerificationStatus::PLATFORM_UNVERIFIED);
}
std::string EncryptKey(const std::string& key,
const std::string& iv,
const std::vector<uint8_t>& plaintext) {
AesCbcEncryptor encryptor;
encryptor.SetKey(reinterpret_cast<const uint8_t*>(key.data()), key.size());
std::vector<uint8_t> ciphertext(plaintext.size());
CHECK(encryptor.Encrypt(reinterpret_cast<const uint8_t*>(iv.data()),
iv.size(), plaintext.data(), plaintext.size(),
ciphertext.data()));
return std::string(ciphertext.begin(), ciphertext.end());
}
std::string DeriveIV(const std::vector<uint8_t>& context) {
const std::string context_str(context.begin(), context.end());
return crypto_util::DeriveIv(context_str);
}
void UpdateKeyControlBlock(
video_widevine::License_KeyContainer_SecurityLevel level,
video_widevine::License_KeyContainer_KeyControl* key_control) {
// The key control block is an 128 bit structure containing the following
// fields. The fields are defined to be in big-endian byte order.
//
// Bytes 0..3: Verification.
// Constant bytes “kctl”, “kc09”, “kc10”, “kc11”, ... “kc15”.
// Bytes 4..7: Obsolete.
// Bytes 8..11: Nonce.
// Bytes 12..15: Control Bits
// Bits 27..26: Security_Level (Only for L3 white-box implementations)
// 0 = SW_SECURE_CRYPTO
// 1 = SW_SECURE_DECODE
// 2 = HW_SECURE_CRYPTO
// 3 = HW_SECURE_DECODE or HW_SECURE_ALL
std::vector<uint8_t> key_control_block = {
'k', 'c', 't', 'l', 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
switch (level) {
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
key_control_block[12] = 0x00 << 2;
break;
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
key_control_block[12] = 0x01 << 2;
break;
case video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO:
key_control_block[12] = 0x02 << 2;
break;
default:
key_control_block[12] = 0x03 << 2;
break;
}
// Key Control Block is no longer encrypted, so no need to set IV.
key_control->set_key_control_block(key_control_block.data(),
key_control_block.size());
}
std::string GenerateCoreMessage(
const std::string& serialized_request,
const std::string& serialized_license_response) {
constexpr uint16_t api_major_version = 16;
constexpr uint16_t api_minor_version = 5;
static_assert(api_major_version == ODK_MAJOR_VERSION,
"Verify ODK library is compatible.");
constexpr uint32_t session_id = 0xcafebabe;
constexpr uint32_t nonce = 0xdeadbeef;
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
// Start by making a call to determine how big the core_message for the
// request needs to be.
size_t core_message_length = 0;
auto odk_result = ODK_PrepareCoreLicenseRequest(
reinterpret_cast<uint8_t*>(const_cast<char*>(serialized_request.data())),
serialized_request.size(), &core_message_length, &nonce_values);
CHECK_EQ(odk_result, OEMCrypto_ERROR_SHORT_BUFFER);
// Now that we know the size, create |combined_request_message| with room
// for the core message and append |serialized_request_|, as the combined
// buffer is needed by ODK_PrepareCoreLicenseRequest().
std::string combined_request_message;
combined_request_message.resize(core_message_length);
combined_request_message.append(serialized_request);
odk_result = ODK_PrepareCoreLicenseRequest(
reinterpret_cast<uint8_t*>(
const_cast<char*>(combined_request_message.data())),
combined_request_message.size(), &core_message_length, &nonce_values);
CHECK_EQ(odk_result, OEMCrypto_SUCCESS);
// As the core_message is the first part of |combined_request_message|,
// extract it.
const std::string request_core_message =
combined_request_message.substr(0, core_message_length);
std::string core_message_hash = widevine::Sha256_Hash(request_core_message);
oemcrypto_core_message::ODK_LicenseRequest core_request{};
CHECK(oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage(
request_core_message, &core_request));
std::string oemcrypto_core_message;
CHECK(oemcrypto_core_message::serialize::CreateCoreLicenseResponseFromProto(
serialized_license_response, core_request, core_message_hash,
/* nonce_required= */ true, &oemcrypto_core_message));
return oemcrypto_core_message;
}
} // namespace
// static
std::vector<uint8_t> TestLicenseBuilder::NoPadding() {
return {};
}
// static
std::vector<uint8_t> TestLicenseBuilder::PKSC8Padding() {
return {
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
};
}
// static
std::vector<uint8_t> TestLicenseBuilder::DefaultSigningKey() {
return {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
};
}
TestLicenseBuilder::TestLicenseBuilder() {
DCHECK_EQ(session_key_.size(), 16u);
// Initialize the request and the response with the static fields that will
// be common across all licences.
InitializeRequest(&request_);
InitializeResponse(request_, &response_);
serialized_request_ = request_.SerializeAsString();
container_key_ = crypto_util::DeriveKey(
session_key_, crypto_util::kWrappingKeyLabel, serialized_request_,
crypto_util::kWrappingKeySizeBits);
}
void TestLicenseBuilder::AddSigningKey(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& padding) {
DCHECK_EQ(key.size(), 64u); // 512 bits
auto* container = response_.add_key();
container->set_type(video_widevine::License_KeyContainer_KeyType_SIGNING);
// To avoid having to define a key iv for each key, derive a key iv from the
// key. This will allows us to have a different IVs between keys but keep it
// deterministic.
const auto key_iv = DeriveIV(key);
container->set_iv(key_iv);
std::vector<uint8_t> final_key = key;
final_key.insert(final_key.end(), padding.begin(), padding.end());
container->set_key(EncryptKey(container_key_, key_iv, final_key));
}
void TestLicenseBuilder::AddStubbedContentKey() {
const video_widevine::License_KeyContainer_SecurityLevel kLevel =
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
auto* container = response_.add_key();
container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT);
container->set_id("stubbed-content-key");
container->set_level(kLevel);
container->set_iv("0000000000000000");
// We don't bother encrypting the key, it should never be used and there is no
// way to verify it. Note that the ODK automatically strips padding, so
// this key needs to include 16 bytes of padding.
container->set_key("00000000000000000000000000000000");
UpdateKeyControlBlock(kLevel, container->mutable_key_control());
}
void TestLicenseBuilder::AddContentKey(
video_widevine::License_KeyContainer_SecurityLevel level,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& padding) {
DCHECK_GT(key_id.size(), 0u);
DCHECK_EQ(key.size(), 16u);
auto* container = response_.add_key();
container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT);
container->set_id(key_id.data(), key_id.size());
container->set_level(level);
// To avoid having to define a key iv for each key, derive a key iv from the
// key. This will allows us to have a different IVs between keys but keep it
// deterministic.
const auto key_iv = DeriveIV(key);
container->set_iv(key_iv);
std::vector<uint8_t> final_key = key;
final_key.insert(final_key.end(), padding.begin(), padding.end());
container->set_key(EncryptKey(container_key_, key_iv, final_key));
UpdateKeyControlBlock(level, container->mutable_key_control());
}
void TestLicenseBuilder::AddOperatorSessionKey(
const std::vector<uint8_t>& key_id) {
DCHECK_GT(key_id.size(), 0u);
// We only set the type and id because the key should not actually be used.
auto* container = response_.add_key();
container->set_type(
video_widevine::License_KeyContainer_KeyType_OPERATOR_SESSION);
container->set_id(key_id.data(), key_id.size());
}
void TestLicenseBuilder::SetRemoteAttestation(RemoteAttestation setting) {
switch (setting) {
case RemoteAttestation::kUnavailable:
response_.clear_remote_attestation_verified();
break;
case RemoteAttestation::kVerified:
response_.set_remote_attestation_verified(true);
break;
case RemoteAttestation::kUnverified:
response_.set_remote_attestation_verified(false);
break;
}
}
void TestLicenseBuilder::SetVerificationStatus(VerificationStatus setting) {
switch (setting) {
case VerificationStatus::kUnavailable:
response_.clear_platform_verification_status();
break;
case VerificationStatus::kHardwareVerified:
response_.set_platform_verification_status(
video_widevine::PLATFORM_HARDWARE_VERIFIED);
break;
case VerificationStatus::kOther:
response_.set_platform_verification_status(
video_widevine::PLATFORM_UNVERIFIED);
break;
}
}
void TestLicenseBuilder::SetUseODK(bool setting) {
use_odk_ = setting;
}
void TestLicenseBuilder::Build(const RsaPublicKey& public_key,
License* license) const {
DCHECK(license);
const std::string message_str = response_.SerializeAsString();
std::string signing_key = crypto_util::DeriveKey(
session_key_, crypto_util::kSigningKeyLabel, serialized_request_,
crypto_util::kSigningKeySizeBits * 2);
signing_key.resize(crypto_util::kSigningKeySizeBytes);
std::string session_key_str;
CHECK(public_key.Encrypt(session_key_, &session_key_str));
const std::string oemcrypto_core_message =
use_odk_ ? GenerateCoreMessage(serialized_request_, message_str)
: std::string();
// If |use_odk_| is false, |oemcrypto_core_message| will be empty.
const std::string signature_str = crypto_util::CreateSignatureHmacSha256(
signing_key, oemcrypto_core_message + message_str);
license->request.assign(serialized_request_.begin(),
serialized_request_.end());
if (!oemcrypto_core_message.empty()) {
license->core_message.assign(oemcrypto_core_message.begin(),
oemcrypto_core_message.end());
}
license->message.assign(message_str.begin(), message_str.end());
license->signature.assign(signature_str.begin(), signature_str.end());
license->session_key.assign(session_key_str.begin(), session_key_str.end());
}
} // namespace widevine

View File

@@ -0,0 +1,93 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_API_LICENSE_BUILDER_H_
#define WHITEBOX_API_LICENSE_BUILDER_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "cdm/protos/license_protocol.pb.h"
#include "crypto_utils/rsa_key.h"
namespace widevine {
struct License {
std::vector<uint8_t> request;
std::vector<uint8_t> core_message;
std::vector<uint8_t> message;
std::vector<uint8_t> signature;
// |session_key_| encrypted using the public key. The white-box expects the
// session key to be encrypted, so we use the name "session_key_" (even if it
// is encrypted), we omit the term "encrypted" to match the naming in the API.
std::vector<uint8_t> session_key;
};
class TestLicenseBuilder {
public:
enum class RemoteAttestation {
kUnavailable,
kVerified,
kUnverified,
};
enum class VerificationStatus {
kUnavailable,
kHardwareVerified,
kOther,
};
// Returns padding data the can be used as |padding| when calling
// AddSigningKey() or AddContentKey().
static std::vector<uint8_t> NoPadding();
static std::vector<uint8_t> PKSC8Padding();
// Returns a default signing key that can be used with AddSigningKey().
static std::vector<uint8_t> DefaultSigningKey();
TestLicenseBuilder();
void AddSigningKey(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& padding = NoPadding());
// Add a content key so that there is some key in the license. This should not
// be used with AddContentKey().
void AddStubbedContentKey();
void AddContentKey(video_widevine::License_KeyContainer_SecurityLevel level,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& padding = NoPadding());
// The key id will matter as we will need to reference it, but the key won't
// matter since we are only using it as a means to verify that a non-content
// key can't be used as a content key.
void AddOperatorSessionKey(const std::vector<uint8_t>& key_id);
void SetRemoteAttestation(RemoteAttestation setting);
void SetVerificationStatus(VerificationStatus setting);
// If set, then Build() will populate |core_message| in License with the
// matching ODK core message. If not set, then |core_message| will be empty.
void SetUseODK(bool setting);
// Gets the serialized license request and response (in components) that would
// have been used in the license exchange.
void Build(const RsaPublicKey& public_key, License* license) const;
private:
const std::string session_key_ = "0123456789ABCDEF";
video_widevine::LicenseRequest request_;
video_widevine::License response_;
std::string serialized_request_;
std::string container_key_;
bool use_odk_ = false;
};
} // namespace widevine
#endif // WHITEBOX_API_LICENSE_BUILDER_H_

View File

@@ -0,0 +1,30 @@
# Copyright 2020 Google LLC. All Rights Reserved.
package(default_visibility = [
"//visibility:public",
])
cc_library(
name = "data_source",
testonly = True,
srcs = [
"data_source.cc",
],
hdrs = [
"data_source.h",
],
)
cc_library(
name = "measurements",
testonly = True,
srcs = [
"measurements.cc",
],
hdrs = [
"measurements.h",
],
deps = [
"//chromium_deps/base:glog",
],
)

View File

@@ -0,0 +1,467 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "benchmarking/data_source.h"
#include <algorithm>
#include <cstring> // memcpy
namespace widevine {
namespace {
const uint8_t kGoldenData[] = {
0x91, 0x08, 0xed, 0x18, 0xe2, 0x8f, 0x22, 0x85, 0x9f, 0x7b, 0x07, 0xdd,
0xd3, 0x37, 0x09, 0xca, 0x53, 0x94, 0x37, 0xcd, 0x09, 0xff, 0x52, 0x77,
0xf0, 0xa5, 0x98, 0x07, 0x65, 0x25, 0x0a, 0x5a, 0x5a, 0xea, 0xfd, 0x24,
0xb0, 0xce, 0xa9, 0xb4, 0x64, 0x4e, 0x1f, 0x00, 0xa6, 0xb8, 0xf1, 0x3b,
0xc0, 0x5a, 0xa4, 0xe8, 0xb0, 0x32, 0x07, 0xff, 0x73, 0xcc, 0xc7, 0xd1,
0xfa, 0x2c, 0xb4, 0x43, 0x4a, 0xb4, 0xae, 0x91, 0xee, 0xb5, 0x6b, 0xce,
0xf6, 0x90, 0xb0, 0x22, 0x0b, 0xe7, 0x98, 0x77, 0xfe, 0x78, 0x68, 0xfd,
0x0e, 0xae, 0xf5, 0x1d, 0x37, 0xb8, 0x34, 0xf3, 0xca, 0xb5, 0x66, 0xba,
0xa2, 0x13, 0xdc, 0x4c, 0xb5, 0x13, 0x79, 0x9c, 0x55, 0xeb, 0x9f, 0x68,
0xd4, 0xfc, 0x0d, 0x41, 0x38, 0x3f, 0xca, 0x02, 0xe8, 0x22, 0xbf, 0xc1,
0xb0, 0x0a, 0xaf, 0x73, 0x9e, 0x9f, 0xee, 0xad, 0xa8, 0x10, 0x03, 0x32,
0xa8, 0x5c, 0x0c, 0x01, 0x64, 0xae, 0x01, 0x44, 0x10, 0x44, 0x79, 0x13,
0x30, 0x3b, 0xa9, 0xa5, 0xb5, 0x20, 0x2b, 0xfa, 0x1b, 0xa0, 0x85, 0x90,
0xfc, 0xef, 0xff, 0x19, 0xcb, 0x18, 0x0e, 0xc2, 0xdf, 0xfa, 0xd2, 0xf2,
0x6e, 0xd7, 0x4a, 0x36, 0xdc, 0xa9, 0x55, 0x67, 0x08, 0x68, 0x19, 0xbd,
0x47, 0x4e, 0xeb, 0x1a, 0x24, 0x05, 0x0e, 0xb9, 0x04, 0x01, 0xaf, 0xf0,
0x89, 0xa4, 0xc8, 0x2a, 0xa5, 0xf4, 0xd3, 0x67, 0x00, 0xbc, 0xe6, 0x6c,
0x32, 0x50, 0x15, 0x7f, 0x7e, 0xd6, 0x31, 0x9e, 0xc0, 0x43, 0x87, 0xa9,
0x29, 0x28, 0x50, 0x3e, 0xd3, 0x47, 0x10, 0xbf, 0xfa, 0x83, 0x20, 0x42,
0x6f, 0x72, 0xac, 0xbc, 0x81, 0xc8, 0xf6, 0xcc, 0x1f, 0xa7, 0xfe, 0x1d,
0xf7, 0x11, 0xe5, 0xe6, 0x6e, 0x9e, 0x38, 0x9e, 0xdd, 0x3b, 0x4b, 0x3d,
0x99, 0x23, 0xe1, 0xc4, 0xe1, 0x93, 0xde, 0x1a, 0x4f, 0xa1, 0x4c, 0xd4,
0x3f, 0xbe, 0x25, 0x37, 0xdd, 0x6f, 0xf3, 0xd4, 0x02, 0xa4, 0x5b, 0x86,
0xf7, 0xea, 0x97, 0x41, 0x67, 0x83, 0xad, 0x61, 0xe4, 0x2a, 0x01, 0x29,
0x6e, 0xdc, 0xe8, 0x73, 0x7d, 0xea, 0x22, 0xd4, 0xfb, 0xe5, 0x5e, 0x56,
0x1a, 0x32, 0x9f, 0x98, 0x20, 0x44, 0x67, 0x4e, 0x22, 0x51, 0xd5, 0x43,
0xbe, 0xb1, 0x65, 0x11, 0x13, 0x08, 0xfd, 0xad, 0x8b, 0xcb, 0x26, 0x2b,
0xee, 0x1e, 0x12, 0xe6, 0x2c, 0xe8, 0xe1, 0x81, 0x6a, 0x14, 0xf1, 0x45,
0x8f, 0x74, 0x94, 0x53, 0xd2, 0x3f, 0x01, 0x3d, 0x07, 0x34, 0xbc, 0xc1,
0x25, 0x32, 0x1d, 0x60, 0x8c, 0x15, 0xb1, 0x1a, 0xd1, 0x17, 0x04, 0x18,
0xfa, 0xdd, 0x48, 0x0f, 0xd3, 0xf9, 0xf4, 0x1f, 0x32, 0xb6, 0xd0, 0xa9,
0x66, 0x61, 0x40, 0x4d, 0x1a, 0xe1, 0xe4, 0x70, 0x6e, 0x04, 0x94, 0xd6,
0xc8, 0xa4, 0x43, 0x57, 0x2a, 0x19, 0x16, 0xfc, 0x56, 0x79, 0x2c, 0x3a,
0x8b, 0xf3, 0xad, 0x7b, 0x4f, 0xf8, 0xc5, 0x4d, 0x14, 0x13, 0x8a, 0xbd,
0x5e, 0xef, 0xdd, 0x79, 0x31, 0xdf, 0x65, 0xe1, 0x62, 0x92, 0x99, 0xf6,
0x1a, 0xd5, 0x18, 0x24, 0xb5, 0x27, 0x22, 0xb2, 0x1a, 0x2c, 0xab, 0x02,
0x7a, 0x47, 0xb4, 0xd6, 0x8b, 0x26, 0xb3, 0x5d, 0x9b, 0xf9, 0x87, 0x78,
0x5e, 0x8e, 0xaa, 0x7d, 0x0d, 0x8c, 0x7c, 0xf7, 0xbc, 0x4c, 0x9b, 0xad,
0x53, 0x8a, 0xdc, 0x50, 0xf5, 0x01, 0xcb, 0x74, 0x98, 0x83, 0x45, 0x41,
0x78, 0xf3, 0x00, 0x55, 0xb7, 0xd0, 0xdf, 0xff, 0xd3, 0xd9, 0xa1, 0xcc,
0xa1, 0xe8, 0x98, 0x33, 0x10, 0xcf, 0x8f, 0xcf, 0x5c, 0x3e, 0x50, 0xd6,
0xdc, 0xf9, 0x70, 0x9f, 0x7e, 0xc4, 0x1b, 0x87, 0x90, 0xbd, 0xb7, 0x2a,
0xb7, 0x91, 0x06, 0x1c, 0xad, 0x6c, 0xce, 0x60, 0x84, 0x51, 0xc4, 0x87,
0xf4, 0x46, 0x87, 0x9e, 0x72, 0x0d, 0x91, 0xe9, 0x69, 0xbf, 0xa3, 0x87,
0x6f, 0x24, 0xf9, 0x24, 0x75, 0xac, 0x7d, 0x36, 0x55, 0x85, 0x50, 0x23,
0x48, 0xb9, 0x74, 0xd3, 0xb7, 0x79, 0x70, 0xcf, 0x13, 0xee, 0x1d, 0x0f,
0xd9, 0x03, 0xe7, 0xdc, 0x4a, 0x3b, 0x06, 0x50, 0x7f, 0x1a, 0x37, 0x8e,
0xdd, 0xd4, 0x6f, 0xc7, 0xd2, 0x13, 0x8b, 0x00, 0xd0, 0x83, 0xe6, 0x15,
0x0f, 0x6b, 0x42, 0x65, 0xdb, 0xcd, 0x20, 0x8f, 0xdc, 0x3b, 0x0d, 0x32,
0x6a, 0x11, 0x18, 0x08, 0xf6, 0x3e, 0x4c, 0xdc, 0x9f, 0x5f, 0x45, 0xdf,
0xf4, 0x28, 0xbc, 0xd6, 0xb8, 0x53, 0x86, 0xe8, 0xfd, 0xa0, 0x68, 0x4e,
0x26, 0xba, 0x80, 0x93, 0x1d, 0x62, 0x2f, 0x4d, 0x2d, 0xf1, 0x65, 0xcf,
0xda, 0xbc, 0x32, 0xfb, 0xaf, 0x0b, 0xa0, 0x24, 0xe1, 0xdd, 0x94, 0x64,
0x0e, 0xb7, 0xf5, 0x04, 0xaf, 0x20, 0xf4, 0xaf, 0x8a, 0x67, 0xe6, 0x47,
0xc9, 0xb2, 0x25, 0x8b, 0x2c, 0xa1, 0xa8, 0x80, 0xa4, 0x70, 0xb9, 0xbe,
0xb9, 0x0b, 0x98, 0xbf, 0x62, 0xad, 0xdc, 0x73, 0x38, 0x0d, 0x74, 0xc4,
0x1b, 0xd5, 0xad, 0x6e, 0x50, 0x94, 0x46, 0xcd, 0xcf, 0x9a, 0x53, 0x04,
0x18, 0x40, 0xe9, 0xad, 0x92, 0x09, 0xd7, 0x6e, 0x16, 0x9c, 0xcf, 0xce,
0x20, 0xd6, 0x2a, 0xbc, 0x84, 0xbf, 0x33, 0xd0, 0x3f, 0x49, 0xa0, 0x66,
0x8c, 0xdf, 0xc7, 0x1f, 0xf7, 0x7c, 0xfa, 0xd3, 0xef, 0x8c, 0x51, 0x13,
0x6b, 0xe5, 0x3d, 0x80, 0x15, 0x11, 0x25, 0x2d, 0xe4, 0x23, 0xe1, 0xd9,
0xfe, 0x69, 0x81, 0xd0, 0x01, 0xc6, 0x41, 0xa1, 0xcf, 0x73, 0x5f, 0xca,
0x3c, 0xe9, 0x59, 0x25, 0x07, 0x97, 0x60, 0xda, 0xc4, 0x57, 0x98, 0x51,
0xd7, 0x30, 0xa5, 0x39, 0x7c, 0x0b, 0x65, 0xb2, 0x41, 0x03, 0xf5, 0x99,
0x1e, 0x0a, 0xc4, 0x2a, 0x14, 0x4e, 0x00, 0xf0, 0x27, 0xa7, 0xe6, 0x73,
0x15, 0xf6, 0x3d, 0x21, 0xc7, 0xeb, 0x53, 0xa7, 0x9b, 0x61, 0xdc, 0x0a,
0xcf, 0x0b, 0x6c, 0xbe, 0xb5, 0x77, 0xb3, 0xbc, 0xe6, 0x29, 0x9e, 0x09,
0x8d, 0x91, 0xde, 0xde, 0xb7, 0xd2, 0x18, 0x89, 0xc0, 0xf0, 0x79, 0xfd,
0xa6, 0xe4, 0xc1, 0x53, 0x04, 0x08, 0xdb, 0x0e, 0x97, 0xda, 0x1e, 0xcd,
0x1a, 0x63, 0x65, 0x24, 0xc2, 0x01, 0x1b, 0x6a, 0xae, 0xb5, 0x56, 0xa8,
0x94, 0xc5, 0x37, 0xb0, 0x2e, 0x69, 0xb0, 0xb9, 0x90, 0xe3, 0xb9, 0x2e,
0x03, 0xad, 0xe2, 0xc5, 0x1e, 0x36, 0x2e, 0xa9, 0xa8, 0xbe, 0xf0, 0xc6,
0xa8, 0x64, 0xb6, 0xb9, 0x9d, 0x25, 0xf1, 0x06, 0x28, 0xf9, 0x6e, 0x3a,
0x4e, 0x23, 0xcd, 0x32, 0x79, 0xa3, 0x19, 0x4a, 0x36, 0x8a, 0xfd, 0x81,
0x8c, 0x0e, 0x4e, 0xd0, 0x6c, 0x06, 0xf7, 0x2f, 0x8d, 0x19, 0x90, 0x47,
0x76, 0x7c, 0x7a, 0xb0, 0x26, 0x85, 0xaa, 0xfc, 0x3b, 0x03, 0xb7, 0x4c,
0xdd, 0xfb, 0xa9, 0x6c, 0x8c, 0x35, 0x88, 0xd9, 0x41, 0xf5, 0xd7, 0x74,
0x30, 0xea, 0xa5, 0xb1, 0xf5, 0x08, 0xeb, 0x29, 0x72, 0x1c, 0x18, 0x5f,
0xf6, 0x7e, 0x5b, 0x1b, 0xc1, 0x40, 0xa7, 0x4f, 0xf2, 0x87, 0x61, 0xca,
0xcd, 0x9a, 0x94, 0xa8, 0xce, 0x33, 0x1d, 0xa4, 0x07, 0x67, 0xb6, 0x37,
0xf4, 0xd0, 0x34, 0xa0, 0x4b, 0x5c, 0x7f, 0xc2, 0x06, 0xaa, 0x6d, 0x23,
0xb8, 0x68, 0xc5, 0xe7, 0x3b, 0xd5, 0x10, 0xce, 0x36, 0x07, 0x60, 0x7f,
0xbb, 0xc0, 0x5b, 0xd2, 0x9f, 0x96, 0x31, 0x67, 0x6c, 0x30, 0x55, 0x88,
0xd3, 0xf0, 0xfe, 0x5e, 0x2f, 0x96, 0x90, 0xcc, 0xfa, 0xe4, 0x08, 0x6e,
0xd4, 0xa1, 0x4a, 0x52, 0xf9, 0x8b, 0xf5, 0x3a, 0x92, 0xae, 0x90, 0x7b,
0xf1, 0xc0, 0x2f, 0xb1, 0xe1, 0x3a, 0x05, 0x60, 0x62, 0x81, 0xd1, 0xaa,
0x47, 0xb9, 0x44, 0x35, 0x6e, 0x77, 0x0b, 0x55, 0x37, 0x31, 0x02, 0xde,
0xfd, 0xa7, 0x25, 0x4a, 0x61, 0x24, 0x8b, 0x0d, 0x68, 0x77, 0x66, 0x31,
0xa9, 0xf4, 0x07, 0x6b, 0x59, 0x51, 0x5a, 0x79, 0x11, 0x2c, 0x45, 0x92,
0xb4, 0x96, 0xe9, 0x7a, 0x1e, 0x0a, 0xf2, 0xd1, 0x74, 0x1e, 0xbb, 0x16,
0x62, 0xd3, 0x20, 0x59, 0x9d, 0x63, 0xd3, 0xa0, 0x36, 0x86, 0xcc, 0xa2,
0xe3, 0x40, 0xdc, 0x65, 0x6b, 0x2d, 0x72, 0xeb, 0x3e, 0x12, 0x71, 0x5a,
0x8d, 0xab, 0xa8, 0xb6, 0x7e, 0x7f, 0x37, 0xf7, 0x12, 0x00, 0xc4, 0xfe,
0xa6, 0xd6, 0xf9, 0xde, 0x4f, 0x43, 0xd2, 0x06, 0x19, 0x70, 0x14, 0x88,
0xe9, 0x69, 0xf5, 0xd1, 0x83, 0x9b, 0x1d, 0x8c, 0x64, 0xd1, 0xa0, 0x3e,
0x11, 0x85, 0x93, 0x9f, 0xf7, 0xaa, 0x43, 0x5d, 0x9f, 0x91, 0x50, 0xbb,
0xe4, 0x0e, 0xba, 0x12, 0x22, 0xc5, 0x9e, 0x93, 0xd7, 0x5c, 0x74, 0xb5,
0x63, 0xaf, 0xf1, 0xae, 0xf6, 0xaf, 0x9a, 0xf4, 0xb4, 0x9f, 0x4d, 0x64,
0x6a, 0xc7, 0x5f, 0xcb, 0x95, 0x5c, 0xc9, 0x62, 0x85, 0x52, 0xb7, 0x58,
0xdd, 0x75, 0x18, 0xf9, 0xfc, 0xf8, 0xe4, 0x82, 0x3b, 0xc3, 0x12, 0x0e,
0x9c, 0xef, 0xa8, 0x2a, 0xd1, 0xf8, 0xc6, 0x53, 0x59, 0xac, 0x69, 0x87,
0x1b, 0xf6, 0xd2, 0x06, 0x4e, 0x9e, 0x25, 0x0c, 0x0e, 0xc3, 0x66, 0x73,
0x00, 0xda, 0x3f, 0xc5, 0x12, 0xed, 0x8d, 0x43, 0xbf, 0x78, 0x7c, 0xa4,
0xc2, 0x96, 0x8e, 0x15, 0x27, 0x38, 0xbd, 0xc7, 0x05, 0xd6, 0xdd, 0x3a,
0x5f, 0xdd, 0xcd, 0x22, 0x9b, 0x19, 0x57, 0x95, 0x2f, 0x51, 0x2a, 0xf9,
0xb5, 0x21, 0x85, 0x26, 0x2c, 0x5f, 0x5b, 0x9c, 0xff, 0x70, 0x9a, 0x78,
0xc2, 0x86, 0x5a, 0xe3, 0x23, 0x30, 0xd7, 0xb0, 0xf7, 0x28, 0xf6, 0xbb,
0x81, 0x85, 0x17, 0x46, 0x22, 0xfa, 0xb9, 0xff, 0x8e, 0x4c, 0xca, 0x58,
0x25, 0x8b, 0xa3, 0x3e, 0xa4, 0xd8, 0xe4, 0x4b, 0x2e, 0x65, 0x80, 0x55,
0xe5, 0x1f, 0x84, 0x13, 0xdf, 0x78, 0x65, 0xce, 0x91, 0x07, 0x6e, 0xe7,
0x59, 0x79, 0x5c, 0x84, 0xf2, 0x38, 0xb4, 0x80, 0xca, 0x71, 0xfe, 0x80,
0xf6, 0x80, 0x09, 0x31, 0x5b, 0xd2, 0xbb, 0x98, 0xcc, 0x27, 0x4e, 0xe2,
0x34, 0x13, 0xd6, 0x79, 0xdf, 0xc0, 0x6e, 0x91, 0xbf, 0xb4, 0x67, 0x49,
0x73, 0x0c, 0x0b, 0xd3, 0x7d, 0x31, 0x01, 0x3b, 0x68, 0xd4, 0x2e, 0x35,
0x09, 0x94, 0x90, 0x83, 0x10, 0x0c, 0x7e, 0xcd, 0x8f, 0xa3, 0xed, 0xd9,
0x8c, 0x23, 0xeb, 0x65, 0x9c, 0xfb, 0x4a, 0x0d, 0x08, 0x6f, 0x13, 0xac,
0x21, 0xff, 0x1d, 0xda, 0xf9, 0xf8, 0x90, 0x35, 0x36, 0x2f, 0xc0, 0xec,
0x8c, 0xca, 0xbf, 0xfb, 0x3f, 0x23, 0x60, 0x0e, 0xca, 0xf5, 0x24, 0x27,
0x0a, 0x15, 0xcd, 0xb7, 0x9d, 0x05, 0x67, 0xe3, 0xd6, 0xe6, 0x22, 0x90,
0x68, 0x68, 0x4a, 0x63, 0x5f, 0xfd, 0xcd, 0xfa, 0xaf, 0xd5, 0x92, 0x5a,
0x09, 0xdc, 0x08, 0xef, 0x01, 0xb8, 0x0f, 0x1f, 0x1d, 0xcf, 0x2d, 0xd3,
0xfc, 0xfd, 0x2e, 0x9b, 0xab, 0x45, 0xa0, 0xaf, 0xe1, 0x06, 0xf9, 0xa4,
0x44, 0xe2, 0x8a, 0x09, 0xdf, 0x91, 0xe7, 0x24, 0x63, 0x78, 0x42, 0xce,
0x69, 0xdc, 0x88, 0x34, 0xde, 0xfd, 0x82, 0x43, 0xf0, 0x3d, 0x0d, 0x48,
0x74, 0xa3, 0x74, 0xde, 0xc7, 0xf1, 0x7c, 0xcd, 0x05, 0xd9, 0xee, 0x8b,
0x59, 0x3f, 0x20, 0x25, 0x8a, 0x61, 0x97, 0x18, 0xda, 0xdd, 0xe4, 0x85,
0x06, 0x82, 0x0b, 0x3a, 0x41, 0xe0, 0x48, 0xf7, 0x8c, 0xc2, 0xf1, 0xc8,
0x93, 0x96, 0x25, 0x71, 0xe4, 0x26, 0x8c, 0xd9, 0x4e, 0x9b, 0x55, 0x6d,
0x1a, 0x1f, 0xcf, 0x68, 0x26, 0xd0, 0xdd, 0xa5, 0x12, 0x12, 0x75, 0xec,
0x5e, 0x41, 0xdd, 0xad, 0x0c, 0xfc, 0xae, 0x0c, 0x4d, 0xfd, 0x7b, 0xa0,
0x20, 0xcb, 0x47, 0xc2, 0xef, 0x77, 0xa2, 0xcf, 0x44, 0x24, 0x75, 0xd3,
0xf7, 0x39, 0x49, 0xad, 0x8c, 0x09, 0xa6, 0x4a, 0x40, 0xb4, 0x38, 0x8e,
0xb7, 0xd2, 0x8b, 0xd3, 0xe5, 0xf6, 0x6c, 0xe7, 0x24, 0x65, 0x04, 0xcb,
0xd4, 0xeb, 0xcb, 0xa2, 0x75, 0xc4, 0x00, 0x60, 0xcc, 0x20, 0xde, 0xa0,
0x83, 0xc9, 0x5b, 0xb8, 0x9a, 0x6c, 0xaa, 0x58, 0xc9, 0x18, 0x21, 0x2d,
0xd1, 0x3e, 0xe3, 0xb7, 0xb8, 0x52, 0x0d, 0x53, 0xc7, 0x8c, 0xbe, 0xa3,
0xb9, 0x15, 0x05, 0x3e, 0x6a, 0x4e, 0xeb, 0xc0, 0xee, 0xab, 0x26, 0x05,
0x94, 0x5c, 0x5a, 0x2d, 0xe2, 0xe5, 0x35, 0x6d, 0x59, 0xba, 0x74, 0xb5,
0x78, 0x07, 0x89, 0xb9, 0x26, 0x1f, 0xd3, 0x08, 0xcb, 0xe0, 0xe3, 0xc3,
0x41, 0xf8, 0x68, 0x19, 0xf3, 0xb5, 0x35, 0x7e, 0xe1, 0x3e, 0xc7, 0xc3,
0x4d, 0x53, 0x78, 0x9d, 0xbf, 0x83, 0x2a, 0xfe, 0xcd, 0x21, 0x82, 0xba,
0x47, 0x11, 0x00, 0x8f, 0xab, 0xb9, 0x30, 0xb0, 0xfc, 0x7b, 0x23, 0x3b,
0xc9, 0x6a, 0xcd, 0x68, 0x12, 0x9a, 0xd4, 0x72, 0xb9, 0x47, 0x66, 0x3f,
0xf5, 0xe0, 0x05, 0x0e, 0x9c, 0x3f, 0xc1, 0x91, 0xd8, 0xa5, 0x1c, 0x18,
0xce, 0x5b, 0xa6, 0x91, 0xc0, 0x24, 0xc1, 0xa9, 0x76, 0xe8, 0x5f, 0x78,
0xea, 0x0f, 0x41, 0x59, 0xb9, 0x19, 0xa9, 0xd0, 0x19, 0x88, 0x12, 0xbe,
0x63, 0x60, 0xff, 0x8c, 0x2e, 0x14, 0xeb, 0x8b, 0x73, 0x5a, 0x35, 0x02,
0xff, 0xe3, 0xf0, 0xe9, 0x19, 0xa5, 0xd8, 0x1c, 0x1b, 0xdd, 0x5c, 0x5a,
0x7b, 0xff, 0x1e, 0x06, 0x2e, 0x78, 0xa6, 0xf9, 0x62, 0xfd, 0xb0, 0x75,
0xec, 0x95, 0x3e, 0xef, 0x4d, 0xba, 0xd6, 0x40, 0xdf, 0x94, 0x47, 0xb3,
0x74, 0x5c, 0x8e, 0xe4, 0xd6, 0x23, 0x13, 0x40, 0x15, 0x06, 0x55, 0x87,
0xd1, 0x3b, 0x53, 0x9e, 0x4f, 0xfd, 0x94, 0xf2, 0x6c, 0xfe, 0x66, 0x8c,
0xa8, 0xf7, 0xf0, 0x40, 0xb2, 0x46, 0x3b, 0xcc, 0x6a, 0x01, 0xee, 0x64,
0x51, 0x73, 0x55, 0x46, 0xd3, 0xb1, 0x9d, 0x8b, 0xbe, 0xfe, 0x5d, 0xe9,
0x65, 0x52, 0xda, 0x4c, 0x47, 0x6d, 0xa0, 0x16, 0xc2, 0xad, 0x3f, 0x35,
0x27, 0x74, 0x26, 0x2f, 0xdb, 0x8a, 0x1b, 0x25, 0x38, 0xeb, 0x98, 0xfb,
0x62, 0x40, 0x94, 0xcb, 0x33, 0xcd, 0xc8, 0x80, 0x7f, 0x0e, 0x28, 0x76,
0x0b, 0xd0, 0xe6, 0x67, 0x17, 0x43, 0xba, 0xf3, 0x56, 0x9d, 0x12, 0xc6,
0xf0, 0xb5, 0x8d, 0x1f, 0xaf, 0x88, 0x44, 0x1d, 0xd2, 0x9b, 0x61, 0xb3,
0x08, 0x7a, 0xc9, 0x39, 0x99, 0xf2, 0x80, 0xbc, 0xdd, 0x05, 0x37, 0x09,
0x75, 0x69, 0x7b, 0x91, 0x52, 0xae, 0x4f, 0x26, 0xa9, 0x70, 0xaa, 0xb7,
0x6c, 0xa3, 0x64, 0x0e, 0xae, 0xc0, 0xb0, 0xe6, 0xdb, 0xd4, 0x9b, 0x28,
0x2d, 0xce, 0x49, 0xbd, 0xc6, 0xe0, 0xdd, 0x04, 0x1a, 0x8f, 0x43, 0x58,
0x3b, 0xd8, 0x31, 0x19, 0x71, 0xb2, 0x0e, 0x30, 0x77, 0x84, 0x71, 0xbe,
0x9a, 0x66, 0x07, 0xdf, 0x94, 0xe1, 0xed, 0x14, 0xf0, 0x4e, 0xbd, 0xde,
0xbb, 0xcd, 0xd8, 0xe2, 0xff, 0x49, 0xef, 0x60, 0x0b, 0x9b, 0xb5, 0x99,
0x35, 0x57, 0xba, 0xf8, 0xeb, 0xdd, 0x2e, 0x5f, 0xc5, 0x1b, 0x15, 0xb2,
0xa0, 0x02, 0xf4, 0xb4, 0x0f, 0x5c, 0x5a, 0x89, 0x69, 0xf1, 0x3d, 0xf4,
0x51, 0x39, 0x55, 0xa1, 0x6a, 0x82, 0x43, 0x33, 0xf4, 0xe8, 0xcf, 0x09,
0x69, 0xcc, 0x3b, 0xf3, 0xd4, 0x8e, 0x46, 0x7d, 0xe7, 0x21, 0x2a, 0x9e,
0x3d, 0x73, 0x9e, 0x5d, 0x5e, 0xdf, 0x5b, 0x5d, 0xcc, 0x10, 0xdb, 0x88,
0xee, 0x4b, 0xdc, 0x19, 0xd2, 0xb2, 0x3e, 0x57, 0x0f, 0x73, 0x48, 0x3e,
0xed, 0x50, 0xae, 0xc3, 0xa1, 0x98, 0xbe, 0xaa, 0x02, 0x5a, 0xf0, 0xad,
0x53, 0x4a, 0xdf, 0x86, 0x6d, 0x49, 0xac, 0xe5, 0xcf, 0x06, 0x02, 0x10,
0x51, 0x2a, 0xce, 0xd6, 0xae, 0x59, 0x7a, 0x10, 0xc5, 0xbf, 0x78, 0xc9,
0x71, 0x20, 0x40, 0x05, 0x95, 0xc8, 0x6c, 0x49, 0x14, 0x95, 0xfd, 0xcc,
0x56, 0x19, 0x87, 0x00, 0xbf, 0x7c, 0xef, 0x55, 0x42, 0x45, 0xf8, 0x39,
0x30, 0x14, 0x0c, 0xc0, 0x2a, 0x98, 0xc2, 0xf8, 0x53, 0x49, 0x7a, 0xd0,
0x6d, 0xb7, 0x4f, 0x5e, 0x3c, 0x9b, 0x6b, 0x86, 0x42, 0xdc, 0x8c, 0xa3,
0xf8, 0xac, 0xef, 0x18, 0x5f, 0x9b, 0xbf, 0x68, 0x2a, 0xb9, 0x73, 0xa8,
0x79, 0x5c, 0x31, 0xb4, 0x57, 0x24, 0x11, 0x67, 0xc3, 0x2c, 0x13, 0x85,
0x77, 0xf7, 0x10, 0x78, 0x87, 0x8f, 0xba, 0xd1, 0x17, 0x38, 0x96, 0x54,
0xa7, 0xca, 0x5e, 0x01, 0x6d, 0xb6, 0xd8, 0x54, 0x40, 0x9e, 0x5d, 0xac,
0x68, 0xf4, 0x67, 0x95, 0x1b, 0x72, 0xbb, 0x33, 0x59, 0x89, 0x23, 0xc6,
0xa9, 0xa9, 0xe4, 0xbc, 0x1e, 0x5f, 0xdc, 0x74, 0xd3, 0x35, 0x6d, 0x46,
0x7d, 0xc3, 0x82, 0xc9, 0xef, 0xa6, 0xd6, 0xc8, 0x3c, 0x6a, 0xc6, 0xb3,
0xa3, 0xbe, 0xeb, 0xee, 0x76, 0xf7, 0x79, 0xa0, 0xd0, 0xce, 0x39, 0x20,
0xb0, 0xff, 0x18, 0x1a, 0xe1, 0x31, 0xae, 0x60, 0xf2, 0x8a, 0xdb, 0xbe,
0xdb, 0x7d, 0x86, 0x88, 0xdc, 0xcb, 0x57, 0xc4, 0x8a, 0x28, 0x3a, 0x93,
0x8f, 0x4f, 0xae, 0xa0, 0x35, 0x12, 0x79, 0x42, 0x30, 0xed, 0xf1, 0x1a,
0x6c, 0xb3, 0x3e, 0x2b, 0x2f, 0xda, 0x4c, 0x6f, 0x8a, 0x02, 0x35, 0x9d,
0x72, 0xaa, 0x4c, 0x87, 0x25, 0x05, 0xe0, 0x73, 0xd8, 0xbb, 0xac, 0x0a,
0x9e, 0xef, 0xba, 0xd6, 0x70, 0x2b, 0xc3, 0x56, 0x3a, 0xd5, 0xea, 0x6c,
0x86, 0xb8, 0x92, 0xa5, 0xd8, 0xc6, 0x5c, 0xc1, 0xad, 0xc5, 0x69, 0x59,
0xa6, 0xb8, 0xa1, 0x82, 0x11, 0x2c, 0x83, 0x7d, 0xe3, 0xff, 0x5a, 0xb1,
0xa9, 0xa9, 0x79, 0x22, 0x4c, 0x7e, 0xfb, 0x53, 0x4a, 0x39, 0x86, 0x3a,
0xac, 0x63, 0xdb, 0x72, 0xb2, 0xe2, 0x6e, 0x65, 0x25, 0xfb, 0x79, 0x04,
0xe6, 0x4a, 0xb7, 0xf0, 0x1d, 0x13, 0x61, 0x7d, 0x64, 0x19, 0xcd, 0x5e,
0x22, 0xc5, 0x9b, 0xf1, 0x38, 0xfb, 0xc0, 0x3c, 0x7a, 0xcc, 0xeb, 0xe5,
0x15, 0x27, 0x10, 0x43, 0xa1, 0xbb, 0x22, 0x2d, 0xcc, 0x1f, 0x88, 0xf4,
0xda, 0x9b, 0x1f, 0xa0, 0xbc, 0x69, 0x80, 0x24, 0x28, 0xba, 0xf6, 0xe8,
0xfc, 0x11, 0x12, 0x94, 0xda, 0x2b, 0xc0, 0xb7, 0x67, 0xeb, 0xfc, 0x54,
0xe2, 0x40, 0xf1, 0x3f, 0x36, 0x12, 0xa2, 0xa5, 0xa7, 0x7f, 0xf8, 0xfb,
0x16, 0x03, 0x6d, 0x9d, 0xcf, 0x96, 0xb7, 0xdd, 0x48, 0xe0, 0x9f, 0x1a,
0xe4, 0x4f, 0xdd, 0x5b, 0x64, 0x79, 0x0d, 0xdb, 0x27, 0x40, 0x2f, 0x10,
0xe5, 0xd3, 0x63, 0x9f, 0x41, 0xd8, 0x79, 0x68, 0x00, 0xf3, 0x33, 0xb7,
0x38, 0x83, 0x93, 0xe7, 0xb1, 0x2f, 0xbb, 0xbd, 0x13, 0xa8, 0x55, 0x56,
0x79, 0x8a, 0x77, 0x1b, 0x35, 0x80, 0x9e, 0x20, 0xc0, 0x5b, 0x45, 0x6b,
0xb7, 0x48, 0xcf, 0x37, 0x5e, 0x03, 0xd4, 0x41, 0x1b, 0x1c, 0x02, 0x7f,
0x8a, 0x25, 0x76, 0x4d, 0xfc, 0xd3, 0x76, 0x43, 0x67, 0x6d, 0xda, 0x75,
0xf9, 0x24, 0xe4, 0xcb, 0x0b, 0x61, 0x73, 0x91, 0x66, 0x14, 0x9d, 0x8b,
0x66, 0x8e, 0x8f, 0x9d, 0xcc, 0x1b, 0xd0, 0x94, 0x0e, 0x9d, 0x31, 0xc7,
0xce, 0xfd, 0x51, 0x98, 0xa6, 0x0d, 0xf7, 0x22, 0x0c, 0xc7, 0x02, 0x24,
0x19, 0xe5, 0x6a, 0xa4, 0x32, 0x38, 0xab, 0x0f, 0xb8, 0xb8, 0x85, 0xe8,
0xc1, 0x86, 0x5d, 0x91, 0xa8, 0xf5, 0x2e, 0xf5, 0x7b, 0x43, 0xf3, 0x47,
0x83, 0xe7, 0x4b, 0xa7, 0x75, 0x5b, 0x04, 0x4d, 0x72, 0x11, 0x3a, 0x9b,
0x1c, 0x3a, 0x06, 0xe6, 0xd3, 0x11, 0x8a, 0x2e, 0x6f, 0x70, 0xc6, 0xba,
0xd0, 0xf9, 0x02, 0x41, 0x12, 0xae, 0x58, 0xf0, 0x6b, 0x06, 0x2c, 0xcb,
0x40, 0xb1, 0x56, 0x90, 0x83, 0xbc, 0x33, 0x4d, 0x70, 0x62, 0xd7, 0x64,
0xb9, 0x96, 0x2f, 0x51, 0x33, 0x99, 0x72, 0x80, 0x09, 0xb9, 0x2b, 0xd5,
0x7e, 0x96, 0x1d, 0x99, 0xf6, 0x4d, 0xe4, 0x1d, 0x15, 0xb3, 0x85, 0xd0,
0x75, 0xa9, 0x6d, 0x9d, 0xfc, 0xfa, 0xd9, 0xf1, 0x0a, 0x28, 0x63, 0x6b,
0x64, 0x06, 0xf5, 0x78, 0x17, 0x9a, 0x72, 0x28, 0x00, 0xb9, 0x8f, 0x3a,
0xd3, 0x87, 0xc0, 0x62, 0x25, 0x9d, 0xd5, 0xcb, 0x36, 0xef, 0x3c, 0x67,
0xdd, 0x4d, 0x50, 0x0d, 0x47, 0x91, 0x9d, 0x60, 0x8b, 0xcb, 0x28, 0x8a,
0x57, 0x9b, 0x7a, 0x0a, 0xfd, 0x3c, 0xf6, 0x18, 0x7d, 0x32, 0xce, 0x74,
0x38, 0xcd, 0x2c, 0x2b, 0x91, 0x72, 0x28, 0xc3, 0x27, 0x20, 0x7d, 0x9e,
0x72, 0xce, 0xf1, 0x4e, 0x18, 0xfb, 0xc3, 0x89, 0x10, 0x4f, 0x84, 0x23,
0xf0, 0x79, 0x64, 0x15, 0xa3, 0xa5, 0xe8, 0xc1, 0xb0, 0x2c, 0x32, 0x39,
0x4c, 0x40, 0x6b, 0x6b, 0xa4, 0x7d, 0xdd, 0x21, 0x63, 0xe5, 0xf6, 0x00,
0x74, 0x73, 0xd8, 0x62, 0x2e, 0xb8, 0xc6, 0xa5, 0x3e, 0xf7, 0xdb, 0xc3,
0x52, 0x21, 0x85, 0xa9, 0x98, 0xb5, 0x79, 0x10, 0x93, 0x5d, 0xd4, 0x3f,
0x3c, 0xa9, 0x2d, 0xb8, 0xe9, 0xa8, 0x09, 0xe9, 0xef, 0xe1, 0x03, 0x34,
0xe5, 0x24, 0x08, 0x03, 0x8e, 0x83, 0xb9, 0x66, 0x2e, 0xa6, 0x89, 0xb5,
0x61, 0x0b, 0x16, 0x0b, 0x6b, 0xa5, 0x4b, 0x7d, 0xe7, 0x8a, 0x28, 0xda,
0xaf, 0x85, 0xd6, 0x27, 0x7e, 0x6a, 0xa0, 0x35, 0xe1, 0x65, 0x50, 0xe5,
0xa9, 0x38, 0xf3, 0xf5, 0x48, 0xa7, 0x7a, 0xbb, 0x77, 0x4f, 0xb5, 0x52,
0x77, 0xec, 0x48, 0x6a, 0xe3, 0x2d, 0xd3, 0x7c, 0xc5, 0xe9, 0xe3, 0x26,
0x14, 0x76, 0xc2, 0x3e, 0xfa, 0x19, 0xa2, 0x9a, 0x00, 0x02, 0x84, 0xae,
0xee, 0xce, 0xf6, 0x49, 0x40, 0x50, 0x56, 0x8f, 0x48, 0x67, 0x8d, 0xb1,
0x8a, 0x56, 0xe0, 0x27, 0x1b, 0xb6, 0xdb, 0x37, 0x78, 0x34, 0x49, 0x26,
0x8d, 0xa8, 0x21, 0xba, 0xf2, 0xd9, 0xe7, 0x67, 0x6a, 0x81, 0x98, 0x16,
0xef, 0x77, 0x51, 0x22, 0x78, 0xa4, 0xb8, 0x75, 0xe1, 0x92, 0xa8, 0x77,
0xbe, 0x03, 0x83, 0xc1, 0xef, 0x10, 0x59, 0xde, 0x92, 0x4c, 0x15, 0x7d,
0xd4, 0x11, 0x5e, 0x5e, 0x40, 0xee, 0xc7, 0x2f, 0x5f, 0xca, 0xda, 0x65,
0x74, 0x92, 0xbd, 0xc9, 0x6b, 0x52, 0x44, 0x66, 0x3a, 0x42, 0x3a, 0x37,
0xeb, 0xb3, 0xb5, 0x73, 0xaa, 0xbf, 0x2d, 0xea, 0x8e, 0x17, 0xd1, 0xa6,
0xf3, 0x99, 0x3d, 0x8d, 0x94, 0x93, 0x7a, 0x0e, 0x45, 0xe6, 0xcf, 0xd1,
0xb2, 0xf3, 0xc6, 0xd5, 0xc6, 0xa1, 0x24, 0x03, 0x4a, 0x12, 0xe3, 0x1e,
0x94, 0x57, 0xac, 0x11, 0x5d, 0xda, 0xdc, 0x9a, 0xfd, 0x7a, 0x37, 0x9c,
0x07, 0x08, 0x2b, 0x3b, 0x59, 0xc5, 0x7f, 0x72, 0x30, 0xf4, 0x40, 0x08,
0x61, 0x19, 0x33, 0x12, 0xaa, 0xaf, 0xbb, 0xa5, 0x8f, 0xfc, 0x37, 0xdb,
0xa0, 0xc6, 0xe1, 0xa4, 0xe7, 0x2f, 0xef, 0xd3, 0x0a, 0x50, 0x10, 0x65,
0xfb, 0xbd, 0xe6, 0x94, 0x4a, 0x5c, 0xc8, 0x11, 0xee, 0xf9, 0xb5, 0xcc,
0x77, 0xe8, 0x74, 0xe8, 0x86, 0x0c, 0xea, 0x03, 0x38, 0x31, 0x85, 0x6f,
0xe8, 0xa7, 0xd5, 0xf7, 0xae, 0x5f, 0x29, 0xa7, 0x53, 0xff, 0x49, 0xfc,
0x62, 0x10, 0xfc, 0xf6, 0x2b, 0xdb, 0x02, 0x86, 0x24, 0x90, 0x43, 0x12,
0x94, 0x34, 0x91, 0xfd, 0x5c, 0xa4, 0x42, 0xb1, 0xdf, 0x90, 0xeb, 0x96,
0x2b, 0xfb, 0xd6, 0x23, 0x5e, 0xb7, 0x64, 0x20, 0x24, 0xd2, 0xc4, 0x88,
0x2b, 0x93, 0xb5, 0x7b, 0x4e, 0x71, 0x0e, 0xbc, 0x4b, 0xdc, 0xeb, 0x05,
0x50, 0xb4, 0x1a, 0x41, 0xce, 0x61, 0xdf, 0x83, 0xe3, 0x15, 0xae, 0x1a,
0x3c, 0xc6, 0x2b, 0x2e, 0x71, 0x50, 0xf1, 0x79, 0xf3, 0x33, 0xdf, 0x37,
0xfa, 0x02, 0x7e, 0x6c, 0x28, 0xf8, 0x31, 0x43, 0x56, 0x82, 0xd3, 0x2f,
0x5f, 0x55, 0x1d, 0xea, 0x10, 0xe3, 0x74, 0x85, 0x1b, 0xcc, 0x00, 0x35,
0x21, 0x76, 0xf3, 0xac, 0x81, 0xf2, 0x4e, 0x42, 0xfc, 0xfb, 0x88, 0xd9,
0x2b, 0xaf, 0x8e, 0x89, 0x24, 0xea, 0x68, 0x2c, 0x1a, 0x91, 0x05, 0x21,
0xc5, 0x6b, 0x80, 0xcb, 0xa1, 0x17, 0x61, 0xf5, 0xac, 0xc8, 0x65, 0xec,
0x6f, 0x5d, 0x4e, 0xa6, 0xd9, 0xb9, 0x13, 0xe9, 0x7e, 0xb7, 0xf2, 0x8d,
0x29, 0x00, 0x49, 0xf3, 0x52, 0x99, 0x3f, 0xd8, 0xf3, 0xfd, 0x86, 0x1c,
0x03, 0x49, 0x21, 0xc1, 0x81, 0xb6, 0xb0, 0xaf, 0x82, 0x30, 0x0d, 0xa1,
0xb6, 0xbf, 0xd2, 0xd8, 0x8c, 0x02, 0xea, 0xdd, 0xcb, 0x36, 0x49, 0x42,
0xe4, 0x25, 0xfe, 0x2a, 0x4e, 0x58, 0x26, 0x96, 0xc6, 0xec, 0x92, 0x34,
0x21, 0x94, 0x55, 0xd3, 0x06, 0xa7, 0x05, 0x60, 0x75, 0x93, 0x46, 0x71,
0xec, 0xba, 0x7c, 0x56, 0xfb, 0xd2, 0x08, 0xa3, 0x26, 0x62, 0x8c, 0x78,
0x53, 0x2c, 0x75, 0x36, 0x38, 0x85, 0xfe, 0xe3, 0x01, 0x20, 0x34, 0x75,
0x51, 0x66, 0xd2, 0x32, 0xcf, 0x4a, 0xf6, 0x21, 0xfe, 0x9a, 0xc2, 0xfc,
0xaa, 0xae, 0x1c, 0x55, 0xa1, 0x2c, 0x24, 0xa1, 0x86, 0x89, 0x54, 0x5e,
0x47, 0x42, 0xd4, 0x63, 0x0e, 0x85, 0xf8, 0x97, 0x55, 0x8c, 0x75, 0xa2,
0x94, 0x25, 0xaf, 0x9c, 0x9e, 0x17, 0xdb, 0xea, 0x16, 0x1e, 0x48, 0x1a,
0x6d, 0xa6, 0x4c, 0x31, 0x25, 0x7e, 0x44, 0xdb, 0x10, 0x0f, 0xf3, 0xaf,
0x63, 0x71, 0xb3, 0xad, 0x09, 0x16, 0xbc, 0xa0, 0x35, 0x5d, 0x17, 0x56,
0xe7, 0x69, 0xd1, 0x5b, 0x26, 0x9d, 0xc3, 0x2d, 0x06, 0x3a, 0x69, 0xfd,
0xfc, 0xa9, 0x31, 0xa7, 0xc5, 0xd4, 0x8f, 0x52, 0x45, 0x44, 0xe2, 0xd2,
0x7b, 0x15, 0xe9, 0xa7, 0xac, 0xe3, 0x51, 0xf0, 0x3d, 0x18, 0xd6, 0x9b,
0xf6, 0xbd, 0x5f, 0x1f, 0xff, 0x27, 0x46, 0x26, 0xfa, 0x0a, 0x63, 0xe0,
0x56, 0x8e, 0x6e, 0xc1, 0x9f, 0xd2, 0xcb, 0x24, 0x22, 0x39, 0x5c, 0x21,
0x83, 0x7d, 0xcf, 0x9f, 0x5e, 0xa1, 0x31, 0x1b, 0x7c, 0x6a, 0x80, 0x88,
0x23, 0x4f, 0xe0, 0x51, 0x4d, 0x01, 0x94, 0xf8, 0x13, 0xf5, 0xf8, 0xb2,
0x20, 0x94, 0xb8, 0x99, 0x5d, 0x5d, 0xa1, 0xf4, 0x4d, 0x02, 0x1a, 0x9e,
0xb6, 0x9b, 0x3b, 0x4e, 0xa5, 0xee, 0x22, 0x26, 0xba, 0x32, 0x29, 0xa3,
0xa1, 0x36, 0x72, 0xde, 0x84, 0x7b, 0xbb, 0x0b, 0x77, 0xad, 0x65, 0xec,
0x0b, 0x0c, 0x5d, 0x8a, 0xfe, 0x98, 0x6a, 0x42, 0xd7, 0xad, 0x91, 0x08,
0x66, 0xc6, 0x30, 0xab, 0x8d, 0x6c, 0x7c, 0xe2, 0x3a, 0xf7, 0x55, 0xe8,
0x15, 0xa5, 0x2a, 0x64, 0xad, 0x1a, 0x3b, 0xcc, 0x93, 0xad, 0xe7, 0x2c,
0xae, 0x80, 0x0a, 0xbb, 0x98, 0xf7, 0x88, 0x3f, 0x1b, 0x30, 0x7b, 0xd6,
0x5e, 0x68, 0x39, 0x63, 0xc3, 0x2b, 0x35, 0xf3, 0x99, 0x3d, 0xa0, 0x36,
0x62, 0x9a, 0xcf, 0xb4, 0xe8, 0x89, 0x0f, 0x13, 0x56, 0x08, 0xef, 0x3c,
0x1c, 0x18, 0x9e, 0x7e, 0x0e, 0xa4, 0xa1, 0x76, 0x5c, 0x0a, 0xa8, 0x83,
0x72, 0xca, 0x35, 0xe6, 0xee, 0x44, 0x6e, 0x01, 0x5d, 0x43, 0x87, 0xd5,
0x5d, 0x77, 0xac, 0x08, 0x99, 0xd2, 0x70, 0x53, 0x57, 0x35, 0xbd, 0xee,
0x3d, 0x71, 0xfd, 0x4e, 0x31, 0xe0, 0xbc, 0x24, 0x4d, 0x27, 0xf3, 0x41,
0x0b, 0xde, 0xc7, 0x21, 0x6b, 0x8c, 0xab, 0xf2, 0x65, 0x86, 0x4c, 0xa5,
0x18, 0x77, 0xa9, 0xbb, 0x71, 0xf9, 0x9d, 0x97, 0x5a, 0x96, 0x61, 0xe8,
0x42, 0x03, 0x07, 0x12, 0x60, 0x33, 0x58, 0x03, 0xf4, 0x3b, 0x2b, 0xd0,
0x68, 0x70, 0x83, 0xf6, 0x3a, 0x73, 0x56, 0xcc, 0x8a, 0x3b, 0xef, 0xf4,
0xcc, 0x89, 0xa1, 0x77, 0x94, 0x62, 0x78, 0x51, 0xb9, 0x0c, 0x0f, 0xf6,
0x00, 0xa9, 0x1e, 0xa5, 0x95, 0xb7, 0x12, 0x36, 0xb8, 0x0e, 0x9e, 0x02,
0xdb, 0x60, 0x5f, 0xe8, 0xfe, 0x91, 0x20, 0x22, 0x23, 0xdd, 0x6d, 0xf9,
0x08, 0xfc, 0xfc, 0x00, 0x14, 0x48, 0xbd, 0x5a, 0x2f, 0xba, 0x70, 0x2b,
0xd1, 0xa6, 0x4f, 0xde, 0x66, 0x4c, 0xaa, 0xd1, 0xc1, 0xcf, 0x3f, 0x5d,
0xaa, 0x3a, 0xe9, 0x00, 0x17, 0xa0, 0x96, 0x90, 0x7f, 0xf5, 0x9f, 0x27,
0xf0, 0x93, 0x42, 0x16, 0x79, 0x2f, 0xaa, 0x95, 0x23, 0x9e, 0x56, 0x31,
0xc2, 0x33, 0x99, 0xc1, 0x6a, 0x0b, 0xdf, 0xd9, 0x6b, 0x27, 0x0b, 0x81,
0x37, 0x19, 0xf9, 0x78, 0xff, 0xfd, 0x88, 0x31, 0x6c, 0xba, 0xa0, 0x3c,
0x22, 0x9f, 0x5f, 0x4c, 0x1a, 0xe8, 0x29, 0x84, 0xcf, 0x64, 0x72, 0xde,
0x60, 0x43, 0x13, 0x69, 0x11, 0x29, 0xd9, 0xec, 0x7e, 0x0d, 0x96, 0xe0,
0xfb, 0x67, 0x14, 0x1d, 0x4a, 0x20, 0x1f, 0xfd, 0xc7, 0xe5, 0x7b, 0x5e,
0x5f, 0xda, 0x7d, 0x85, 0x2c, 0xa9, 0x84, 0x98, 0x63, 0x0a, 0x2e, 0xef,
0xba, 0xfd, 0x41, 0x6d, 0xfa, 0xad, 0xcd, 0x60, 0xa5, 0xe9, 0x08, 0xdf,
0x3d, 0xd2, 0xaf, 0x3d, 0xc4, 0xb7, 0xa0, 0x0d, 0x99, 0xdd, 0x9c, 0xcb,
0x31, 0xf6, 0x1d, 0xe1, 0x4f, 0x64, 0x2c, 0x5c, 0xca, 0x2e, 0xc9, 0x67,
0x0b, 0x4e, 0x14, 0xf6, 0x98, 0x20, 0x09, 0xab, 0x3b, 0x22, 0x01, 0xf0,
0x0b, 0x8e, 0x41, 0x25, 0x4f, 0x9d, 0x6b, 0x02, 0x3d, 0xab, 0xb4, 0xbb,
0xbe, 0x12, 0x84, 0x88, 0xf6, 0xe6, 0x20, 0xcd, 0x45, 0xca, 0xd0, 0x3c,
0xe3, 0x87, 0x33, 0x9b, 0x27, 0xf4, 0x30, 0xa2, 0x61, 0xde, 0x06, 0x07,
0x46, 0x47, 0x8a, 0xae, 0x65, 0x17, 0x25, 0xf5, 0x57, 0x7b, 0x9c, 0xd8,
0x74, 0xc5, 0xa6, 0xd3, 0xa4, 0xee, 0x50, 0x02, 0x9a, 0x22, 0x57, 0x3b,
0xf6, 0xcf, 0xa0, 0xb4, 0x7a, 0x4f, 0x31, 0x6c, 0xc1, 0x16, 0x05, 0xfd,
0xa8, 0x70, 0x9a, 0xc4, 0xdf, 0xac, 0x0d, 0xcc, 0x9b, 0x61, 0x8c, 0xf7,
0x58, 0xec, 0xf0, 0x06, 0x1b, 0x56, 0x98, 0xad, 0x37, 0xbb, 0xd4, 0x72,
0x60, 0x59, 0x84, 0xbf, 0xd8, 0xad, 0x08, 0x89, 0x4d, 0x28, 0x94, 0x35,
0x0a, 0x6c, 0xc1, 0x37, 0x7b, 0xc8, 0xb1, 0x3f, 0x84, 0x6b, 0xe9, 0x67,
0x18, 0x9a, 0x9d, 0x6e, 0xc1, 0xa2, 0x0a, 0x3e, 0x20, 0x58, 0xf5, 0x97,
0x12, 0xa7, 0x38, 0xeb, 0x0e, 0xd8, 0x67, 0xfb, 0x1e, 0xc1, 0xaa, 0x32,
0x27, 0x4c, 0x14, 0xe7, 0x39, 0x8d, 0x21, 0x68, 0x05, 0x93, 0x78, 0x12,
0xbc, 0x19, 0xe4, 0x57, 0x0f, 0xd2, 0xbf, 0x61, 0x3d, 0x96, 0x5a, 0xa6,
0xdb, 0xb3, 0xde, 0xf1, 0x2d, 0x7e, 0x44, 0x6f, 0xde, 0x81, 0x46, 0x9e,
0xec, 0x1a, 0xc8, 0x38, 0x97, 0x73, 0x5e, 0x1a, 0xc8, 0xf2, 0x6d, 0x1d,
0xdd, 0x30, 0x0c, 0x85, 0x73, 0x6a, 0xfd, 0x05, 0x57, 0x4c, 0x75, 0x43,
0x59, 0xc1, 0x11, 0x15, 0x64, 0xf3, 0x88, 0x50, 0x8d, 0xf3, 0xf4, 0xbf,
0x25, 0x5a, 0xc2, 0x78, 0x95, 0xf4, 0xb4, 0x70, 0x6d, 0x7a, 0x4a, 0xb8,
0x56, 0xe9, 0xad, 0x0f, 0xbf, 0x29, 0xd1, 0x09, 0x38, 0xef, 0xca, 0xab,
0x4d, 0x87, 0xd9, 0x10, 0x5d, 0x57, 0x6b, 0xe0, 0xea, 0x71, 0x51, 0x36,
0x8e, 0xb3, 0x7a, 0xe5, 0xb7, 0x13, 0x66, 0x70, 0x30, 0xa5, 0xd2, 0xc6,
0x49, 0x12, 0xc4, 0x70, 0x08, 0xe9, 0x8f, 0x3d, 0xd5, 0x6a, 0xbf, 0xf5,
0x61, 0x3e, 0x35, 0xc2, 0xfa, 0xb0, 0x55, 0x92, 0x32, 0xf2, 0x27, 0x6a,
0x19, 0x9f, 0xc5, 0xc8, 0xee, 0x3a, 0xa2, 0x8c, 0x23, 0x40, 0xd6, 0x7d,
0x8e, 0x15, 0x78, 0xcd, 0x22, 0xad, 0xea, 0xf2, 0x2a, 0xf7, 0x43, 0x50,
0xc9, 0xa1, 0x53, 0x12, 0x4e, 0x67, 0x38, 0xb5, 0x80, 0xae, 0xdf, 0x26,
0x98, 0xb4, 0x75, 0x88, 0x5f, 0x1f, 0xf5, 0xa9, 0x93, 0x90, 0x60, 0x28,
0xba, 0xd2, 0x26, 0x7d, 0x22, 0xa4, 0x23, 0xd4, 0xff, 0x4e, 0xac, 0x1b,
0x2f, 0xac, 0x6a, 0x7c, 0xfa, 0x9e, 0x0d, 0xfb, 0x17, 0x7a, 0x9d, 0x04,
0xd8, 0xfb, 0xd9, 0x96, 0xd1, 0xd1, 0x7d, 0xc0, 0xe9, 0xf0, 0x34, 0x19,
0x89, 0xe2, 0x15, 0x65, 0x7b, 0x9c, 0x94, 0x6a, 0x17, 0x59, 0x28, 0x1f,
0x28, 0x10, 0x98, 0xaf, 0x1a, 0xc7, 0x62, 0x41, 0x77, 0xe2, 0x00, 0x7f,
0x20, 0x0d, 0xb3, 0x35, 0x86, 0xfb, 0xae, 0xa5, 0xaf, 0xf7, 0x3c, 0x3c,
0x63, 0xe2, 0xa2, 0xd4, 0x44, 0x3a, 0xe1, 0x2d, 0x3b, 0x38, 0x01, 0x89,
0x0d, 0xca, 0x67, 0xa4, 0x5f, 0xcb, 0x9b, 0xdc, 0x7a, 0xb9, 0xf2, 0x8e,
0x43, 0x18, 0x74, 0xd9, 0xd6, 0x4d, 0x95, 0x80, 0x54, 0x62, 0xf9, 0x15,
0xdc, 0x0a, 0xcb, 0xac, 0x52, 0xf4, 0xe9, 0xb8, 0x2b, 0x75, 0xda, 0x17,
0x70, 0x3f, 0xe5, 0xc4, 0x87, 0x11, 0x87, 0x27, 0x0c, 0xe1, 0xf8, 0x8e,
0xec, 0xae, 0x33, 0x63, 0x5e, 0xef, 0x94, 0xe4, 0xa6, 0x85, 0xd0, 0x52,
0x60, 0xc3, 0x47, 0x02, 0x21, 0xff, 0xd6, 0x42, 0x98, 0xf3, 0x36, 0xfd,
0xca, 0x00, 0xf0, 0x58, 0x24, 0x02, 0x4e, 0xf5, 0x1f, 0xc6, 0x46, 0x9c,
0xa2, 0x36, 0x3f, 0x50, 0x9a, 0x25, 0x34, 0x03, 0xe5, 0xc3, 0x43, 0xe2,
0x5d, 0x3d, 0xf0, 0xa1, 0x70, 0xb6, 0xa1, 0x5e, 0x54, 0x7d, 0x8e, 0xbe,
0x80, 0xbd, 0x62, 0xd6, 0x71, 0xf6, 0xe2, 0xc0, 0x3d, 0xa3, 0x5d, 0x6f,
0xd3, 0x1c, 0x00, 0x1e, 0x9b, 0x4e, 0x02, 0xd9, 0x2e, 0x4f, 0x30, 0xc6,
0x0d, 0xba, 0xef, 0x7a, 0xe1, 0xc5, 0xc7, 0x77, 0xef, 0x10, 0x27, 0x9f,
0x8f, 0x4b, 0x3f, 0xb9, 0xa9, 0x8b, 0xfa, 0x8d, 0x1f, 0x27, 0xf0, 0xf5,
0x01, 0x65, 0x7b, 0x8c, 0xdf, 0x4b, 0xa9, 0x1e, 0xbe, 0x3a, 0xae, 0x14,
0x8b, 0x16, 0xf8, 0xa9, 0xa3, 0xe1, 0xc3, 0xe9, 0x1f, 0xbc, 0x86, 0x70,
0x5c, 0xe7, 0x53, 0x0a, 0xb7, 0x61, 0x52, 0x5f, 0x6f, 0xa4, 0x80, 0x64,
0x0f, 0xa9, 0x97, 0x07, 0xb5, 0x5d, 0x80, 0x6e, 0xb3, 0xbe, 0xfb, 0x0e,
0xdf, 0xe1, 0x71, 0xb9, 0xf0, 0xa6, 0xaf, 0xdf, 0xa6, 0xca, 0xd7, 0x4b,
0x9d, 0xc2, 0x45, 0xc3, 0x8d, 0x99, 0x2b, 0xc1, 0x2b, 0xb8, 0x92, 0x61,
0x41, 0x54, 0xaa, 0x6a, 0xab, 0xea, 0x7c, 0x48, 0xee, 0xdd, 0x32, 0x7b,
0xf4, 0xad, 0xe8, 0xbe, 0x71, 0x5e, 0x3e, 0x57, 0xb4, 0x1a, 0x78, 0x7a,
0x1d, 0xcd, 0x26, 0xfa, 0x72, 0x17, 0x55, 0xd7, 0x93, 0x2a, 0xc8, 0x34,
0xd2, 0x50, 0x2a, 0x44, 0x5e, 0x2f, 0x99, 0xd8, 0x08, 0x86, 0xa7, 0xb6,
0x07, 0x1e, 0x59, 0x2b, 0x65, 0x99, 0x2a, 0xe4, 0xf4, 0x7a, 0x1a, 0x5b,
0xf6, 0x28, 0x38, 0x2f, 0x56, 0x1a, 0xf3, 0xec, 0x81, 0xde, 0xc3, 0x4b,
0xd9, 0x62, 0xc8, 0x72, 0x3b, 0x96, 0x09, 0x8c, 0xda, 0x9f, 0x58, 0x13,
0x7b, 0x72, 0x32, 0x71, 0xff, 0x3b, 0x29, 0x31, 0x2d, 0x52, 0x15, 0xb9,
0xef, 0x09, 0x03, 0x66, 0xfb, 0xa0, 0x64, 0xdf, 0xa3, 0x99, 0x96, 0xc4,
0xb0, 0x8d, 0xcd, 0x82, 0xac, 0xfe, 0xc7, 0xf4, 0x5d, 0x97, 0x75, 0x4d,
0xb6, 0x81, 0xd7, 0xdb, 0xa1, 0x5d, 0xd8, 0x33, 0x59, 0x70, 0x35, 0x36,
0x41, 0x89, 0x70, 0xa5, 0x81, 0xdd, 0x2b, 0x84, 0x4b, 0x1c, 0xd7, 0xef,
0x4b, 0x3f, 0xdb, 0xdd, 0x7c, 0xe3, 0x9a, 0xb2, 0xd7, 0x66, 0x32, 0x53,
0xc4, 0xa0, 0x1c, 0x8b, 0xd9, 0x9f, 0x03, 0x0b, 0xf9, 0x6c, 0x18, 0x68,
0x10, 0x70, 0xb1, 0xd7, 0xdf, 0xa0, 0x7a, 0xbb, 0x78, 0x23, 0x8b, 0x20,
0xf9, 0x30, 0x07, 0x76, 0x1f, 0x64, 0x79, 0x78, 0x5b, 0x0f, 0x40, 0x90,
0x4d, 0xaa, 0x5d, 0xee, 0x54, 0x84, 0xeb, 0xcb, 0xab, 0xf8, 0xbf, 0x18,
0xa9, 0xf2, 0xbb, 0xe4, 0x36, 0x3e, 0xc8, 0x2d, 0x5c, 0x8e, 0x24, 0x2b,
0xf5, 0xe7, 0x9a, 0x52, 0x36, 0xf0, 0x35, 0x4e, 0xaa, 0x60, 0xa5, 0x51,
0x39, 0x7b, 0xae, 0xa1, 0x3c, 0x77, 0x42, 0xb3, 0xfd, 0xba, 0x6e, 0xb8,
0xcc, 0x7e, 0xc4, 0xbf, 0x49, 0xbc, 0x7d, 0x99, 0xc7, 0x58, 0xd4, 0x18,
0x6f, 0x26, 0xaf, 0x68, 0x74, 0xad, 0x05, 0xa5, 0x39, 0xc0, 0x42, 0xa3,
0x26, 0x4a, 0xad, 0x8d, 0x89, 0xaa, 0x8b, 0x48, 0xbc, 0x2d, 0xe2, 0xc9,
0xc2, 0xe9, 0x73, 0x32, 0x0f, 0xb5, 0xb6, 0x02, 0xa5, 0xa4, 0x18, 0x1b,
0x70, 0xf7, 0x6f, 0x96, 0xa4, 0xa9, 0xfe, 0x87, 0x1a, 0x45, 0x08, 0x71,
0x12, 0xba, 0xad, 0xc9, 0x82, 0x54, 0x83, 0xb2, 0xf8, 0x42, 0xe8, 0xb2,
0x63, 0x89, 0xbd, 0xe8, 0xb6, 0xfa, 0xa2, 0xc0, 0x4f, 0xd2, 0xcb, 0xa1,
0xd3, 0x0f, 0x31, 0xb0, 0x3c, 0xf6, 0xc2, 0x16, 0x37, 0x44, 0x30, 0xff,
0x4d, 0x00, 0xb9, 0x6f, 0xbb, 0xf5, 0xd9, 0x8d, 0x60, 0xce, 0xa0, 0x33,
0x7f, 0xef, 0x4c, 0xd8, 0xb8, 0xc8, 0xdc, 0x9b, 0x1e, 0x01, 0x91, 0x9d,
0x4d, 0xe0, 0x4e, 0x6b, 0x9d, 0x36, 0xa7, 0x42, 0x86, 0xce, 0x97, 0xbc,
0x3b, 0x63, 0xa5, 0x9f, 0xc6, 0xff, 0x22, 0x08, 0x7d, 0xe3, 0x59, 0xb2,
0x25, 0xa0, 0x5d, 0xf1, 0x3b, 0x22, 0xbd, 0x7a, 0xf2, 0x5a, 0xb7, 0x31,
0x72, 0x4f, 0x58, 0x3b, 0x1f, 0x37, 0xa6, 0xd4, 0x1f, 0xa1, 0x24, 0x4c,
0x8d, 0x3f, 0xbb, 0xf5, 0x28, 0xbf, 0x8a, 0x7e, 0x64, 0xcb, 0x14, 0x03,
0x7c, 0x32, 0x72, 0xd7, 0x71, 0xc3, 0x27, 0xfe, 0xdb, 0x9e, 0x29, 0x64,
0xa7, 0xd0, 0x52, 0x88, 0xa8, 0x35, 0x9f, 0xc2, 0x92, 0x17, 0xd8, 0x6e,
0x5e, 0x1b, 0xe5, 0xa3, 0xdf, 0xee, 0xc6, 0x00, 0xf3, 0xdc, 0xba, 0x49,
0x7c, 0xb4, 0x4e, 0x69, 0xee, 0x9e, 0x05, 0x6a, 0x6c, 0xdd, 0xfb, 0x90,
0xa2, 0xb7, 0x84, 0xea, 0x32, 0xca, 0xec, 0x15, 0x1c, 0x24, 0xd6, 0x60,
0xef, 0xf2, 0x2f, 0x4b, 0x87, 0x46, 0xeb, 0xe1, 0x89, 0x78, 0xba, 0xd0,
0xcd, 0xd7, 0x41, 0x01, 0xf9, 0xb4, 0x2c, 0x33, 0x34, 0xb1, 0x07, 0xe5,
0x9f, 0x0e, 0xb3, 0xc9, 0x60, 0xc1, 0x3c, 0xbd, 0x4e, 0x58, 0x08, 0x05,
0x3b, 0xf8, 0x29, 0x0c, 0x26, 0x68, 0x18, 0xaf, 0x84, 0x55, 0xc0, 0xd1,
0xfb, 0x08, 0xac, 0x40, 0x04, 0xf8, 0xd0, 0x73, 0x54, 0x64, 0x0f, 0xbd,
0x1f, 0x40, 0xd8, 0x16, 0xa4, 0x0b, 0x9a, 0xd9, 0x4e, 0x0e, 0xcb, 0x14,
0xe4, 0xec, 0xa3, 0xb0, 0xef, 0x45, 0xeb, 0x03, 0xdf, 0x62, 0x9f, 0x5d,
0x4a, 0x84, 0x12, 0x78, 0xb0, 0x3b, 0x94, 0x69, 0xd0, 0x5f, 0x99, 0x40,
0x1e, 0x82, 0x0b, 0x9e, 0x73, 0x45, 0xd9, 0x6d, 0xc3, 0x92, 0x22, 0x3e,
0x3e, 0x93, 0x41, 0x46, 0x64, 0xcd, 0xe8, 0x09, 0x8f, 0xe1, 0x13, 0xfb,
0xd5, 0xe4, 0xbc, 0x70, 0xdd, 0x6c, 0x98, 0xe8};
const size_t kGoldenDataSize = sizeof(kGoldenDataSize);
} // namespace
std::vector<uint8_t> DataSource::Get(size_t size) {
std::vector<uint8_t> buffer(size);
size_t write_head = 0;
while (write_head < size) {
const size_t remaining_read = kGoldenDataSize - read_head_;
const size_t remaining_write = size - write_head;
// Figure out how much more we can write. If we are nearing the end of the
// read buffer, we have to cut short our read so that we can loop back to
// the start of the read buffer.
size_t next_write = std::min(remaining_read, remaining_write);
memcpy(buffer.data() + write_head, kGoldenData + read_head_, next_write);
// Move both the read and write heads forward. Make sure to loop the read
// head back to the start so that we we can restart reading when necessary.
write_head += next_write;
read_head_ = (read_head_ + next_write) % kGoldenDataSize;
}
return buffer;
}
} // namespace widevine

View File

@@ -0,0 +1,23 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_BENCHMARKING_DATA_SOURCE_H_
#define WHITEBOX_BENCHMARKING_DATA_SOURCE_H_
#include <stddef.h>
#include <stdint.h>
#include <vector>
namespace widevine {
class DataSource {
public:
std::vector<uint8_t> Get(size_t size);
private:
size_t read_head_ = 0;
};
} // namespace widevine
#endif // WHITEBOX_BENCHMARKING_DATA_SOURCE_H_

View File

@@ -0,0 +1,78 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "benchmarking/measurements.h"
#include <algorithm>
#include "base/logging.h"
namespace widevine {
namespace {
size_t RoundUp(size_t num, size_t denum) {
return (num + denum - 1) / denum;
}
} // namespace
void Timer::Reset() {
start_ = Clock::now();
}
Period Timer::Get() const {
const auto now = Clock::now();
return std::chrono::duration_cast<Period>(now - start_);
}
Throughput::Throughput() : bytes(0), microseconds(0), bits_per_second(0) {}
Throughput::Throughput(const Period& duration, size_t bytes) {
this->bytes = bytes;
microseconds = duration.count();
bits_per_second = 0;
if (microseconds > 0) {
// Make sure to only do one division to ensure that we are not loosing too
// much information as we are avoiding floating-point values.
bits_per_second = (bytes * 8 * 1000 * 1000) / microseconds;
}
}
const Period& Sampler::Percentile::Get(size_t percentile) const {
// Nearest-rank method:
// https://en.wikipedia.org/wiki/Percentile#The_nearest-rank_method
const size_t index = RoundUp(samples_.size() * percentile, 100);
// The index will be from 1 to N, but we need it to be 0 to N-1.
return samples_[index - 1];
}
void Sampler::Push(const Period& period) {
samples_.push_back(period);
}
void PrettyPrint(const std::string& title,
const Throughput& throughput,
size_t bytes_per_call) {
LOG(INFO) << title;
LOG(INFO) << " bytes per call: " << bytes_per_call;
LOG(INFO) << " bytes: " << throughput.bytes;
LOG(INFO) << " microseconds: " << throughput.microseconds;
LOG(INFO) << " bits per second: " << throughput.bits_per_second;
}
void PrettyPrint(const std::string& title,
const Sampler& samples,
size_t per_sample_size) {
const auto& percentiles = samples.Percentiles();
LOG(INFO) << title;
LOG(INFO) << " Sample Size: " << samples.SampleSize() << " ("
<< per_sample_size << " bytes)";
LOG(INFO) << " Min (0%): " << percentiles.Get(0).count() << " us";
LOG(INFO) << " 25%: " << percentiles.Get(25).count() << " us";
LOG(INFO) << " Median (50%): " << percentiles.Get(50).count() << " us";
LOG(INFO) << " 75%: " << percentiles.Get(75).count() << " us";
LOG(INFO) << " Max (100%): " << percentiles.Get(100).count() << " us";
}
} // namespace widevine

View File

@@ -0,0 +1,72 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_BENCHMARKING_MEASUREMENT_H_
#define WHITEBOX_BENCHMARKING_MEASUREMENT_H_
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <chrono>
#include <string>
#include <vector>
namespace widevine {
using Clock = std::chrono::high_resolution_clock;
using Period = std::chrono::microseconds;
class Timer {
public:
void Reset();
Period Get() const;
private:
Clock::time_point start_;
};
struct Throughput {
size_t bytes;
uint64_t microseconds;
uint64_t bits_per_second;
Throughput();
Throughput(const Period& duration, size_t bytes);
};
class Sampler {
public:
class Percentile {
public:
Percentile(const std::vector<Period>& samples) : samples_(samples) {
std::sort(samples_.begin(), samples_.end());
};
const Period& Get(size_t percentile) const;
private:
std::vector<Period> samples_;
};
void Push(const Period& period);
Percentile Percentiles() const { return Percentile(samples_); }
size_t SampleSize() const { return samples_.size(); }
private:
std::vector<Period> samples_;
};
void PrettyPrint(const std::string& title,
const Throughput& throughput,
size_t bytes_per_call);
void PrettyPrint(const std::string& title,
const Sampler& samples,
size_t sample_input_size);
} // namespace widevine
#endif // WHITEBOX_BENCHMARKING_MEASUREMENT_H_

View File

@@ -0,0 +1,16 @@
# Copyright 2020 Google LLC. All Rights Reserved.
cc_library(
name = "glog",
hdrs = [
"check.h",
"check_op.h",
"logging.h",
],
strip_include_prefix = "//chromium_deps",
visibility = ["//visibility:public"],
deps = [
"//external:gflags",
"//external:glog",
],
)

View File

@@ -0,0 +1,13 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef BASE_CHECK_H_
#define BASE_CHECK_H_
// Chromium has split CHECK/DCHECK out of logging.h into it's own header.
// However, it's all still together in the GLOG header, so just include
// it. This may cause problems when importing into Alcatraz as code using
// anyone of these headers gets all the functions, not just the ones
// represented by this header.
#include "base/logging.h"
#endif // BASE_CHECK_H_

View File

@@ -0,0 +1,13 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef BASE_CHECK_OP_H_
#define BASE_CHECK_OP_H_
// Chromium has split CHECK_EQ/etc. out of logging.h into it's own header.
// However, it's all still together in the GLOG header, so just include
// it. This may cause problems when importing into Alcatraz as code using
// anyone of these headers gets all the functions, not just the ones
// represented by this header.
#include "base/logging.h"
#endif // BASE_CHECK_OP_H_

View File

@@ -0,0 +1,10 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef BASE_LOGGING_H_
#define BASE_LOGGING_H_
// See https://github.com/google/glog/blob/master/doc/glog.html
#define GLOG_NO_ABBREVIATED_SEVERITIES
#include "glog/logging.h"
#endif // BASE_LOGGING_H_

View File

@@ -0,0 +1,16 @@
# Copyright 2020 Google LLC. All Rights Reserved.
cc_library(
name = "api",
hdrs = ["certs.h"],
strip_include_prefix = "//chromium_deps",
visibility = ["//visibility:public"],
)
cc_library(
name = "dev_certs",
srcs = ["dev_rsa_drm_certificate.cc"],
strip_include_prefix = "//chromium_deps",
visibility = ["//visibility:public"],
deps = [":api"],
)

View File

@@ -0,0 +1,17 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef CDM_KEYS_CERTS_H_
#define CDM_KEYS_CERTS_H_
#include <stdint.h>
#include <cstddef>
namespace wvcdm {
// RsaDrmCertificate Generated by the Keysmith provisioning service.
extern const uint8_t kRsaDrmCertificate[];
extern const size_t kRsaDrmCertificateSize;
} // namespace wvcdm
#endif // CDM_KEYS_CERTS_H_

View File

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

View File

@@ -0,0 +1,13 @@
# Copyright 2020 Google LLC. All Rights Reserved.
# Protobuf generated code doesn't like it when include prefixes are
# stripped off. So this is a stub to mimic the protobuf C++ header.
cc_library(
name = "license_protocol_proto",
hdrs = ["license_protocol.pb.h"],
strip_include_prefix = "//chromium_deps",
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/cdm/protos/defs:license_protocol_proto",
],
)

View File

@@ -0,0 +1,41 @@
# Copyright 2020 Google LLC. All Rights Reserved.
# Protocol buffer definitions for Widevine Services.
package(default_visibility = ["//visibility:public"])
load("@com_google_protobuf//:protobuf.bzl", "cc_proto_library")
cc_proto_library(
name = "certificate_provisioning_proto",
srcs = ["certificate_provisioning.proto"],
default_runtime = "@com_google_protobuf//:protobuf",
protoc = "@com_google_protobuf//:protoc",
)
cc_proto_library(
name = "client_identification_proto",
srcs = ["client_identification.proto"],
default_runtime = "@com_google_protobuf//:protobuf",
protoc = "@com_google_protobuf//:protoc",
)
cc_proto_library(
name = "license_protocol_proto",
srcs = ["license_protocol.proto"],
default_runtime = "@com_google_protobuf//:protobuf",
protoc = "@com_google_protobuf//:protoc",
deps = [
":client_identification_proto",
":remote_attestation_proto",
],
)
cc_proto_library(
name = "remote_attestation_proto",
srcs = ["remote_attestation.proto"],
default_runtime = "@com_google_protobuf//:protobuf",
protoc = "@com_google_protobuf//:protoc",
deps = [
":client_identification_proto",
],
)

View File

@@ -0,0 +1,43 @@
// Copyright 2020 Google LLC. All Rights Reserved.
// This file is a reduced copy from the Google3 code and is only here to allow
// the ODK code to compile. This is not used in the CDM.
syntax = "proto2";
package video_widevine;
// Provisioning response sent by the provisioning server to client devices.
// This message is used for both regular Widevine DRM certificates and for
// application-specific X.509 certificates.
message ProvisioningResponse {
message OtaKeybox {
// Iv used along with SessionKeys.encryption_key for encrypting device key.
optional bytes device_key_encryption_iv = 1;
// Device key component of the keybox, encrypted using the
// SessionKeys.encryption_key in the request and |device_key_encryption_iv|
// above.
optional bytes encrypted_device_key = 2;
// Device CA token component of the keybox.
optional bytes device_ca_token = 3;
}
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
// Required. For X.509 certificates, the private RSA key may also include
// a prefix as specified by private_key_prefix in the X509CertificateMetadata
// 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
// SignedDrmCertificate. For X.509 certificates, this contains the PEM
// encoded X.509 certificate. Required.
optional bytes device_certificate = 3;
// Nonce value matching nonce in ProvisioningRequest. Required.
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;
// Only populated in OTA keybox provisioning response.
optional OtaKeybox ota_keybox = 6;
}

View File

@@ -0,0 +1,124 @@
// Copyright 2016 Google LLC. All rights reserved.
// Author: tinskip@google.com (Thomas Inskip)
//
// Description:
// ClientIdentification messages used by provisioning and license protocols.
syntax = "proto2";
package video_widevine;
option optimize_for = LITE_RUNTIME;
// 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_V2_3 = 5;
HDCP_NO_DIGITAL_OUTPUT = 0xff;
}
enum CertificateKeyType {
RSA_2048 = 0;
RSA_3072 = 1;
ECC_SECP256R1 = 2;
ECC_SECP384R1 = 3;
ECC_SECP521R1 = 4;
}
enum AnalogOutputCapabilities {
ANALOG_OUTPUT_UNKNOWN = 0;
ANALOG_OUTPUT_NONE = 1;
ANALOG_OUTPUT_SUPPORTED = 2;
ANALOG_OUTPUT_SUPPORTS_CGMS_A = 3;
}
optional bool client_token = 1 [default = false];
optional bool session_token = 2 [default = false];
optional bool video_resolution_constraints = 3 [default = false];
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;
optional AnalogOutputCapabilities analog_output_capabilities = 10
[default = ANALOG_OUTPUT_UNKNOWN];
optional bool can_disable_analog_output = 11 [default = false];
// Clients can indicate a performance level supported by OEMCrypto.
// This will allow applications and providers to choose an appropriate
// quality of content to serve. Currently defined tiers are
// 1 (low), 2 (medium) and 3 (high). Any other value indicate that
// the resource rating is unavailable or reporting erroneous values
// for that device. For details see,
// https://docs.google.com/document/d/1wodSYK-Unj3AgTSXqujWuBCAFC00qF85G1AhfLtqdko
optional uint32 resource_rating_tier = 12 [default = 0];
}
message ClientCredentials {
optional TokenType type = 1 [default = KEYBOX];
optional bytes token = 2;
}
// 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;
// Optional field that may contain additional provisioning credentials.
repeated ClientCredentials device_credentials = 8;
}
// 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;
}

View File

@@ -0,0 +1,460 @@
// Copyright 2016 Google LLC. All rights reserved.
//
// Description:
// Definitions of the protocol buffer messages used in the Widevine license
// exchange protocol, described in Widevine license exchange protocol document
// TODO(yawenyu): find out a right way to strip out all the doc link.
// MOE:begin_strip
// Design doc at:
// http://doc/1cng6cDnchbDQDymLEd5MxMc_laS3EDv6IsoW3IzpgwQ
// MOE:end_strip
syntax = "proto2";
package video_widevine;
import "chromium_deps/cdm/protos/defs/client_identification.proto";
import "chromium_deps/cdm/protos/defs/remote_attestation.proto";
option optimize_for = LITE_RUNTIME;
enum LicenseType {
STREAMING = 1;
OFFLINE = 2;
}
enum PlatformVerificationStatus {
// The platform is not verified.
PLATFORM_UNVERIFIED = 0;
// Tampering detected on the platform.
PLATFORM_TAMPERED = 1;
// The platform has been verified by means of software.
PLATFORM_SOFTWARE_VERIFIED = 2;
// The platform has been verified by means of hardware (e.g. secure boot).
PLATFORM_HARDWARE_VERIFIED = 3;
// Platform verification was not performed.
PLATFORM_NO_VERIFICATION = 4;
// Platform and secure storage capability have been verified by means of
// software.
PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED = 5;
}
// LicenseIdentification is propagated from LicenseRequest to License,
// incrementing version with each iteration.
message LicenseIdentification {
optional bytes request_id = 1;
optional bytes session_id = 2;
optional bytes purchase_id = 3;
optional LicenseType type = 4;
optional int32 version = 5;
optional bytes provider_session_token = 6;
}
message License {
// LINT.IfChange
message Policy {
// Indicates that playback of the content is allowed.
optional bool can_play = 1 [default = false];
// Indicates that the license may be persisted to non-volatile
// storage for offline use.
optional bool can_persist = 2 [default = false];
// Indicates that renewal of this license is allowed.
optional bool can_renew = 3 [default = false];
// For the |*duration*| fields, playback must halt when
// license_start_time (seconds since the epoch (UTC)) +
// license_duration_seconds is exceeded. A value of 0
// indicates that there is no limit to the duration.
// Indicates the rental window.
optional int64 rental_duration_seconds = 4 [default = 0];
// Indicates the viewing window, once playback has begun.
optional int64 playback_duration_seconds = 5 [default = 0];
// Indicates the time window for this specific license.
optional int64 license_duration_seconds = 6 [default = 0];
// The |renewal*| fields only apply if |can_renew| is true.
// The window of time, in which playback is allowed to continue while
// renewal is attempted, yet unsuccessful due to backend problems with
// the license server.
optional int64 renewal_recovery_duration_seconds = 7 [default = 0];
// All renewal requests for this license shall be directed to the
// specified URL.
optional string renewal_server_url = 8;
// How many seconds after license_start_time, before renewal is first
// attempted.
optional int64 renewal_delay_seconds = 9 [default = 0];
// Specifies the delay in seconds between subsequent license
// renewal requests, in case of failure.
optional int64 renewal_retry_interval_seconds = 10 [default = 0];
// Indicates that the license shall be sent for renewal when usage is
// started.
optional bool renew_with_usage = 11 [default = false];
// Indicates to client that license renewal and release requests ought to
// include ClientIdentification (client_id).
optional bool always_include_client_id = 12 [default = false];
// Duration of grace period before playback_duration_seconds (short window)
// goes into effect. Optional.
optional int64 play_start_grace_period_seconds = 13 [default = 0];
// Enables "soft enforcement" of playback_duration_seconds, letting the user
// finish playback even if short window expires. Optional.
optional bool soft_enforce_playback_duration = 14 [default = false];
// Enables "soft enforcement" of rental_duration_seconds. Initial playback
// must always start before rental duration expires. In order to allow
// subsequent playbacks to start after the rental duration expires,
// soft_enforce_playback_duration must be true. Otherwise, subsequent
// playbacks will not be allowed once rental duration expires. Optional.
optional bool soft_enforce_rental_duration = 15 [default = true];
}
// LINT.ThenChange(//depot/google3/google/chrome/widevine/licensedata/v1/license_policy.proto)
message KeyContainer {
enum KeyType {
SIGNING = 1; // Exactly one key of this type must appear.
CONTENT = 2; // Content key.
KEY_CONTROL = 3; // Key control block for license renewals. No key.
OPERATOR_SESSION = 4; // wrapped keys for auxiliary crypto operations.
ENTITLEMENT = 5; // Entitlement keys.
OEM_CONTENT = 6; // Partner-specific content key.
}
// The SecurityLevel enumeration allows the server to communicate the level
// of robustness required by the client, in order to use the key.
enum SecurityLevel {
// Software-based whitebox crypto is required.
SW_SECURE_CRYPTO = 1;
// Software crypto and an obfuscated decoder is required.
SW_SECURE_DECODE = 2;
// The key material and crypto operations must be performed within a
// hardware backed trusted execution environment.
HW_SECURE_CRYPTO = 3;
// The crypto and decoding of content must be performed within a hardware
// backed trusted execution environment.
HW_SECURE_DECODE = 4;
// The crypto, decoding and all handling of the media (compressed and
// uncompressed) must be handled within a hardware backed trusted
// execution environment.
HW_SECURE_ALL = 5;
}
message KeyControl {
// MOE:begin_strip
// |key_control| is documented here:
// http://doc/1pHSJ2IKL0axmQz2gmDZ7olxPWb_ZcULaJrYwDZAeS7k/edit#heading=h.pua7563f80h6
// MOE:end_strip
// If present, the key control must be communicated to the secure
// environment prior to any usage. This message is automatically generated
// by the Widevine License Server SDK.
optional bytes key_control_block = 1;
optional bytes iv = 2;
}
message OutputProtection {
// Indicates whether HDCP is required on digital outputs, and which
// version should be used.
enum HDCP {
HDCP_NONE = 0;
HDCP_V1 = 1;
HDCP_V2 = 2;
HDCP_V2_1 = 3;
HDCP_V2_2 = 4;
HDCP_V2_3 = 5;
HDCP_NO_DIGITAL_OUTPUT = 0xff;
}
optional HDCP hdcp = 1 [default = HDCP_NONE];
// Indicate the CGMS setting to be inserted on analog output.
enum CGMS {
CGMS_NONE = 42;
COPY_FREE = 0;
COPY_ONCE = 2;
COPY_NEVER = 3;
}
optional CGMS cgms_flags = 2 [default = CGMS_NONE];
enum HdcpSrmRule {
HDCP_SRM_RULE_NONE = 0;
// In 'required_protection', this means most current SRM is required.
// Update the SRM on the device. If update cannot happen,
// do not allow the key.
// In 'requested_protection', this means most current SRM is requested.
// Update the SRM on the device. If update cannot happen,
// allow use of the key anyway.
CURRENT_SRM = 1;
}
optional HdcpSrmRule hdcp_srm_rule = 3 [default = HDCP_SRM_RULE_NONE];
// Optional requirement to indicate analog output is not allowed.
optional bool disable_analog_output = 4 [default = false];
// Optional requirement to indicate digital output is not allowed.
optional bool disable_digital_output = 5 [default = false];
}
message VideoResolutionConstraint {
// Minimum and maximum video resolutions in the range (height x width).
optional uint32 min_resolution_pixels = 1;
optional uint32 max_resolution_pixels = 2;
// Optional output protection requirements for this range. If not
// specified, the OutputProtection in the KeyContainer applies.
optional OutputProtection required_protection = 3;
}
message OperatorSessionKeyPermissions {
// Permissions/key usage flags for operator service keys
// (type = OPERATOR_SESSION).
optional bool allow_encrypt = 1 [default = false];
optional bool allow_decrypt = 2 [default = false];
optional bool allow_sign = 3 [default = false];
optional bool allow_signature_verify = 4 [default = false];
}
optional bytes id = 1;
optional bytes iv = 2;
optional bytes key = 3;
optional KeyType type = 4;
optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO];
optional OutputProtection required_protection = 6;
// NOTE: Use of requested_protection is not recommended as it is only
// supported on a small number of platforms.
optional OutputProtection requested_protection = 7;
optional KeyControl key_control = 8;
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
// Optional video resolution constraints. If the video resolution of the
// content being decrypted/decoded falls within one of the specified ranges,
// the optional required_protections may be applied. Otherwise an error will
// be reported.
// NOTE: Use of this feature is not recommended, as it is only supported on
// a small number of platforms.
repeated VideoResolutionConstraint video_resolution_constraints = 10;
// Optional flag to indicate the key must only be used if the client
// supports anti rollback of the user table. Content provider can query the
// client capabilities to determine if the client support this feature.
optional bool anti_rollback_usage_table = 11 [default = false];
// Optional not limited to commonly known track types such as SD, HD.
// It can be some provider defined label to identify the track.
optional string track_label = 12;
}
// LINT.ThenChange(//depot/google3/google/chrome/widevine/licensedata/v1/key_container.proto)
optional LicenseIdentification id = 1;
optional Policy policy = 2;
repeated KeyContainer key = 3;
// Time of the request in seconds (UTC) as set in
// LicenseRequest.request_time. If this time is not set in the request,
// the local time at the license service is used in this field.
optional int64 license_start_time = 4;
// TODO(b/65054419): Deprecate remote_attestation_verified in favor of
// platform_verification_status, below.
optional bool remote_attestation_verified = 5 [default = false];
// Client token generated by the content provider. Optional.
optional bytes provider_client_token = 6;
// 4cc code specifying the CENC protection scheme as defined in the CENC 3.0
// specification. Propagated from Widevine PSSH box. Optional.
optional uint32 protection_scheme = 7;
// 8 byte verification field "HDCPDATA" followed by unsigned 32 bit minimum
// HDCP SRM version (whether the version is for HDCP1 SRM or HDCP2 SRM
// depends on client max_hdcp_version).
// MOE:begin_strip
// Additional details at:
// http://doc/1MYwkQjcdeP7eMAZGeYCvjlXMO0MqRKTUYk2SlsoSXXc/#heading=h.8l3xqpa3rvfi.
// MOE:end_strip
optional bytes srm_requirement = 8;
// If present this contains a signed SRM file (either HDCP1 SRM or HDCP2 SRM
// depending on client max_hdcp_version) that should be installed on the
// client device.
optional bytes srm_update = 9;
// Indicates the status of any type of platform verification performed by the
// server.
optional PlatformVerificationStatus platform_verification_status = 10
[default = PLATFORM_NO_VERIFICATION];
// IDs of the groups for which keys are delivered in this license, if any.
repeated bytes group_ids = 11;
}
enum ProtocolVersion {
VERSION_2_0 = 20;
VERSION_2_1 = 21;
VERSION_2_2 = 22;
}
message LicenseRequest {
message ContentIdentification {
message WidevinePsshData {
repeated bytes pssh_data = 1;
optional LicenseType license_type = 2;
optional bytes request_id = 3; // Opaque, client-specified.
}
message WebmKeyId {
optional bytes header = 1;
optional LicenseType license_type = 2;
optional bytes request_id = 3; // Opaque, client-specified.
}
message ExistingLicense {
optional LicenseIdentification license_id = 1;
optional int64 seconds_since_started = 2;
optional int64 seconds_since_last_played = 3;
optional bytes session_usage_table_entry = 4;
}
message InitData {
enum InitDataType {
CENC = 1;
WEBM = 2;
}
optional InitDataType init_data_type = 1 [default = CENC];
optional bytes init_data = 2;
optional LicenseType license_type = 3;
optional bytes request_id = 4;
}
oneof content_id_variant {
// Exactly one of these must be present.
WidevinePsshData widevine_pssh_data = 1;
WebmKeyId webm_key_id = 2;
ExistingLicense existing_license = 3;
InitData init_data = 4;
}
}
enum RequestType {
NEW = 1;
RENEWAL = 2;
RELEASE = 3;
}
// The client_id provides information authenticating the calling device. It
// contains the Widevine keybox token that was installed on the device at the
// factory. This field or encrypted_client_id below is required for a valid
// license request, but both should never be present in the same request.
optional ClientIdentification client_id = 1;
optional ContentIdentification content_id = 2;
optional RequestType type = 3;
// Time of the request in seconds (UTC) as set by the client.
optional int64 request_time = 4;
// Old-style decimal-encoded string key control nonce.
optional bytes key_control_nonce_deprecated = 5;
optional ProtocolVersion protocol_version = 6 [default = VERSION_2_0];
// New-style uint32 key control nonce, please use instead of
// key_control_nonce_deprecated.
optional uint32 key_control_nonce = 7;
// Encrypted ClientIdentification message, used for privacy purposes.
optional EncryptedClientIdentification encrypted_client_id = 8;
}
message LicenseError {
enum Error {
// The device credentials are invalid. The device must re-provision.
INVALID_DRM_DEVICE_CERTIFICATE = 1;
// The device credentials have been revoked. Re-provisioning is not
// possible.
REVOKED_DRM_DEVICE_CERTIFICATE = 2;
// The service is currently unavailable due to the backend being down
// or similar circumstances.
SERVICE_UNAVAILABLE = 3;
}
optional Error error_code = 1;
}
message MetricData {
enum MetricType {
// The time spent in the 'stage', specified in microseconds.
LATENCY = 1;
// The UNIX epoch timestamp at which the 'stage' was first accessed in
// microseconds.
TIMESTAMP = 2;
}
message TypeValue {
optional MetricType type = 1;
// The value associated with 'type'. For example if type == LATENCY, the
// value would be the time in microseconds spent in this 'stage'.
optional int64 value = 2 [default = 0];
}
// 'stage' that is currently processing the SignedMessage. Required.
optional string stage_name = 1;
// metric and associated value.
repeated TypeValue metric_data = 2;
}
message VersionInfo {
// License SDK version reported by the Widevine License SDK. This field
// is populated automatically by the SDK.
optional string license_sdk_version = 1;
// Version of the service hosting the license SDK. This field is optional.
// It may be provided by the hosting service.
optional string license_service_version = 2;
}
message SignedMessage {
enum MessageType {
LICENSE_REQUEST = 1;
LICENSE = 2;
ERROR_RESPONSE = 3;
SERVICE_CERTIFICATE_REQUEST = 4;
SERVICE_CERTIFICATE = 5;
SUB_LICENSE = 6;
CAS_LICENSE_REQUEST = 7;
CAS_LICENSE = 8;
EXTERNAL_LICENSE_REQUEST = 9;
EXTERNAL_LICENSE = 10;
}
enum SessionKeyType {
UNDEFINED = 0;
WRAPPED_AES_KEY = 1;
EPHERMERAL_ECC_PUBLIC_KEY = 2;
}
optional MessageType type = 1;
optional bytes msg = 2;
// Required field that contains the signature of the bytes of msg.
// For license requests, the signing algorithm is determined by the
// certificate contained in the request.
// For license responses, the signing algorithm is HMAC with signing key based
// on |session_key|.
optional bytes signature = 3;
// If populated, the contents of this field will be signaled by the
// |session_key_type| type. If the |session_key_type| is WRAPPED_AES_KEY the
// key is the bytes of an encrypted AES key. If the |session_key_type| is
// EPHERMERAL_ECC_PUBLIC_KEY the field contains the bytes of an RFC5208 ASN1
// serialized ECC public key.
optional bytes session_key = 4;
// Remote attestation data which will be present in the initial license
// request for ChromeOS client devices operating in verified mode. Remote
// attestation challenge data is |msg| field above. Optional.
optional RemoteAttestation remote_attestation = 5;
// MOE:begin_strip
// Design doc at:
// http://doc/1LqEzxw1v2CVBx_zv5lVX55FcGKjx6JLnFdXjEUbNTOQ
// MOE:end_strip
repeated MetricData metric_data = 6;
// Version information from the SDK and license service. This information is
// provided in the license response.
optional VersionInfo service_version_info = 7;
// Optional field that contains the algorithm type used to generate the
// session_key and signature in a LICENSE message.
optional SessionKeyType session_key_type = 8 [default = WRAPPED_AES_KEY];
// The core message is the simple serialization of fields used by OEMCrypto.
// This field was introduced in OEMCrypto API v16.
optional bytes oemcrypto_core_message = 9;
}

View File

@@ -0,0 +1,24 @@
// Copyright 2017 Google LLC. All rights reserved.
//
// Description:
// Remote attestation is used by ChromeOS device to authenticate itself
// to Widevine services for both licensing and keybox provisioning.
syntax = "proto2";
package video_widevine;
import "chromium_deps/cdm/protos/defs/client_identification.proto";
option optimize_for = LITE_RUNTIME;
message RemoteAttestation {
// Encrypted ClientIdentification message containing the device remote
// attestation certificate. Required.
optional EncryptedClientIdentification certificate = 1;
// Bytes of salt which were added to the remote attestation challenge prior to
// signing it. Required.
optional bytes salt = 2;
// Signed remote attestation challenge + salt. Required.
optional bytes signature = 3;
}

View File

@@ -0,0 +1,10 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef CDM_PROTOS_LICENSE_PROTOCOL_PB_H_
#define CDM_PROTOS_LICENSE_PROTOCOL_PB_H_
// Protobuf generated code doesn't like it when include prefixes are stripped
// off. So simply including the actual generated protobuf C++ header.
#include "chromium_deps/cdm/protos/defs/license_protocol.pb.h"
#endif // CDM_PROTOS_LICENSE_PROTOCOL_PB_H_

View File

@@ -0,0 +1,12 @@
# Copyright 2020 Google LLC. All Rights Reserved.
cc_library(
name = "testing",
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/testing/include/gmock",
"//chromium_deps/testing/include/gtest",
"//external:gtest",
"//external:gtest_main",
],
)

View File

@@ -0,0 +1,11 @@
# Copyright 2020 Google LLC. All Rights Reserved.
cc_library(
name = "gmock",
hdrs = [
"gmock.h",
],
include_prefix = "testing/gmock/include/gmock/",
strip_include_prefix = "//chromium_deps/testing/include/gmock/",
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef TESTING_GMOCK_INCLUDE_GMOCK_H_
#define TESTING_GMOCK_INCLUDE_GMOCK_H_
#include "gmock/gmock.h"
#endif // TESTING_GMOCK_INCLUDE_GMOCK_H_

View File

@@ -0,0 +1,11 @@
# Copyright 2020 Google LLC. All Rights Reserved.
cc_library(
name = "gtest",
hdrs = [
"gtest.h",
],
include_prefix = "testing/gtest/include/gtest/",
strip_include_prefix = "//chromium_deps/testing/include/gtest/",
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef TESTING_GUNIT_INCLUDE_GUNIT_H_
#define TESTING_GUNIT_INCLUDE_GUNIT_H_
#include "gtest/gtest.h"
#endif // TESTING_GUNIT_INCLUDE_GUNIT_H_

View File

@@ -0,0 +1,27 @@
# Copyright 2020 Google LLC. All Rights Reserved.
cc_library(
name = "boringssl",
hdrs = [
"src/include/openssl/aead.h",
"src/include/openssl/aes.h",
"src/include/openssl/bio.h",
"src/include/openssl/bn.h",
"src/include/openssl/bytestring.h",
"src/include/openssl/cmac.h",
"src/include/openssl/err.h",
"src/include/openssl/evp.h",
"src/include/openssl/hmac.h",
"src/include/openssl/mem.h",
"src/include/openssl/pem.h",
"src/include/openssl/rand.h",
"src/include/openssl/rsa.h",
"src/include/openssl/sha.h",
"src/include/openssl/x509.h",
],
strip_include_prefix = "//chromium_deps",
visibility = ["//visibility:public"],
deps = [
"//external:boringssl",
],
)

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_AEAD_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_AEAD_H_
#include "openssl/aead.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_AEAD_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_AES_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_AES_H_
#include "openssl/aes.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_AES_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_BIO_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_BIO_H_
#include "openssl/bio.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_BIO_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_BN_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_BN_H_
#include "openssl/bn.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_BN_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_BYTESTRING_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_BYTESTRING_H_
#include "openssl/bytestring.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_BYTESTRING_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_CMAC_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_CMAC_H_
#include "openssl/cmac.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_CMAC_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_ERR_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_ERR_H_
#include "openssl/err.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_ERR_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_EVP_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_EVP_H_
#include "openssl/evp.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_EVP_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_HMAC_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_HMAC_H_
#include "openssl/hmac.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_HMAC_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_MEM_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_MEM_H_
#include "openssl/mem.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_MEM_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_PEM_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_PEM_H_
#include "openssl/pem.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_PEM_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_RAND_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_RAND_H_
#include "openssl/rand.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_RAND_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_RSA_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_RSA_H_
#include "openssl/rsa.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_RSA_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_SHA_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_SHA_H_
#include "openssl/sha.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_SHA_H_

View File

@@ -0,0 +1,8 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_X509_H_
#define THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_X509_H_
#include "openssl/x509.h"
#endif // THIRD_PARTY_BORINGSSL_SRC_INCLUDE_OPENSSL_X509_H_

214
whitebox/crypto_utils/BUILD Normal file
View File

@@ -0,0 +1,214 @@
################################################################################
# Copyright 2017 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
#
# Constants, data structures, util classes for Widevine libraries.
cc_library(
name = "aes_cbc_decryptor",
srcs = ["aes_cbc_decryptor.cc"],
hdrs = ["aes_cbc_decryptor.h"],
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/base:glog",
"//chromium_deps/third_party/boringssl",
],
)
cc_test(
name = "aes_cbc_decryptor_test",
size = "small",
srcs = ["aes_cbc_decryptor_test.cc"],
deps = [
":aes_cbc_decryptor",
"//chromium_deps/testing",
],
)
cc_library(
name = "aes_cbc_encryptor",
srcs = ["aes_cbc_encryptor.cc"],
hdrs = ["aes_cbc_encryptor.h"],
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/base:glog",
"//chromium_deps/third_party/boringssl",
],
)
cc_test(
name = "aes_cbc_encryptor_test",
size = "small",
srcs = ["aes_cbc_encryptor_test.cc"],
deps = [
":aes_cbc_encryptor",
"//chromium_deps/base:glog",
"//chromium_deps/testing",
],
)
cc_library(
name = "aes_ctr_encryptor",
srcs = ["aes_ctr_encryptor.cc"],
hdrs = ["aes_ctr_encryptor.h"],
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/base:glog",
"//chromium_deps/third_party/boringssl",
],
)
cc_test(
name = "aes_ctr_encryptor_test",
size = "small",
srcs = ["aes_ctr_encryptor_test.cc"],
deps = [
":aes_ctr_encryptor",
"//chromium_deps/testing",
],
)
cc_library(
name = "crypto_util",
srcs = ["crypto_util.cc"],
hdrs = ["crypto_util.h"],
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/base:glog",
"//chromium_deps/third_party/boringssl",
],
)
cc_test(
name = "crypto_util_test",
size = "small",
srcs = ["crypto_util_test.cc"],
deps = [
":crypto_util",
"//chromium_deps/testing",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "private_key_util",
hdrs = ["private_key_util.h"],
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/third_party/boringssl",
],
)
cc_library(
name = "rsa_util",
srcs = ["rsa_util.cc"],
hdrs = ["rsa_util.h"],
visibility = ["//visibility:public"],
deps = [
":private_key_util",
"//chromium_deps/base:glog",
"//chromium_deps/third_party/boringssl",
],
)
cc_test(
name = "rsa_util_test",
size = "medium",
timeout = "short",
srcs = ["rsa_util_test.cc"],
deps = [
":rsa_test_keys",
":rsa_util",
"//chromium_deps/testing",
"//chromium_deps/third_party/boringssl",
],
)
cc_library(
name = "openssl_util",
hdrs = ["openssl_util.h"],
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/third_party/boringssl",
],
)
cc_library(
name = "random_util",
srcs = ["random_util.cc"],
hdrs = ["random_util.h"],
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/base:glog",
"//chromium_deps/third_party/boringssl",
],
)
cc_test(
name = "random_util_test",
size = "small",
srcs = ["random_util_test.cc"],
deps = [
":random_util",
"//chromium_deps/testing",
],
)
cc_library(
name = "rsa_key",
srcs = ["rsa_key.cc"],
hdrs = ["rsa_key.h"],
visibility = ["//visibility:public"],
deps = [
":rsa_util",
":sha_util",
"//chromium_deps/third_party/boringssl",
],
)
cc_test(
name = "rsa_key_test",
size = "medium",
timeout = "short",
srcs = ["rsa_key_test.cc"],
deps = [
":rsa_key",
":rsa_test_keys",
":rsa_util",
"//chromium_deps/testing",
],
)
cc_library(
name = "rsa_test_keys",
testonly = 1,
srcs = ["rsa_test_keys.cc"],
hdrs = ["rsa_test_keys.h"],
visibility = ["//visibility:public"],
deps = [
],
)
cc_library(
name = "sha_util",
srcs = ["sha_util.cc"],
hdrs = ["sha_util.h"],
visibility = ["//visibility:public"],
deps = [
"//chromium_deps/third_party/boringssl",
],
)
cc_test(
name = "sha_util_test",
srcs = ["sha_util_test.cc"],
deps = [
":sha_util",
"//chromium_deps/testing",
"@abseil_repo//absl/strings",
],
)

View File

@@ -0,0 +1,62 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "crypto_utils/aes_cbc_decryptor.h"
#include <cstddef>
#include <cstdint>
#include <vector>
#include "base/check.h"
#include "base/logging.h"
namespace widevine {
namespace {
constexpr size_t kAesBlockSize = 16;
} // namespace
bool AesCbcDecryptor::SetKey(const uint8_t* key, size_t key_size) {
DCHECK(key);
if (key_size != kAesBlockSize && key_size != kAesBlockSize * 2) {
LOG(WARNING) << "Incorrect key size " << key_size;
return false;
}
if (AES_set_decrypt_key(key, key_size * 8, &aes_key_) != 0) {
LOG(WARNING) << "Invalid AES key.";
return false;
}
aes_key_size_ = key_size;
return true;
}
bool AesCbcDecryptor::Decrypt(const uint8_t* iv,
size_t iv_size,
const uint8_t* input_data,
size_t input_data_size,
uint8_t* output_data) {
DCHECK(iv);
DCHECK(input_data);
DCHECK(output_data);
if (aes_key_size_ == 0) {
LOG(WARNING) << "This class has not been initialized.";
return false;
}
// IV is allowed to be either AES BLOCK size or half of it.
if (iv_size != kAesBlockSize && iv_size != kAesBlockSize / 2) {
LOG(WARNING) << "Invalid IV size " << iv_size;
return false;
}
if ((input_data_size % kAesBlockSize) != 0) {
LOG(WARNING) << "Input data size must be multiple of 16: "
<< input_data_size;
return false;
}
std::vector<uint8_t> local_iv(iv, iv + iv_size);
local_iv.resize(kAesBlockSize);
AES_cbc_encrypt(input_data, output_data, input_data_size, &aes_key_,
local_iv.data(), AES_DECRYPT);
return true;
}
} // namespace widevine

View File

@@ -0,0 +1,32 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_CRYPTO_UTILS_AES_CBC_DECRYPTOR_H_
#define WHITEBOX_CRYPTO_UTILS_AES_CBC_DECRYPTOR_H_
#include <cstddef>
#include <cstdint>
#include "third_party/boringssl/src/include/openssl/aes.h"
namespace widevine {
class AesCbcDecryptor {
public:
// Prepares the encryptor with the key.
virtual bool SetKey(const uint8_t* key, size_t key_size);
// Puts the decrypted result of |input_data| into |output_data|.
virtual bool Decrypt(const uint8_t* iv,
size_t iv_size,
const uint8_t* input_data,
size_t input_data_size,
uint8_t* output_data);
private:
AES_KEY aes_key_;
size_t aes_key_size_ = 0;
};
} // namespace widevine
#endif // WHITEBOX_CRYPTO_UTILS_AES_CBC_DECRYPTOR_H_

View File

@@ -0,0 +1,91 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "crypto_utils/aes_cbc_decryptor.h"
#include <cstdint>
#include <vector>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::ElementsAreArray;
namespace widevine {
TEST(AesCbcDecryptorTest, NistTestCase) {
// NIST SP 800-38A test vector F.2.1 CBC-AES128.Encrypt.
static const uint8_t kAesCbcKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae,
0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88,
0x09, 0xcf, 0x4f, 0x3c};
static const uint8_t kAesCbcIv[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f};
static const uint8_t kAesCbcPlaintext[] = {
// Block #1
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
0x73, 0x93, 0x17, 0x2a,
// Block #2
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac,
0x45, 0xaf, 0x8e, 0x51,
// Block #3
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19,
0x1a, 0x0a, 0x52, 0xef,
// Block #4
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
0xe6, 0x6c, 0x37, 0x10};
static const uint8_t kAesCbcCiphertext[] = {
// Block #1
0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b,
0x12, 0xe9, 0x19, 0x7d,
// Block #2
0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a,
0x91, 0x76, 0x78, 0xb2,
// Block #3
0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e,
0x22, 0x22, 0x95, 0x16,
// Block #4
0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30,
0x75, 0x86, 0xe1, 0xa7};
AesCbcDecryptor decryptor;
ASSERT_TRUE(decryptor.SetKey(kAesCbcKey, sizeof(kAesCbcKey)));
std::vector<uint8_t> output(sizeof(kAesCbcCiphertext));
ASSERT_TRUE(decryptor.Decrypt(kAesCbcIv, sizeof(kAesCbcIv), kAesCbcCiphertext,
sizeof(kAesCbcCiphertext), output.data()));
EXPECT_THAT(output,
ElementsAreArray(kAesCbcPlaintext, sizeof(kAesCbcPlaintext)));
}
TEST(AesCbcEncryptorTest, NistTestCase2) {
// NIST SP 800-38A test vector F.2.1 CBC-AES128.Encrypt.
static const uint8_t kAesCbcKey[] = {
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
};
static const uint8_t kAesCbcIv[] = {
0x2B, 0x70, 0xA4, 0xB5, 0xC2, 0x67, 0x8F, 0xD7,
0xEC, 0x75, 0xB6, 0x44, 0xE8, 0xC7, 0x6A, 0x2B,
};
static const uint8_t kAesCbcPlaintext[] = {
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
};
static const uint8_t kAesCbcCiphertext[] = {
0xF5, 0x9B, 0x16, 0x1F, 0x4B, 0xBC, 0x45, 0xC5, 0x16, 0x02, 0x25,
0xD3, 0x7D, 0xDA, 0x22, 0xF4, 0xA7, 0x5D, 0x5F, 0x1A, 0x75, 0x2A,
0xD0, 0xFF, 0x4C, 0x78, 0x7E, 0x2B, 0x36, 0x37, 0xC1, 0x7F,
};
AesCbcDecryptor decryptor;
ASSERT_TRUE(decryptor.SetKey(kAesCbcKey, sizeof(kAesCbcKey)));
std::vector<uint8_t> output(sizeof(kAesCbcPlaintext));
ASSERT_TRUE(decryptor.Decrypt(kAesCbcIv, sizeof(kAesCbcIv), kAesCbcCiphertext,
sizeof(kAesCbcCiphertext), output.data()));
EXPECT_THAT(output,
ElementsAreArray(kAesCbcPlaintext, sizeof(kAesCbcPlaintext)));
}
} // namespace widevine

View File

@@ -0,0 +1,62 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "crypto_utils/aes_cbc_encryptor.h"
#include <cstddef>
#include <cstdint>
#include <vector>
#include "base/check.h"
#include "base/logging.h"
namespace widevine {
namespace {
constexpr size_t kAesBlockSize = 16;
} // namespace
bool AesCbcEncryptor::SetKey(const uint8_t* key, size_t key_size) {
DCHECK(key);
if (key_size != kAesBlockSize && key_size != kAesBlockSize * 2) {
LOG(WARNING) << "Incorrect key size " << key_size;
return false;
}
if (AES_set_encrypt_key(key, key_size * 8, &aes_key_) != 0) {
LOG(WARNING) << "Invalid AES key.";
return false;
}
aes_key_size_ = key_size;
return true;
}
bool AesCbcEncryptor::Encrypt(const uint8_t* iv,
size_t iv_size,
const uint8_t* input_data,
size_t input_data_size,
uint8_t* output_data) {
DCHECK(iv);
DCHECK(input_data);
DCHECK(output_data);
if (aes_key_size_ == 0) {
LOG(WARNING) << "This class has not been initialized.";
return false;
}
// IV is allowed to be either AES BLOCK size or half of it.
if (iv_size != kAesBlockSize && iv_size != kAesBlockSize / 2) {
LOG(WARNING) << "Invalid IV size " << iv_size;
return false;
}
if ((input_data_size % kAesBlockSize) != 0) {
LOG(WARNING) << "Input data size must be multiple of 16: "
<< input_data_size;
return false;
}
std::vector<uint8_t> local_iv(iv, iv + iv_size);
local_iv.resize(kAesBlockSize);
AES_cbc_encrypt(input_data, output_data, input_data_size, &aes_key_,
local_iv.data(), AES_ENCRYPT);
return true;
}
} // namespace widevine

View File

@@ -0,0 +1,32 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_CRYPTO_UTILS_AES_CBC_ENCRYPTOR_H_
#define WHITEBOX_CRYPTO_UTILS_AES_CBC_ENCRYPTOR_H_
#include <cstddef>
#include <cstdint>
#include "third_party/boringssl/src/include/openssl/aes.h"
namespace widevine {
class AesCbcEncryptor {
public:
// Prepares the encryptor with the key.
virtual bool SetKey(const uint8_t* key, size_t key_size);
// Puts the encrypted result of |input_data| into |output_data|.
virtual bool Encrypt(const uint8_t* iv,
size_t iv_size,
const uint8_t* input_data,
size_t input_data_size,
uint8_t* output_data);
private:
AES_KEY aes_key_;
size_t aes_key_size_ = 0;
};
} // namespace widevine
#endif // WHITEBOX_CRYPTO_UTILS_AES_CBC_ENCRYPTOR_H_

View File

@@ -0,0 +1,60 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "crypto_utils/aes_cbc_encryptor.h"
#include <cstdint>
#include <vector>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::ElementsAreArray;
namespace widevine {
TEST(AesCbcEncryptorTest, NistTestCase) {
// NIST SP 800-38A test vector F.2.1 CBC-AES128.Encrypt.
static const uint8_t kAesCbcKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae,
0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88,
0x09, 0xcf, 0x4f, 0x3c};
static const uint8_t kAesCbcIv[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f};
static const uint8_t kAesCbcPlaintext[] = {
// Block #1
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
0x73, 0x93, 0x17, 0x2a,
// Block #2
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac,
0x45, 0xaf, 0x8e, 0x51,
// Block #3
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19,
0x1a, 0x0a, 0x52, 0xef,
// Block #4
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
0xe6, 0x6c, 0x37, 0x10};
static const uint8_t kAesCbcCiphertext[] = {
// Block #1
0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b,
0x12, 0xe9, 0x19, 0x7d,
// Block #2
0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a,
0x91, 0x76, 0x78, 0xb2,
// Block #3
0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e,
0x22, 0x22, 0x95, 0x16,
// Block #4
0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30,
0x75, 0x86, 0xe1, 0xa7};
AesCbcEncryptor encryptor;
ASSERT_TRUE(encryptor.SetKey(kAesCbcKey, sizeof(kAesCbcKey)));
std::vector<uint8_t> output(sizeof(kAesCbcPlaintext));
ASSERT_TRUE(encryptor.Encrypt(kAesCbcIv, sizeof(kAesCbcIv), kAesCbcPlaintext,
sizeof(kAesCbcPlaintext), output.data()));
EXPECT_THAT(output,
ElementsAreArray(kAesCbcCiphertext, sizeof(kAesCbcCiphertext)));
}
} // namespace widevine

View File

@@ -0,0 +1,79 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#include "crypto_utils/aes_ctr_encryptor.h"
#include <cstddef>
#include <cstdint>
#include <vector>
#include "base/check.h"
#include "base/logging.h"
namespace widevine {
namespace {
constexpr size_t kAesBlockSize = 16;
// Increment an 8-byte counter by 1. Return true if overflowed.
bool Increment64(uint8_t* counter) {
DCHECK(counter);
for (int i = 7; i >= 0; --i) {
if (++counter[i] != 0)
return false;
}
return true;
}
} // namespace
bool AesCtrEncryptor::SetKey(const uint8_t* key, size_t key_size) {
DCHECK(key);
if (key_size != kAesBlockSize && key_size != kAesBlockSize * 2) {
LOG(WARNING) << "Incorrect key size " << key_size;
return false;
}
if (AES_set_encrypt_key(key, key_size * 8, &aes_key_) != 0) {
LOG(WARNING) << "Invalid AES key.";
return false;
}
aes_key_size_ = key_size;
return true;
}
bool AesCtrEncryptor::Encrypt(const uint8_t* iv,
size_t iv_size,
const uint8_t* input_data,
size_t input_data_size,
uint8_t* output_data) {
DCHECK(iv);
DCHECK(input_data);
DCHECK(output_data);
if (aes_key_size_ == 0) {
LOG(WARNING) << "This class has not been initialized.";
return false;
}
// IV is allowed to be either AES BLOCK size or half of it.
if (iv_size != kAesBlockSize && iv_size != kAesBlockSize / 2) {
LOG(WARNING) << "Invalid IV size " << iv_size;
return false;
}
std::vector<uint8_t> counter(iv, iv + iv_size);
counter.resize(kAesBlockSize);
std::vector<uint8_t> encrypted_counter(kAesBlockSize);
for (size_t i = 0; i < input_data_size; i += 16) {
AES_encrypt(counter.data(), encrypted_counter.data(), &aes_key_);
// As mentioned in ISO/IEC 23001-7:2016 CENC spec, of the 16 byte counter
// block, bytes 8 to 15 (i.e. the least significant bytes) are used as a
// simple 64 bit unsigned integer that is incremented by one for each
// subsequent block of sample data processed and is kept in network byte
// order.
Increment64(counter.data() + 8);
for (size_t j = 0; j < kAesBlockSize && i + j < input_data_size; j++)
output_data[i + j] = input_data[i + j] ^ encrypted_counter[j];
}
return true;
}
} // namespace widevine

View File

@@ -0,0 +1,32 @@
// Copyright 2020 Google LLC. All Rights Reserved.
#ifndef WHITEBOX_CRYPTO_UTILS_AES_CTR_ENCRYPTOR_H_
#define WHITEBOX_CRYPTO_UTILS_AES_CTR_ENCRYPTOR_H_
#include <cstddef>
#include <cstdint>
#include "third_party/boringssl/src/include/openssl/aes.h"
namespace widevine {
class AesCtrEncryptor {
public:
// Prepares the encryptor with the key.
virtual bool SetKey(const uint8_t* key, size_t key_size);
// Puts the encrypted result of |input_data| into |output_data|.
virtual bool Encrypt(const uint8_t* iv,
size_t iv_size,
const uint8_t* input_data,
size_t input_data_size,
uint8_t* output_data);
private:
AES_KEY aes_key_;
size_t aes_key_size_ = 0;
};
} // namespace widevine
#endif // WHITEBOX_CRYPTO_UTILS_AES_CTR_ENCRYPTOR_H_

View File

@@ -0,0 +1,86 @@
#include "crypto_utils/aes_ctr_encryptor.h"
#include <cstdint>
#include <vector>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::ElementsAreArray;
namespace widevine {
namespace {
// From NIST SP 800-38a test case: - F.5.1 CTR-AES128.Encrypt
// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
const uint8_t kAesKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
const uint8_t kAesIv[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
const uint8_t kAesCtrPlaintext[] = {
// Block #1
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
0x73, 0x93, 0x17, 0x2a,
// Block #2
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac,
0x45, 0xaf, 0x8e, 0x51,
// Block #3
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19,
0x1a, 0x0a, 0x52, 0xef,
// Block #4
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
0xe6, 0x6c, 0x37, 0x10};
const uint8_t kAesCtrCiphertext[] = {
// Block #1
0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64,
0x99, 0x0d, 0xb6, 0xce,
// Block #2
0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b,
0xb9, 0xff, 0xfd, 0xff,
// Block #3
0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02,
0x0d, 0xb0, 0x3e, 0xab,
// Block #4
0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0,
0xf3, 0x00, 0x9c, 0xee};
const uint8_t kIv128Zero[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const uint8_t kIv128Max64[] = {0, 0, 0, 0, 0, 0, 0, 0,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
} // namespace
TEST(AesCtrEncryptorTest, NistTestCase) {
AesCtrEncryptor encryptor;
ASSERT_TRUE(encryptor.SetKey(kAesKey, sizeof(kAesKey)));
std::vector<uint8_t> output(sizeof(kAesCtrPlaintext));
ASSERT_TRUE(encryptor.Encrypt(kAesIv, sizeof(kAesIv), kAesCtrPlaintext,
sizeof(kAesCtrPlaintext), output.data()));
EXPECT_THAT(output,
ElementsAreArray(kAesCtrCiphertext, sizeof(kAesCtrCiphertext)));
}
TEST(AesCtrEncryptorTest, 128BitIVMax64BoundaryCaseEncryption) {
AesCtrEncryptor encryptor;
ASSERT_TRUE(encryptor.SetKey(kAesKey, sizeof(kAesKey)));
std::vector<uint8_t> output(sizeof(kAesCtrPlaintext));
ASSERT_TRUE(encryptor.Encrypt(kIv128Max64, sizeof(kIv128Max64),
kAesCtrPlaintext, sizeof(kAesCtrPlaintext),
output.data()));
// There are four blocks in plain text. The first block should be encrypted
// with IV = kIv128Max64, the subsequent blocks should be encrypted with iv 0
// to 3.
std::vector<uint8_t> output2(sizeof(kAesCtrPlaintext));
ASSERT_TRUE(encryptor.Encrypt(kIv128Max64, sizeof(kIv128Max64),
kAesCtrPlaintext, 16, &output2[0]));
ASSERT_TRUE(encryptor.Encrypt(kIv128Zero, sizeof(kIv128Zero),
&kAesCtrPlaintext[16],
sizeof(kAesCtrPlaintext) - 16, &output2[16]));
EXPECT_EQ(output, output2);
}
} // namespace widevine

View File

@@ -0,0 +1,204 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Implementation of Common crypto utilities used by Widevine services.
#include "crypto_utils/crypto_util.h"
#include "base/check.h"
#include "third_party/boringssl/src/include/openssl/aes.h"
#include "third_party/boringssl/src/include/openssl/cmac.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
#include "third_party/boringssl/src/include/openssl/hmac.h"
#include "third_party/boringssl/src/include/openssl/sha.h"
namespace widevine {
namespace crypto_util {
const char kWrappingKeyLabel[] = "ENCRYPTION";
const int kWrappingKeySizeBits = 128;
const char kSigningKeyLabel[] = "AUTHENTICATION";
const int kSigningKeySizeBits = 256;
const size_t kSigningKeySizeBytes = 32;
const char kIvMasterKey[] = "1234567890123456";
const char kIvLabel[] = "IV_ENCRYPTION";
const int kIvSizeBits = 128;
const char kKeyIdMasterKey[] = "0123456789abcdef";
const char kKeyIdLabel[] = "KEY_ID_ENCRYPTION";
const int kKeyIdSizeBits = 128;
const char kGroupKeyLabel[] = "GROUP_ENCRYPTION";
// TODO(user): This is a temporary key for development. Replace this with
// a real group master key in keystore.
// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy
// like VerifySignatureHmacSha1.
const char kPhonyGroupMasterKey[] = "fedcba9876543210";
const int kAes128KeySizeBits = 128;
const int kAes128KeySizeBytes = 16;
const int kAes256KeySizeBytes = 32;
const char kKeyboxV3Label[] = "Keyboxv3";
const int kAesBlockSizeBits = AES_BLOCK_SIZE * 8;
const int kAesMaxDerivedBlocks = 255;
const uint32_t kCENCSchemeID = 0x63656E63; // 'cenc' (AES-CTR): 0x63656E63
const uint32_t kCBC1SchemeID = 0x63626331; // 'cbc1' (AES-CBC): 0x63626331
const uint32_t kCENSSchemeID =
0x63656E73; // 'cens' (AES-CTR subsample): 0x63656E73
const uint32_t kCBCSSchemeID =
0x63626373; // 'cbcs' (AES-CBC subsample): 0x63626373
// Creates a SHA-256 HMAC signature for the given message.
std::string CreateSignatureHmacSha256(absl::string_view key,
absl::string_view message) {
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha256());
HMAC_Update(&ctx, reinterpret_cast<const unsigned char*>(message.data()),
message.size());
unsigned char digest[SHA256_DIGEST_LENGTH];
unsigned int digest_len;
HMAC_Final(&ctx, digest, &digest_len);
HMAC_CTX_cleanup(&ctx);
std::string s(reinterpret_cast<char*>(digest), digest_len);
return s;
}
// Compares the SHA-256 HMAC against the provided signature.
bool VerifySignatureHmacSha256(absl::string_view key,
absl::string_view signature,
absl::string_view message) {
return CreateSignatureHmacSha256(key, message) == signature;
}
// Creates a SHA-1 HMAC signature for the given message.
std::string CreateSignatureHmacSha1(absl::string_view key,
absl::string_view message) {
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha1());
HMAC_Update(&ctx, reinterpret_cast<const unsigned char*>(message.data()),
message.size());
unsigned char digest[SHA_DIGEST_LENGTH];
unsigned int digest_len;
HMAC_Final(&ctx, digest, &digest_len);
HMAC_CTX_cleanup(&ctx);
std::string s(reinterpret_cast<char*>(digest), digest_len);
return s;
}
// Compares the SHA-1 HMAC against the provided signature.
bool VerifySignatureHmacSha1(absl::string_view key,
absl::string_view signature,
absl::string_view message) {
return CreateSignatureHmacSha1(key, message) == signature;
}
// Derives a key from the provided AES 128 or 256 key and additional info.
std::string DeriveKey(absl::string_view key,
absl::string_view label,
absl::string_view context,
const uint32_t size_bits) {
// We only handle multiples of AES blocks (16 bytes) with a maximum of 255
// blocks.
const uint32_t output_block_count = size_bits / kAesBlockSizeBits;
if (size_bits % kAesBlockSizeBits ||
output_block_count > kAesMaxDerivedBlocks) {
return "";
}
const EVP_CIPHER* cipher = nullptr;
const size_t key_size_bytes = key.size();
switch (key_size_bytes) {
case kAes128KeySizeBytes:
cipher = EVP_aes_128_cbc();
break;
case kAes256KeySizeBytes:
cipher = EVP_aes_256_cbc();
break;
default:
return "";
}
std::string result;
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
for (unsigned char counter = 0; counter < output_block_count; counter++) {
if (CMAC_Init(cmac_ctx, key.data(), key_size_bytes, cipher, nullptr)) {
std::string message;
message.append(1, counter + 1);
message.append(label.data(), label.size());
message.append(1, '\0');
message.append(context.data(), context.size());
message.append(1, (size_bits >> 24) & 0xFF);
message.append(1, (size_bits >> 16) & 0xFF);
message.append(1, (size_bits >> 8) & 0xFF);
message.append(1, size_bits & 0xFF);
if (CMAC_Update(cmac_ctx,
reinterpret_cast<const uint8_t*>(message.data()),
message.size())) {
size_t reslen;
unsigned char res[AES_BLOCK_SIZE];
if (CMAC_Final(cmac_ctx, res, &reslen)) {
result.append(reinterpret_cast<const char*>(res), reslen);
}
DCHECK(reslen == AES_BLOCK_SIZE);
}
}
}
CMAC_CTX_free(cmac_ctx);
return result;
}
// Derives an IV from the provided info.
std::string DeriveIv(absl::string_view context) {
return DeriveKey(kIvMasterKey, kIvLabel, context, kIvSizeBits);
}
// Derives a key ID from the provided info.
std::string DeriveKeyId(absl::string_view context) {
return DeriveKey(kKeyIdMasterKey, kKeyIdLabel, context, kKeyIdSizeBits);
}
std::string DeriveGroupSessionKey(absl::string_view context,
const uint32_t size_bits) {
return DeriveKey(kPhonyGroupMasterKey, kGroupKeyLabel, context, size_bits);
}
std::string DeriveSigningKey(absl::string_view key,
absl::string_view context,
const uint32_t size_bits) {
return DeriveKey(key, kSigningKeyLabel, context, size_bits);
}
bool FourCCEncryptionSchemeIDFromString(const std::string& requested,
uint32_t* four_cc_code) {
if (requested.size() != 4 || four_cc_code == nullptr)
return false;
uint32_t result = 0;
for (auto i = 0; i < 4; ++i) {
result <<= 8;
result |= requested[i];
}
switch (result) {
case kCENCSchemeID:
case kCBC1SchemeID:
case kCENSSchemeID:
case kCBCSSchemeID:
*four_cc_code = result;
return true;
default:
return false;
}
}
} // namespace crypto_util
} // namespace widevine

View File

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

View File

@@ -0,0 +1,307 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Unit tests for the crypto_util helper functions.
#include "crypto_utils/crypto_util.h"
#include <string>
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/boringssl/src/include/openssl/aes.h"
namespace widevine {
namespace crypto_util {
const char kCENCStr[] = "cenc";
const char kCBC1Str[] = "cbc1";
const char kCENSStr[] = "cens";
const char kCBCSStr[] = "cbcs";
static unsigned char kAes128KeyData[] = {0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82,
0x32, 0x9e, 0x6b, 0x3b, 0x4e, 0x29,
0xfa, 0x3b, 0x00, 0x4b};
static unsigned char kAes256KeyData[] = {
0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e, 0x6b, 0x3b, 0x4e,
0x29, 0xfa, 0x3b, 0x00, 0x4b, 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82,
0x32, 0x9e, 0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b};
static unsigned char kAes128IvData[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f};
class CryptoUtilTest : public ::testing::Test {
public:
CryptoUtilTest()
: aes_128_key_(kAes128KeyData, kAes128KeyData + sizeof(kAes128KeyData)),
aes_256_key_(kAes256KeyData, kAes256KeyData + sizeof(kAes256KeyData)),
iv_128_(kAes128IvData, kAes128IvData + sizeof(kAes128IvData)) {}
protected:
std::string aes_128_key_;
std::string aes_256_key_;
std::string iv_128_;
};
TEST_F(CryptoUtilTest, DeriveAes128MasterKeyTest) {
unsigned char label[] = {0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1, 0x92,
0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08, 0xbc, 0x23,
0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60, 0xd1, 0xcf, 0x6a,
0x4d, 0x40, 0xa1, 0xe3, 0xfe, 0xd3, 0xe9, 0xa6, 0x58,
0x4c, 0xd4, 0xad, 0xa4, 0xa2};
unsigned char context[] = {0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d,
0x6d, 0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5,
0x5b, 0x80, 0x81, 0x91, 0xff};
unsigned char output0[] = {0xd5, 0xad, 0x2d, 0xb1, 0x5a, 0x06, 0xcb, 0x50,
0xf2, 0x59, 0x5a, 0xb2, 0xb2, 0x0d, 0x44, 0x4e};
unsigned char output1[] = {
0xdf, 0x38, 0x45, 0x97, 0x5d, 0x7a, 0x81, 0xb4, 0x94, 0x86, 0xaf, 0x0c,
0xdc, 0x4d, 0xeb, 0x62, 0x31, 0x39, 0x67, 0x8f, 0xff, 0x5d, 0x68, 0x35,
0xdc, 0x89, 0x5f, 0x47, 0xca, 0xe0, 0x2d, 0x3a, 0x10, 0x24, 0xf8, 0x7e,
0x5b, 0x70, 0xe1, 0xa3, 0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22};
std::string label_str(label, label + sizeof(label));
std::string context_str(context, context + sizeof(context));
std::string result = DeriveKey(aes_128_key_, label_str, context_str, 128);
std::string output_128(output0, output0 + sizeof(output0));
ASSERT_EQ(result, output_128);
result = DeriveKey(aes_128_key_, label_str, context_str, 384);
std::string output_384(output1, output1 + sizeof(output1));
ASSERT_EQ(result, output_384);
}
TEST_F(CryptoUtilTest, DeriveAes256MasterKeyTest) {
const unsigned char label[] = {
0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1, 0x92, 0xa0, 0x34,
0x8a, 0x8b, 0x6b, 0x77, 0x08, 0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda,
0xfb, 0x60, 0xd1, 0xcf, 0x6a, 0x4d, 0x40, 0xa1, 0xe3, 0xfe, 0xd3,
0xe9, 0xa6, 0x58, 0x4c, 0xd4, 0xad, 0xa4, 0xa2};
const unsigned char context[] = {0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d,
0x6d, 0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5,
0x5b, 0x80, 0x81, 0x91, 0xff};
const unsigned char expected_128[] = {0x76, 0x36, 0x33, 0x0e, 0x0b, 0x2c,
0x38, 0xc2, 0x9e, 0x53, 0x23, 0x8d,
0x2e, 0xc6, 0x3a, 0x46};
const std::string label_str(label, label + sizeof(label));
const std::string context_str(context, context + sizeof(context));
std::string result = DeriveKey(aes_256_key_, label_str, context_str, 128);
EXPECT_EQ(std::string(expected_128, expected_128 + sizeof(expected_128)),
result)
<< ::absl::BytesToHexString(result);
const unsigned char expected_256[] = {
0xfb, 0x8f, 0xdf, 0x0e, 0x22, 0xfe, 0xf7, 0x2b, 0xd1, 0x9a, 0x1d,
0xd2, 0xcb, 0xb0, 0x11, 0x5c, 0x6c, 0xa7, 0xe1, 0x7f, 0x72, 0xce,
0x3a, 0x60, 0x34, 0x89, 0x6d, 0x08, 0xef, 0xde, 0x19, 0x45};
result = DeriveKey(aes_256_key_, label_str, context_str, 256);
EXPECT_EQ(std::string(expected_256, expected_256 + sizeof(expected_256)),
result)
<< ::absl::BytesToHexString(result);
const unsigned char expected_384[] = {
0x65, 0xbc, 0xe3, 0xf3, 0xfb, 0xfa, 0xce, 0x1d, 0x24, 0x63, 0x9c, 0x8f,
0x48, 0x0e, 0xbd, 0x76, 0xd1, 0x14, 0x0b, 0xb1, 0x3a, 0x3d, 0x6e, 0x30,
0xa9, 0xf4, 0x40, 0x35, 0x0d, 0x6b, 0xc5, 0x1e, 0x9c, 0xa9, 0x5f, 0xf9,
0xde, 0x96, 0xa0, 0xa4, 0x22, 0x62, 0x21, 0xc5, 0xd6, 0xd4, 0xf4, 0x6f};
result = DeriveKey(aes_256_key_, label_str, context_str, 384);
EXPECT_EQ(std::string(expected_384, expected_384 + sizeof(expected_384)),
result)
<< ::absl::BytesToHexString(result);
}
TEST_F(CryptoUtilTest, DeriveAesInvalidSizeModulus) {
// This is the control case that we correctly derive 128 bits.
EXPECT_NE("", DeriveKey(aes_128_key_, "foo", "bar", 128));
EXPECT_EQ("", DeriveKey(aes_128_key_, "foo", "bar", 127));
}
TEST_F(CryptoUtilTest, DeriveAesMaxBlocks) {
EXPECT_EQ(
255u * AES_BLOCK_SIZE,
DeriveKey(aes_128_key_, "foo", "bar", AES_BLOCK_SIZE * 8 * 255).size());
}
TEST_F(CryptoUtilTest, DeriveAesTooManyBlocks) {
EXPECT_EQ("",
DeriveKey(aes_128_key_, "foo", "bar", AES_BLOCK_SIZE * 8 * 256));
}
TEST_F(CryptoUtilTest, DeriveAes128InvalidKeySize) {
EXPECT_EQ("", DeriveKey(aes_128_key_.substr(0, 15), "foo", "bar", 128));
}
TEST_F(CryptoUtilTest, DeriveAes256InvalidKeySize) {
EXPECT_EQ("", DeriveKey(aes_256_key_.substr(0, 31), "foo", "bar", 128));
}
TEST_F(CryptoUtilTest, DeriveGroupSesionKey) {
unsigned char output[] = {0x92, 0x6c, 0x2f, 0x5, 0xa6, 0x4f, 0xff, 0xb1,
0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1};
std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128);
EXPECT_EQ(crypto_util::kAes128KeySizeBytes, group_session_key.size());
const std::string output_128(output, output + sizeof(output));
ASSERT_EQ(output_128, group_session_key);
}
TEST_F(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
unsigned char message_data[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, 0x99, 0x7a, 0x7d,
0x9b, 0x0c, 0xcf, 0xfd, 0xb2, 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad,
0x23, 0x91, 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, 0xf6,
0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, 0x99, 0x7a, 0x63, 0x47,
0x2e, 0x54, 0x35, 0xb5, 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2,
0x54, 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda};
std::string message(message_data, message_data + sizeof(message_data));
std::string signature(CreateSignatureHmacSha256(aes_128_key_, message));
ASSERT_EQ(signature.size(), 32u);
ASSERT_TRUE(VerifySignatureHmacSha256(aes_128_key_, signature, message));
}
TEST_F(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) {
unsigned char message_data[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, 0x99, 0x7a, 0x7d,
0x9b, 0x0c, 0xcf, 0xfd, 0xb2, 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad,
0x23, 0x91, 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, 0xf6,
0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, 0x99, 0x7a, 0x63, 0x47,
0x2e, 0x54, 0x35, 0xb5, 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2,
0x54, 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda};
std::string message(message_data, message_data + sizeof(message_data));
// Test with bogus key;
std::string bogus_key("bogus");
std::string signature(CreateSignatureHmacSha256(bogus_key, message));
// This should still produce an hmac signature.
ASSERT_EQ(signature.size(), 32u);
// Create valid signature to compare.
signature = CreateSignatureHmacSha256(aes_128_key_, message);
// Test with bogus key.
ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message));
// Test with munged signature.
signature[0] = 0xFF;
ASSERT_FALSE(VerifySignatureHmacSha256(aes_128_key_, signature, message));
// Test with bogus signature.
ASSERT_FALSE(VerifySignatureHmacSha256(aes_128_key_, "bogus", message));
}
TEST_F(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) {
unsigned char message_data[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, 0x99, 0x7a, 0x7d,
0x9b, 0x0c, 0xcf, 0xfd, 0xb2, 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad,
0x23, 0x91, 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, 0xf6,
0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, 0x99, 0x7a, 0x63, 0x47,
0x2e, 0x54, 0x35, 0xb5, 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2,
0x54, 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda};
std::string message(message_data, message_data + sizeof(message_data));
std::string signature(CreateSignatureHmacSha1(aes_128_key_, message));
ASSERT_EQ(20u, signature.size());
ASSERT_TRUE(VerifySignatureHmacSha1(aes_128_key_, signature, message));
}
TEST_F(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) {
unsigned char message_data[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, 0x99, 0x7a, 0x7d,
0x9b, 0x0c, 0xcf, 0xfd, 0xb2, 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad,
0x23, 0x91, 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, 0xf6,
0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, 0x99, 0x7a, 0x63, 0x47,
0x2e, 0x54, 0x35, 0xb5, 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2,
0x54, 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda};
std::string message(message_data, message_data + sizeof(message_data));
// Test with bogus key;
std::string bogus_key("bogus");
std::string signature(CreateSignatureHmacSha1(bogus_key, message));
// This should still produce an hmac signature.
ASSERT_EQ(20u, signature.size());
// Create valid signature to compare.
signature = CreateSignatureHmacSha1(aes_128_key_, message);
// Test with bogus key.
ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message));
// Test with munged signature.
signature[0] = 0xFF;
ASSERT_FALSE(VerifySignatureHmacSha1(aes_128_key_, signature, message));
// Test with bogus signature.
ASSERT_FALSE(VerifySignatureHmacSha1(aes_128_key_, "bogus", message));
}
TEST_F(CryptoUtilTest, DeriveIv) {
// First value in the pair is the key_id, second value is the expected IV.
std::pair<std::string, std::string> id_iv_pairs[] = {
{"1234567890123456", "3278234c7682d1a2e153af4912975f5f"},
{"0987654321098765", "cf09abd30f04b60544910791a6b904cf"}};
for (const auto& id_iv_pair : id_iv_pairs) {
SCOPED_TRACE(::absl::StrCat("test case:", id_iv_pair.first));
EXPECT_EQ(id_iv_pair.second,
::absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
// Repeat same call to verify derivied result is repeatable.
EXPECT_EQ(id_iv_pair.second,
::absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
}
}
TEST_F(CryptoUtilTest, DeriveKeyId) {
// First value in the pair is the context, second value is the expected id.
std::pair<std::string, std::string> context_id_pairs[] = {
{"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"},
{"0987654321098765", "084fc6bece9688ccce6b1672d9b47e22"}};
for (const auto& context_id_pair : context_id_pairs) {
SCOPED_TRACE(::absl::StrCat("test case:", context_id_pair.first));
EXPECT_EQ(context_id_pair.second,
::absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
// Repeat same call to verify derivied result is repeatable.
EXPECT_EQ(context_id_pair.second,
::absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
}
}
TEST_F(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) {
uint32_t cc_code;
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code));
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code));
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code));
}
TEST_F(CryptoUtilTest, Verify4CCEncryptionIDFromString) {
uint32_t cc_code = 0;
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code));
ASSERT_EQ(kCENCSchemeID, cc_code);
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBC1Str, &cc_code));
ASSERT_EQ(kCBC1SchemeID, cc_code);
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENSStr, &cc_code));
ASSERT_EQ(kCENSSchemeID, cc_code);
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBCSStr, &cc_code));
ASSERT_EQ(kCBCSSchemeID, cc_code);
}
} // namespace crypto_util
} // namespace widevine

View File

@@ -0,0 +1,81 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// RAII wrapper classes for cleaning up various OpenSSL dynamically allocated
// structures.
#ifndef WHITEBOX_CRYPTO_UTILS_OPENSSL_UTIL_H_
#define WHITEBOX_CRYPTO_UTILS_OPENSSL_UTIL_H_
#include "third_party/boringssl/src/include/openssl/bio.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
#include "third_party/boringssl/src/include/openssl/pkcs7.h"
#include "third_party/boringssl/src/include/openssl/rsa.h"
#include "third_party/boringssl/src/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 ScopedBIGNUM = ScopedOpenSSLType<BIGNUM, BN_free>;
using ScopedBIO = ScopedOpenSSLType<BIO, BIO_vfree>;
typedef ScopedOpenSSLType<EC_KEY, EC_KEY_free> ScopedECKEY;
using ScopedPKCS7 = ScopedOpenSSLType<PKCS7, PKCS7_free>;
using ScopedPKEY = ScopedOpenSSLType<EVP_PKEY, EVP_PKEY_free>;
using ScopedRSA = ScopedOpenSSLType<RSA, RSA_free>;
using ScopedX509 = ScopedOpenSSLType<X509, X509_free>;
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 ScopedX509Req = ScopedOpenSSLType<X509_REQ, X509_REQ_free>;
using ScopedAsn1UtcTime = ScopedOpenSSLType<ASN1_UTCTIME, ASN1_UTCTIME_free>;
using ScopedAsn1Time = ScopedOpenSSLType<ASN1_TIME, ASN1_TIME_free>;
using ScopedAsn1Utc8String =
ScopedOpenSSLType<ASN1_UTF8STRING, ASN1_UTF8STRING_free>;
using ScopedAsn1Integer = ScopedOpenSSLType<ASN1_INTEGER, ASN1_INTEGER_free>;
using ScopedAsn1Object = ScopedOpenSSLType<ASN1_OBJECT, ASN1_OBJECT_free>;
using ScopedAsn1OctetString =
ScopedOpenSSLType<ASN1_OCTET_STRING, ASN1_OCTET_STRING_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 // WHITEBOX_CRYPTO_UTILS_OPENSSL_UTIL_H_

View File

@@ -0,0 +1,86 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software 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:
// Shared private key utilities between RSA and EC.
#ifndef WHITEBOX_CRYPTO_UTILS_PRIVATE_KEY_UTIL_H_
#define WHITEBOX_CRYPTO_UTILS_PRIVATE_KEY_UTIL_H_
#include <string>
#include "base/logging.h"
#include "third_party/boringssl/src/include/openssl/bio.h"
namespace widevine {
namespace private_key_util {
template <class Key>
bool SerializeKey(const Key* key,
int (*serialization_func)(BIO*, Key*),
std::string* serialized_key) {
if (key == nullptr) {
LOG(ERROR) << "Key is nullptr.";
return false;
}
if (serialized_key == nullptr) {
LOG(ERROR) << "Pointer to hold serialized key is nullptr.";
return false;
}
BIO* bio = BIO_new(BIO_s_mem());
if (bio == nullptr) {
LOG(ERROR) << "BIO_new returned nullptr";
return false;
}
bool success = false;
if (serialization_func(bio, const_cast<Key*>(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) << "Key serialization failure";
}
BIO_free(bio);
return success;
}
template <class Key>
bool DeserializeKey(const std::string& serialized_key,
Key* (*deserialization_func)(BIO*, Key**),
Key** key) {
if (serialized_key.empty()) {
LOG(ERROR) << "Serialized key is empty.";
return false;
}
if (key == nullptr) {
LOG(ERROR) << "Pointer to hold new key is nullptr.";
return false;
}
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
serialized_key.size());
if (bio == nullptr) {
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
return false;
}
*key = deserialization_func(bio, nullptr);
BIO_free(bio);
if (*key == nullptr) {
LOG(ERROR) << "Key deserialization failure";
}
return *key != nullptr;
}
} // namespace private_key_util
} // namespace widevine
#endif // WHITEBOX_CRYPTO_UTILS_PRIVATE_KEY_UTIL_H_

View File

@@ -0,0 +1,27 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "crypto_utils/random_util.h"
#include "base/check.h"
#include "third_party/boringssl/src/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);
}
std::string Random16Bytes() {
std::string output;
CHECK(RandomBytes(16u, &output));
return output;
}
} // namespace widevine

View File

@@ -0,0 +1,25 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef WHITEBOX_CRYPTO_UTILS_RANDOM_UTIL_H_
#define WHITEBOX_CRYPTO_UTILS_RANDOM_UTIL_H_
#include <string>
namespace widevine {
// Generates a random string.
// Returns true on success, false otherwise.
bool RandomBytes(size_t num_bytes, std::string* output);
// Returns a 16-byte std::string suitable for use as an AES key
std::string Random16Bytes();
} // namespace widevine
#endif // WHITEBOX_CRYPTO_UTILS_RANDOM_UTIL_H_

View File

@@ -0,0 +1,29 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "crypto_utils/random_util.h"
#include "testing/gtest/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

View File

@@ -0,0 +1,336 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Definition of classes representing RSA private and public keys used
// for message signing, signature verification, encryption and decryption.
//
// RSA signature details:
// Algorithm: RSASSA-PSS
// Hash algorithm: SHA1
// Mask generation function: mgf1SHA1
// Salt length: 20 bytes
// Trailer field: 0xbc
//
// RSA encryption details:
// Algorithm: RSA-OAEP
// Mask generation function: mgf1SHA1
// Label (encoding paramter): empty std::string
#include "crypto_utils/rsa_key.h"
#include "base/check.h"
#include "base/logging.h"
#include "crypto_utils/rsa_util.h"
#include "crypto_utils/sha_util.h"
#include "third_party/boringssl/src/include/openssl/bn.h"
#include "third_party/boringssl/src/include/openssl/err.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
#include "third_party/boringssl/src/include/openssl/rsa.h"
#include "third_party/boringssl/src/include/openssl/sha.h"
static const int kPssSaltLength = 20;
namespace {
// Check if two RSA keys match. If matches, they are either a public-private key
// pair or the same public key or the same private key.
bool RsaKeyMatch(const RSA* key1, const RSA* key2) {
if (!key1 || !key2)
return false;
return BN_cmp(key1->n, key2->n) == 0;
}
std::string OpenSSLErrorString(uint32_t error) {
char buf[ERR_ERROR_STRING_BUF_LEN];
ERR_error_string_n(error, buf, sizeof(buf));
return buf;
}
} // namespace
namespace widevine {
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(key) {
CHECK(key_ != nullptr);
}
RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key)
: key_(RSAPrivateKey_dup(rsa_key.key_)) {
CHECK(key_ != nullptr);
}
RsaPrivateKey::~RsaPrivateKey() {
RSA_free(key_);
}
RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) {
RSA* key;
if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key))
return nullptr;
if (RSA_check_key(key) != 1) {
LOG(ERROR) << "Invalid private RSA key: "
<< OpenSSLErrorString(ERR_get_error());
RSA_free(key);
}
return new RsaPrivateKey(key);
}
bool RsaPrivateKey::Decrypt(const std::string& encrypted_message,
std::string* decrypted_message) const {
DCHECK(decrypted_message);
size_t rsa_size = RSA_size(key_);
if (encrypted_message.size() != rsa_size) {
LOG(ERROR) << "Encrypted RSA message has the wrong size (expected "
<< rsa_size << ", actual " << encrypted_message.size() << ")";
return false;
}
decrypted_message->assign(rsa_size, 0);
int decrypted_size = RSA_private_decrypt(
rsa_size,
const_cast<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: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
decrypted_message->resize(decrypted_size);
return true;
}
bool RsaPrivateKey::GenerateSignature(const std::string& message,
std::string* signature) const {
DCHECK(signature);
if (message.empty()) {
LOG(ERROR) << "Message to be signed is empty";
return false;
}
// Hash the message using SHA1.
std::string message_digest = Sha1_Hash(message);
// Add PSS padding.
size_t rsa_size = RSA_size(key_);
std::string padded_digest(rsa_size, 0);
if (!RSA_padding_add_PKCS1_PSS_mgf1(
key_, reinterpret_cast<unsigned char*>(&padded_digest[0]),
reinterpret_cast<unsigned char*>(&message_digest[0]), EVP_sha1(),
EVP_sha1(), kPssSaltLength)) {
LOG(ERROR) << "RSA padding failure: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
// Encrypt PSS padded digest.
signature->assign(rsa_size, 0);
if (RSA_private_encrypt(padded_digest.size(),
reinterpret_cast<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: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
}
bool RsaPrivateKey::GenerateSignatureSha256Pkcs7(const std::string& message,
std::string* signature) const {
DCHECK(signature);
if (message.empty()) {
LOG(ERROR) << "Empty signature verification message";
return false;
}
unsigned char digest[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<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());
}
uint32_t RsaPrivateKey::KeySize() const {
return RSA_size(key_);
}
RsaPublicKey::RsaPublicKey(RSA* key) : key_(key) {
CHECK(key_ != nullptr);
}
RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key)
: key_(RSAPublicKey_dup(rsa_key.key_)) {
CHECK(key_ != nullptr);
}
RsaPublicKey::RsaPublicKey(const RsaPrivateKey& rsa_key)
: key_(RSAPublicKey_dup(rsa_key.key_)) {
CHECK(key_ != nullptr);
}
RsaPublicKey::~RsaPublicKey() {
RSA_free(key_);
}
RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) {
RSA* key;
if (!rsa_util::DeserializeRsaPublicKey(serialized_key, &key))
return nullptr;
if (RSA_size(key) == 0) {
LOG(ERROR) << "Invalid public RSA key: "
<< OpenSSLErrorString(ERR_get_error());
RSA_free(key);
}
return new RsaPublicKey(key);
}
bool RsaPublicKey::Encrypt(const std::string& clear_message,
std::string* encrypted_message) const {
DCHECK(encrypted_message);
if (clear_message.empty()) {
LOG(ERROR) << "Message to be encrypted is empty";
return false;
}
size_t rsa_size = RSA_size(key_);
encrypted_message->assign(rsa_size, 0);
if (RSA_public_encrypt(
clear_message.size(),
const_cast<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: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
}
bool RsaPublicKey::VerifySignature(const std::string& message,
const std::string& signature) const {
if (message.empty()) {
LOG(ERROR) << "Signed message is empty";
return false;
}
size_t rsa_size = RSA_size(key_);
if (signature.size() != rsa_size) {
LOG(ERROR) << "Message signature is of the wrong size (expected "
<< rsa_size << ", actual " << signature.size() << ")";
return false;
}
// Decrypt the signature.
std::string padded_digest(signature.size(), 0);
if (RSA_public_decrypt(
signature.size(),
const_cast<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: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
// Hash the message using SHA1.
std::string message_digest = Sha1_Hash(message);
// Verify PSS padding.
if (RSA_verify_PKCS1_PSS_mgf1(
key_, reinterpret_cast<unsigned char*>(&message_digest[0]),
EVP_sha1(), EVP_sha1(),
reinterpret_cast<unsigned char*>(&padded_digest[0]),
kPssSaltLength) == 0) {
LOG(ERROR) << "RSA Verify PSS padding failure: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
}
bool RsaPublicKey::VerifySignatureSha256Pkcs7(
const std::string& message,
const std::string& signature) const {
if (message.empty()) {
LOG(ERROR) << "Empty signature verification message";
return false;
}
if (signature.empty()) {
LOG(ERROR) << "Empty signature";
return false;
}
if (signature.size() != RSA_size(key_)) {
LOG(ERROR) << "RSA signature has the wrong size";
return false;
}
unsigned char digest[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<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());
}
uint32_t RsaPublicKey::KeySize() const {
return RSA_size(key_);
}
RsaKeyFactory::RsaKeyFactory() {}
RsaKeyFactory::~RsaKeyFactory() {}
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs1PrivateKey(
const std::string& private_key) const {
return std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key));
}
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
const std::string& private_key,
const std::string& private_key_passphrase) const {
std::string 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) const {
return std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key));
}
} // namespace widevine

View File

@@ -0,0 +1,163 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Declaration of classes representing RSA private and public keys used
// for message signing, signature verification, encryption and decryption.
#ifndef WHITEBOX_CRYPTO_UTILS_RSA_KEY_H_
#define WHITEBOX_CRYPTO_UTILS_RSA_KEY_H_
#include <cstdint>
#include <memory>
#include <string>
#include "third_party/boringssl/src/include/openssl/rsa.h"
namespace widevine {
class RsaPublicKey;
class RsaPrivateKey {
public:
explicit RsaPrivateKey(RSA* key);
RsaPrivateKey(const RsaPrivateKey&);
virtual ~RsaPrivateKey();
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
// Returns NULL on failure.
static RsaPrivateKey* Create(const std::string& serialized_key);
// Decrypt a message using RSA-OAEP. Caller retains ownership of all
// parameters. Returns true if successful, false otherwise.
virtual bool Decrypt(const std::string& encrypted_message,
std::string* decrypted_message) const;
// Generate RSSASSA-PSS signature. Caller retains ownership of all parameters.
// Returns true if successful, false otherwise.
virtual bool GenerateSignature(const std::string& message,
std::string* signature) const;
// Generate SHA256 digest, PKCS#7 padded signature. Caller retains ownership
// of all parameters. Returns true if successful, false otherwise.
virtual bool GenerateSignatureSha256Pkcs7(const std::string& message,
std::string* signature) const;
// Return true if the underlying key matches with |private_key|.
virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const;
// Return true if the underlying key is a public-private key pair with
// |public_key|.
virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const;
// Returns the RSA key size (modulus) in bytes.
virtual uint32_t KeySize() const;
private:
friend class RsaPublicKey;
friend class X509CertificateBuilder; // TODO(user): Get rid of this.
const RSA* key() const { return key_; }
RSA* key_;
// SWIG appears to think this declaration is a syntax error. Excluding it for
// python SWIG wrapping.
#ifndef SWIG
// Disallow assignment operator.
RsaPrivateKey& operator=(const RsaPrivateKey&) = delete;
#endif // SWIG
};
class RsaPublicKey {
public:
explicit RsaPublicKey(RSA* key);
// Copy constructor.
RsaPublicKey(const RsaPublicKey& rsa_key);
// Construct RsaPublicKey object from RsaPrivateKey.
explicit RsaPublicKey(const RsaPrivateKey& 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;
// Returns the RSA key size (modulus) in bytes.
virtual uint32_t KeySize() const;
private:
friend class RsaPrivateKey;
friend class X509CertificateBuilder; // TODO(user): Get rid of this.
const RSA* key() const { return key_; }
RSA* key_;
// SWIG appears to think this declaration is a syntax error. Excluding it for
// python SWIG wrapping.
#ifndef SWIG
// Disallow assignment operator.
RsaPublicKey& operator=(const RsaPublicKey&) = delete;
#endif // SWIG
};
class RsaKeyFactory {
public:
RsaKeyFactory();
RsaKeyFactory(const RsaKeyFactory&) = delete;
RsaKeyFactory& operator=(const RsaKeyFactory&) = delete;
virtual ~RsaKeyFactory();
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs1PrivateKey(
const std::string& private_key) const;
// Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or
// EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty).
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs8PrivateKey(
const std::string& private_key,
const std::string& private_key_passphrase) const;
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
virtual std::unique_ptr<RsaPublicKey> CreateFromPkcs1PublicKey(
const std::string& public_key) const;
};
} // namespace widevine
#endif // WHITEBOX_CRYPTO_UTILS_RSA_KEY_H_

View File

@@ -0,0 +1,268 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Unit test for rsa_key RSA encryption and signing.
#include "crypto_utils/rsa_key.h"
#include <memory>
#include "crypto_utils/rsa_test_keys.h"
#include "crypto_utils/rsa_util.h"
#include "testing/gtest/include/gtest/gtest.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_;
};
TEST_F(RsaKeyTest, CopyConstructor) {
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
std::unique_ptr<RsaPublicKey> public_key(
RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits()));
std::unique_ptr<RsaPrivateKey> private_key_copy(
new RsaPrivateKey(*private_key));
std::unique_ptr<RsaPublicKey> public_key_copy(new RsaPublicKey(*public_key));
EXPECT_TRUE(public_key_copy->MatchesPublicKey(*public_key));
EXPECT_TRUE(public_key_copy->MatchesPrivateKey(*private_key));
EXPECT_TRUE(private_key_copy->MatchesPublicKey(*public_key));
EXPECT_TRUE(private_key_copy->MatchesPrivateKey(*private_key));
}
void RsaKeyTest::TestEncryption(std::unique_ptr<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, RsaPublicKeyFromPrivateKey) {
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
ASSERT_TRUE(private_key);
std::unique_ptr<RsaPublicKey> public_key(new RsaPublicKey(*private_key));
ASSERT_TRUE(public_key);
EXPECT_TRUE(private_key->MatchesPublicKey(*public_key));
EXPECT_TRUE(public_key->MatchesPrivateKey(*private_key));
TestEncryption(std::move(private_key), std::move(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, KeySize) {
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
std::unique_ptr<RsaPublicKey> public_key(
RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits()));
EXPECT_EQ(256u, private_key->KeySize());
EXPECT_EQ(256u, public_key->KeySize());
}
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

View File

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

View File

@@ -0,0 +1,93 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// RSA keys generated using fake_prng for purposes of testing.
#ifndef WHITEBOX_CRYPTO_UTILS_RSA_TEST_KEYS_H_
#define WHITEBOX_CRYPTO_UTILS_RSA_TEST_KEYS_H_
#include <string>
namespace widevine {
// Container for test RSA keys
//
// Except for the keys noted below, these keys were generated using Euler's
// Totient.
//
class RsaTestKeys {
public:
RsaTestKeys();
~RsaTestKeys();
// Returns 3072-bit private RSA test key 1
const std::string& private_test_key_1_3072_bits() const {
return private_key_1_3072_bits_;
}
// Returns 3072-bit public RSA test key 1
const std::string& public_test_key_1_3072_bits() const {
return public_key_1_3072_bits_;
}
// Returns 2048-bit private RSA test key 2
const std::string& private_test_key_2_2048_bits() const {
return private_key_2_2048_bits_;
}
// Returns 2048-bit public RSA test key 2
const std::string& public_test_key_2_2048_bits() const {
return public_key_2_2048_bits_;
}
// Returns 2048-bit private RSA test key 3
const std::string& private_test_key_3_2048_bits() const {
return private_key_3_2048_bits_;
}
// Returns 2048-bit public RSA test key 3
const std::string& public_test_key_3_2048_bits() const {
return public_key_3_2048_bits_;
}
// This key was converted to a Carmichael totient version from the original
// private_key_2_2048_bits_.
const std::string& private_test_key_2_carmichael_totient_2048_bits() const {
return private_key_2_carmichael_totient_2048_bits_;
}
// This key was converted to a Carmichael totient version from the original
// private_key_3_2048_bits_.
const std::string& private_test_key_3_carmichael_totient_2048_bits() const {
return private_key_3_carmichael_totient_2048_bits_;
}
// This key has been created using the Carmichael totient. This is
// useful for testing with some RSA implementations that had challenges
// with keys generated this way.
const std::string& private_test_key_4_carmichael_totient_2048_bits() const {
return private_key_4_carmichael_totient_2048_bits_;
}
private:
RsaTestKeys(const RsaTestKeys&) = delete;
RsaTestKeys& operator=(const RsaTestKeys&) = delete;
std::string private_key_1_3072_bits_;
std::string public_key_1_3072_bits_;
std::string private_key_2_2048_bits_;
std::string public_key_2_2048_bits_;
std::string private_key_3_2048_bits_;
std::string public_key_3_2048_bits_;
// Tests keys that use the Carmichael totient to calculate d.
std::string private_key_2_carmichael_totient_2048_bits_;
std::string private_key_3_carmichael_totient_2048_bits_;
std::string private_key_4_carmichael_totient_2048_bits_;
};
} // namespace widevine
#endif // WHITEBOX_CRYPTO_UTILS_RSA_TEST_KEYS_H_

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