Initial Code Drop
This is the initial code drop of the reference implementation and test cases for the Widevine Whitebox API. In this drop, the full reference implementation for the AEAD white-box is provided and all test cases verifying the top-level behave have are enabled. Since the implementations can vary so much the testing is mostly left to verifying the return codes for specific parameter conditions. A full reference implementation for the license white-box is provided, however not all tests are implemented or enabled. A number of tests have been disabled as they required a loaded license and test licenses are still being worked on. The two license white-box API functions that are the further from competition are ProcessLicenseResponse() and MaskedDecryt(). ProcessLicenseResponse() is still being worked on and MaskedDecrypt() is waiting on Decrypt() to be fully functional. Most tests focus on verifying return code for specific parameter conditions, but as test licenses are created, tests looking to test the internal behaviour of license management will be added to ProcessLicenseResponse(), Decrypt(), and MaskedDecrypt().
This commit is contained in:
172
.clang-format
Normal file
172
.clang-format
Normal 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
|
||||
...
|
||||
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bazel-*
|
||||
150
README.md
Normal file
150
README.md
Normal 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"
|
||||
```
|
||||
91
WORKSPACE
Normal file
91
WORKSPACE
Normal file
@@ -0,0 +1,91 @@
|
||||
# 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: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"],
|
||||
)
|
||||
|
||||
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",
|
||||
)
|
||||
89
api/BUILD
Normal file
89
api/BUILD
Normal file
@@ -0,0 +1,89 @@
|
||||
# Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "result",
|
||||
hdrs = [
|
||||
"result.h",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "aead_whitebox",
|
||||
hdrs = [
|
||||
"aead_whitebox.h",
|
||||
],
|
||||
deps = [
|
||||
":result",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "license_whitebox",
|
||||
hdrs = [
|
||||
"license_whitebox.h",
|
||||
],
|
||||
deps = [
|
||||
":result",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "test_data",
|
||||
testonly = True,
|
||||
hdrs = [
|
||||
"test_data.h",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "license_test_helper",
|
||||
testonly = True,
|
||||
srcs = ["license_test_helper.cc"],
|
||||
hdrs = ["license_test_helper.h"],
|
||||
deps = [
|
||||
"//chromium_deps/base",
|
||||
"//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",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "aead_whitebox_test",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"aead_whitebox_create_test.cc",
|
||||
"aead_whitebox_decrypt_test.cc",
|
||||
"aead_whitebox_encrypt_test.cc",
|
||||
],
|
||||
deps = [
|
||||
":aead_whitebox",
|
||||
":test_data",
|
||||
"//chromium_deps/testing:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "license_whitebox_test",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"license_whitebox_create_test.cc",
|
||||
"license_whitebox_decrypt_test.cc",
|
||||
"license_whitebox_get_secret_string_test.cc",
|
||||
"license_whitebox_sign_renewal_request_test.cc",
|
||||
"license_whitebox_verify_renewal_response_test.cc",
|
||||
],
|
||||
deps = [
|
||||
":license_test_helper",
|
||||
":license_whitebox",
|
||||
":test_data",
|
||||
"//chromium_deps/cdm/keys:api",
|
||||
"//chromium_deps/cdm/protos:license_protocol_proto",
|
||||
"//chromium_deps/testing:gtest",
|
||||
"//crypto_utils:rsa_key",
|
||||
],
|
||||
)
|
||||
132
api/aead_whitebox.h
Normal file
132
api/aead_whitebox.h
Normal file
@@ -0,0 +1,132 @@
|
||||
// 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/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_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.
|
||||
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_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_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_
|
||||
75
api/aead_whitebox_create_test.cc
Normal file
75
api/aead_whitebox_create_test.cc
Normal 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/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
|
||||
156
api/aead_whitebox_decrypt_test.cc
Normal file
156
api/aead_whitebox_decrypt_test.cc
Normal 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/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
|
||||
119
api/aead_whitebox_encrypt_test.cc
Normal file
119
api/aead_whitebox_encrypt_test.cc
Normal 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/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
|
||||
345
api/license_test_helper.cc
Normal file
345
api/license_test_helper.cc
Normal file
@@ -0,0 +1,345 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_test_helper.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "cdm/keys/certs.h"
|
||||
#include "cdm/protos/license_protocol.pb.h"
|
||||
#include "crypto_utils/aes_cbc_encryptor.h"
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
|
||||
struct KeyData {
|
||||
video_widevine::License_KeyContainer_SecurityLevel level;
|
||||
std::vector<uint8_t> key_id;
|
||||
std::vector<uint8_t> iv;
|
||||
std::vector<uint8_t> content_key;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
KeyData SoftwareCryptoKey() {
|
||||
return {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
|
||||
{ // key_id:
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '0', '1', '2', '3', '4', '5',
|
||||
},
|
||||
{ // iv:
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
},
|
||||
{ // content_key:
|
||||
0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
|
||||
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
KeyData SoftwareDecodeKey() {
|
||||
return {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE,
|
||||
{ // key_id:
|
||||
'9', '8', '7', '6', '5', '4', '3', '2',
|
||||
'1', '0', '9', '8', '7', '6', '5', '4',
|
||||
},
|
||||
{ // iv:
|
||||
0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24,
|
||||
0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32,
|
||||
},
|
||||
{ // content_key:
|
||||
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
KeyData HardwareCryptoKey() {
|
||||
return {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO,
|
||||
{ // key_id:
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||
},
|
||||
{ // iv:
|
||||
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40,
|
||||
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
},
|
||||
{ // content_key:
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
}
|
||||
};
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
// Returns |buffer| converted into a string.
|
||||
std::string AsString(const uint8_t* buffer, size_t buffer_size) {
|
||||
return std::string(reinterpret_cast<const char*>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
// Returns the encryption key based on |license_request| and |session_key|.
|
||||
std::string GetEncryptionKey(const std::string& license_request,
|
||||
const std::string& session_key) {
|
||||
return widevine::crypto_util::DeriveKey(
|
||||
session_key, widevine::crypto_util::kWrappingKeyLabel, license_request,
|
||||
widevine::crypto_util::kWrappingKeySizeBits);
|
||||
}
|
||||
|
||||
std::string AesEncrypt(const std::string& key,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
const uint8_t* plain_text,
|
||||
size_t plain_text_size) {
|
||||
widevine::AesCbcEncryptor encryptor;
|
||||
encryptor.SetKey(reinterpret_cast<const uint8_t*>(key.data()), key.size());
|
||||
std::vector<uint8_t> cipher_text(plain_text_size);
|
||||
CHECK(encryptor.Encrypt(iv, iv_size, plain_text, plain_text_size,
|
||||
cipher_text.data()));
|
||||
return AsString(cipher_text.data(), cipher_text.size());
|
||||
}
|
||||
|
||||
video_widevine::License::KeyContainer CreateContentKeyContainer(
|
||||
const KeyData& key_data,
|
||||
const std::string& key_container_encryption_key) {
|
||||
video_widevine::License::KeyContainer key;
|
||||
key.set_id(key_data.key_id.data(), key_data.key_id.size());
|
||||
key.set_iv(key_data.iv.data(), key_data.iv.size());
|
||||
key.set_type(video_widevine::License_KeyContainer_KeyType_CONTENT);
|
||||
key.set_level(key_data.level);
|
||||
key.set_key(AesEncrypt(key_container_encryption_key, key_data.iv.data(),
|
||||
key_data.iv.size(), key_data.content_key.data(),
|
||||
key_data.content_key.size()));
|
||||
return key;
|
||||
}
|
||||
|
||||
video_widevine::License::KeyContainer CreateSigningKeyContainer(
|
||||
const std::string& key_container_encryption_key) {
|
||||
const std::vector<uint8_t> signing_iv = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '0', '1', '2', '3', '4', '5',
|
||||
};
|
||||
// 512 bits of key material for dual server/client signing key.
|
||||
const std::vector<uint8_t> signing_key = {
|
||||
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,
|
||||
};
|
||||
|
||||
video_widevine::License::KeyContainer key;
|
||||
key.set_iv(signing_iv.data(), signing_iv.size());
|
||||
key.set_type(video_widevine::License_KeyContainer_KeyType_SIGNING);
|
||||
key.set_key(AesEncrypt(key_container_encryption_key, signing_iv.data(),
|
||||
signing_iv.size(), signing_key.data(),
|
||||
signing_key.size()));
|
||||
return key;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<uint8_t> GetSoftwareCryptoKeyId() {
|
||||
const auto key = SoftwareCryptoKey();
|
||||
return key.key_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetSoftwareDecodeKeyId() {
|
||||
const auto key = SoftwareDecodeKey();
|
||||
return key.key_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetHardwareCryptoKeyId() {
|
||||
const auto key = HardwareCryptoKey();
|
||||
return key.key_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetUnusedKeyId() {
|
||||
return {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
}
|
||||
|
||||
// Returns a serialized license request with the following data:
|
||||
// {
|
||||
// "requestTime": <current time>,
|
||||
// "clientId": {
|
||||
// "clientCapabilities": {
|
||||
// "videoResolutionConstraints": true,
|
||||
// "clientToken": true,
|
||||
// "sessionToken": false,
|
||||
// "maxHdcpVersion": "HDCP_V1"
|
||||
// },
|
||||
// "clientInfo": [{
|
||||
// "name": "architecture_name",
|
||||
// "value": "x86-64"
|
||||
// },{
|
||||
// "name": "company_name",
|
||||
// "value": "Google"
|
||||
// },{
|
||||
// "name": "model_name",
|
||||
// "value": "ChromeCDM"
|
||||
// },{
|
||||
// "name": "platform_name",
|
||||
// "value": "Windows"
|
||||
// },{
|
||||
// "name": "widevine_cdm_version",
|
||||
// "value": "4.10.1686.29"
|
||||
// }],
|
||||
// "type": "DRM_DEVICE_CERTIFICATE",
|
||||
// "token": {
|
||||
// <test device certificate>
|
||||
// }"
|
||||
// }",
|
||||
// "contentId": {
|
||||
// "webmKeyId": {
|
||||
// "licenseType": "STREAMING",
|
||||
// "requestId": "REQUEST_ID",
|
||||
// "header": "01234567890123456"
|
||||
// }
|
||||
// },
|
||||
// "protocolVersion": "VERSION_2_1",
|
||||
// "type": "NEW"
|
||||
// }
|
||||
std::string CreateLicenseRequest() {
|
||||
video_widevine::LicenseRequest license_request;
|
||||
|
||||
// Request time is now.
|
||||
license_request.set_request_time(std::time(nullptr));
|
||||
|
||||
auto* client_id = license_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 = license_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");
|
||||
|
||||
license_request.set_protocol_version(video_widevine::VERSION_2_1);
|
||||
license_request.set_type(video_widevine::LicenseRequest::NEW);
|
||||
return license_request.SerializeAsString();
|
||||
}
|
||||
|
||||
// Returns a serialized license using values from |serialized_license_request|
|
||||
// as well as |session_key|. Some of the values match what is in the license
|
||||
// request, but as they are not checked it really doesn't matter. The license
|
||||
// will contain all 3 keys |kKeyId1|, |kKeyId2|, and |kKeyId3| with different
|
||||
// security levels. The license is of the form:
|
||||
// {
|
||||
// "id": {
|
||||
// "requestId": "REQUEST_ID",
|
||||
// "sessionId": "SESSION_ID",
|
||||
// "type": "STREAMING",
|
||||
// "version": 0
|
||||
// },
|
||||
// "policy": {
|
||||
// "canPlay": true,
|
||||
// "canPersist": false,
|
||||
// "canRenew": true,
|
||||
// "licenseDurationSeconds": "600",
|
||||
// "renewalDelaySeconds": "30",
|
||||
// "renewalRetryIntervalSeconds": "10",
|
||||
// "renewWithUsage": false
|
||||
// },
|
||||
// "key": [{
|
||||
// "iv": <|kInitializationVector|>,
|
||||
// "type": "SIGNING",
|
||||
// "key": <computed>
|
||||
// },{
|
||||
// "id": <|kKeyId1|>,
|
||||
// "iv": <|kInitializationVector|>,
|
||||
// "type": "CONTENT",
|
||||
// "level": "SW_SECURE_CRYPTO",
|
||||
// "key": <|kContentKey1|>
|
||||
// },{
|
||||
// "id": <|kKeyId2|>,
|
||||
// "iv": <|kInitializationVector|>,
|
||||
// "type": "CONTENT",
|
||||
// "level": "SW_SECURE_DECODE",
|
||||
// "key": <|kContentKey2|>
|
||||
// },{
|
||||
// "id": <|kKeyId3|>,
|
||||
// "iv": <|kInitializationVector|>,
|
||||
// "type": "CONTENT",
|
||||
// "level": "HW_SECURE_CRYPTO",
|
||||
// "key": <|kContentKey3|>
|
||||
// }],
|
||||
// "licenseStartTime": <from license_request.requestTime>,
|
||||
// "remoteAttestationVerified": false,
|
||||
// "platformVerificationStatus": "PLATFORM_UNVERIFIED"
|
||||
// }
|
||||
std::string CreateLicense(const std::string& serialized_license_request,
|
||||
const std::string& session_key) {
|
||||
DCHECK_EQ(session_key.size(), 16u);
|
||||
|
||||
// Extract values needed for the license.
|
||||
video_widevine::LicenseRequest license_request;
|
||||
CHECK(license_request.ParseFromString(serialized_license_request));
|
||||
const auto request_time = license_request.request_time();
|
||||
|
||||
const std::string key_container_encryption_key =
|
||||
GetEncryptionKey(serialized_license_request, session_key);
|
||||
|
||||
// Now create the license.
|
||||
video_widevine::License license;
|
||||
auto* license_id = license.mutable_id();
|
||||
license_id->set_request_id("REQUEST_ID");
|
||||
license_id->set_session_id("SESSION_ID");
|
||||
license_id->set_type(video_widevine::STREAMING);
|
||||
license_id->set_version(0);
|
||||
|
||||
auto* policy = license.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);
|
||||
|
||||
license.add_key()->CopyFrom(
|
||||
CreateSigningKeyContainer(key_container_encryption_key));
|
||||
license.add_key()->CopyFrom(CreateContentKeyContainer(
|
||||
SoftwareCryptoKey(), key_container_encryption_key));
|
||||
license.add_key()->CopyFrom(CreateContentKeyContainer(
|
||||
SoftwareDecodeKey(), key_container_encryption_key));
|
||||
license.add_key()->CopyFrom(CreateContentKeyContainer(
|
||||
HardwareCryptoKey(), key_container_encryption_key));
|
||||
|
||||
license.set_license_start_time(request_time);
|
||||
license.set_remote_attestation_verified(false);
|
||||
license.set_platform_verification_status(
|
||||
video_widevine::PlatformVerificationStatus::PLATFORM_UNVERIFIED);
|
||||
|
||||
return license.SerializeAsString();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
33
api/license_test_helper.h
Normal file
33
api/license_test_helper.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_LICENSE_TEST_HELPER_H_
|
||||
#define WHITEBOX_API_LICENSE_TEST_HELPER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Returns a serialized LicenseRequest.
|
||||
std::string CreateLicenseRequest();
|
||||
|
||||
// Returns a serialized License based on |serialized_license_request| and
|
||||
// |session_key|. The license contains 3 content keys (key1, key2, key3)
|
||||
// with levels SW_SECURE_CRYPTO, SW_SECURE_DECODE, and HW_SECURE_CRYPTO,
|
||||
// respectively.
|
||||
std::string CreateLicense(const std::string& serialized_license_request,
|
||||
const std::string& session_key);
|
||||
|
||||
// Returns the key ID for each key.
|
||||
std::vector<uint8_t> GetSoftwareCryptoKeyId();
|
||||
std::vector<uint8_t> GetSoftwareDecodeKeyId();
|
||||
std::vector<uint8_t> GetHardwareCryptoKeyId();
|
||||
|
||||
// Returns a key ID which is not included in the license.
|
||||
std::vector<uint8_t> GetUnusedKeyId();
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_API_LICENSE_TEST_HELPER_H_
|
||||
397
api/license_whitebox.h
Normal file
397
api/license_whitebox.h
Normal file
@@ -0,0 +1,397 @@
|
||||
// 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/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_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.
|
||||
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_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, extract
|
||||
// the content keys and signing keys, and load them into the white-box.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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_SIGNATURE if |message|'s signature does not match
|
||||
// |signature|.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| 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
|
||||
// |session_key| could not be unwrapped correctly, if |license_request| was
|
||||
// null, if |license_request_size| was zero, or if |license_request| was
|
||||
// malformed.
|
||||
WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
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_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 no keys.
|
||||
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_NO_SUCH_KEY if |key_id| did not match any keys in |whitebox|.
|
||||
//
|
||||
// WB_RESULT_WRONG_KEY_TYPE if |key_id| referred to a key in |whitebox| but the
|
||||
// key was not a content key.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| refers to a key in
|
||||
// |whitebox| but the key's security level was neither SW_SECURE_CRYPTO nor
|
||||
// SW_SECURE_DECODE.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |secret_string_size| (as input) was less than
|
||||
// the required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
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_NO_SUCH_KEY if |key_id| matches no key in |whitebox|.
|
||||
//
|
||||
// WB_RESULT_WRONG_KEY_TYPE if |key_id| referred to a key in |whitebox| but
|
||||
// the key was not a content key.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key in
|
||||
// |whitebox| but the key's security level was not SW_SECURE_CRYPTO.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
|
||||
// the required size.
|
||||
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 |output_data| was null, or if |output_data_size| was null.
|
||||
//
|
||||
// WB_RESULT_NO_SUCH_KEY if |key_id| matches no key in |whitebox|.
|
||||
//
|
||||
// WB_RESULT_WRONG_KEY_TYPE if |key_id| referred to a key in |whitebox| but the
|
||||
// key was not a content key.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key in
|
||||
// |whitebox| but the key's security level was neither SW_SECURE_CRYPTO nor
|
||||
// SW_SECURE_DECODE.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |masked_output_data_size| (as input) was less
|
||||
// than the required size.
|
||||
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 the data in |buffer| using |secret_string|. |buffer| is operated on
|
||||
// in-place.
|
||||
//
|
||||
// Args:
|
||||
// secret_string (in) : The "key" used to unmask the data in |buffer|.
|
||||
//
|
||||
// secret_string_size (in) : The number of bytes in |secret_string|.
|
||||
//
|
||||
// buffer (in/out) : As input, this is the masked data. As output, this is the
|
||||
// unmasked data. The number of bytes in the masked data will be equal to the
|
||||
// number of bytes in the unmasked data.
|
||||
//
|
||||
// buffer_size (in) : The number of bytes in |buffer|.
|
||||
void WB_License_Unmask(const uint8_t* secret_string,
|
||||
size_t secret_string_size,
|
||||
uint8_t* buffer,
|
||||
size_t buffer_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WHITEBOX_API_LICENSE_WHITEBOX_H_
|
||||
57
api/license_whitebox_create_test.cc
Normal file
57
api/license_whitebox_create_test.cc
Normal 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/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
|
||||
355
api/license_whitebox_decrypt_test.cc
Normal file
355
api/license_whitebox_decrypt_test.cc
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// All decrypt tests require a valid (initialized) white-box. However, not all
|
||||
// tests need to load a license, so that is left out of SetUp().
|
||||
class LicenseWhiteboxDecryptTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
// All of our tests here require a valid whitebox.
|
||||
std::vector<uint8_t> init_data = GetLicenseInitData();
|
||||
ASSERT_GT(init_data.size(), 0u);
|
||||
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_TRUE(whitebox_);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
void LoadLicense() {
|
||||
// TODO: Load the license here. It would be nice if we could do it in
|
||||
// SetUp(), but since we need to support the WB_RESULT_INVALID_STATE test
|
||||
// case, we need a way to not load a license.
|
||||
}
|
||||
|
||||
WB_License_Whitebox* whitebox_ = nullptr;
|
||||
|
||||
// TODO(vaage): Replace key ids with the key ids in the license. Since we
|
||||
// don't have a license right now, any id will do.
|
||||
const std::vector<uint8_t> crypto_key_id_ = {0xAA, 0xBB, 0xAA, 0xBB};
|
||||
const std::vector<uint8_t> decode_key_id_ = {0xAA, 0xBB, 0xAA, 0xBB};
|
||||
const std::vector<uint8_t> renewal_key_id_ = {0xAA, 0xBB, 0xAA, 0xBB};
|
||||
|
||||
// TODO(vaage): Replace with golden ciphertext that will match the golden
|
||||
// plaintext.
|
||||
const std::vector<uint8_t> ciphertext_ = {
|
||||
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 std::vector<uint8_t> iv_ = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// TODO(vaage): Replace with golden plaintext that will match the golden
|
||||
// ciphertext.
|
||||
const std::vector<uint8_t> plaintext_ = {
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, Success) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to look-up
|
||||
// the key before attempting to decrypt anything. Since |plaintext_| and
|
||||
// |ciphertext_| are not even set correctly, this wouldn't pass even if it had
|
||||
// a license.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_OK);
|
||||
|
||||
// Since we are not using any padding, the ciphertext and plaintext should be
|
||||
// the same size. However, in the case that something went wrong, resize the
|
||||
// plaintext so that ASSERT_EQ will operate on the correct size.
|
||||
plaintext.resize(plaintext_size);
|
||||
|
||||
ASSERT_EQ(plaintext, plaintext_);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) {
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
nullptr, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCipherMode) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
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);
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, invalid_mode, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(),
|
||||
ciphertext_.size(), iv_.data(), iv_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) {
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, nullptr,
|
||||
crypto_key_id_.size(), ciphertext_.data(),
|
||||
ciphertext_.size(), iv_.data(), iv_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullZeroKeyIdSize) {
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
0, ciphertext_.data(), ciphertext_.size(), iv_.data(),
|
||||
iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
nullptr, ciphertext_.size(), iv_.data(),
|
||||
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. Size zero input is not considered invalid input.
|
||||
TEST_F(LicenseWhiteboxDecryptTest,
|
||||
InvalidParameterForCBCAndInvalidInputDataSize) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
ciphertext_.data(), 14, iv_.data(), iv_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
ciphertext_.data(), ciphertext_.size(), nullptr,
|
||||
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) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), 9, plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), nullptr, &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, NoSuchKey) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key and see that there are no keys, which will result in
|
||||
// WB_RESULT_INVALID_STATE.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
// For our fake key id, it is uncommon for key ids to be short. So using a one
|
||||
// byte key id should safe as they are allowed, just not likely to appear in
|
||||
// any test license.
|
||||
std::vector<uint8_t> not_a_key_id = {0xFF};
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, not_a_key_id.data(),
|
||||
not_a_key_id.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_NO_SUCH_KEY);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, WrongKeyType) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key and see that there are no keys, which will result in
|
||||
// WB_RESULT_INVALID_STATE.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, renewal_key_id_.data(),
|
||||
renewal_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_WRONG_KEY_TYPE);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevel) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key and see that there are no keys, which will result in
|
||||
// WB_RESULT_INVALID_STATE.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
// Use the software decode key as they are limited to
|
||||
// WB_License_MaskedDecrypt().
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, decode_key_id_.data(),
|
||||
decode_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL);
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, BufferTooSmall) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
// Our ciphertext will be large enough that we should not need to worry about
|
||||
// using a constant here.
|
||||
size_t plaintext_size = 8;
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), 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, ciphertext_.size());
|
||||
}
|
||||
} // namespace
|
||||
180
api/license_whitebox_get_secret_string_test.cc
Normal file
180
api/license_whitebox_get_secret_string_test.cc
Normal file
@@ -0,0 +1,180 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class LicenseWhiteboxGetSecretStringTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
// 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_);
|
||||
|
||||
const std::vector<uint8_t> init_data = GetLicenseInitData();
|
||||
|
||||
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_TRUE(whitebox_);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
void LoadLicense() {
|
||||
// TODO: We will need to load a license for most tests as they will require
|
||||
// a key id look-up and we get keys from licenses.
|
||||
}
|
||||
|
||||
// TODO: These needs to be replaced with the actual keys in the loaded
|
||||
// license.
|
||||
const std::vector<uint8_t> crypto_key_id_ = {1, 2, 3};
|
||||
const std::vector<uint8_t> hardware_key_id_ = {2, 3, 4};
|
||||
const std::vector<uint8_t> bad_key_id_ = {3, 4, 5};
|
||||
const std::vector<uint8_t> non_content_key_id_ = {4, 5, 6};
|
||||
|
||||
size_t secret_string_size_;
|
||||
std::vector<uint8_t> secret_string_;
|
||||
|
||||
WB_License_Whitebox* whitebox_;
|
||||
};
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, Success) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_OK);
|
||||
secret_string_.resize(secret_string_size_);
|
||||
ASSERT_GT(secret_string_.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullWhitebox) {
|
||||
LoadLicense();
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(nullptr, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_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, crypto_key_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, crypto_key_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, crypto_key_id_.data(),
|
||||
crypto_key_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, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), secret_string_.data(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, NoSuchKey) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
bad_key_id_.data(), bad_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_NO_SUCH_KEY);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, WrongKeyType) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
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_NO_SUCH_KEY);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InsufficientSecurityLevel) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, hardware_key_id_.data(),
|
||||
hardware_key_id_.size(), secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_NO_SUCH_KEY);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, BufferTooSmall) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
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,
|
||||
crypto_key_id_.data(), crypto_key_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_, 1);
|
||||
}
|
||||
|
||||
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,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
149
api/license_whitebox_sign_renewal_request_test.cc
Normal file
149
api/license_whitebox_sign_renewal_request_test.cc
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const uint8_t kDummyMessage[] = {
|
||||
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,
|
||||
};
|
||||
const size_t kDummyMessageSize = sizeof(kDummyMessage);
|
||||
|
||||
// Size of a license signature. This must be big enough to hold the signature
|
||||
// returned by WB_License_SignRenewalRequest().
|
||||
constexpr size_t kSignatureSize = 256;
|
||||
|
||||
// These tests assume that WB_License_Create() and
|
||||
// WB_License_ProcessLicenseResponse() work as all tests will require a valid
|
||||
// white-box instance with a valid license already loaded.
|
||||
class LicenseWhiteboxSignRenewalRequestTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const std::vector<uint8_t> init_data_ = GetLicenseInitData();
|
||||
ASSERT_EQ(
|
||||
WB_License_Create(init_data_.data(), init_data_.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
void LoadLicense() {
|
||||
// TODO: Load the license here. It would be nice if we could do it in
|
||||
// SetUp(), but since we need to support the WB_RESULT_INVALID_STATE test
|
||||
// case, we need a way to not load a license.
|
||||
}
|
||||
|
||||
WB_License_Whitebox* whitebox_ = nullptr;
|
||||
};
|
||||
|
||||
// TODO: Implement the success test case (ideally with a real renewal request).
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(nullptr, kDummyMessage, kDummyMessageSize,
|
||||
signature.data(), &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, nullptr, kDummyMessageSize,
|
||||
signature.data(), &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||
InvalidParameterForZeroMessageSize) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, kDummyMessage, 0,
|
||||
signature.data(), &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||
InvalidParameterForNullSignature) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
nullptr, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||
InvalidParameterForNullSignatureSize) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature.data(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, BufferTooSmall) {
|
||||
// TODO: This test must be skipped as the "too small" check takes place after
|
||||
// we check for a key.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
// 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".
|
||||
size_t signature_size = 1;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
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, 1);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidState) {
|
||||
// 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.
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature.data(), &signature_size),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
}
|
||||
} // namespace
|
||||
257
api/license_whitebox_test.cc
Normal file
257
api/license_whitebox_test.cc
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/license_test_helper.h"
|
||||
#include "api/test_data.h"
|
||||
#include "crypto_utils/rsa_key.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
using RsaPublicKey = widevine::RsaPublicKey;
|
||||
|
||||
namespace {
|
||||
|
||||
const uint8_t kDummyMessage[] = {
|
||||
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,
|
||||
};
|
||||
const size_t kDummyMessageSize = sizeof(kDummyMessage);
|
||||
|
||||
// Size of a license signature. This must be big enough to hold the signature
|
||||
// returned by WB_License_SignLicenseRequest().
|
||||
constexpr size_t kSignatureSize = 256;
|
||||
|
||||
std::string AsString(const uint8_t* buffer, size_t buffer_size) {
|
||||
return std::string(reinterpret_cast<const char*>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
// Used to test the rest of the whitebox API as this class creates |whitebox_|
|
||||
// during setup, so it is available for all tests. It also creates |public_key_|
|
||||
// which is the matching RSA public key that can be used to verify signing
|
||||
// operations inside the whitebox.
|
||||
class LicenseWhiteboxTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const std::vector<uint8_t> init_data = GetLicenseInitData();
|
||||
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
|
||||
std::vector<uint8_t> public_key = GetMatchingLicensePublicKey();
|
||||
public_key_.reset(
|
||||
RsaPublicKey::Create(AsString(public_key.data(), public_key.size())));
|
||||
ASSERT_TRUE(public_key_);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
// Public key as an usable object.
|
||||
std::unique_ptr<RsaPublicKey> public_key_;
|
||||
|
||||
WB_License_Whitebox* whitebox_;
|
||||
};
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignAndVerifySucceedsWithRandomData) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_OK);
|
||||
|
||||
// Verify the signature.
|
||||
ASSERT_TRUE(
|
||||
public_key_->VerifySignature(AsString(kDummyMessage, kDummyMessageSize),
|
||||
AsString(signature, signature_size)));
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignAndVerifySucceedsWithLicenseRequest) {
|
||||
const std::string license_request = widevine::CreateLicenseRequest();
|
||||
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(
|
||||
whitebox_, reinterpret_cast<const uint8_t*>(license_request.data()),
|
||||
license_request.size(), signature, &signature_size),
|
||||
WB_RESULT_OK);
|
||||
|
||||
ASSERT_TRUE(public_key_->VerifySignature(
|
||||
license_request, AsString(signature, signature_size)));
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithWhiteboxNull) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(nullptr, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithMessageNull) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, nullptr, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithMessageSizeZero) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, kDummyMessage, 0,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureNull) {
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
nullptr, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureSizeNull) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureSizeZero) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = 0;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureTooSmall) {
|
||||
uint8_t signature[10];
|
||||
size_t signature_size = sizeof(signature);
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithWhiteboxNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
nullptr, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithMessageNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, nullptr, message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithMessageSizeZero) {
|
||||
std::vector<uint8_t> message{/* empty */};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSignatureNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, nullptr, message.size(), nullptr, signature.size(),
|
||||
session_key.data(), session_key.size(), license_request.data(),
|
||||
license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSignatureSizeZero) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{/* empty */};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSessionKeyNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), nullptr, session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSessionKeySizeZero) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{/* empty */};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithRequestNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
nullptr, license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithRequestSizeZero) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{/* empty */};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
142
api/license_whitebox_verify_renewal_response_test.cc
Normal file
142
api/license_whitebox_verify_renewal_response_test.cc
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO: This needs to be the real response.
|
||||
const uint8_t kResponse[] = {
|
||||
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,
|
||||
};
|
||||
const size_t kResponseSize = sizeof(kResponse);
|
||||
|
||||
// TODO: This needs to be the actual signature.
|
||||
const uint8_t kSignature[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const uint8_t kSignatureSize = sizeof(kSignature);
|
||||
|
||||
// These tests assume that WB_License_Create() and
|
||||
// WB_License_ProcessLicenseResponse() work as all tests will require a valid
|
||||
// white-box instance with a valid license already loaded.
|
||||
class LicenseWhiteboxVerifyRenewalResponseTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const std::vector<uint8_t> init_data_ = GetLicenseInitData();
|
||||
ASSERT_EQ(
|
||||
WB_License_Create(init_data_.data(), init_data_.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
void LoadLicense() {
|
||||
// TODO: Load the license here. It would be nice if we could do it in
|
||||
// SetUp(), but since we need to support the WB_RESULT_INVALID_STATE test
|
||||
// case, we need a way to not load a license.
|
||||
}
|
||||
|
||||
WB_License_Whitebox* whitebox_ = nullptr;
|
||||
};
|
||||
|
||||
// TODO: Implement the success test case.
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForNullWhitebox) {
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(nullptr, kResponse, kResponseSize,
|
||||
kSignature, kSignatureSize),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForNullMessage) {
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, nullptr, kResponseSize,
|
||||
kSignature, kSignatureSize),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForZeroMessageSize) {
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, kResponse, 0,
|
||||
kSignature, kSignatureSize),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForNullSignature) {
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(
|
||||
whitebox_, kResponse, kResponseSize, nullptr, kSignatureSize),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForInvalidSignatureSize) {
|
||||
// TODO: This test needs to be skipped for now as the check for signature size
|
||||
// is done after we compute the signature. This requires a valid license to
|
||||
// have been loaded.
|
||||
GTEST_SKIP();
|
||||
|
||||
// We need an invalid signature size, the easiest way to do this is to take
|
||||
// the expected signature size and trim a bit off the end.
|
||||
const size_t invalid_signature_size = kSignatureSize - 1;
|
||||
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_VerifyRenewalResponse(whitebox_, kResponse, kResponseSize,
|
||||
kSignature, invalid_signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidSignatureForInvalidSignature) {
|
||||
// TODO: This test needs to be skipped for now as comparing signatures
|
||||
// requires a key provided by a valid license.
|
||||
GTEST_SKIP();
|
||||
|
||||
// In order to create an invalid signature, copy the valid signature and flip
|
||||
// one byte. This will ensure that our invalid signature is always different
|
||||
// from the valid signature.
|
||||
std::vector<uint8_t> invalid_signature(kSignature,
|
||||
kSignature + kSignatureSize);
|
||||
invalid_signature[0] = ~invalid_signature[0];
|
||||
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(
|
||||
whitebox_, kResponse, kResponseSize, invalid_signature.data(),
|
||||
invalid_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_, kResponse, kResponseSize,
|
||||
kSignature, kSignatureSize),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
}
|
||||
} // namespace
|
||||
55
api/result.h
Normal file
55
api/result.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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,
|
||||
|
||||
// A key was requested that had not been loaded into the white-box.
|
||||
WB_RESULT_NO_SUCH_KEY = 6,
|
||||
|
||||
// A key was requested that was found in the white-box, but is being used
|
||||
// improperly.
|
||||
WB_RESULT_WRONG_KEY_TYPE = 7,
|
||||
|
||||
// The requested key's security level is not sufficient for the operation.
|
||||
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL = 8,
|
||||
|
||||
// The input data failed to be verified. This may happen if the data was
|
||||
// corrupted or tampered.
|
||||
WB_RESULT_DATA_VERIFICATION_ERROR = 9,
|
||||
|
||||
// The padding at the end of the decrypted data does not match the
|
||||
// specification.
|
||||
WB_RESULT_INVALID_PADDING = 10,
|
||||
} WB_Result;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WHITEBOX_API_WB_RESULT_H_
|
||||
27
api/test_data.h
Normal file
27
api/test_data.h
Normal 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_
|
||||
22
chromium_deps/base/BUILD
Normal file
22
chromium_deps/base/BUILD
Normal file
@@ -0,0 +1,22 @@
|
||||
# Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
cc_library(
|
||||
name = "base",
|
||||
srcs = [
|
||||
"base64.cc",
|
||||
"strings/string_number_conversions.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"base64.h",
|
||||
"logging.h",
|
||||
"strings/string_number_conversions.h",
|
||||
],
|
||||
strip_include_prefix = "//chromium_deps",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//external:gflags",
|
||||
"//external:glog",
|
||||
"@abseil_repo//absl/base",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
63
chromium_deps/base/base64.cc
Normal file
63
chromium_deps/base/base64.cc
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "base/base64.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
static const char kBase64Codes[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
|
||||
// Calculates a/b using round-up division (only works for numbers
|
||||
// greater than 0).
|
||||
constexpr size_t CeilDivide(size_t a, size_t b) {
|
||||
return ((a - 1) / b) + 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Base64Encode(const std::string& input, std::string* output) {
|
||||
if (input.empty()) {
|
||||
output->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// |temp| stores a 24-bit block that is treated as an array where insertions
|
||||
// occur from high to low.
|
||||
uint32_t temp = 0;
|
||||
size_t out_index = 0;
|
||||
const size_t out_size = CeilDivide(input.size(), 3) * 4;
|
||||
std::string result(out_size, '\0');
|
||||
for (size_t i = 0; i < input.size(); i++) {
|
||||
// "insert" 8-bits of data
|
||||
temp = (temp << 8) | (input[i] & 0x0FF);
|
||||
|
||||
if (i % 3 == 2) {
|
||||
result[out_index++] = kBase64Codes[(temp >> 18) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 12) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 6) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[temp & 0x3f];
|
||||
temp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (input.size() % 3 == 1) {
|
||||
result[out_index++] = kBase64Codes[(temp >> 18) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 12) & 0x3f];
|
||||
result[out_index++] = '=';
|
||||
result[out_index++] = '=';
|
||||
} else if (input.size() % 3 == 2) {
|
||||
result[out_index++] = kBase64Codes[(temp >> 18) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 12) & 0x3f];
|
||||
result[out_index++] = kBase64Codes[(temp >> 6) & 0x3f];
|
||||
result[out_index++] = '=';
|
||||
}
|
||||
|
||||
output->swap(result);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
18
chromium_deps/base/base64.h
Normal file
18
chromium_deps/base/base64.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef BASE_BASE64_H_
|
||||
#define BASE_BASE64_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace base {
|
||||
|
||||
// Encodes the input string in base64.
|
||||
// Note that the Chromium code uses base::StringPiece for |input|, but to
|
||||
// avoid dragging in too much code, use std::string instead (which is
|
||||
// convertable to base::StringPiece automatically).
|
||||
void Base64Encode(const std::string& input, std::string* output);
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_BASE64_H_
|
||||
10
chromium_deps/base/logging.h
Normal file
10
chromium_deps/base/logging.h
Normal 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_
|
||||
14
chromium_deps/base/strings/string_number_conversions.cc
Normal file
14
chromium_deps/base/strings/string_number_conversions.cc
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
std::string HexEncode(const void* bytes, size_t size) {
|
||||
return absl::BytesToHexString(
|
||||
std::string(reinterpret_cast<const char*>(bytes), size));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
17
chromium_deps/base/strings/string_number_conversions.h
Normal file
17
chromium_deps/base/strings/string_number_conversions.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
|
||||
#define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
namespace base {
|
||||
|
||||
// Returns a hex string representation of a binary buffer.
|
||||
std::string HexEncode(const void* bytes, size_t size);
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
|
||||
16
chromium_deps/cdm/keys/BUILD
Normal file
16
chromium_deps/cdm/keys/BUILD
Normal 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"],
|
||||
)
|
||||
17
chromium_deps/cdm/keys/certs.h
Normal file
17
chromium_deps/cdm/keys/certs.h
Normal 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_
|
||||
116
chromium_deps/cdm/keys/dev_rsa_drm_certificate.cc
Normal file
116
chromium_deps/cdm/keys/dev_rsa_drm_certificate.cc
Normal 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
|
||||
13
chromium_deps/cdm/protos/BUILD
Normal file
13
chromium_deps/cdm/protos/BUILD
Normal 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",
|
||||
],
|
||||
)
|
||||
34
chromium_deps/cdm/protos/defs/BUILD
Normal file
34
chromium_deps/cdm/protos/defs/BUILD
Normal file
@@ -0,0 +1,34 @@
|
||||
# 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 = "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",
|
||||
],
|
||||
)
|
||||
124
chromium_deps/cdm/protos/defs/client_identification.proto
Normal file
124
chromium_deps/cdm/protos/defs/client_identification.proto
Normal 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;
|
||||
}
|
||||
453
chromium_deps/cdm/protos/defs/license_protocol.proto
Normal file
453
chromium_deps/cdm/protos/defs/license_protocol.proto
Normal file
@@ -0,0 +1,453 @@
|
||||
// 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];
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
24
chromium_deps/cdm/protos/defs/remote_attestation.proto
Normal file
24
chromium_deps/cdm/protos/defs/remote_attestation.proto
Normal 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;
|
||||
}
|
||||
10
chromium_deps/cdm/protos/license_protocol.pb.h
Normal file
10
chromium_deps/cdm/protos/license_protocol.pb.h
Normal 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_
|
||||
15
chromium_deps/testing/BUILD
Normal file
15
chromium_deps/testing/BUILD
Normal file
@@ -0,0 +1,15 @@
|
||||
# Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
cc_library(
|
||||
name = "gtest",
|
||||
hdrs = [
|
||||
"include/gmock/gmock.h",
|
||||
"include/gtest/gtest.h",
|
||||
],
|
||||
strip_include_prefix = "//chromium_deps",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//external:gtest",
|
||||
"//external:gtest_main",
|
||||
],
|
||||
)
|
||||
8
chromium_deps/testing/include/gmock/gmock.h
Normal file
8
chromium_deps/testing/include/gmock/gmock.h
Normal 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_
|
||||
8
chromium_deps/testing/include/gtest/gtest.h
Normal file
8
chromium_deps/testing/include/gtest/gtest.h
Normal 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_
|
||||
27
chromium_deps/third_party/boringssl/BUILD
vendored
Normal file
27
chromium_deps/third_party/boringssl/BUILD
vendored
Normal 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",
|
||||
],
|
||||
)
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/aead.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/aead.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/aes.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/aes.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/bio.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/bio.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/bn.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/bn.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/bytestring.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/bytestring.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/cmac.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/cmac.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/err.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/err.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/evp.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/evp.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/hmac.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/hmac.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/mem.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/mem.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/pem.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/pem.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/rand.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/rand.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/rsa.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/rsa.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/sha.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/sha.h
vendored
Normal 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_
|
||||
8
chromium_deps/third_party/boringssl/src/include/openssl/x509.h
vendored
Normal file
8
chromium_deps/third_party/boringssl/src/include/openssl/x509.h
vendored
Normal 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_
|
||||
220
crypto_utils/BUILD
Normal file
220
crypto_utils/BUILD
Normal file
@@ -0,0 +1,220 @@
|
||||
################################################################################
|
||||
# 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",
|
||||
"//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:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "aes_cbc_encryptor",
|
||||
srcs = ["aes_cbc_encryptor.cc"],
|
||||
hdrs = ["aes_cbc_encryptor.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//chromium_deps/base",
|
||||
"//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/testing:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "aes_ctr_encryptor",
|
||||
srcs = ["aes_ctr_encryptor.cc"],
|
||||
hdrs = ["aes_ctr_encryptor.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//chromium_deps/base",
|
||||
"//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:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "crypto_util",
|
||||
srcs = ["crypto_util.cc"],
|
||||
hdrs = ["crypto_util.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//chromium_deps/base",
|
||||
"//chromium_deps/third_party/boringssl",
|
||||
# TODO(hmchen): replace absl::string_view with std::string so that
|
||||
# these files can be used by other projects which does not use absl.
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "crypto_util_test",
|
||||
size = "small",
|
||||
srcs = ["crypto_util_test.cc"],
|
||||
deps = [
|
||||
":crypto_util",
|
||||
"//chromium_deps/testing:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "private_key_util",
|
||||
hdrs = ["private_key_util.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//chromium_deps/base",
|
||||
"//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",
|
||||
"//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/base",
|
||||
"//chromium_deps/testing:gtest",
|
||||
"//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",
|
||||
"//chromium_deps/third_party/boringssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "random_util_test",
|
||||
size = "small",
|
||||
srcs = ["random_util_test.cc"],
|
||||
deps = [
|
||||
":random_util",
|
||||
"//chromium_deps/testing:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "rsa_key",
|
||||
srcs = ["rsa_key.cc"],
|
||||
hdrs = ["rsa_key.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":rsa_util",
|
||||
":sha_util",
|
||||
"//chromium_deps/base",
|
||||
"//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:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "rsa_test_keys",
|
||||
testonly = 1,
|
||||
srcs = ["rsa_test_keys.cc"],
|
||||
hdrs = ["rsa_test_keys.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//chromium_deps/base",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "sha_util",
|
||||
srcs = ["sha_util.cc"],
|
||||
hdrs = ["sha_util.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//chromium_deps/base",
|
||||
"//chromium_deps/third_party/boringssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "sha_util_test",
|
||||
srcs = ["sha_util_test.cc"],
|
||||
deps = [
|
||||
":sha_util",
|
||||
"//chromium_deps/testing:gtest",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
61
crypto_utils/aes_cbc_decryptor.cc
Normal file
61
crypto_utils/aes_cbc_decryptor.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "crypto_utils/aes_cbc_decryptor.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
32
crypto_utils/aes_cbc_decryptor.h
Normal file
32
crypto_utils/aes_cbc_decryptor.h
Normal 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_
|
||||
91
crypto_utils/aes_cbc_decryptor_test.cc
Normal file
91
crypto_utils/aes_cbc_decryptor_test.cc
Normal 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/include/gmock/gmock.h"
|
||||
#include "testing/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
|
||||
61
crypto_utils/aes_cbc_encryptor.cc
Normal file
61
crypto_utils/aes_cbc_encryptor.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "crypto_utils/aes_cbc_encryptor.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
32
crypto_utils/aes_cbc_encryptor.h
Normal file
32
crypto_utils/aes_cbc_encryptor.h
Normal 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_
|
||||
60
crypto_utils/aes_cbc_encryptor_test.cc
Normal file
60
crypto_utils/aes_cbc_encryptor_test.cc
Normal 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/include/gmock/gmock.h"
|
||||
#include "testing/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
|
||||
78
crypto_utils/aes_ctr_encryptor.cc
Normal file
78
crypto_utils/aes_ctr_encryptor.cc
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "crypto_utils/aes_ctr_encryptor.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
32
crypto_utils/aes_ctr_encryptor.h
Normal file
32
crypto_utils/aes_ctr_encryptor.h
Normal 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_
|
||||
86
crypto_utils/aes_ctr_encryptor_test.cc
Normal file
86
crypto_utils/aes_ctr_encryptor_test.cc
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "crypto_utils/aes_ctr_encryptor.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
#include "testing/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
|
||||
205
crypto_utils/crypto_util.cc
Normal file
205
crypto_utils/crypto_util.cc
Normal file
@@ -0,0 +1,205 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 "absl/strings/string_view.h"
|
||||
#include "base/logging.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
|
||||
94
crypto_utils/crypto_util.h
Normal file
94
crypto_utils/crypto_util.h
Normal file
@@ -0,0 +1,94 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
// Default constants used for key derivation for encryption and signing.
|
||||
// TODO(user): These are duplicated in session.cc in the sdk. de-dup.
|
||||
extern const char kWrappingKeyLabel[];
|
||||
extern const int kWrappingKeySizeBits;
|
||||
extern const char kSigningKeyLabel[];
|
||||
extern const int kSigningKeySizeBits;
|
||||
extern const size_t kSigningKeySizeBytes;
|
||||
extern const char kIvMasterKey[];
|
||||
extern const char kIvLabel[];
|
||||
extern const int kIvSizeBits;
|
||||
extern const int kAes128KeySizeBits;
|
||||
extern const int kAes128KeySizeBytes;
|
||||
extern const 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_
|
||||
308
crypto_utils/crypto_util_test.cc
Normal file
308
crypto_utils/crypto_util_test.cc
Normal file
@@ -0,0 +1,308 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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/include/gmock/gmock.h"
|
||||
#include "testing/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(
|
||||
255 * 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(), 32);
|
||||
|
||||
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(), 32);
|
||||
|
||||
// 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(20, 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(20, 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
|
||||
81
crypto_utils/openssl_util.h
Normal file
81
crypto_utils/openssl_util.h
Normal 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_
|
||||
86
crypto_utils/private_key_util.h
Normal file
86
crypto_utils/private_key_util.h
Normal 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_
|
||||
27
crypto_utils/random_util.cc
Normal file
27
crypto_utils/random_util.cc
Normal 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/logging.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
|
||||
25
crypto_utils/random_util.h
Normal file
25
crypto_utils/random_util.h
Normal 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_
|
||||
29
crypto_utils/random_util_test.cc
Normal file
29
crypto_utils/random_util_test.cc
Normal 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/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
|
||||
335
crypto_utils/rsa_key.cc
Normal file
335
crypto_utils/rsa_key.cc
Normal file
@@ -0,0 +1,335 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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/logging.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"
|
||||
#include "crypto_utils/rsa_util.h"
|
||||
#include "crypto_utils/sha_util.h"
|
||||
|
||||
static const int kPssSaltLength = 20;
|
||||
|
||||
namespace {
|
||||
|
||||
// Check if two RSA keys match. If matches, they are either a public-private key
|
||||
// pair or the same public key or the same private key.
|
||||
bool RsaKeyMatch(const RSA* key1, const RSA* key2) {
|
||||
if (!key1 || !key2)
|
||||
return false;
|
||||
return BN_cmp(key1->n, key2->n) == 0;
|
||||
}
|
||||
|
||||
std::string OpenSSLErrorString(uint32_t error) {
|
||||
char buf[ERR_ERROR_STRING_BUF_LEN];
|
||||
ERR_error_string_n(error, buf, sizeof(buf));
|
||||
return buf;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
|
||||
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(key) {
|
||||
CHECK(key_ != nullptr);
|
||||
}
|
||||
|
||||
RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key)
|
||||
: key_(RSAPrivateKey_dup(rsa_key.key_)) {
|
||||
CHECK(key_ != nullptr);
|
||||
}
|
||||
|
||||
RsaPrivateKey::~RsaPrivateKey() {
|
||||
RSA_free(key_);
|
||||
}
|
||||
|
||||
RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) {
|
||||
RSA* key;
|
||||
if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key))
|
||||
return nullptr;
|
||||
if (RSA_check_key(key) != 1) {
|
||||
LOG(ERROR) << "Invalid private RSA key: "
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
RSA_free(key);
|
||||
}
|
||||
return new RsaPrivateKey(key);
|
||||
}
|
||||
|
||||
bool RsaPrivateKey::Decrypt(const std::string& encrypted_message,
|
||||
std::string* decrypted_message) const {
|
||||
DCHECK(decrypted_message);
|
||||
|
||||
size_t rsa_size = RSA_size(key_);
|
||||
if (encrypted_message.size() != rsa_size) {
|
||||
LOG(ERROR) << "Encrypted RSA message has the wrong size (expected "
|
||||
<< rsa_size << ", actual " << encrypted_message.size() << ")";
|
||||
return false;
|
||||
}
|
||||
decrypted_message->assign(rsa_size, 0);
|
||||
int decrypted_size = RSA_private_decrypt(
|
||||
rsa_size,
|
||||
const_cast<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
|
||||
163
crypto_utils/rsa_key.h
Normal file
163
crypto_utils/rsa_key.h
Normal 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_
|
||||
268
crypto_utils/rsa_key_test.cc
Normal file
268
crypto_utils/rsa_key_test.cc
Normal 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 "testing/include/gtest/gtest.h"
|
||||
#include "crypto_utils/rsa_test_keys.h"
|
||||
#include "crypto_utils/rsa_util.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
static const char kTestMessage[] =
|
||||
"A fool thinks himself to be wise, but a wise man knows himself to be a "
|
||||
"fool.";
|
||||
|
||||
class RsaKeyTest : public ::testing::Test {
|
||||
protected:
|
||||
void TestEncryption(std::unique_ptr<RsaPrivateKey> private_key,
|
||||
std::unique_ptr<RsaPublicKey> public_key);
|
||||
|
||||
void TestSigning(std::unique_ptr<RsaPrivateKey> private_key,
|
||||
std::unique_ptr<RsaPublicKey> public_key);
|
||||
|
||||
void TestSigningSha256Pkcs7(std::unique_ptr<RsaPrivateKey> private_key,
|
||||
std::unique_ptr<RsaPublicKey> public_key);
|
||||
|
||||
RsaTestKeys test_keys_;
|
||||
RsaKeyFactory factory_;
|
||||
};
|
||||
|
||||
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(256, private_key->KeySize());
|
||||
EXPECT_EQ(256, 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
|
||||
786
crypto_utils/rsa_test_keys.cc
Normal file
786
crypto_utils/rsa_test_keys.cc
Normal file
@@ -0,0 +1,786 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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)) {}
|
||||
|
||||
} // namespace widevine
|
||||
92
crypto_utils/rsa_test_keys.h
Normal file
92
crypto_utils/rsa_test_keys.h
Normal file
@@ -0,0 +1,92 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Description:
|
||||
// RSA keys generated using fake_prng for purposes of testing.
|
||||
|
||||
#ifndef 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();
|
||||
|
||||
// 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_
|
||||
454
crypto_utils/rsa_util.cc
Normal file
454
crypto_utils/rsa_util.cc
Normal file
@@ -0,0 +1,454 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// RSA utility functions for serializing and deserializing RSA keys,
|
||||
// encryption, and signing.
|
||||
|
||||
#include "crypto_utils/rsa_util.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "third_party/boringssl/src/include/openssl/pem.h"
|
||||
#include "third_party/boringssl/src/include/openssl/x509.h"
|
||||
#include "crypto_utils/private_key_util.h"
|
||||
|
||||
namespace {
|
||||
int BigNumGreaterThanPow2(const BIGNUM* b, int n) {
|
||||
if (BN_is_negative(b) || n == INT_MAX) {
|
||||
return 0;
|
||||
}
|
||||
int b_bits = BN_num_bits(b);
|
||||
return b_bits > n + 1 || (b_bits == n + 1 && !BN_is_pow2(b));
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
namespace widevine {
|
||||
namespace rsa_util {
|
||||
|
||||
bool SerializeRsaPrivateKey(const RSA* private_key,
|
||||
std::string* serialized_private_key) {
|
||||
return private_key_util::SerializeKey<RSA>(private_key, i2d_RSAPrivateKey_bio,
|
||||
serialized_private_key);
|
||||
}
|
||||
|
||||
bool DeserializeRsaPrivateKey(const std::string& serialized_private_key,
|
||||
RSA** private_key) {
|
||||
return private_key_util::DeserializeKey<RSA>(
|
||||
serialized_private_key, d2i_RSAPrivateKey_bio, private_key);
|
||||
}
|
||||
|
||||
bool SerializeRsaPublicKey(const RSA* public_key,
|
||||
std::string* serialized_public_key) {
|
||||
return private_key_util::SerializeKey<RSA>(public_key, i2d_RSAPublicKey_bio,
|
||||
serialized_public_key);
|
||||
}
|
||||
|
||||
bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
|
||||
RSA** public_key) {
|
||||
return private_key_util::DeserializeKey<RSA>(
|
||||
serialized_public_key, d2i_RSAPublicKey_bio, public_key);
|
||||
}
|
||||
|
||||
bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||
std::string* serialized_private_key) {
|
||||
if (private_key == nullptr) {
|
||||
LOG(ERROR) << "Private RSA key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (serialized_private_key == nullptr) {
|
||||
LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is nullptr.";
|
||||
return false;
|
||||
}
|
||||
// The following method of serializing a PKCS#8 PrivateKeyInfo object
|
||||
// was obtained from analyzing the openssl utility code, as the official
|
||||
// mechanism via i2d_PKCS8PrivateKey_bio is broken in the current openssl
|
||||
// version (1.0.0c). Please refer to b/8560683.
|
||||
EVP_PKEY* evp = EVP_PKEY_new();
|
||||
if (evp == nullptr) {
|
||||
LOG(ERROR) << "EVP_PKEY_new returned nullptr.";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
PKCS8_PRIV_KEY_INFO* pkcs8_pki = nullptr;
|
||||
BIO* bio = nullptr;
|
||||
if (EVP_PKEY_set1_RSA(evp, const_cast<RSA*>(private_key)) == 0) {
|
||||
LOG(ERROR) << "EVP_PKEY_set1_RSA failed.";
|
||||
goto cleanup;
|
||||
}
|
||||
pkcs8_pki = EVP_PKEY2PKCS8(evp);
|
||||
if (pkcs8_pki == nullptr) {
|
||||
LOG(ERROR) << "EVP_PKEY2PKCS8 returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
if (i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8_pki) == 0) {
|
||||
LOG(ERROR) << "i2d_PKCS8_PRIV_KEY_INFO_bio failed.";
|
||||
goto cleanup;
|
||||
}
|
||||
{
|
||||
int serialized_size = BIO_pending(bio);
|
||||
serialized_private_key->assign(serialized_size, 0);
|
||||
if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) !=
|
||||
serialized_size) {
|
||||
LOG(ERROR) << "BIO_read failed.";
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
if (bio != nullptr) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
if (pkcs8_pki != nullptr) {
|
||||
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
|
||||
}
|
||||
EVP_PKEY_free(evp);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
|
||||
RSA** private_key) {
|
||||
if (serialized_private_key.empty()) {
|
||||
LOG(ERROR) << "Serialized PrivateKeyInfo is empty.";
|
||||
return false;
|
||||
}
|
||||
if (private_key == nullptr) {
|
||||
LOG(ERROR) << "Pointer to hold new RSA private key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
// The following method of deserializing a PKCS#8 PrivateKeyInfo object
|
||||
// was obtained from analyzing the openssl utility code, as the official
|
||||
// mechanism via d2i_PKCS8PrivateKey_bio is broken in the current openssl
|
||||
// version (1.0.0c). Please refer to b/8560683.
|
||||
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_private_key.data()),
|
||||
serialized_private_key.size());
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
EVP_PKEY* evp = nullptr;
|
||||
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
|
||||
if (pkcs8_pki == nullptr) {
|
||||
LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
evp = EVP_PKCS82PKEY(pkcs8_pki);
|
||||
if (evp == nullptr) {
|
||||
LOG(ERROR) << "EVP_PKCS82PKEY returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
*private_key = EVP_PKEY_get1_RSA(evp);
|
||||
if (*private_key == nullptr) {
|
||||
LOG(ERROR) << "PrivateKeyInfo did not contain an RSA key.";
|
||||
goto cleanup;
|
||||
}
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
if (evp != nullptr) {
|
||||
EVP_PKEY_free(evp);
|
||||
}
|
||||
if (pkcs8_pki != nullptr) {
|
||||
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
|
||||
}
|
||||
BIO_free(bio);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
std::string* private_key_info) {
|
||||
RSA* key = nullptr;
|
||||
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
|
||||
bool success = SerializePrivateKeyInfo(key, private_key_info);
|
||||
RSA_free(key);
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
std::string* rsa_private_key) {
|
||||
RSA* key = nullptr;
|
||||
if (DeserializePrivateKeyInfo(private_key_info, &key)) {
|
||||
bool success = SerializeRsaPrivateKey(key, rsa_private_key);
|
||||
RSA_free(key);
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
|
||||
const std::string& passphrase,
|
||||
std::string* serialized_private_key) {
|
||||
if (private_key == nullptr) {
|
||||
LOG(ERROR) << "Private RSA key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (passphrase.empty()) {
|
||||
LOG(ERROR) << "Passphrase for RSA key encryption is empty.";
|
||||
return false;
|
||||
}
|
||||
if (serialized_private_key == nullptr) {
|
||||
LOG(ERROR)
|
||||
<< "Pointer to hold serialized EncryptedPrivateKeyInfo is nullptr.";
|
||||
return false;
|
||||
}
|
||||
EVP_PKEY* evp = EVP_PKEY_new();
|
||||
if (evp == nullptr) {
|
||||
LOG(ERROR) << "EVP_PKEY_new returned nullptr.";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
BIO* bio = nullptr;
|
||||
if (EVP_PKEY_set1_RSA(evp, const_cast<RSA*>(private_key)) == 0) {
|
||||
LOG(ERROR) << "EVP_PKEY_set1_RSA failed.";
|
||||
goto cleanup;
|
||||
}
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
if (i2d_PKCS8PrivateKey_bio(bio, evp, EVP_aes_256_cbc(),
|
||||
const_cast<char*>(passphrase.data()),
|
||||
passphrase.size(), nullptr, nullptr) == 0) {
|
||||
LOG(ERROR) << "i2d_PKCS8PrivateKey_bio failed.";
|
||||
goto cleanup;
|
||||
}
|
||||
{
|
||||
int serialized_size = BIO_pending(bio);
|
||||
serialized_private_key->assign(serialized_size, 0);
|
||||
if (BIO_read(bio, &(*serialized_private_key)[0], serialized_size) !=
|
||||
serialized_size) {
|
||||
LOG(ERROR) << "BIO_read failed.";
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
if (bio != nullptr) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
EVP_PKEY_free(evp);
|
||||
return success;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below.
|
||||
int get_password(char* buf, int size, int rwflag, void* u) {
|
||||
CHECK(buf);
|
||||
CHECK(u);
|
||||
const std::string* pass(static_cast<const std::string*>(u));
|
||||
if (!pass->empty() && size >= static_cast<int>(pass->size())) {
|
||||
memcpy(buf, pass->data(), pass->size());
|
||||
return pass->size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool DeserializeEncryptedPrivateKeyInfo(
|
||||
const std::string& serialized_private_key,
|
||||
const std::string& passphrase,
|
||||
RSA** private_key) {
|
||||
if (serialized_private_key.empty()) {
|
||||
LOG(ERROR) << "Serialized RSAEncryptedPrivateKeyInfo is empty.";
|
||||
return false;
|
||||
}
|
||||
if (passphrase.empty()) {
|
||||
LOG(ERROR) << "Passphrase for RSA key decryption is empty.";
|
||||
return false;
|
||||
}
|
||||
if (private_key == nullptr) {
|
||||
LOG(ERROR) << "Pointer to hold new RSA private key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_private_key.data()),
|
||||
serialized_private_key.size());
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(
|
||||
bio, nullptr, get_password, const_cast<std::string*>(&passphrase));
|
||||
if (evp == nullptr) {
|
||||
LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
*private_key = EVP_PKEY_get1_RSA(evp);
|
||||
if (*private_key == nullptr) {
|
||||
LOG(ERROR) << "EncryptedPrivateKeyInfo did not contain an RSA key.";
|
||||
goto cleanup;
|
||||
}
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
if (evp != nullptr) {
|
||||
EVP_PKEY_free(evp);
|
||||
}
|
||||
BIO_free(bio);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
const std::string& passphrase,
|
||||
std::string* private_key_info) {
|
||||
RSA* key = nullptr;
|
||||
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
|
||||
bool success =
|
||||
SerializeEncryptedPrivateKeyInfo(key, passphrase, private_key_info);
|
||||
RSA_free(key);
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
const std::string& passphrase,
|
||||
std::string* rsa_private_key) {
|
||||
RSA* key = nullptr;
|
||||
if (DeserializeEncryptedPrivateKeyInfo(private_key_info, passphrase, &key)) {
|
||||
bool success = SerializeRsaPrivateKey(key, rsa_private_key);
|
||||
RSA_free(key);
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConvertToEulerTotient(RSA* rsa) {
|
||||
// RSA key generation requires computing e (public exponent) and d (private
|
||||
// exponent), such that m^(e * d) = 1 (mod n) for all m coprime to n.
|
||||
// BoringSSL previously computed this by taking the inverse of e modulo the
|
||||
// Euler totient, (p - 1) * (q - 1). However, it now uses the Carmichael
|
||||
// totient, lcm(p - 1, q - 1). These two methods produce equivalent RSA keys.
|
||||
//
|
||||
// This breaks some vendors' RSA code which use a custom RSA format (rather
|
||||
// than the standard one in RFC 8017) which omits most of the required
|
||||
// parameters. They then attempt to recover those parameters, but their
|
||||
// recovery algorithm breaks when using the Carmichael totient. To work around
|
||||
// this bug, re-compute the private exponent against the Euler totient.
|
||||
const BIGNUM *e, *p, *q;
|
||||
RSA_get0_key(rsa, nullptr /* n */, &e, nullptr /* d */);
|
||||
RSA_get0_factors(rsa, &p, &q);
|
||||
|
||||
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
|
||||
bssl::UniquePtr<BIGNUM> pm1(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> qm1(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> totient(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> d(BN_new());
|
||||
if (!ctx || !pm1 || !qm1 || !totient || !d ||
|
||||
!BN_sub(pm1.get(), p, BN_value_one()) ||
|
||||
!BN_sub(qm1.get(), q, BN_value_one()) ||
|
||||
!BN_mul(totient.get(), pm1.get(), qm1.get(), ctx.get()) ||
|
||||
!BN_mod_inverse(d.get(), e, totient.get(), ctx.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform a sanity check that d is still valid after conversion.
|
||||
// d > 2 ^ (nlen / 2) per Appendix B 3.1 in FIPS 186/4.
|
||||
int prime_bits = (8 * RSA_size(rsa)) / 2;
|
||||
if (!BigNumGreaterThanPow2(d.get(), prime_bits)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RSA_set0_key(rsa, nullptr /* n */, nullptr /* e */, d.get())) {
|
||||
return false;
|
||||
}
|
||||
d.release(); // RSA_set0_key takes ownership on success.
|
||||
|
||||
if (!RSA_check_key(rsa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertToEulerTotient(const std::string& private_key,
|
||||
std::string* euler_private_key) {
|
||||
CHECK(euler_private_key);
|
||||
RSA* rsa_ptr;
|
||||
if (!rsa_util::DeserializeRsaPrivateKey(private_key, &rsa_ptr)) {
|
||||
return false;
|
||||
}
|
||||
bssl::UniquePtr<RSA> rsa(rsa_ptr);
|
||||
if (!rsa_util::ConvertToEulerTotient(rsa.get()) ||
|
||||
!rsa_util::SerializeRsaPrivateKey(rsa.get(), euler_private_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertToCarmichaelTotient(RSA* rsa) {
|
||||
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
|
||||
bssl::UniquePtr<BIGNUM> pm1(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> qm1(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> gcd(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> totient(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> d(BN_new());
|
||||
// This calculates d = e^-1 (mod lcm(p-1, q-1)).
|
||||
// This is equivalent to what is used in RSA_generate_key in BoringSSL.
|
||||
if (!BN_sub(pm1.get(), rsa->p, BN_value_one()) ||
|
||||
!BN_sub(qm1.get(), rsa->q, BN_value_one()) ||
|
||||
!BN_mul(totient.get(), pm1.get(), qm1.get(), ctx.get()) ||
|
||||
!BN_gcd(gcd.get(), pm1.get(), qm1.get(), ctx.get()) ||
|
||||
!BN_div(totient.get(), nullptr, totient.get(), gcd.get(), ctx.get()) ||
|
||||
!BN_mod_inverse(d.get(), rsa->e, totient.get(), ctx.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform a sanity check that d is still valid after conversion.
|
||||
// d > 2 ^ (nlen / 2) per Appendix B 3.1 in FIPS 186/4.
|
||||
int prime_bits = (8 * RSA_size(rsa)) / 2;
|
||||
if (!BigNumGreaterThanPow2(d.get(), prime_bits)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(user): Replace this with |RSA_set0_key| once BoringSSL has
|
||||
// finished transitioning to the OpenSSL 1.1.0 API.
|
||||
BN_free(rsa->d);
|
||||
rsa->d = d.release();
|
||||
|
||||
if (!RSA_check_key(rsa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertToCarmichaelTotient(const std::string& private_key,
|
||||
std::string* carmichael_private_key) {
|
||||
CHECK(carmichael_private_key);
|
||||
RSA* rsa_ptr;
|
||||
if (!rsa_util::DeserializeRsaPrivateKey(private_key, &rsa_ptr)) {
|
||||
return false;
|
||||
}
|
||||
bssl::UniquePtr<RSA> rsa(rsa_ptr);
|
||||
if (!rsa_util::ConvertToCarmichaelTotient(rsa.get()) ||
|
||||
!rsa_util::SerializeRsaPrivateKey(rsa.get(), carmichael_private_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace rsa_util
|
||||
} // namespace widevine
|
||||
210
crypto_utils/rsa_util.h
Normal file
210
crypto_utils/rsa_util.h
Normal file
@@ -0,0 +1,210 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Description:
|
||||
// RSA utility functions for serializing and deserializing RSA keys,
|
||||
// encryption, and signing.
|
||||
|
||||
#ifndef WHITEBOX_CRYPTO_UTILS_RSA_UTIL_H_
|
||||
#define WHITEBOX_CRYPTO_UTILS_RSA_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "third_party/boringssl/src/include/openssl/rsa.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace rsa_util {
|
||||
|
||||
// Serialize RSA private key into DER encoded PKCS#1 RSAPrivateKey.
|
||||
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||
// - serialized_private_key is a pointer to the std::string to hold the
|
||||
// serialized
|
||||
// PKCS#1 RSAPrivateKey object. Caller retains ownership of the string. This
|
||||
// parameter must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool SerializeRsaPrivateKey(const RSA* private_key,
|
||||
std::string* serialized_private_key);
|
||||
|
||||
// Deserialize RSA private key from DER encoded PKCS#1 RSAPrivateKey.
|
||||
// - serialized_private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
|
||||
// deserialized.
|
||||
// - private_key is a pointer to an RSA structure pointer to point to a newly
|
||||
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||
// which is not allocated if the method fails. This parameter must not be
|
||||
// NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DeserializeRsaPrivateKey(const std::string& serialized_private_key,
|
||||
RSA** private_key);
|
||||
|
||||
// Serialize RSA key into DER encoded PKCS#1 RSAPublicKey.
|
||||
// - public_key is the RSA key to be serialized, which must not be NULL.
|
||||
// - serialized_public_key is a pointer to the std::string to hold the
|
||||
// serialized
|
||||
// PKCS#1 RSAPublicKey object. Caller retains ownership of the string. This
|
||||
// parameter must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool SerializeRsaPublicKey(const RSA* public_key,
|
||||
std::string* serialized_public_key);
|
||||
|
||||
// Deserialize RSA public key from DER encoded PKCS#1 RSAPublicKey.
|
||||
// - serialized_public_key is the DER-encoded PKCS#1 RSAPublicKey to be
|
||||
// deserialized.
|
||||
// - public_key is a pointer to an RSA structure pointer to point to a newly
|
||||
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||
// which is not allocated if the method fails. This parameter must not be
|
||||
// NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
|
||||
RSA** public_key);
|
||||
|
||||
// Serialize RSA private key into DER encoded PKCS#8 PrivateKeyInfo.
|
||||
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||
// - serialized_private_key is a pointer to the std::string to hold the
|
||||
// serialized
|
||||
// PKCS#8 PrivateKeyInfo object. Caller retains ownership of the string. This
|
||||
// parameter must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||
std::string* serialized_private_key);
|
||||
|
||||
// Deserialize RSA private key from DER encoded PKCS#8 PrivateKeyInfo.
|
||||
// - serialized_private_key is the DER-encoded PKCS#8 PrivateKeyInfo to be
|
||||
// deserialized.
|
||||
// - private_key is a pointer to an RSA structure pointer to point to a newly
|
||||
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||
// which is not allocated if the method fails. This parameter must not be
|
||||
// NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
|
||||
RSA** private_key);
|
||||
|
||||
// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8
|
||||
// PrivateKeyInfo.
|
||||
// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted.
|
||||
// - private_key_info is a pointer to std::string to hold the PKCS#8
|
||||
// PrivateKeyInfo.
|
||||
// The caller retains ownership of this parameter, which must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
std::string* private_key_info);
|
||||
|
||||
// Convert DER-encoded PKCS#8 PrivateKeyInfo to DER-encoded PKCS#1
|
||||
// RSAPrivateKey.
|
||||
// - private_key_info is the PKCS#8 PrivateKeyInfo to be converted.
|
||||
// - rsa_private_key is a pointer to std::string to hold the PKCS#1
|
||||
// RSAPrivateKey.
|
||||
// The caller retains ownership of this parameter, which must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
std::string* rsa_private_key);
|
||||
|
||||
// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo.
|
||||
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||
// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption.
|
||||
// - serialized_private_key is a pointer to the std::string to hold the
|
||||
// serialized
|
||||
// PKCS#8 EncryptedPrivateKeyInfo object. Caller retains ownership of the
|
||||
// string. This parameter must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
|
||||
const std::string& passphrase,
|
||||
std::string* serialized_private_key);
|
||||
|
||||
// Deserialize RSA private key from DER encoded PKCS#8 EncryptedPrivateKeyInfo.
|
||||
// - serialized_private_key is the DER-encoded PKCS#8 EncryptedPrivateKeyInfo to
|
||||
// be deserialized.
|
||||
// - passphrase is the password to use for key decryption.
|
||||
// - private_key is a pointer to an RSA structure pointer to point to a newly
|
||||
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||
// which is not allocated if the method fails. This parameter must not be
|
||||
// NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DeserializeEncryptedPrivateKeyInfo(
|
||||
const std::string& serialized_private_key,
|
||||
const std::string& passphrase,
|
||||
RSA** private_key);
|
||||
|
||||
// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8
|
||||
// EncryptedPrivateKeyInfo.
|
||||
// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted.
|
||||
// - passphrase is the password to use for PKCS#5 v2.1 AES-256-CBC encryption.
|
||||
// - private_key_info is a pointer to std::string to hold the PKCS#8
|
||||
// EncryptedPrivateKeyInfo.
|
||||
// The caller retains ownership of this parameter, which must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
const std::string& passphrase,
|
||||
std::string* private_key_info);
|
||||
|
||||
// Convert DER-encoded PKCS#8 EncryptedPrivateKeyInfo to DER-encoded PKCS#1
|
||||
// RSAPrivateKey.
|
||||
// - private_key_info is the PKCS#8 EncryptedPrivateKeyInfo to be converted.
|
||||
// - passphrase is the password to use for key decryption.
|
||||
// - rsa_private_key is a pointer to std::string to hold the PKCS#1
|
||||
// RSAPrivateKey.
|
||||
// The caller retains ownership of this parameter, which must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
const std::string& passphrase,
|
||||
std::string* rsa_private_key);
|
||||
|
||||
// This method changes |rsa| to use the more common, but not FIPS 186-4
|
||||
// compliant, computation for the RSA private exponent (d). This used to be the
|
||||
// form produced by the BoringSSL method RSA_generate_key. This changed in this
|
||||
// CL: https://boringssl-review.googlesource.com/c/boringssl/+/15944
|
||||
//
|
||||
// This method is used to produce a "backward-compatible" version. Some vendor
|
||||
// RSA implementations do not handle the new computation properly.
|
||||
//
|
||||
// - rsa is the openssl RSA structure. Must not be null. Caller retains
|
||||
// ownership.
|
||||
//
|
||||
// Returns true if the key was successfully updated, false otherwise.
|
||||
bool ConvertToEulerTotient(RSA* rsa);
|
||||
|
||||
// This is wrapper to the other SwitchToEulerTotient that supports deserializing
|
||||
// and serializing from and to a DER encoded PKCS#1 RSAPrivateKey.
|
||||
//
|
||||
// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
|
||||
// deserialized and converted.
|
||||
// - euler_private_key is a pointer to the std::string to hold the converted and
|
||||
// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the
|
||||
// string. This parameter must not be NULL.
|
||||
// Returns true if the key was successfully updated, false otherwise.
|
||||
bool ConvertToEulerTotient(const std::string& private_key,
|
||||
std::string* euler_private_key);
|
||||
|
||||
// This method changes |rsa| to use the FIPS 186-4 compliant computation for d.
|
||||
// This uses the Carmichael totient. This is equivalent to the way the key
|
||||
// is generated in BoringSSL as of this change:
|
||||
// https://boringssl-review.googlesource.com/c/boringssl/+/15944
|
||||
//
|
||||
// This method is mostly used for testing. It allows tests to convert back and
|
||||
// forth between forms and verify the output.
|
||||
//
|
||||
// Returns true if the key can be successfully updated, false otherwise.
|
||||
bool ConvertToCarmichaelTotient(RSA* rsa);
|
||||
|
||||
// This is wrapper to the other SwitchToCarmichaelTotient that supports
|
||||
// deserializing and serializing from and to a DER encoded PKCS#1 RSAPrivateKey.
|
||||
//
|
||||
// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
|
||||
// deserialized and converted.
|
||||
// - carmichael_private_key is a pointer to the std::string to hold the
|
||||
// converted and
|
||||
// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the
|
||||
// string. This parameter must not be NULL.
|
||||
// Returns true if the key was successfully updated, false otherwise.
|
||||
bool ConvertToCarmichaelTotient(const std::string& private_key,
|
||||
std::string* carmichael_private_key);
|
||||
|
||||
} // namespace rsa_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_CRYPTO_UTILS_RSA_UTIL_H_
|
||||
352
crypto_utils/rsa_util_test.cc
Normal file
352
crypto_utils/rsa_util_test.cc
Normal file
@@ -0,0 +1,352 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Description:
|
||||
// Unit test for rsa_util RSA utilties library.
|
||||
|
||||
#include "crypto_utils/rsa_util.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
#include "third_party/boringssl/src/include/openssl/bn.h"
|
||||
#include "crypto_utils/rsa_test_keys.h"
|
||||
|
||||
using ::testing::NotNull;
|
||||
|
||||
namespace {
|
||||
const uint32_t kRsaPublicExponent = 65537;
|
||||
const int kTestRsaBits = 2048;
|
||||
} // anonymous namespace
|
||||
|
||||
namespace widevine {
|
||||
namespace rsa_util {
|
||||
|
||||
class RsaUtilTest : public ::testing::Test {
|
||||
protected:
|
||||
RsaTestKeys test_keys_;
|
||||
};
|
||||
|
||||
TEST_F(RsaUtilTest, SerializeDeserializePrivateKey) {
|
||||
RSA* private_key;
|
||||
std::string serialized_private_key;
|
||||
// Key 1
|
||||
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_1_3072_bits(), &private_key));
|
||||
ASSERT_TRUE(private_key != NULL);
|
||||
EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key));
|
||||
EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_1_3072_bits());
|
||||
EXPECT_EQ(RSA_check_key(private_key), 1);
|
||||
RSA_free(private_key);
|
||||
// Key 2
|
||||
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_2_2048_bits(), &private_key));
|
||||
ASSERT_TRUE(private_key != NULL);
|
||||
EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key));
|
||||
EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_2_2048_bits());
|
||||
EXPECT_EQ(RSA_check_key(private_key), 1);
|
||||
RSA_free(private_key);
|
||||
// Key 3
|
||||
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_3_2048_bits(), &private_key));
|
||||
ASSERT_TRUE(private_key != NULL);
|
||||
EXPECT_TRUE(SerializeRsaPrivateKey(private_key, &serialized_private_key));
|
||||
EXPECT_EQ(serialized_private_key, test_keys_.private_test_key_3_2048_bits());
|
||||
EXPECT_EQ(RSA_check_key(private_key), 1);
|
||||
RSA_free(private_key);
|
||||
// Invalid key
|
||||
EXPECT_FALSE(DeserializeRsaPrivateKey("this is a bad key", &private_key));
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, SerializeDeserializePublicKey) {
|
||||
RSA* public_key;
|
||||
std::string serialized_public_key;
|
||||
// Key 1
|
||||
EXPECT_TRUE(DeserializeRsaPublicKey(test_keys_.public_test_key_1_3072_bits(),
|
||||
&public_key));
|
||||
ASSERT_TRUE(public_key != NULL);
|
||||
EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key));
|
||||
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits());
|
||||
RSA_free(public_key);
|
||||
// Key 2
|
||||
EXPECT_TRUE(DeserializeRsaPublicKey(test_keys_.public_test_key_2_2048_bits(),
|
||||
&public_key));
|
||||
ASSERT_TRUE(public_key != NULL);
|
||||
EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key));
|
||||
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits());
|
||||
RSA_free(public_key);
|
||||
// Key 3
|
||||
EXPECT_TRUE(DeserializeRsaPublicKey(test_keys_.public_test_key_3_2048_bits(),
|
||||
&public_key));
|
||||
ASSERT_TRUE(public_key != NULL);
|
||||
EXPECT_TRUE(SerializeRsaPublicKey(public_key, &serialized_public_key));
|
||||
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits());
|
||||
RSA_free(public_key);
|
||||
// Invalid key
|
||||
EXPECT_FALSE(DeserializeRsaPublicKey("this is a bad key", &public_key));
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, PublicKeyExtraction) {
|
||||
RSA* private_key;
|
||||
std::string serialized_public_key;
|
||||
// Key 1
|
||||
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_1_3072_bits(), &private_key));
|
||||
ASSERT_TRUE(private_key != NULL);
|
||||
EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key));
|
||||
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_3072_bits());
|
||||
RSA_free(private_key);
|
||||
// Key 2
|
||||
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_2_2048_bits(), &private_key));
|
||||
ASSERT_TRUE(private_key != NULL);
|
||||
EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key));
|
||||
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_2_2048_bits());
|
||||
RSA_free(private_key);
|
||||
// Key 3
|
||||
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_3_2048_bits(), &private_key));
|
||||
ASSERT_TRUE(private_key != NULL);
|
||||
EXPECT_TRUE(SerializeRsaPublicKey(private_key, &serialized_public_key));
|
||||
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_3_2048_bits());
|
||||
RSA_free(private_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, Pkcs8PrivateKeyInfo) {
|
||||
// The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8
|
||||
// serialization/deserialization functionality , so we test those.
|
||||
std::string serialized_pkcs8;
|
||||
std::string serialized_pkcs1;
|
||||
// Key 1
|
||||
EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo(
|
||||
test_keys_.private_test_key_1_3072_bits(), &serialized_pkcs8));
|
||||
EXPECT_TRUE(
|
||||
PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, &serialized_pkcs1));
|
||||
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||
EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1);
|
||||
// Key 2
|
||||
EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), &serialized_pkcs8));
|
||||
EXPECT_TRUE(
|
||||
PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, &serialized_pkcs1));
|
||||
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1);
|
||||
// Key 3
|
||||
EXPECT_TRUE(RsaPrivateKeyToPrivateKeyInfo(
|
||||
test_keys_.private_test_key_3_2048_bits(), &serialized_pkcs8));
|
||||
EXPECT_TRUE(
|
||||
PrivateKeyInfoToRsaPrivateKey(serialized_pkcs8, &serialized_pkcs1));
|
||||
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||
EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, Pkcs8EncryptedPrivateKeyInfo) {
|
||||
// The PKCS#1 <-> PKCS#8 conversion routines exercise all the PKCS#8
|
||||
// serialization/deserialization functionality , so we test those.
|
||||
std::string serialized_pkcs8;
|
||||
std::string serialized_pkcs1;
|
||||
std::string passphrase("passphrase");
|
||||
// Key 1
|
||||
EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_1_3072_bits(), passphrase,
|
||||
&serialized_pkcs8));
|
||||
EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(
|
||||
serialized_pkcs8, passphrase, &serialized_pkcs1));
|
||||
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||
EXPECT_EQ(test_keys_.private_test_key_1_3072_bits(), serialized_pkcs1);
|
||||
// Key 2
|
||||
EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), passphrase,
|
||||
&serialized_pkcs8));
|
||||
EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(
|
||||
serialized_pkcs8, passphrase, &serialized_pkcs1));
|
||||
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), serialized_pkcs1);
|
||||
// Key 3
|
||||
EXPECT_TRUE(RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_3_2048_bits(), passphrase,
|
||||
&serialized_pkcs8));
|
||||
EXPECT_TRUE(EncryptedPrivateKeyInfoToRsaPrivateKey(
|
||||
serialized_pkcs8, passphrase, &serialized_pkcs1));
|
||||
EXPECT_NE(serialized_pkcs1, serialized_pkcs8);
|
||||
EXPECT_EQ(test_keys_.private_test_key_3_2048_bits(), serialized_pkcs1);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, FailOnInvalidParams) {
|
||||
RSA* test_input_key = NULL;
|
||||
RSA* test_output_key = NULL;
|
||||
std::string test_string;
|
||||
std::string pass("password");
|
||||
ASSERT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_2_2048_bits(), &test_input_key));
|
||||
ASSERT_TRUE(test_input_key != NULL);
|
||||
EXPECT_FALSE(SerializeRsaPrivateKey(NULL, &test_string));
|
||||
EXPECT_FALSE(SerializeRsaPrivateKey(test_input_key, NULL));
|
||||
EXPECT_FALSE(SerializeRsaPublicKey(NULL, &test_string));
|
||||
EXPECT_FALSE(SerializeRsaPublicKey(test_input_key, NULL));
|
||||
EXPECT_FALSE(SerializePrivateKeyInfo(NULL, &test_string));
|
||||
EXPECT_FALSE(SerializePrivateKeyInfo(test_input_key, NULL));
|
||||
EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(NULL, pass, &test_string));
|
||||
EXPECT_FALSE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass, NULL));
|
||||
EXPECT_FALSE(DeserializeRsaPrivateKey("", &test_output_key));
|
||||
EXPECT_FALSE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_2_2048_bits(), NULL));
|
||||
EXPECT_FALSE(DeserializeRsaPublicKey("", &test_output_key));
|
||||
EXPECT_FALSE(
|
||||
DeserializeRsaPublicKey(test_keys_.public_test_key_2_2048_bits(), NULL));
|
||||
EXPECT_FALSE(DeserializePrivateKeyInfo("", &test_output_key));
|
||||
EXPECT_FALSE(DeserializePrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), NULL));
|
||||
EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo("", pass, &test_output_key));
|
||||
EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), pass, NULL));
|
||||
RSA_free(test_input_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, Pkcs8FailOnInvalidPassword) {
|
||||
RSA* test_input_key = NULL;
|
||||
RSA* test_output_key = NULL;
|
||||
std::string serialized_pkcs8;
|
||||
std::string pass("password");
|
||||
ASSERT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_2_2048_bits(), &test_input_key));
|
||||
EXPECT_FALSE(
|
||||
SerializeEncryptedPrivateKeyInfo(test_input_key, "", &serialized_pkcs8));
|
||||
ASSERT_TRUE(SerializeEncryptedPrivateKeyInfo(test_input_key, pass,
|
||||
&serialized_pkcs8));
|
||||
EXPECT_FALSE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass + "a",
|
||||
&test_output_key));
|
||||
EXPECT_TRUE(DeserializeEncryptedPrivateKeyInfo(serialized_pkcs8, pass,
|
||||
&test_output_key));
|
||||
RSA_free(test_input_key);
|
||||
RSA_free(test_output_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToCarmichaelTotient_ExistingKey_Success) {
|
||||
bssl::UniquePtr<RSA> original_private_key;
|
||||
RSA* original_key_ptr = nullptr;
|
||||
|
||||
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_2_2048_bits(), &original_key_ptr));
|
||||
original_private_key.reset(original_key_ptr);
|
||||
ASSERT_THAT(original_private_key, NotNull());
|
||||
|
||||
bssl::UniquePtr<RSA> private_key(
|
||||
RSAPrivateKey_dup(original_private_key.get()));
|
||||
ASSERT_THAT(private_key, NotNull());
|
||||
EXPECT_TRUE(ConvertToCarmichaelTotient(private_key.get()));
|
||||
|
||||
// Confirm that the key is valid and has changed from the original.
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
std::string serialized_carmichael_private_key;
|
||||
EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(),
|
||||
&serialized_carmichael_private_key));
|
||||
EXPECT_NE(serialized_carmichael_private_key,
|
||||
test_keys_.private_test_key_2_2048_bits());
|
||||
|
||||
// Convert back and make sure the serialized key matches the original.
|
||||
EXPECT_TRUE(ConvertToEulerTotient(private_key.get()));
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
std::string serialized_euler_private_key;
|
||||
EXPECT_TRUE(
|
||||
SerializeRsaPrivateKey(private_key.get(), &serialized_euler_private_key));
|
||||
EXPECT_EQ(serialized_euler_private_key,
|
||||
test_keys_.private_test_key_2_2048_bits());
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToEulerTotient_NewKey_Success) {
|
||||
bssl::UniquePtr<RSA> rsa;
|
||||
bssl::UniquePtr<RSA> private_key;
|
||||
bssl::UniquePtr<BIGNUM> exponent(BN_new());
|
||||
ASSERT_TRUE(BN_set_word(exponent.get(), kRsaPublicExponent));
|
||||
|
||||
// It is possible that sometimes, the d value generated using carmichael
|
||||
// and euler is the same. For this test, find a key where they are not the
|
||||
// same (max 100 tries).
|
||||
bool found_distinct_keys = false;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
rsa.reset(RSA_new());
|
||||
ASSERT_TRUE(
|
||||
RSA_generate_key_ex(rsa.get(), kTestRsaBits, exponent.get(), nullptr));
|
||||
|
||||
private_key.reset(RSAPrivateKey_dup(rsa.get()));
|
||||
EXPECT_TRUE(ConvertToEulerTotient(private_key.get()));
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
|
||||
// If the values are different, break.
|
||||
if (BN_cmp(private_key->d, rsa->d) != 0) {
|
||||
found_distinct_keys = true;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Euler and Carmichael d values are the same. Count: " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(found_distinct_keys)
|
||||
<< "Reached maximum attempts, but did not generate distinct keys";
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
|
||||
// Sanity check that the serialized keys are distinct.
|
||||
std::string serialized_carmichael_private_key;
|
||||
std::string serialized_private_key;
|
||||
EXPECT_TRUE(
|
||||
SerializeRsaPrivateKey(rsa.get(), &serialized_carmichael_private_key));
|
||||
|
||||
EXPECT_TRUE(
|
||||
SerializeRsaPrivateKey(private_key.get(), &serialized_private_key));
|
||||
EXPECT_NE(serialized_carmichael_private_key, serialized_private_key);
|
||||
|
||||
// Convert back to Carmichael, validate, and confirm that the keys are the
|
||||
// same.
|
||||
EXPECT_TRUE(ConvertToCarmichaelTotient(private_key.get()));
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
EXPECT_TRUE(
|
||||
SerializeRsaPrivateKey(private_key.get(), &serialized_private_key));
|
||||
EXPECT_EQ(serialized_carmichael_private_key, serialized_private_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToSerializedCarmichaelTotient_Success) {
|
||||
std::string private_key;
|
||||
EXPECT_TRUE(ConvertToCarmichaelTotient(
|
||||
test_keys_.private_test_key_2_2048_bits(), &private_key));
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_carmichael_totient_2048_bits(),
|
||||
private_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToSerializedEulerTotient_Success) {
|
||||
std::string private_key;
|
||||
EXPECT_TRUE(ConvertToEulerTotient(
|
||||
test_keys_.private_test_key_2_carmichael_totient_2048_bits(),
|
||||
&private_key));
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), private_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToEulerTotient_Idempotent_Success) {
|
||||
std::string private_key;
|
||||
EXPECT_TRUE(ConvertToEulerTotient(test_keys_.private_test_key_2_2048_bits(),
|
||||
&private_key));
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), private_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToCarmichaelTotient_Idempotent_Success) {
|
||||
std::string private_key;
|
||||
EXPECT_TRUE(ConvertToCarmichaelTotient(
|
||||
test_keys_.private_test_key_2_carmichael_totient_2048_bits(),
|
||||
&private_key));
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_carmichael_totient_2048_bits(),
|
||||
private_key);
|
||||
}
|
||||
|
||||
} // namespace rsa_util
|
||||
} // namespace widevine
|
||||
73
crypto_utils/sha_util.cc
Normal file
73
crypto_utils/sha_util.cc
Normal file
@@ -0,0 +1,73 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "crypto_utils/sha_util.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "third_party/boringssl/src/include/openssl/sha.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
std::string Sha1_Hash(const std::string& message) {
|
||||
std::string digest;
|
||||
digest.resize(SHA_DIGEST_LENGTH);
|
||||
SHA1(reinterpret_cast<const uint8_t*>(message.data()), message.size(),
|
||||
reinterpret_cast<uint8_t*>(&digest[0]));
|
||||
return digest;
|
||||
}
|
||||
|
||||
std::string Sha256_Hash(const std::string& message) {
|
||||
std::string digest;
|
||||
digest.resize(SHA256_DIGEST_LENGTH);
|
||||
SHA256(reinterpret_cast<const uint8_t*>(message.data()), message.size(),
|
||||
reinterpret_cast<uint8_t*>(&digest[0]));
|
||||
return digest;
|
||||
}
|
||||
|
||||
std::string Sha512_Hash(const std::string& message) {
|
||||
std::string digest;
|
||||
digest.resize(SHA512_DIGEST_LENGTH);
|
||||
SHA512(reinterpret_cast<const uint8_t*>(message.data()), message.size(),
|
||||
reinterpret_cast<uint8_t*>(&digest[0]));
|
||||
return digest;
|
||||
}
|
||||
|
||||
std::string GenerateSha1Uuid(const std::string& name_space,
|
||||
const std::string& name) {
|
||||
// X.667 14 Setting the fields of a name-based UUID.
|
||||
// - Allocate a UUID to use as a "name space identifier" for all UUIDs
|
||||
// generated from names in that name space.
|
||||
// - Compute the 16-octet hash value of the name space identifier concatenated
|
||||
// with the name.
|
||||
SHA_CTX ctx;
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, name_space.data(), name_space.length());
|
||||
SHA1_Update(&ctx, name.data(), name.length());
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
SHA1_Final(hash, &ctx);
|
||||
std::string hash_str =
|
||||
std::string(reinterpret_cast<const char*>(hash), SHA_DIGEST_LENGTH);
|
||||
|
||||
// - For a SHA-1 hash function, the "hash value" referenced in 14.1 shall be
|
||||
// octets zero to 15.
|
||||
std::string uuid = hash_str.substr(0, 16);
|
||||
|
||||
// - Overwrite the four most significant bits (bits 15 through 12) of the
|
||||
// "VersionAndTimeHigh" field with the four-bit version number from Table 3
|
||||
// of 12.2 for the hash function that was used. [Name-based SHA-1 is 5]
|
||||
(uuid[6] &= 0xF) |= 0x50;
|
||||
|
||||
// - Overwrite the two most significant bits (bits 7 and 6) of the
|
||||
// "VariantAndClockSeqHigh" field with 1 and 0, respectively.
|
||||
(uuid[8] &= 0x3F) |= 0x80;
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
34
crypto_utils/sha_util.h
Normal file
34
crypto_utils/sha_util.h
Normal file
@@ -0,0 +1,34 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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_SHA_UTIL_H_
|
||||
#define WHITEBOX_CRYPTO_UTILS_SHA_UTIL_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Calculates SHA1 hash.
|
||||
std::string Sha1_Hash(const std::string& message);
|
||||
|
||||
// Calculates SHA256 hash.
|
||||
std::string Sha256_Hash(const std::string& message);
|
||||
|
||||
// Calculate SHA512 hash.
|
||||
std::string Sha512_Hash(const std::string& message);
|
||||
|
||||
// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based,
|
||||
// 16-byte binary representation. Name_space is a GUID prefix; name is a unique
|
||||
// name in the namespace.
|
||||
std::string GenerateSha1Uuid(const std::string& name_space,
|
||||
const std::string& name);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_CRYPTO_UTILS_SHA_UTIL_H_
|
||||
74
crypto_utils/sha_util_test.cc
Normal file
74
crypto_utils/sha_util_test.cc
Normal file
@@ -0,0 +1,74 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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/sha_util.h"
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
TEST(ShaUtilTest, Sha1Empty) {
|
||||
const uint8_t kExpected[] = {
|
||||
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
|
||||
0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha1_Hash(""));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, Sha256Empty) {
|
||||
const uint8_t kExpected[] = {
|
||||
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
|
||||
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
|
||||
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha256_Hash(""));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, Sha1) {
|
||||
const uint8_t kExpected[] = {
|
||||
0x6b, 0x63, 0xa5, 0xe3, 0x6b, 0xfe, 0x54, 0x19, 0x4e, 0xfe,
|
||||
0x9f, 0xe0, 0x29, 0x7e, 0xd8, 0x11, 0x0f, 0x10, 0x0d, 0x1d,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha1_Hash("random data"));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, Sha256) {
|
||||
const uint8_t kExpected[] = {
|
||||
0x62, 0x4b, 0x56, 0xb3, 0x38, 0x1b, 0xbc, 0xe0, 0x4f, 0x58, 0x88,
|
||||
0x83, 0xb4, 0x2f, 0x4e, 0x27, 0xfe, 0xc0, 0x95, 0x56, 0xf8, 0x61,
|
||||
0xcf, 0x94, 0x49, 0xe6, 0x5f, 0x26, 0xea, 0x70, 0xad, 0x88,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha256_Hash("random data"));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, Sha512) {
|
||||
const uint8_t kExpected[] = {
|
||||
0x8f, 0x49, 0x93, 0x1f, 0x4d, 0x4a, 0x67, 0x6f, 0x9a, 0x7e, 0x62,
|
||||
0x60, 0xea, 0xe1, 0x54, 0xfe, 0xe2, 0x75, 0x3c, 0xec, 0x3b, 0xb2,
|
||||
0x2e, 0xd7, 0x51, 0xcc, 0x39, 0xf9, 0x89, 0x69, 0xad, 0xd0, 0x14,
|
||||
0xaa, 0xbe, 0x40, 0xce, 0xe3, 0xab, 0xef, 0x10, 0x2f, 0x24, 0x0e,
|
||||
0xd8, 0x26, 0x7b, 0xb5, 0x7d, 0x86, 0xce, 0xbd, 0xd7, 0x32, 0x86,
|
||||
0x3a, 0x5e, 0x9e, 0x8d, 0x23, 0x77, 0x10, 0x80, 0x0c};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha512_Hash("random data"));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, GenerateSha1Uuid) {
|
||||
std::string name_space =
|
||||
absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774");
|
||||
std::string name = "some seed value";
|
||||
EXPECT_EQ("730621a55c675c4086be38e72aefa03e",
|
||||
absl::BytesToHexString(GenerateSha1Uuid(name_space, name)));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
89
impl/reference/BUILD
Normal file
89
impl/reference/BUILD
Normal file
@@ -0,0 +1,89 @@
|
||||
# Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
package(default_visibility = [
|
||||
"//visibility:private",
|
||||
])
|
||||
|
||||
cc_library(
|
||||
name = "aead_whitebox",
|
||||
srcs = [
|
||||
"aead_whitebox_impl.cc",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":memory_util",
|
||||
":string_view_util",
|
||||
"//api:aead_whitebox",
|
||||
"//api:result",
|
||||
"//chromium_deps/base",
|
||||
"//chromium_deps/third_party/boringssl",
|
||||
"//crypto_utils:crypto_util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "license_whitebox",
|
||||
srcs = [
|
||||
"license_whitebox_impl.cc",
|
||||
],
|
||||
deps = [
|
||||
":memory_util",
|
||||
":string_view_util",
|
||||
"//api:license_whitebox",
|
||||
"//api:result",
|
||||
"//chromium_deps/base",
|
||||
"//chromium_deps/cdm/keys:dev_certs",
|
||||
"//chromium_deps/cdm/protos:license_protocol_proto",
|
||||
"//crypto_utils:aes_cbc_decryptor",
|
||||
"//crypto_utils:aes_ctr_encryptor",
|
||||
"//crypto_utils:crypto_util",
|
||||
"//crypto_utils:rsa_key",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "test_data",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"test_data.cc",
|
||||
],
|
||||
deps = [
|
||||
"//api:test_data",
|
||||
"//crypto_utils:rsa_test_keys",
|
||||
],
|
||||
)
|
||||
|
||||
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",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "string_view_util",
|
||||
srcs = ["string_view_util.cc"],
|
||||
hdrs = ["string_view_util.h"],
|
||||
deps = [
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "memory_util",
|
||||
srcs = ["memory_util.cc"],
|
||||
hdrs = ["memory_util.h"],
|
||||
)
|
||||
216
impl/reference/aead_whitebox_impl.cc
Normal file
216
impl/reference/aead_whitebox_impl.cc
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
#include "api/aead_whitebox.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
#include "impl/reference/memory_util.h"
|
||||
#include "impl/reference/string_view_util.h"
|
||||
#include "third_party/boringssl/src/include/openssl/aead.h"
|
||||
#include "third_party/boringssl/src/include/openssl/rand.h"
|
||||
|
||||
struct WB_Aead_Whitebox {
|
||||
const EVP_AEAD* algorithm;
|
||||
size_t nonce_size;
|
||||
|
||||
EVP_AEAD_CTX context;
|
||||
};
|
||||
|
||||
namespace {
|
||||
// EVP_AEAD_CTX_init(), EVP_AEAD_CTX_open(), and EVP_AEAD_CTX_seal() return 1
|
||||
// for success and 0 for failure.
|
||||
const int kAeadSuccess = 1;
|
||||
|
||||
void InitAeadMetadata(WB_Aead_Whitebox* whitebox) {
|
||||
const EVP_AEAD* algorithm = EVP_aead_aes_128_gcm_siv();
|
||||
|
||||
whitebox->algorithm = algorithm;
|
||||
whitebox->nonce_size = EVP_AEAD_nonce_length(algorithm);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> DeriveKey(const WB_Aead_Whitebox* whitebox,
|
||||
const uint8_t* init_data,
|
||||
size_t init_data_size,
|
||||
const uint8_t* context,
|
||||
size_t context_size) {
|
||||
// To derive a key, we need a label to introduce some entropy. The label only
|
||||
// needs to be some fixed, arbitrary non-trivial string.
|
||||
constexpr char kLabel[] = "Covfefe";
|
||||
|
||||
const auto init_data_view = widevine::AsStringView(init_data, init_data_size);
|
||||
const auto context_view = widevine::AsStringView(context, context_size);
|
||||
|
||||
// While we get the key size in bytes, DeriveKey() needs the key size in bits.
|
||||
const auto derived_key = widevine::crypto_util::DeriveKey(
|
||||
init_data_view, kLabel, context_view,
|
||||
EVP_AEAD_key_length(whitebox->algorithm) * 8);
|
||||
|
||||
return std::vector<uint8_t>(derived_key.begin(), derived_key.end());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
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) {
|
||||
if (!whitebox_init_data || !context || !whitebox) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (whitebox_init_data_size == 0 || context_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Use a unique pointer internally so that we can return early and
|
||||
// automatically release the pointer. Should always be non-null on modern
|
||||
// compilers (https://isocpp.org/wiki/faq/freestore-mgmt).
|
||||
std::unique_ptr<WB_Aead_Whitebox> aead_whitebox(new WB_Aead_Whitebox());
|
||||
InitAeadMetadata(aead_whitebox.get());
|
||||
|
||||
// Rather than using a fixed key, shared across all instances, derive a key
|
||||
// for each instance using the provided context. This will allow each instance
|
||||
// to have its own unique key (assuming they provide a unique context) while
|
||||
// still allowing a specific key to be recreated when needed (e.g.
|
||||
// device-locked key).
|
||||
const std::vector<uint8_t> key =
|
||||
DeriveKey(aead_whitebox.get(), whitebox_init_data,
|
||||
whitebox_init_data_size, context, context_size);
|
||||
|
||||
const int result = EVP_AEAD_CTX_init(
|
||||
&aead_whitebox->context, aead_whitebox->algorithm, key.data(), key.size(),
|
||||
EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr);
|
||||
|
||||
if (result != kAeadSuccess) {
|
||||
DVLOG(1) << "Invalid parameter: invalid init data.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// release() will release ownership of the pointer and return the unmanaged
|
||||
// raw pointer.
|
||||
*whitebox = aead_whitebox.release();
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
void WB_Aead_Delete(WB_Aead_Whitebox* whitebox) {
|
||||
if (whitebox != nullptr) {
|
||||
EVP_AEAD_CTX_cleanup(&whitebox->context);
|
||||
}
|
||||
|
||||
// Safe to delete nullptr (https://isocpp.org/wiki/faq/freestore-mgmt).
|
||||
delete whitebox;
|
||||
}
|
||||
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) {
|
||||
if (!whitebox || !input_data || !output_data || !output_data_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (input_data_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> nonce(whitebox->nonce_size);
|
||||
RAND_bytes(nonce.data(), nonce.size());
|
||||
|
||||
std::vector<uint8_t> output(input_data_size +
|
||||
EVP_AEAD_max_overhead(whitebox->algorithm));
|
||||
size_t sealed_size;
|
||||
CHECK_EQ(EVP_AEAD_CTX_seal(&whitebox->context, output.data(), &sealed_size,
|
||||
output.size(), nonce.data(), nonce.size(),
|
||||
input_data, input_data_size, nullptr, 0),
|
||||
kAeadSuccess);
|
||||
output.resize(sealed_size);
|
||||
|
||||
// At this point, |output| will have the encrypted data and authentication
|
||||
// tag, but in order to decrypt later, we will need the nonce, so append the
|
||||
// nonce to the output.
|
||||
output.insert(output.end(), nonce.begin(), nonce.end());
|
||||
|
||||
if (!widevine::MemCopy(output.data(), output.size(), output_data,
|
||||
*output_data_size)) {
|
||||
DVLOG(1) << "Buffer too small: output needs " << output.size() << ".";
|
||||
*output_data_size = output.size();
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
*output_data_size = output.size();
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!whitebox || !input_data || !output_data || !output_data_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// If we were to encrypt nothing, we would still get the nonce, so the input
|
||||
// must at least be large enough to contain the nonce.
|
||||
if (input_data_size <= whitebox->nonce_size) {
|
||||
DVLOG(1) << "Invalid parameter: invalid input size.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// The input follows the structure:
|
||||
// |--------input---------|
|
||||
// |---payload---||-nonce-|
|
||||
//
|
||||
// The payload is the combination of authentication tag and ciphertext
|
||||
// created by our previous call to EVP_AEAD_CTX_seal().
|
||||
//
|
||||
// To use EVP_AEAD_CTX_open(), we need to separate the payload and nonce as
|
||||
// they must be passed to EVP_AEAD_CTX_open() separately. The result of
|
||||
// calling EVP_AEAD_CTX_open() will be the plaintext, which will be smaller
|
||||
// than the payload as the payload included the ciphertext and authentication
|
||||
// tag.
|
||||
|
||||
const uint8_t* payload_start = input_data;
|
||||
const size_t payload_size = input_data_size - whitebox->nonce_size;
|
||||
|
||||
const uint8_t* nonce_start = payload_start + payload_size;
|
||||
const size_t nonce_size = whitebox->nonce_size;
|
||||
|
||||
// Remember, the plaintext will be smaller than the payload, but we are going
|
||||
// to start with the payload size as it is our best estimate and will scale it
|
||||
// down when we have the final size.
|
||||
std::vector<uint8_t> plaintext(payload_size);
|
||||
size_t plaintext_size;
|
||||
|
||||
const int result = EVP_AEAD_CTX_open(
|
||||
&whitebox->context, plaintext.data(), &plaintext_size, plaintext.size(),
|
||||
nonce_start, nonce_size, payload_start, payload_size, nullptr, 0);
|
||||
|
||||
if (result != kAeadSuccess) {
|
||||
DVLOG(1) << "Data verification error: failed to verify input data.";
|
||||
return WB_RESULT_DATA_VERIFICATION_ERROR;
|
||||
}
|
||||
|
||||
// Know that EVP_AEAD_CTX_open() has opened the payload, we know the actual
|
||||
// plaintext size and can now shrink the vector to the correct size.
|
||||
plaintext.resize(plaintext_size);
|
||||
|
||||
if (!widevine::MemCopy(plaintext.data(), plaintext.size(), output_data,
|
||||
*output_data_size)) {
|
||||
DVLOG(1) << "Buffer too small: output needs " << plaintext.size() << ".";
|
||||
*output_data_size = plaintext.size();
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
*output_data_size = plaintext.size();
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
614
impl/reference/license_whitebox_impl.cc
Normal file
614
impl/reference/license_whitebox_impl.cc
Normal file
@@ -0,0 +1,614 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "cdm/protos/license_protocol.pb.h"
|
||||
#include "crypto_utils/aes_cbc_decryptor.h"
|
||||
#include "crypto_utils/aes_ctr_encryptor.h"
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
#include "crypto_utils/rsa_key.h"
|
||||
#include "impl/reference/memory_util.h"
|
||||
#include "impl/reference/string_view_util.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/err.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/rsa.h"
|
||||
#include "third_party/boringssl/src/include/openssl/sha.h"
|
||||
|
||||
namespace {
|
||||
using KeyContainer = video_widevine::License_KeyContainer;
|
||||
using RsaPrivateKey = widevine::RsaPrivateKey;
|
||||
using AesCbcDecryptor = widevine::AesCbcDecryptor;
|
||||
using AesCtrDecryptor = widevine::AesCtrEncryptor;
|
||||
using SecurityLevel = video_widevine::License_KeyContainer_SecurityLevel;
|
||||
|
||||
struct ContentKey {
|
||||
// Minimum security level that this key requires in order to be used.
|
||||
SecurityLevel level;
|
||||
|
||||
// Key used to decrypt content.
|
||||
std::vector<uint8_t> key;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// The white-box type can't be in the namespace as it is defined in the header.
|
||||
struct WB_License_Whitebox {
|
||||
// CDM key, used for license requests.
|
||||
std::unique_ptr<RsaPrivateKey> key;
|
||||
|
||||
// Keys used for license renewal.
|
||||
std::string server_signing_key;
|
||||
std::string client_signing_key;
|
||||
|
||||
// We use two data structures to track keys. We track all the key ids seen
|
||||
// when loading the license and the content keys. This allows us to tell the
|
||||
// difference between a key missing and a key being misused.
|
||||
std::set<std::string> all_key_ids;
|
||||
std::map<std::string, ContentKey> content_keys;
|
||||
};
|
||||
|
||||
namespace {
|
||||
// Secret string value. For simplicity the pattern will just be 0xAAAAAAAA which
|
||||
// is xor'd to the buffer, and removed by xor'ing it a second time.
|
||||
const uint8_t kSecretStringPattern[] = {
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
};
|
||||
const size_t kSecretStringPatternSize = sizeof(kSecretStringPattern);
|
||||
static_assert(kSecretStringPatternSize == AES_BLOCK_SIZE,
|
||||
"Secret string must be AES_BLOCK_SIZE.");
|
||||
|
||||
std::string AsString(const uint8_t* buffer, size_t buffer_size) {
|
||||
return std::string(reinterpret_cast<const char*>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
void ApplyPattern(const uint8_t* input_buffer,
|
||||
size_t input_buffer_size,
|
||||
const uint8_t* pattern,
|
||||
size_t pattern_size,
|
||||
uint8_t* output_buffer) {
|
||||
DCHECK(input_buffer);
|
||||
DCHECK(pattern);
|
||||
DCHECK(output_buffer);
|
||||
DCHECK_GT(pattern_size, 0u);
|
||||
DCHECK_EQ(input_buffer_size % pattern_size, 0u);
|
||||
|
||||
for (size_t i = 0; i < input_buffer_size; ++i) {
|
||||
output_buffer[i] = input_buffer[i] ^ pattern[i % pattern_size];
|
||||
}
|
||||
}
|
||||
|
||||
WB_Result FindKey(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* id,
|
||||
size_t id_size,
|
||||
ContentKey* key) {
|
||||
DCHECK(whitebox);
|
||||
DCHECK(id);
|
||||
DCHECK_GT(id_size, 0u);
|
||||
DCHECK(key);
|
||||
|
||||
const std::string key_id = AsString(id, id_size);
|
||||
|
||||
if (whitebox->all_key_ids.empty()) {
|
||||
DVLOG(1) << "Invalid state: no keys have been loaded.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (whitebox->all_key_ids.find(key_id) == whitebox->all_key_ids.end()) {
|
||||
DVLOG(1) << "No such key: " << base::HexEncode(id, id_size) << ".";
|
||||
return WB_RESULT_NO_SUCH_KEY;
|
||||
}
|
||||
|
||||
const auto found = whitebox->content_keys.find(key_id);
|
||||
|
||||
if (found == whitebox->content_keys.end()) {
|
||||
DVLOG(1) << "Wrong key type: " << base::HexEncode(id, id_size) << ".";
|
||||
return WB_RESULT_WRONG_KEY_TYPE;
|
||||
}
|
||||
|
||||
*key = found->second;
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
WB_Result DecryptBuffer(WB_CipherMode mode,
|
||||
const std::vector<uint8_t>& key,
|
||||
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) {
|
||||
if (!input_data || !iv || !output_data || !output_data_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (input_data_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Data must be "block aligned" for CBC. CTR does not have this requirement.
|
||||
if (mode == WB_CIPHER_MODE_CBC && input_data_size % AES_BLOCK_SIZE != 0) {
|
||||
DVLOG(1) << "Invalid parameter: bad block size.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// We only support 16 byte IVs. CENC allows 8 byte, but it is expected that
|
||||
// the IV will be padded before entering the whitebox.
|
||||
if (iv_size != 16) {
|
||||
DVLOG(1) << "Invalid parameter: invalid iv size.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// There is no padding, so the output will be the same length as the input.
|
||||
if (*output_data_size < input_data_size) {
|
||||
DVLOG(1) << "Buffer too small: needs " << input_data_size << " but got "
|
||||
<< *output_data_size << ".";
|
||||
*output_data_size = input_data_size;
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
// By this point, we have verified everything that need to be verified.
|
||||
// Decryption should just work.
|
||||
if (mode == WB_CIPHER_MODE_CBC) {
|
||||
AesCbcDecryptor decryptor;
|
||||
CHECK(decryptor.SetKey(key.data(), key.size()));
|
||||
|
||||
*output_data_size = input_data_size;
|
||||
CHECK(decryptor.Decrypt(iv, iv_size, input_data, input_data_size,
|
||||
output_data));
|
||||
} else if (mode == WB_CIPHER_MODE_CTR) {
|
||||
AesCtrDecryptor decryptor;
|
||||
CHECK(decryptor.SetKey(key.data(), key.size()));
|
||||
|
||||
// Encrypt and Decrypt for CBC use the same interface.
|
||||
*output_data_size = input_data_size;
|
||||
CHECK(decryptor.Encrypt(iv, iv_size, input_data, input_data_size,
|
||||
output_data));
|
||||
} else {
|
||||
DVLOG(1) << "Invalid parameter: invalid cipher mode.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
WB_License_Whitebox** whitebox) {
|
||||
if (!whitebox_init_data || !whitebox) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (whitebox_init_data_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// |whitebox_init_data| is simply the bytes of a PKCS #8 PrivateKeyInfo block
|
||||
// of a RSA 2048 bit key.
|
||||
std::unique_ptr<RsaPrivateKey> key(RsaPrivateKey::Create(
|
||||
AsString(whitebox_init_data, whitebox_init_data_size)));
|
||||
if (!key) {
|
||||
DVLOG(1) << "Invalid parameter: invalid init data.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Should always be non-null on modern compilers
|
||||
// (https://isocpp.org/wiki/faq/freestore-mgmt).
|
||||
*whitebox = new WB_License_Whitebox();
|
||||
(*whitebox)->key.swap(key);
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
void WB_License_Delete(WB_License_Whitebox* whitebox) {
|
||||
// Safe to delete nullptr (https://isocpp.org/wiki/faq/freestore-mgmt).
|
||||
delete whitebox;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!whitebox || !license_request || !signature || !signature_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (license_request_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::string result;
|
||||
DCHECK(whitebox->key->GenerateSignature(
|
||||
AsString(license_request, license_request_size), &result));
|
||||
|
||||
if (!widevine::MemCopy(result.data(), result.size(), signature,
|
||||
*signature_size)) {
|
||||
DVLOG(1) << "Buffer too small: signature needs " << result.size() << ".";
|
||||
*signature_size = result.size();
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
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) {
|
||||
if (!whitebox || !message || !signature || !session_key || !license_request) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (message_size == 0 || license_request_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::string decrypted_session_key;
|
||||
if (!whitebox->key->Decrypt(AsString(session_key, session_key_size),
|
||||
&decrypted_session_key)) {
|
||||
DVLOG(1) << "Invalid parameter: invalid session key.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::string signing_key_material = widevine::crypto_util::DeriveKey(
|
||||
decrypted_session_key, widevine::crypto_util::kSigningKeyLabel,
|
||||
widevine::AsStringView(license_request, license_request_size),
|
||||
widevine::crypto_util::kSigningKeySizeBits * 2);
|
||||
|
||||
if (signing_key_material.size() <
|
||||
widevine::crypto_util::kSigningKeySizeBytes * 2) {
|
||||
DVLOG(1) << "Invalid parameter: invalid session key size.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!widevine::crypto_util::VerifySignatureHmacSha256(
|
||||
signing_key_material,
|
||||
widevine::AsStringView(signature, signature_size),
|
||||
widevine::AsStringView(message, message_size))) {
|
||||
DVLOG(1) << "Failed to verify signed message.";
|
||||
return WB_RESULT_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
std::string decryption_key = widevine::crypto_util::DeriveKey(
|
||||
decrypted_session_key, widevine::crypto_util::kWrappingKeyLabel,
|
||||
widevine::AsStringView(license_request, license_request_size),
|
||||
widevine::crypto_util::kWrappingKeySizeBits);
|
||||
if (decryption_key.empty()) {
|
||||
DVLOG(1) << "Failed to decrypt decryption key";
|
||||
return WB_RESULT_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
AesCbcDecryptor decryptor;
|
||||
CHECK(
|
||||
decryptor.SetKey(reinterpret_cast<const uint8_t*>(decryption_key.data()),
|
||||
decryption_key.size()));
|
||||
|
||||
video_widevine::License license;
|
||||
if (!license.ParseFromArray(message, message_size)) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid license.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
CHECK(!license.key().empty());
|
||||
for (const auto& key : license.key()) {
|
||||
const std::string wrapped_key = key.key();
|
||||
std::vector<uint8_t> unwrapped_key(wrapped_key.size());
|
||||
|
||||
if (!decryptor.Decrypt(reinterpret_cast<const uint8_t*>(key.iv().data()),
|
||||
key.iv().size(),
|
||||
reinterpret_cast<const uint8_t*>(wrapped_key.data()),
|
||||
wrapped_key.size(), unwrapped_key.data())) {
|
||||
// The input has to be a specific length, so if it is not, it means that
|
||||
// something is wrong with the license.
|
||||
DVLOG(1) << "Invalid parameter: Invalid license.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
whitebox->all_key_ids.insert(key.id());
|
||||
|
||||
switch (key.type()) {
|
||||
case KeyContainer::SIGNING: {
|
||||
const size_t kSigningKeySizeBytes =
|
||||
widevine::crypto_util::kSigningKeySizeBytes;
|
||||
if (unwrapped_key.size() < kSigningKeySizeBytes * 2) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid signing key.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const std::string signing_key =
|
||||
AsString(unwrapped_key.data(), unwrapped_key.size());
|
||||
whitebox->server_signing_key =
|
||||
signing_key.substr(0, kSigningKeySizeBytes);
|
||||
whitebox->client_signing_key =
|
||||
signing_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
|
||||
break;
|
||||
}
|
||||
|
||||
case KeyContainer::CONTENT: {
|
||||
constexpr size_t kContentKeySizeBytes = 16;
|
||||
if (unwrapped_key.size() < kContentKeySizeBytes) {
|
||||
DVLOG(1) << "Invalid parameter: Invalid content key.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
unwrapped_key.resize(kContentKeySizeBytes);
|
||||
whitebox->content_keys[key.id()] = {key.level(),
|
||||
std::move(unwrapped_key)};
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// We purposefully ignore the other cases. In the case that any other
|
||||
// key appeared in the license, the key id would have been tracked in
|
||||
// |all_key_ids|.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!whitebox || !message || !signature || !signature_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (message_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// License request must have been processed at least once before calling this
|
||||
// function.
|
||||
if (whitebox->client_signing_key.empty()) {
|
||||
DVLOG(1) << __func__ << " called before license received.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
|
||||
const std::string computed_signature =
|
||||
widevine::crypto_util::CreateSignatureHmacSha256(
|
||||
whitebox->client_signing_key,
|
||||
widevine::AsStringView(message, message_size));
|
||||
|
||||
if (!widevine::MemCopy(computed_signature.data(), computed_signature.size(),
|
||||
signature, *signature_size)) {
|
||||
DVLOG(1) << "Buffer too small: signature needs "
|
||||
<< computed_signature.size() << ".";
|
||||
*signature_size = computed_signature.size();
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!whitebox || !message || !signature) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (message_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// License request must have been processed at least once before calling this
|
||||
// function.
|
||||
if (whitebox->server_signing_key.empty()) {
|
||||
DVLOG(1) << __func__ << " called before license received.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
|
||||
const std::string computed_signature =
|
||||
widevine::crypto_util::CreateSignatureHmacSha256(
|
||||
whitebox->server_signing_key,
|
||||
widevine::AsStringView(message, message_size));
|
||||
|
||||
if (signature_size < computed_signature.size()) {
|
||||
DVLOG(1) << "Invalid parameters: invalid signature "
|
||||
"size.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (computed_signature != widevine::AsStringView(signature, signature_size)) {
|
||||
DVLOG(1) << "Data verification error: signatures do not match.";
|
||||
return WB_RESULT_DATA_VERIFICATION_ERROR;
|
||||
}
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!whitebox || !key_id || !secret_string || !secret_string_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (mode != WB_CIPHER_MODE_CTR && mode != WB_CIPHER_MODE_CBC) {
|
||||
DVLOG(1) << "Invalid parameter: invalid cipher mode.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (key_id_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// The secret string can differ between keys, so we need to make sure that
|
||||
// the key id is actually a content key.
|
||||
ContentKey content_key;
|
||||
const WB_Result key_lookup_result =
|
||||
FindKey(whitebox, key_id, key_id_size, &content_key);
|
||||
if (key_lookup_result != WB_RESULT_OK) {
|
||||
return key_lookup_result;
|
||||
}
|
||||
|
||||
if (!widevine::MemCopy(kSecretStringPattern, kSecretStringPatternSize,
|
||||
secret_string, *secret_string_size)) {
|
||||
DVLOG(1) << "Buffer too small: needs " << kSecretStringPatternSize << ".";
|
||||
*secret_string_size = kSecretStringPatternSize;
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!whitebox || !key_id) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (key_id_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ContentKey content_key;
|
||||
const WB_Result key_lookup_result =
|
||||
FindKey(whitebox, key_id, key_id_size, &content_key);
|
||||
if (key_lookup_result != WB_RESULT_OK) {
|
||||
return key_lookup_result;
|
||||
}
|
||||
|
||||
// Check if decryption is allowed by the policy.
|
||||
switch (content_key.level) {
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
|
||||
break;
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO:
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE:
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_ALL:
|
||||
return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL;
|
||||
}
|
||||
|
||||
// DecryptBuffer() will validate the remaining decryption parameters.
|
||||
return DecryptBuffer(mode, content_key.key, input_data, input_data_size, iv,
|
||||
iv_size, output_data, output_data_size);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!whitebox || !key_id) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (key_id_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ContentKey content_key;
|
||||
const WB_Result key_lookup_result =
|
||||
FindKey(whitebox, key_id, key_id_size, &content_key);
|
||||
if (key_lookup_result != WB_RESULT_OK) {
|
||||
return key_lookup_result;
|
||||
}
|
||||
|
||||
// Check if decryption is allowed by the policy.
|
||||
switch (content_key.level) {
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
|
||||
break;
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO:
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE:
|
||||
case video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_ALL:
|
||||
return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL;
|
||||
}
|
||||
|
||||
// DecryptBuffer() will validate all the parameters, so just make sure it is
|
||||
// safe to resize this and let the normal validation process handle anything
|
||||
// wrong with the output size.
|
||||
std::vector<uint8_t> output;
|
||||
if (masked_output_data_size) {
|
||||
output.resize(*masked_output_data_size);
|
||||
}
|
||||
|
||||
// DecryptBuffer() will validate the remaining decryption parameters and set
|
||||
// |masked_output_data_size|.
|
||||
const WB_Result result =
|
||||
DecryptBuffer(mode, content_key.key, input_data, input_data_size, iv,
|
||||
iv_size, output.data(), masked_output_data_size);
|
||||
|
||||
if (result != WB_RESULT_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Trivial implementation that simply takes the decrypted output and XORs it
|
||||
// with a fixed pattern. |output|'s size is based on |masked_output_data| so
|
||||
// we shouldn't need to worry about overflow.
|
||||
ApplyPattern(output.data(), output.size(), kSecretStringPattern,
|
||||
kSecretStringPatternSize, masked_output_data);
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
void WB_License_Unmask(const uint8_t* secret_string,
|
||||
size_t secret_string_size,
|
||||
uint8_t* buffer,
|
||||
size_t buffer_size) {
|
||||
// No return code, so only check if parameters are valid.
|
||||
DCHECK(secret_string);
|
||||
DCHECK(secret_string_size);
|
||||
DCHECK(buffer);
|
||||
DCHECK(buffer_size);
|
||||
|
||||
ApplyPattern(buffer, buffer_size, secret_string, secret_string_size, buffer);
|
||||
}
|
||||
20
impl/reference/memory_util.cc
Normal file
20
impl/reference/memory_util.cc
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "impl/reference/memory_util.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
bool MemCopy(const void* src, size_t src_size, void* dest, size_t dest_size) {
|
||||
if (dest_size < src_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we could use memcpy_s(), we would not need this function, but memcpy_s()
|
||||
// is not available on some platforms (e.g. mac).
|
||||
memcpy(dest, src, src_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
28
impl/reference/memory_util.h
Normal file
28
impl/reference/memory_util.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_IMPL_REFERENCE_MEMORY_UTIL_H_
|
||||
#define WHITEBOX_IMPL_REFERENCE_MEMORY_UTIL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Copied from the documentation for memcpy() with minor modifications:
|
||||
//
|
||||
// Copies |src_size| bytes from |src| to |dest| if |dest_size| is greater or
|
||||
// equal to |src_size|.
|
||||
//
|
||||
// The underlying type of the objects pointed to by |src| and |dest| are
|
||||
// irrelevant for this function; the result is a binary copy of the data.
|
||||
//
|
||||
// To avoid overflows, |src_size| is compared against |dest_size|. If there
|
||||
// won't be enough room, the copy is not attempted and false is returned.
|
||||
//
|
||||
// |src| and |dest| should not overlap. For overlapping memory blocks,
|
||||
// memmove is a safer approach).
|
||||
bool MemCopy(const void* src, size_t src_size, void* dest, size_t dest_size);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_IMPL_REFERENCE_MEMORY_UTIL_H_
|
||||
11
impl/reference/string_view_util.cc
Normal file
11
impl/reference/string_view_util.cc
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "impl/reference/string_view_util.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
absl::string_view AsStringView(const uint8_t* buffer, size_t buffer_size) {
|
||||
return absl::string_view(reinterpret_cast<const char*>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
17
impl/reference/string_view_util.h
Normal file
17
impl/reference/string_view_util.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_IMPL_REFERENCE_STRING_VIEW_UTIL_H_
|
||||
#define WHITEBOX_IMPL_REFERENCE_STRING_VIEW_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Create a string view out of a uint8_t array. String view won't create a copy
|
||||
// copy of the data.
|
||||
absl::string_view AsStringView(const uint8_t* buffer, size_t buffer_size);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_IMPL_REFERENCE_STRING_VIEW_UTIL_H_
|
||||
42
impl/reference/test_data.cc
Normal file
42
impl/reference/test_data.cc
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/test_data.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "crypto_utils/rsa_test_keys.h"
|
||||
|
||||
std::vector<uint8_t> GetValidAeadInitData() {
|
||||
// Valid init data for our AEAD implementation is any AES key, so it just
|
||||
// needs to be 16 bytes.
|
||||
return {
|
||||
0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x0,
|
||||
0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x0,
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetInvalidAeadInitData() {
|
||||
// Valid init data would be any 16 bytes. To avoid returning a length of
|
||||
// zero, just return one byte, that way we are still returning "some data".
|
||||
return {0x00};
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetLicenseInitData() {
|
||||
// For the OpenSSL implementation |init_data| is simply a RSA 2048-bit
|
||||
// private key. Matching public key is public_test_key_2_2048_bits().
|
||||
widevine::RsaTestKeys key_generator;
|
||||
std::string init_data = key_generator.private_test_key_2_2048_bits();
|
||||
return std::vector<uint8_t>(init_data.begin(), init_data.end());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetMatchingLicensePublicKey() {
|
||||
widevine::RsaTestKeys key_generator;
|
||||
std::string init_data = key_generator.public_test_key_2_2048_bits();
|
||||
return std::vector<uint8_t>(init_data.begin(), init_data.end());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetInvalidLicenseInitData() {
|
||||
// For the OpenSSL implementation |init_data| is a private key. So a random
|
||||
// collection of bytes should not be accepted.
|
||||
return {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
}
|
||||
Reference in New Issue
Block a user