Export media_cas_proxy_sdk
This commit is contained in:
30
BUILD
Normal file
30
BUILD
Normal file
@@ -0,0 +1,30 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
# Build MediaCas Proxy SDK tar package.
|
||||
|
||||
load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
|
||||
|
||||
pkg_tar(
|
||||
name = "media_cas_proxy_sdk_files",
|
||||
strip_prefix = "/",
|
||||
files = [
|
||||
"//common:binary_release_files",
|
||||
"//media_cas_proxy_sdk/external/common/wvpl:binary_release_files",
|
||||
"//sdk/external/common/wvpl:binary_release_files",
|
||||
"//util:binary_release_files",
|
||||
],
|
||||
mode = "0644",
|
||||
)
|
||||
|
||||
pkg_tar(
|
||||
name = "media_cas_proxy_sdk-bin",
|
||||
deps = [":media_cas_proxy_sdk_files"],
|
||||
files = ["//media_cas_proxy_sdk/external/common/wvpl:libwvpl_cas_proxy.so"],
|
||||
mode = "0755",
|
||||
)
|
||||
4
LICENSE
Normal file
4
LICENSE
Normal file
@@ -0,0 +1,4 @@
|
||||
Copyright 2017 Google, Inc.
|
||||
|
||||
This software is licensed under the terms defined in the Widevine Master License Agreement.
|
||||
For a copy of this agreement, please contact widevine-licensing@google.com.
|
||||
89
WORKSPACE
Normal file
89
WORKSPACE
Normal file
@@ -0,0 +1,89 @@
|
||||
workspace(name = "media_cas_proxy_sdk")
|
||||
|
||||
# CCTZ (Time-zone framework), needed by abseil.
|
||||
git_repository(
|
||||
name = "com_googlesource_code_cctz",
|
||||
remote = "https://github.com/google/cctz.git",
|
||||
tag = "v2.2",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "abseil_repo",
|
||||
commit = "475d64f2de7403a01b1b36c487328ed41d29c20c", #2018-04-10
|
||||
remote = "https://github.com/abseil/abseil-cpp.git",
|
||||
)
|
||||
|
||||
# Name 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.5.0",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "boringssl_repo",
|
||||
commit = "27ae6cadd74fd054208730827a8de3fe9bc648f0", # 2017-12-07
|
||||
remote = "https://github.com/google/boringssl.git",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "gflags_repo",
|
||||
commit = "fe57e5af4db74ab298523f06d2c43aa895ba9f98", # 2016-07-20
|
||||
remote = "https://github.com/gflags/gflags.git",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "googletest_repo",
|
||||
commit = "9816b96a6ddc0430671693df90192bbee57108b6", # 2017-08-11
|
||||
remote = "https://github.com/google/googletest.git",
|
||||
)
|
||||
|
||||
maven_jar(
|
||||
name = "junit_junit",
|
||||
artifact = "junit:junit:4.11",
|
||||
sha1 = "4e031bb61df09069aeb2bffb4019e7a5034a4ee0",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "glog_repo",
|
||||
build_file = "glog.BUILD",
|
||||
commit = "0472b91c5defdf90cff7292e3bf7bd86770a9a0a", # 2016-07-13
|
||||
remote = "https://github.com/google/glog.git",
|
||||
)
|
||||
|
||||
bind(
|
||||
name = "protobuf",
|
||||
actual = "@com_google_protobuf//:protobuf",
|
||||
)
|
||||
|
||||
bind(
|
||||
name = "protobuf_java",
|
||||
actual = "@com_google_protobuf//:protobuf_java",
|
||||
)
|
||||
|
||||
bind(
|
||||
name = "openssl",
|
||||
actual = "@boringssl_repo//:crypto",
|
||||
)
|
||||
|
||||
bind(
|
||||
name = "gflags",
|
||||
actual = "@gflags_repo//:gflags",
|
||||
)
|
||||
|
||||
bind(
|
||||
name = "gtest",
|
||||
actual = "@googletest_repo//:gtest",
|
||||
)
|
||||
|
||||
bind(
|
||||
name = "gtest_main",
|
||||
actual = "@googletest_repo//:gtest_main",
|
||||
)
|
||||
|
||||
bind(
|
||||
name = "glog",
|
||||
actual = "@glog_repo//:glog",
|
||||
)
|
||||
25
base/BUILD
Normal file
25
base/BUILD
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.
|
||||
################################################################################
|
||||
|
||||
package(
|
||||
default_visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "base",
|
||||
hdrs = [
|
||||
"macros.h",
|
||||
"thread_annotations.h",
|
||||
"timestamp.h",
|
||||
],
|
||||
deps = [
|
||||
"@abseil_repo//absl/base",
|
||||
"//external:gflags",
|
||||
"//external:glog",
|
||||
],
|
||||
)
|
||||
31
base/macros.h
Normal file
31
base/macros.h
Normal file
@@ -0,0 +1,31 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef BASE_MACROS_H_
|
||||
#define BASE_MACROS_H_
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
|
||||
// DISALLOW_COPY_AND_ASSIGN disallows the copy constructor and copy assignment
|
||||
// operator. DISALLOW_IMPLICIT_CONSTRUCTORS is like DISALLOW_COPY_AND_ASSIGN,
|
||||
// but also disallows the default constructor, intended to help make a
|
||||
// class uninstantiable.
|
||||
//
|
||||
// These must be placed in the private: declarations for a class.
|
||||
//
|
||||
// Note: New code should prefer static_assert over COMPILE_ASSERT.
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
TypeName& operator=(const TypeName&) = delete
|
||||
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
TypeName() = delete; \
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
|
||||
#define arraysize(array) ABSL_ARRAYSIZE(array)
|
||||
|
||||
#endif // BASE_MACROS_H_
|
||||
14
base/thread_annotations.h
Normal file
14
base/thread_annotations.h
Normal file
@@ -0,0 +1,14 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef BASE_THREAD_ANNOTATIONS_H_
|
||||
#define BASE_THREAD_ANNOTATIONS_H_
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
|
||||
#endif // BASE_THREAD_ANNOTATIONS_H_
|
||||
21
base/timestamp.h
Normal file
21
base/timestamp.h
Normal file
@@ -0,0 +1,21 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef BASE_TIMESTAMP_H_
|
||||
#define BASE_TIMESTAMP_H_
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class BuildData {
|
||||
public:
|
||||
static const char* Timestamp() { return __DATE__ " " __TIME__; }
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // BASE_TIMESTAMP_H_
|
||||
577
common/BUILD
Normal file
577
common/BUILD
Normal file
@@ -0,0 +1,577 @@
|
||||
################################################################################
|
||||
# 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.
|
||||
|
||||
package(
|
||||
default_visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "binary_release_files",
|
||||
srcs = [
|
||||
"certificate_type.h",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "widevine_system_id",
|
||||
srcs = ["widevine_system_id.cc"],
|
||||
hdrs = ["widevine_system_id.h"],
|
||||
deps = ["//base"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "certificate_type",
|
||||
hdrs = ["certificate_type.h"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "drm_root_certificate",
|
||||
srcs = ["drm_root_certificate.cc"],
|
||||
hdrs = ["drm_root_certificate.h"],
|
||||
deps = [
|
||||
":certificate_type",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//external:openssl",
|
||||
"//util:status",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "drm_root_certificate_test",
|
||||
timeout = "short",
|
||||
srcs = ["drm_root_certificate_test.cc"],
|
||||
deps = [
|
||||
":drm_root_certificate",
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "certificate_util",
|
||||
srcs = ["certificate_util.cc"],
|
||||
hdrs = ["certificate_util.h"],
|
||||
deps = [
|
||||
":certificate_type",
|
||||
":drm_root_certificate",
|
||||
":drm_service_certificate",
|
||||
":verified_media_pipeline",
|
||||
":vmp_checker",
|
||||
"//base",
|
||||
"//util:status",
|
||||
"//license_server_sdk/internal:sdk",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "client_id_util",
|
||||
srcs = ["client_id_util.cc"],
|
||||
hdrs = ["client_id_util.h"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":drm_service_certificate",
|
||||
":error_space",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//util:status",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:errors_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "rsa_util",
|
||||
srcs = ["rsa_util.cc"],
|
||||
hdrs = ["rsa_util.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "rsa_util_test",
|
||||
size = "medium",
|
||||
timeout = "short",
|
||||
srcs = ["rsa_util_test.cc"],
|
||||
deps = [
|
||||
":rsa_test_keys",
|
||||
":rsa_util",
|
||||
"//base",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "openssl_util",
|
||||
hdrs = ["openssl_util.h"],
|
||||
deps = [
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "rsa_key",
|
||||
srcs = ["rsa_key.cc"],
|
||||
hdrs = ["rsa_key.h"],
|
||||
deps = [
|
||||
":rsa_util",
|
||||
":sha_util",
|
||||
"//base",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "rsa_key_test",
|
||||
size = "medium",
|
||||
timeout = "short",
|
||||
srcs = ["rsa_key_test.cc"],
|
||||
deps = [
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
":rsa_util",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "rsa_test_keys",
|
||||
testonly = 1,
|
||||
srcs = ["rsa_test_keys.cc"],
|
||||
hdrs = ["rsa_test_keys.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "mock_rsa_key",
|
||||
testonly = 1,
|
||||
hdrs = ["mock_rsa_key.h"],
|
||||
deps = [
|
||||
":rsa_key",
|
||||
"//testing:gunit",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "aes_cbc_util",
|
||||
srcs = ["aes_cbc_util.cc"],
|
||||
hdrs = ["aes_cbc_util.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "aes_cbc_util_test",
|
||||
srcs = ["aes_cbc_util_test.cc"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "crypto_util",
|
||||
srcs = ["crypto_util.cc"],
|
||||
hdrs = ["crypto_util.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//external:openssl",
|
||||
"//util/endian",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "crypto_util_test",
|
||||
size = "medium",
|
||||
srcs = ["crypto_util_test.cc"],
|
||||
deps = [
|
||||
":crypto_util",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "ecb_util",
|
||||
srcs = ["ecb_util.cc"],
|
||||
hdrs = ["ecb_util.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ecb_util_test",
|
||||
size = "small",
|
||||
srcs = ["ecb_util_test.cc"],
|
||||
deps = [
|
||||
":ecb_util",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "file_util",
|
||||
srcs = ["file_util.cc"],
|
||||
hdrs = ["file_util.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "file_util_test",
|
||||
srcs = ["file_util_test.cc"],
|
||||
deps = [
|
||||
":file_util",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "random_util",
|
||||
srcs = ["random_util.cc"],
|
||||
hdrs = ["random_util.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "random_util_test",
|
||||
srcs = ["random_util_test.cc"],
|
||||
deps = [
|
||||
":random_util",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "sha_util",
|
||||
srcs = ["sha_util.cc"],
|
||||
hdrs = ["sha_util.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "sha_util_test",
|
||||
srcs = ["sha_util_test.cc"],
|
||||
deps = [
|
||||
":sha_util",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "signature_util",
|
||||
srcs = ["signature_util.cc"],
|
||||
hdrs = ["signature_util.h"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":rsa_key",
|
||||
":sha_util",
|
||||
"//base",
|
||||
"//util:status",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "signing_key_util",
|
||||
srcs = ["signing_key_util.cc"],
|
||||
hdrs = ["signing_key_util.h"],
|
||||
deps = [
|
||||
":crypto_util",
|
||||
"//base",
|
||||
"//protos/public:license_protocol_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "signing_key_util_test",
|
||||
size = "small",
|
||||
srcs = ["signing_key_util_test.cc"],
|
||||
deps = [
|
||||
":crypto_util",
|
||||
":signing_key_util",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:license_protocol_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "test_certificates",
|
||||
testonly = 1,
|
||||
srcs = ["test_certificates.cc"],
|
||||
hdrs = ["test_certificates.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "wvm_token_handler",
|
||||
srcs = ["wvm_token_handler.cc"],
|
||||
hdrs = ["wvm_token_handler.h"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":ecb_util",
|
||||
":sha_util",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//util/endian",
|
||||
"//util/gtl:map_util",
|
||||
"//util:status",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "wvm_token_handler_test",
|
||||
size = "small",
|
||||
srcs = ["wvm_token_handler_test.cc"],
|
||||
deps = [
|
||||
":wvm_test_keys",
|
||||
":wvm_token_handler",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "wvm_test_keys",
|
||||
testonly = 1,
|
||||
srcs = ["wvm_test_keys.cc"],
|
||||
hdrs = ["wvm_test_keys.h"],
|
||||
deps = [
|
||||
":wvm_token_handler",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "error_space",
|
||||
srcs = ["error_space.cc"],
|
||||
hdrs = ["error_space.h"],
|
||||
deps = [
|
||||
"//util:status",
|
||||
"//util:proto_status",
|
||||
"//protos/public:errors_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "remote_attestation_verifier",
|
||||
srcs = ["remote_attestation_verifier.cc"],
|
||||
hdrs = ["remote_attestation_verifier.h"],
|
||||
deps = [
|
||||
":client_id_util",
|
||||
":drm_service_certificate",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":x509_cert",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//util:status",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:remote_attestation_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "drm_service_certificate",
|
||||
srcs = ["drm_service_certificate.cc"],
|
||||
hdrs = ["drm_service_certificate.h"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":certificate_type",
|
||||
":drm_root_certificate",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":rsa_util",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//util/gtl:map_util",
|
||||
"//util:status",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "drm_service_certificate_test",
|
||||
timeout = "short",
|
||||
srcs = ["drm_service_certificate_test.cc"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":drm_service_certificate",
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
":rsa_util",
|
||||
":test_certificates",
|
||||
"//base",
|
||||
"//external:protobuf",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:license_server_sdk_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "verified_media_pipeline",
|
||||
srcs = ["verified_media_pipeline.cc"],
|
||||
hdrs = ["verified_media_pipeline.h"],
|
||||
deps = [
|
||||
":vmp_checker",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//util:status",
|
||||
"//protos/public:license_protocol_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "x509_cert",
|
||||
srcs = ["x509_cert.cc"],
|
||||
hdrs = ["x509_cert.h"],
|
||||
deps = [
|
||||
":openssl_util",
|
||||
":rsa_key",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//external:openssl",
|
||||
"//util:status",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "test_utils",
|
||||
testonly = 1,
|
||||
srcs = ["test_utils.cc"],
|
||||
hdrs = ["test_utils.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//external:openssl",
|
||||
"//util:status",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "x509_cert_test",
|
||||
timeout = "short",
|
||||
srcs = ["x509_cert_test.cc"],
|
||||
deps = [
|
||||
":rsa_key",
|
||||
":test_utils",
|
||||
":x509_cert",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "vmp_checker",
|
||||
srcs = ["vmp_checker.cc"],
|
||||
hdrs = ["vmp_checker.h"],
|
||||
deps = [
|
||||
":certificate_type",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":x509_cert",
|
||||
"//base",
|
||||
"//util:status",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:verified_media_pipeline_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "vmp_checker_test",
|
||||
timeout = "short",
|
||||
srcs = ["vmp_checker_test.cc"],
|
||||
deps = [
|
||||
":rsa_key",
|
||||
":vmp_checker",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:verified_media_pipeline_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "string_util",
|
||||
srcs = ["string_util.cc"],
|
||||
hdrs = ["string_util.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//util:status",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "string_util_test",
|
||||
srcs = ["string_util_test.cc"],
|
||||
deps = [
|
||||
":string_util",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
132
common/aes_cbc_util.cc
Normal file
132
common/aes_cbc_util.cc
Normal file
@@ -0,0 +1,132 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/aes_cbc_util.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "openssl/aes.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
// Encrypts the provided plantext std::string using AES-CBC encryption.
|
||||
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
||||
const std::string& plaintext) {
|
||||
const size_t num_padding_bytes =
|
||||
AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE);
|
||||
std::string padded_text = plaintext;
|
||||
padded_text.append(num_padding_bytes, static_cast<char>(num_padding_bytes));
|
||||
return EncryptAesCbcNoPad(key, iv, padded_text);
|
||||
}
|
||||
|
||||
std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||
const std::string& plaintext) {
|
||||
if (iv.size() != AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
|
||||
return std::string();
|
||||
}
|
||||
if (plaintext.size() % AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "Invalid AEC-CBC plaintext size: " << plaintext.size();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
AES_KEY aes_key;
|
||||
if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||
key.size() * 8, &aes_key) != 0) {
|
||||
LOG(WARNING) << "Invalid AES key.";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string encrypted(plaintext.size(), 0);
|
||||
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
|
||||
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(plaintext.data()),
|
||||
reinterpret_cast<uint8_t*>(&encrypted[0]), plaintext.size(),
|
||||
&aes_key, &local_iv[0], AES_ENCRYPT);
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
// Decrypts the AES-CBC encrypted text. Returns an empty std::string on error or
|
||||
// the plaintext on success.
|
||||
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
||||
const std::string& ciphertext) {
|
||||
if (ciphertext.empty()) {
|
||||
LOG(WARNING) << "Empty ciphertext.";
|
||||
return std::string();
|
||||
}
|
||||
if (iv.size() != AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
|
||||
return std::string();
|
||||
}
|
||||
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) {
|
||||
LOG(WARNING) << "Ciphertext not a multiple of AES block size: "
|
||||
<< ciphertext.size();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
AES_KEY aes_key;
|
||||
if (AES_set_decrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||
key.size() * 8, &aes_key) != 0) {
|
||||
LOG(WARNING) << "Invalid AES key.";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string cleartext(ciphertext.size(), 0);
|
||||
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
|
||||
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(ciphertext.data()),
|
||||
reinterpret_cast<uint8_t*>(&cleartext[0]), ciphertext.size(),
|
||||
&aes_key, &local_iv[0], AES_DECRYPT);
|
||||
|
||||
const uint8_t num_padding_bytes = cleartext[cleartext.size() - 1];
|
||||
if (num_padding_bytes > AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "AES padding too long: " << num_padding_bytes;
|
||||
return std::string();
|
||||
}
|
||||
for (uint8_t i = 0; i < num_padding_bytes; ++i) {
|
||||
if (cleartext[cleartext.size() - 1 - i] != num_padding_bytes) {
|
||||
LOG(WARNING) << "Padding verification failure.";
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
cleartext.resize(cleartext.size() - num_padding_bytes);
|
||||
return cleartext;
|
||||
}
|
||||
|
||||
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||
const std::string& ciphertext) {
|
||||
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
|
||||
if (local_iv.empty()) {
|
||||
local_iv.resize(AES_BLOCK_SIZE, '\0');
|
||||
} else if (local_iv.size() != AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
|
||||
return std::string();
|
||||
}
|
||||
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) {
|
||||
LOG(WARNING) << "Ciphertext not a multiple of AES block size: "
|
||||
<< ciphertext.size();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
AES_KEY aes_key;
|
||||
if (AES_set_decrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||
key.size() * 8, &aes_key) != 0) {
|
||||
LOG(WARNING) << "Invalid AES key.";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string cleartext(ciphertext.size(), 0);
|
||||
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(ciphertext.data()),
|
||||
reinterpret_cast<uint8_t*>(&cleartext[0]), ciphertext.size(),
|
||||
&aes_key, &local_iv[0], AES_DECRYPT);
|
||||
return cleartext;
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
43
common/aes_cbc_util.h
Normal file
43
common/aes_cbc_util.h
Normal file
@@ -0,0 +1,43 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_AES_CBC_UTIL_H_
|
||||
#define COMMON_AES_CBC_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
// Helper for wrapping AES CBC encryption. Uses PKCS7 padding.
|
||||
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
||||
const std::string& plaintext);
|
||||
|
||||
// Helper for wrapping AES CBC encryption. Adds no padding, so the input
|
||||
// must be an multiple of the 16-byte AES block size. Returns empty std::string
|
||||
// on error.
|
||||
std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||
const std::string& plaintext);
|
||||
|
||||
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
|
||||
// empty std::string on error or the plaintext on success. Expects PKCS7 padding.
|
||||
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
||||
const std::string& ciphertext);
|
||||
|
||||
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
|
||||
// empty std::string on error or the plaintext on success.
|
||||
// Uses no padding; fails if the ciphertext is not a multiple of 16 bytes.
|
||||
// This is used to decrypt the encrypted blob in the WVM keyboxes, with
|
||||
// a zero iv.
|
||||
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||
const std::string& ciphertext);
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_AES_CBC_UTIL_H_
|
||||
161
common/aes_cbc_util_test.cc
Normal file
161
common/aes_cbc_util_test.cc
Normal file
@@ -0,0 +1,161 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const uint8_t kKey[] = {
|
||||
0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e,
|
||||
0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b,
|
||||
};
|
||||
|
||||
const uint8_t kIv[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
TEST(CryptoUtilTest, EncryptAndDecryptAesCbc) {
|
||||
std::string plain_text("Foo");
|
||||
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||
std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
||||
std::string expected_ciphertext(
|
||||
"\xCF\x1A\x3\x1C\x9C\x8C\xB9Z\xEC\xC0\x17\xDCRxX\xD7");
|
||||
ASSERT_EQ(0, ciphertext.size() % 16);
|
||||
ASSERT_GT(ciphertext.size(), plain_text.size());
|
||||
ASSERT_EQ(expected_ciphertext, ciphertext);
|
||||
|
||||
std::string decrypted = DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||
std::string(kIv, kIv + sizeof(kIv)), ciphertext);
|
||||
ASSERT_EQ(plain_text, decrypted);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, DecryptAesCbcNoPad) {
|
||||
const uint8_t kKey[] = {
|
||||
0xdd, 0x71, 0x39, 0xea, 0xfa, 0xce, 0xed, 0x7c,
|
||||
0xda, 0x9f, 0x25, 0xda, 0x8a, 0xa9, 0x15, 0xea,
|
||||
};
|
||||
const uint8_t kIv[] = {
|
||||
0x5d, 0x16, 0x44, 0xea, 0xec, 0x11, 0xf9, 0x83,
|
||||
0x14, 0x75, 0x41, 0xe4, 0x6e, 0xeb, 0x27, 0x74,
|
||||
};
|
||||
const uint8_t kCiphertext[] = {
|
||||
0x6d, 0xa6, 0xda, 0xe4, 0xee, 0x40, 0x09, 0x17,
|
||||
0x54, 0x7b, 0xba, 0xa5, 0x27, 0xb8, 0x82, 0x1b,
|
||||
};
|
||||
const uint8_t kExpectedPlaintext[] = {
|
||||
0xb3, 0x49, 0xd4, 0x80, 0x9e, 0x91, 0x06, 0x87,
|
||||
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10,
|
||||
};
|
||||
const uint8_t kExpectedPlaintextEmptyIv[] = {
|
||||
0xee, 0x5f, 0x90, 0x6a, 0x72, 0x80, 0xff, 0x04,
|
||||
0x14, 0x75, 0x41, 0xa4, 0x6e, 0xeb, 0x27, 0x64,
|
||||
};
|
||||
|
||||
std::string decrypted = DecryptAesCbcNoPad(
|
||||
std::string(kKey, kKey + sizeof(kKey)), std::string(kIv, kIv + sizeof(kIv)),
|
||||
std::string(kCiphertext, kCiphertext + sizeof(kCiphertext)));
|
||||
ASSERT_EQ(std::string(kExpectedPlaintext,
|
||||
kExpectedPlaintext + sizeof(kExpectedPlaintext)),
|
||||
decrypted);
|
||||
|
||||
std::string dummy_iv;
|
||||
decrypted = DecryptAesCbcNoPad(
|
||||
std::string(kKey, kKey + sizeof(kKey)), dummy_iv,
|
||||
std::string(kCiphertext, kCiphertext + sizeof(kCiphertext)));
|
||||
ASSERT_EQ(
|
||||
std::string(kExpectedPlaintextEmptyIv,
|
||||
kExpectedPlaintextEmptyIv + sizeof(kExpectedPlaintextEmptyIv)),
|
||||
decrypted);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestFailedEncrypt) {
|
||||
// Test with bogus initialization vector.
|
||||
std::string plain_text("Foo");
|
||||
std::string bogus_iv("bogus");
|
||||
std::string ciphertext =
|
||||
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, plain_text);
|
||||
ASSERT_EQ(ciphertext.size(), 0);
|
||||
|
||||
// Test with bogus key.
|
||||
std::string bogus_key("bogus");
|
||||
ciphertext =
|
||||
EncryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
||||
ASSERT_EQ(ciphertext.size(), 0);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestFailedEncryptNoPad) {
|
||||
std::string plaintext("0123456789abcdef");
|
||||
std::string key(kKey, kKey + sizeof(kKey));
|
||||
std::string iv(kIv, kIv + sizeof(kIv));
|
||||
|
||||
// Control.
|
||||
std::string ciphertext = EncryptAesCbcNoPad(key, iv, plaintext);
|
||||
ASSERT_EQ(plaintext.size(), ciphertext.size());
|
||||
|
||||
// Bogus key.
|
||||
std::string bogus_key("bogus");
|
||||
ciphertext = EncryptAesCbcNoPad(bogus_key, iv, plaintext);
|
||||
EXPECT_EQ(ciphertext.size(), 0);
|
||||
|
||||
// Bogus IV.
|
||||
std::string bogus_iv("bogus");
|
||||
ciphertext = EncryptAesCbcNoPad(key, bogus_iv, plaintext);
|
||||
EXPECT_EQ(ciphertext.size(), 0);
|
||||
|
||||
// Incorrectly-sized plaintext.
|
||||
std::string bad_plaintext("Foo");
|
||||
ciphertext = EncryptAesCbcNoPad(key, iv, bad_plaintext);
|
||||
EXPECT_EQ(ciphertext.size(), 0);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestFailedDecrypt) {
|
||||
// First, encrypt the data.
|
||||
std::string plain_text("Foo");
|
||||
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||
std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
||||
ASSERT_NE(ciphertext.size(), 0);
|
||||
|
||||
// Test Decrypt with bogus iv.
|
||||
std::string bogus_iv("bogus");
|
||||
plain_text =
|
||||
DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, ciphertext);
|
||||
ASSERT_EQ(plain_text.size(), 0);
|
||||
|
||||
// Test Decrypt with bogus key.
|
||||
std::string bogus_key("bogus");
|
||||
plain_text =
|
||||
DecryptAesCbc(bogus_key, std::string(kIv, kIv + sizeof(kIv)), ciphertext);
|
||||
ASSERT_EQ(plain_text.size(), 0);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestEmptyEncrypt) {
|
||||
EXPECT_EQ("\xDBx\xD9\x91\xE8\x1D\xD9\x19\x80r\x12\x89\xD7Kp\xEB",
|
||||
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||
std::string(kIv, kIv + sizeof(kIv)), ""));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestEmptyDecryptAesCbc) {
|
||||
EXPECT_EQ("", DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||
std::string(kIv, kIv + sizeof(kIv)), ""));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestEmptyDecryptAesCbcNoPad) {
|
||||
EXPECT_EQ("", DecryptAesCbcNoPad(std::string(kKey, kKey + sizeof(kKey)),
|
||||
std::string(kIv, kIv + sizeof(kIv)), ""));
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
22
common/certificate_type.h
Normal file
22
common/certificate_type.h
Normal file
@@ -0,0 +1,22 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_CERTIFICATE_TYPE_H_
|
||||
#define COMMON_CERTIFICATE_TYPE_H_
|
||||
|
||||
namespace widevine {
|
||||
|
||||
enum CertificateType {
|
||||
kCertificateTypeTesting,
|
||||
kCertificateTypeDevelopment,
|
||||
kCertificateTypeProduction,
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_CERTIFICATE_TYPE_H_
|
||||
55
common/certificate_util.cc
Normal file
55
common/certificate_util.cc
Normal file
@@ -0,0 +1,55 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/certificate_util.h"
|
||||
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/drm_service_certificate.h"
|
||||
#include "common/verified_media_pipeline.h"
|
||||
#include "common/vmp_checker.h"
|
||||
#include "license_server_sdk/internal/client_cert.h"
|
||||
#include "license_server_sdk/internal/device_status_list.h"
|
||||
|
||||
namespace widevine {
|
||||
util::Status SetCertificateStatusList(
|
||||
CertificateType cert_type, const std::string& signed_certificate_status_list,
|
||||
uint32_t expiration_period_seconds, bool allow_unknown_devices) {
|
||||
util::Status status =
|
||||
VmpChecker::Instance()->SelectDrmCertificateType(cert_type);
|
||||
if (!status.ok()) return status;
|
||||
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
status = DrmRootCertificate::CreateByType(cert_type, &root_cert);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
status = CertificateClientCert::SetDrmRootCertificatePublicKey(
|
||||
root_cert->public_key());
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
DeviceStatusList::Instance()->set_allow_unknown_devices(
|
||||
allow_unknown_devices);
|
||||
return DeviceStatusList::Instance()->UpdateStatusList(
|
||||
root_cert->public_key(), signed_certificate_status_list,
|
||||
expiration_period_seconds);
|
||||
}
|
||||
|
||||
util::Status AddDrmServiceCertificate(
|
||||
CertificateType cert_type, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase) {
|
||||
util::Status status =
|
||||
VmpChecker::Instance()->SelectDrmCertificateType(cert_type);
|
||||
if (!status.ok()) return status;
|
||||
return DrmServiceCertificate::AddDrmServiceCertificate(
|
||||
cert_type, service_certificate, service_private_key,
|
||||
service_private_key_passphrase);
|
||||
}
|
||||
} // namespace widevine
|
||||
42
common/certificate_util.h
Normal file
42
common/certificate_util.h
Normal file
@@ -0,0 +1,42 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_CERTIFICATE_UTIL_H_
|
||||
#define COMMON_CERTIFICATE_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/status.h"
|
||||
#include "common/certificate_type.h"
|
||||
|
||||
namespace widevine {
|
||||
// Set the certificate status list system-wide. |cert_type| specifies
|
||||
// whether to use development or production root certificates.
|
||||
// |expiration_period| is the number of seconds until the
|
||||
// certificate_status_list expires after its creation time
|
||||
// (creation_time_seconds). If |allow_unknown_devices| is false, an error is
|
||||
// returned if the device does not appear in the certificate_status_list.
|
||||
util::Status SetCertificateStatusList(CertificateType cert_type,
|
||||
const std::string& certificate_status_list,
|
||||
uint32_t expiration_period_seconds,
|
||||
bool allow_unknown_devices);
|
||||
|
||||
// Add a service certificate system-wide. |cert_type| indicates the type of
|
||||
// root certificate used to sign the service certificate;
|
||||
// |service_certificate| is a Google-generated certificate used to
|
||||
// authenticate the service provider for purposes of device privacy;
|
||||
// |service_private_key| is the encrypted PKCS#8 private RSA key corresponding
|
||||
// to the service certificate; and |service_private_key_passphrase| is the
|
||||
// password required to decrypt |service_private_key|.
|
||||
util::Status AddDrmServiceCertificate(
|
||||
CertificateType cert_type, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_CERTIFICATE_UTIL_H_
|
||||
89
common/client_id_util.cc
Normal file
89
common/client_id_util.cc
Normal file
@@ -0,0 +1,89 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/client_id_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/drm_service_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||
absl::string_view value) {
|
||||
ClientIdentification_NameValue* nv = client_id->add_client_info();
|
||||
nv->set_name(std::string(name));
|
||||
nv->set_value(std::string(value));
|
||||
}
|
||||
|
||||
bool SetClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||
absl::string_view value) {
|
||||
int n = client_id->client_info_size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (client_id->client_info(i).name() == name) {
|
||||
client_id->mutable_client_info(i)->set_value(std::string(value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
AddClientInfo(client_id, name, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
absl::string_view name) {
|
||||
return GetClientInfo(client_id, name, std::string());
|
||||
}
|
||||
|
||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
absl::string_view name, const std::string& default_value) {
|
||||
for (const auto& nv : client_id.client_info()) {
|
||||
if (nv.name() == name) {
|
||||
return nv.value();
|
||||
}
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
util::Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id) {
|
||||
return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id,
|
||||
client_id);
|
||||
}
|
||||
|
||||
util::Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
const std::string& privacy_key, ClientIdentification* client_id) {
|
||||
DCHECK(client_id);
|
||||
if (!encrypted_client_id.has_encrypted_client_id() ||
|
||||
encrypted_client_id.encrypted_client_id().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id");
|
||||
}
|
||||
if (!encrypted_client_id.has_encrypted_client_id_iv() ||
|
||||
encrypted_client_id.encrypted_client_id_iv().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id-iv");
|
||||
}
|
||||
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
||||
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
||||
encrypted_client_id.encrypted_client_id()));
|
||||
if (serialized_client_id.empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-decryption-failed");
|
||||
}
|
||||
if (!client_id->ParseFromString(serialized_client_id)) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-parse-failed");
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
61
common/client_id_util.h
Normal file
61
common/client_id_util.h
Normal file
@@ -0,0 +1,61 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Utilities for manipulating the ClientIdentification proto.
|
||||
// ClientIdentification.client_info() contains a sequence of
|
||||
// arbitrary name-value pairs; this code consolidates the
|
||||
// accessors for them in one place.
|
||||
#ifndef COMMON_CLIENT_ID_UTIL_H_
|
||||
#define COMMON_CLIENT_ID_UTIL_H_
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "util/status.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Append the given name/value pair to client_id->client_info(). Does not
|
||||
// check for duplicates.
|
||||
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||
absl::string_view value);
|
||||
|
||||
// Append the given name/value pair to client_id->client_info(). If the
|
||||
// given name already had a value, replaces it and returns true.
|
||||
bool SetClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||
absl::string_view value);
|
||||
|
||||
// Return the value from client_id.client_info() matching the given name,
|
||||
// or the empty std::string if not found.
|
||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
absl::string_view name);
|
||||
|
||||
// Return the value from client_id.client_info() matching the given name,
|
||||
// or the given default value if not found.
|
||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
absl::string_view name, const std::string& default_value);
|
||||
|
||||
// Decrypts the encrypted client identification in |encrypted_client_id| into
|
||||
// |client_id| using the private key for the service certificate which was
|
||||
// used to encrypt the information.
|
||||
// |client_id| is owned by caller.
|
||||
// Returns util::Status::OK, if successful, else an error.
|
||||
util::Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id);
|
||||
|
||||
// Decrypts the encrypted client identification in |encrypted_client_id| into
|
||||
// |client_id| using |privacy_key|.
|
||||
// |client_id| is owned by caller.
|
||||
// Returns util::Status::OK, if successful, else an error.
|
||||
util::Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
const std::string& privacy_key, ClientIdentification* client_id);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_CLIENT_ID_UTIL_H_
|
||||
181
common/crypto_util.cc
Normal file
181
common/crypto_util.cc
Normal file
@@ -0,0 +1,181 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Implementation of Common crypto utilities used by Widevine services.
|
||||
|
||||
#include "common/crypto_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/cmac.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/hmac.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "util/endian/endian.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
const char kEncryptionKeyLabel[] = "ENCRYPTION";
|
||||
const int kEncryptionKeySizeBits = 128;
|
||||
const char kSigningKeyLabel[] = "AUTHENTICATION";
|
||||
const int kSigningKeySizeBits = 256;
|
||||
const size_t kSigningKeySizeBytes = 32;
|
||||
const char kIvMasterKey[] = "1234567890123456";
|
||||
const char kIvLabel[] = "IV_ENCRYPTION";
|
||||
const int kIvSizeBits = 128;
|
||||
const char kKeyIdMasterKey[] = "0123456789abcdef";
|
||||
const char kKeyIdLabel[] = "KEY_ID_ENCRYPTION";
|
||||
const int kKeyIdSizeBits = 128;
|
||||
const char kGroupKeyLabel[] = "GROUP_ENCRYPTION";
|
||||
// TODO(user): This is a temporary key for development. Replace this with
|
||||
// a real group master key in keystore.
|
||||
// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy
|
||||
// like VerifySignatureHmacSha1.
|
||||
const char kPhonyGroupMasterKey[] = "fedcba9876543210";
|
||||
const int kAes128KeySizeBits = 128;
|
||||
const int kAes128KeySizeBytes = 16;
|
||||
|
||||
const uint32_t kCENCSchemeID = 0x63656E63; // 'cenc' (AES-CTR): 0x63656E63
|
||||
const uint32_t kCBC1SchemeID = 0x63626331; // 'cbc1' (AES-CBC): 0x63626331
|
||||
const uint32_t kCENSSchemeID =
|
||||
0x63656E73; // 'cens' (AES-CTR subsample): 0x63656E73
|
||||
const uint32_t kCBCSSchemeID =
|
||||
0x63626373; // 'cbcs' (AES-CBC subsample): 0x63626373
|
||||
|
||||
// Creates a SHA-256 HMAC signature for the given message.
|
||||
std::string CreateSignatureHmacSha256(absl::string_view key,
|
||||
absl::string_view message) {
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha256());
|
||||
HMAC_Update(&ctx, reinterpret_cast<const unsigned char*>(message.data()),
|
||||
message.size());
|
||||
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||
unsigned int digest_len;
|
||||
HMAC_Final(&ctx, digest, &digest_len);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
std::string s(reinterpret_cast<char*>(digest), digest_len);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Compares the SHA-256 HMAC against the provided signature.
|
||||
bool VerifySignatureHmacSha256(absl::string_view key,
|
||||
absl::string_view signature,
|
||||
absl::string_view message) {
|
||||
return CreateSignatureHmacSha256(key, message) == signature;
|
||||
}
|
||||
|
||||
// Creates a SHA-1 HMAC signature for the given message.
|
||||
std::string CreateSignatureHmacSha1(absl::string_view key,
|
||||
absl::string_view message) {
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha1());
|
||||
HMAC_Update(&ctx, reinterpret_cast<const unsigned char*>(message.data()),
|
||||
message.size());
|
||||
unsigned char digest[SHA_DIGEST_LENGTH];
|
||||
unsigned int digest_len;
|
||||
HMAC_Final(&ctx, digest, &digest_len);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
std::string s(reinterpret_cast<char*>(digest), digest_len);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Compares the SHA-1 HMAC against the provided signature.
|
||||
bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature,
|
||||
absl::string_view message) {
|
||||
return CreateSignatureHmacSha1(key, message) == signature;
|
||||
}
|
||||
|
||||
// Derives an AES 128 key from the provided key and additional info.
|
||||
std::string DeriveKey(absl::string_view key, absl::string_view label,
|
||||
absl::string_view context, const uint32_t size_bits) {
|
||||
if (key.size() != kAes128KeySizeBytes) return "";
|
||||
|
||||
// We only handle even multiples of 16 bytes (128 bits) right now.
|
||||
if ((size_bits % 128) || (size_bits > (128 * 255))) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string result;
|
||||
|
||||
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
|
||||
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
||||
|
||||
for (unsigned char counter = 1; counter <= (size_bits / 128); counter++) {
|
||||
if (CMAC_Init(cmac_ctx, key.data(), key.size(), cipher, 0)) {
|
||||
std::string message;
|
||||
message.append(1, counter);
|
||||
message.append(label.data(), label.size());
|
||||
message.append(1, '\0');
|
||||
message.append(context.data(), context.size());
|
||||
char size_string[4];
|
||||
BigEndian::Store32(&size_string, size_bits);
|
||||
message.append(&size_string[0], &size_string[0] + 4);
|
||||
if (CMAC_Update(cmac_ctx, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size())) {
|
||||
size_t reslen;
|
||||
unsigned char res[AES_BLOCK_SIZE];
|
||||
if (CMAC_Final(cmac_ctx, res, &reslen)) {
|
||||
result.append(reinterpret_cast<const char*>(res), reslen);
|
||||
}
|
||||
DCHECK(reslen == AES_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Derives an IV from the provided info.
|
||||
std::string DeriveIv(absl::string_view context) {
|
||||
return DeriveKey(kIvMasterKey, kIvLabel, context, kIvSizeBits);
|
||||
}
|
||||
|
||||
// Derives a key ID from the provided info.
|
||||
std::string DeriveKeyId(absl::string_view context) {
|
||||
return DeriveKey(kKeyIdMasterKey, kKeyIdLabel, context, kKeyIdSizeBits);
|
||||
}
|
||||
|
||||
std::string DeriveGroupSessionKey(absl::string_view context,
|
||||
const uint32_t size_bits) {
|
||||
return DeriveKey(kPhonyGroupMasterKey, kGroupKeyLabel, context, size_bits);
|
||||
}
|
||||
|
||||
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
|
||||
const uint32_t size_bits) {
|
||||
return DeriveKey(key, kSigningKeyLabel, context, size_bits);
|
||||
}
|
||||
|
||||
bool FourCCEncryptionSchemeIDFromString(const std::string& requested,
|
||||
uint32_t* four_cc_code) {
|
||||
if (requested.size() != 4 || four_cc_code == nullptr) return false;
|
||||
|
||||
uint32_t result = 0;
|
||||
for (auto i = 0; i < 4; ++i) {
|
||||
result <<= 8;
|
||||
result |= requested[i];
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case kCENCSchemeID:
|
||||
case kCBC1SchemeID:
|
||||
case kCENSSchemeID:
|
||||
case kCBCSSchemeID:
|
||||
*four_cc_code = result;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
89
common/crypto_util.h
Normal file
89
common/crypto_util.h
Normal file
@@ -0,0 +1,89 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Contains common crypto routines for widevine protocols. These routines are
|
||||
// used as part of licensing and provisioning request handling.
|
||||
|
||||
#ifndef COMMON_CRYPTO_UTIL_H_
|
||||
#define COMMON_CRYPTO_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
// Default constants used for key derivation for encryption and signing.
|
||||
// TODO(user): These are duplicated in session.cc in the sdk. de-dup.
|
||||
extern const char kEncryptionKeyLabel[];
|
||||
extern const int kEncryptionKeySizeBits;
|
||||
extern const char kSigningKeyLabel[];
|
||||
extern const int kSigningKeySizeBits;
|
||||
extern const size_t kSigningKeySizeBytes;
|
||||
extern const char kIvMasterKey[];
|
||||
extern const char kIvLabel[];
|
||||
extern const int kIvSizeBits;
|
||||
extern const int kAes128KeySizeBits;
|
||||
extern const int kAes128KeySizeBytes;
|
||||
|
||||
extern const uint32_t kCENCSchemeID; // 'cenc' (AES-CTR): 0x63656E63
|
||||
extern const uint32_t kCBC1SchemeID; // 'cbc1' (AES-CBC): 0x63626331
|
||||
extern const uint32_t kCENSSchemeID; // 'cens' (AES-CTR subsample): 0x63656E73
|
||||
extern const uint32_t kCBCSSchemeID; // 'cbcs' (AES-CBC subsample): 0x63626373
|
||||
|
||||
// DeriveKey uses the NIST 800-108 KDF recommendation, using AES-CMAC PRF.
|
||||
// NIST 800-108:
|
||||
// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf
|
||||
// AES-CMAC:
|
||||
// http://tools.ietf.org/html/rfc4493
|
||||
std::string DeriveKey(absl::string_view key, absl::string_view label,
|
||||
absl::string_view context, const uint32_t size_bits);
|
||||
|
||||
// Derives an IV from the provided |context|.
|
||||
std::string DeriveIv(absl::string_view context);
|
||||
|
||||
// Derives a key ID from the provided |context|.
|
||||
std::string DeriveKeyId(absl::string_view context);
|
||||
|
||||
// Helper function to derive a key using the group master key and context.
|
||||
std::string DeriveGroupSessionKey(absl::string_view context, const uint32_t size_bits);
|
||||
|
||||
// Helper function to derive a signing key for from the signing context.
|
||||
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
|
||||
const uint32_t size_bits);
|
||||
|
||||
// Helper function to create a SHA-256 HMAC signature for the given message.
|
||||
std::string CreateSignatureHmacSha256(absl::string_view key,
|
||||
absl::string_view message);
|
||||
|
||||
// Helper function which compares the SHA-256 HMAC against the provided
|
||||
// signature.
|
||||
bool VerifySignatureHmacSha256(absl::string_view key,
|
||||
absl::string_view signature,
|
||||
absl::string_view message);
|
||||
|
||||
// Helper function to create a SHA-1 HMAC signature for the given message.
|
||||
std::string CreateSignatureHmacSha1(absl::string_view key,
|
||||
absl::string_view message);
|
||||
|
||||
// Helper function which compares the SHA-1 HMAC against the provided
|
||||
// signature.
|
||||
bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature,
|
||||
absl::string_view message);
|
||||
|
||||
// Converts a requested 4CC encryption scheme ID from a std::string to a uint32_t and
|
||||
// verifies it is a correct value.
|
||||
bool FourCCEncryptionSchemeIDFromString(const std::string& requested,
|
||||
uint32_t* four_cc_code);
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_CRYPTO_UTIL_H_
|
||||
236
common/crypto_util_test.cc
Normal file
236
common/crypto_util_test.cc
Normal file
@@ -0,0 +1,236 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Unit tests for the crypto_util helper functions.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/crypto_util.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
const char kCENCStr[] = "cenc";
|
||||
const char kCBC1Str[] = "cbc1";
|
||||
const char kCENSStr[] = "cens";
|
||||
const char kCBCSStr[] = "cbcs";
|
||||
|
||||
static unsigned char key_data[] =
|
||||
{ 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e,
|
||||
0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b };
|
||||
|
||||
static std::string key_str(key_data, key_data + sizeof(key_data));
|
||||
|
||||
static unsigned char iv_data[] =
|
||||
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
|
||||
|
||||
static std::string iv_str(iv_data, iv_data + sizeof(iv_data));
|
||||
|
||||
TEST(CryptoUtilTest, DeriveAes128KeyTest) {
|
||||
unsigned char label[] = { 0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1,
|
||||
0x92, 0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08,
|
||||
0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60,
|
||||
0xd1, 0xcf, 0x6a, 0x4d, 0x40, 0xa1, 0xe3, 0xfe,
|
||||
0xd3, 0xe9, 0xa6, 0x58, 0x4c, 0xd4, 0xad, 0xa4,
|
||||
0xa2 };
|
||||
|
||||
unsigned char context[] = { 0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d, 0x6d,
|
||||
0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5, 0x5b, 0x80,
|
||||
0x81, 0x91, 0xff };
|
||||
|
||||
unsigned char output0[] = { 0xd5, 0xad, 0x2d, 0xb1, 0x5a, 0x06, 0xcb, 0x50,
|
||||
0xf2, 0x59, 0x5a, 0xb2, 0xb2, 0x0d, 0x44, 0x4e };
|
||||
|
||||
unsigned char output1[] = { 0xdf, 0x38, 0x45, 0x97, 0x5d, 0x7a, 0x81, 0xb4,
|
||||
0x94, 0x86, 0xaf, 0x0c, 0xdc, 0x4d, 0xeb, 0x62,
|
||||
0x31, 0x39, 0x67, 0x8f, 0xff, 0x5d, 0x68, 0x35,
|
||||
0xdc, 0x89, 0x5f, 0x47, 0xca, 0xe0, 0x2d, 0x3a,
|
||||
0x10, 0x24, 0xf8, 0x7e, 0x5b, 0x70, 0xe1, 0xa3,
|
||||
0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22 };
|
||||
|
||||
std::string label_str(label, label + sizeof(label));
|
||||
std::string key_str(key_data, key_data + sizeof(key_data));
|
||||
std::string context_str(context, context + sizeof(context));
|
||||
std::string result = DeriveKey(key_str, label_str, context_str, 128);
|
||||
|
||||
std::string output_128(output0, output0 + sizeof(output0));
|
||||
|
||||
ASSERT_EQ(result, output_128);
|
||||
|
||||
result = DeriveKey(key_str, label_str, context_str, 384);
|
||||
|
||||
std::string output_384(output1, output1 + sizeof(output1));
|
||||
|
||||
ASSERT_EQ(result, output_384);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, DeriveGroupSesionKey) {
|
||||
unsigned char output[] = { 0x92, 0x6c, 0x2f, 0x5, 0xa6, 0x4f, 0xff, 0xb1,
|
||||
0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1 };
|
||||
std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128);
|
||||
EXPECT_EQ(crypto_util::kAes128KeySizeBytes, group_session_key.size());
|
||||
const std::string output_128(output, output + sizeof(output));
|
||||
ASSERT_EQ(output_128, group_session_key);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
|
||||
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
|
||||
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
|
||||
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
|
||||
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
|
||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
std::string signature(CreateSignatureHmacSha256(key_str, message));
|
||||
|
||||
ASSERT_EQ(signature.size(), 32);
|
||||
|
||||
ASSERT_TRUE(VerifySignatureHmacSha256(key_str, signature, message));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
|
||||
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
|
||||
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
|
||||
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
|
||||
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
|
||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
// Test with bogus key;
|
||||
std::string bogus_key("bogus");
|
||||
std::string signature(CreateSignatureHmacSha256(bogus_key, message));
|
||||
|
||||
// This should still produce an hmac signature.
|
||||
ASSERT_EQ(signature.size(), 32);
|
||||
|
||||
// Create valid signature to compare.
|
||||
signature = CreateSignatureHmacSha256(key_str, message);
|
||||
|
||||
// Test with bogus key.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message));
|
||||
|
||||
// Test with munged signature.
|
||||
signature[0] = 0xFF;
|
||||
ASSERT_FALSE(VerifySignatureHmacSha256(key_str, signature, message));
|
||||
|
||||
// Test with bogus signature.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha256(key_str, "bogus", message));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
|
||||
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
|
||||
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
|
||||
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
|
||||
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
|
||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
std::string signature(CreateSignatureHmacSha1(key_str, message));
|
||||
|
||||
ASSERT_EQ(20, signature.size());
|
||||
ASSERT_TRUE(VerifySignatureHmacSha1(key_str, signature, message));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
|
||||
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
|
||||
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
|
||||
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
|
||||
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
|
||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
// Test with bogus key;
|
||||
std::string bogus_key("bogus");
|
||||
std::string signature(CreateSignatureHmacSha1(bogus_key, message));
|
||||
|
||||
// This should still produce an hmac signature.
|
||||
ASSERT_EQ(20, signature.size());
|
||||
// Create valid signature to compare.
|
||||
signature = CreateSignatureHmacSha1(key_str, message);
|
||||
// Test with bogus key.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message));
|
||||
// Test with munged signature.
|
||||
signature[0] = 0xFF;
|
||||
ASSERT_FALSE(VerifySignatureHmacSha1(key_str, signature, message));
|
||||
// Test with bogus signature.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha1(key_str, "bogus", message));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, DeriveIv) {
|
||||
// First value in the pair is the key_id, second value is the expected IV.
|
||||
std::pair<std::string, std::string> id_iv_pairs[] = {
|
||||
{"1234567890123456", "3278234c7682d1a2e153af4912975f5f"},
|
||||
{"0987654321098765", "cf09abd30f04b60544910791a6b904cf"}};
|
||||
for (const auto& id_iv_pair : id_iv_pairs) {
|
||||
SCOPED_TRACE(absl::StrCat("test case:", id_iv_pair.first));
|
||||
EXPECT_EQ(id_iv_pair.second,
|
||||
absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
|
||||
// Repeat same call to verify derivied result is repeatable.
|
||||
EXPECT_EQ(id_iv_pair.second,
|
||||
absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, DeriveKeyId) {
|
||||
// First value in the pair is the context, second value is the expected id.
|
||||
std::pair<std::string, std::string> context_id_pairs[] = {
|
||||
{"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"},
|
||||
{"0987654321098765", "084fc6bece9688ccce6b1672d9b47e22"}};
|
||||
for (const auto& context_id_pair : context_id_pairs) {
|
||||
SCOPED_TRACE(absl::StrCat("test case:", context_id_pair.first));
|
||||
EXPECT_EQ(context_id_pair.second,
|
||||
absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
|
||||
// Repeat same call to verify derivied result is repeatable.
|
||||
EXPECT_EQ(context_id_pair.second,
|
||||
absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) {
|
||||
uint32_t cc_code;
|
||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code));
|
||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code));
|
||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromString) {
|
||||
uint32_t cc_code = 0;
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code));
|
||||
ASSERT_EQ(kCENCSchemeID, cc_code);
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBC1Str, &cc_code));
|
||||
ASSERT_EQ(kCBC1SchemeID, cc_code);
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENSStr, &cc_code));
|
||||
ASSERT_EQ(kCENSSchemeID, cc_code);
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBCSStr, &cc_code));
|
||||
ASSERT_EQ(kCBCSSchemeID, cc_code);
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
320
common/drm_root_certificate.cc
Normal file
320
common/drm_root_certificate.cc
Normal file
@@ -0,0 +1,320 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/drm_root_certificate.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// From common::TestCertificates.
|
||||
// TODO(user): common::test_certificates is a testonly target, consider
|
||||
// how to use instead of dupliciating the test cert here.
|
||||
static const unsigned char kTestRootCertificate[] = {
|
||||
0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
|
||||
0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5,
|
||||
0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78,
|
||||
0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f,
|
||||
0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca,
|
||||
0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b,
|
||||
0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46,
|
||||
0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01,
|
||||
0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c,
|
||||
0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14,
|
||||
0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd,
|
||||
0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6,
|
||||
0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde,
|
||||
0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31,
|
||||
0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a,
|
||||
0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90,
|
||||
0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49,
|
||||
0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f,
|
||||
0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda,
|
||||
0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17,
|
||||
0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30,
|
||||
0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf,
|
||||
0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16,
|
||||
0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42,
|
||||
0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9,
|
||||
0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4,
|
||||
0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a,
|
||||
0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72,
|
||||
0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde,
|
||||
0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04,
|
||||
0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b,
|
||||
0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33,
|
||||
0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67,
|
||||
0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02,
|
||||
0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a,
|
||||
0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35,
|
||||
0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe,
|
||||
0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49,
|
||||
0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6,
|
||||
0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58,
|
||||
0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce,
|
||||
0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27,
|
||||
0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5,
|
||||
0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c,
|
||||
0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e,
|
||||
0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12,
|
||||
0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54,
|
||||
0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8,
|
||||
0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1,
|
||||
0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1,
|
||||
0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8,
|
||||
0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4,
|
||||
0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9,
|
||||
0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd,
|
||||
0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9,
|
||||
0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f,
|
||||
0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92,
|
||||
0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15,
|
||||
0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0,
|
||||
0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9,
|
||||
0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1,
|
||||
0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2,
|
||||
0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c,
|
||||
0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36,
|
||||
0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35,
|
||||
0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7,
|
||||
0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44};
|
||||
|
||||
static const unsigned char kDevRootCertificate[] = {
|
||||
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xc3, 0x94, 0x88,
|
||||
0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
|
||||
0x81, 0x00, 0xc0, 0x00, 0x36, 0x6f, 0x8e, 0xe9, 0xcf, 0x86, 0xdb, 0xcd,
|
||||
0xdd, 0x4e, 0xfd, 0xcd, 0x45, 0xbf, 0x6d, 0x96, 0x05, 0x00, 0xb8, 0x72,
|
||||
0xff, 0x9c, 0xb4, 0x39, 0xa8, 0xd8, 0xc0, 0x09, 0x73, 0xc0, 0x24, 0x6a,
|
||||
0x39, 0x4d, 0x36, 0x3f, 0x9a, 0xe4, 0xb8, 0x76, 0xdc, 0x34, 0xe3, 0xee,
|
||||
0x5f, 0xdd, 0x13, 0x20, 0x08, 0xdc, 0x4e, 0x6f, 0x4e, 0x9f, 0xc0, 0x36,
|
||||
0xf9, 0xce, 0xc6, 0xb7, 0xdb, 0xe0, 0x51, 0x2d, 0x30, 0x0b, 0xae, 0x0a,
|
||||
0x20, 0xd2, 0x29, 0x3c, 0x2c, 0x1d, 0x87, 0x65, 0xeb, 0x5f, 0x93, 0xd7,
|
||||
0x3f, 0x12, 0x08, 0x50, 0x0e, 0x55, 0xf3, 0xf1, 0x19, 0xee, 0x18, 0x21,
|
||||
0x6e, 0xea, 0xb6, 0x0a, 0x4a, 0x0b, 0x9c, 0x72, 0x37, 0xeb, 0x0b, 0x68,
|
||||
0xfc, 0x52, 0x46, 0x62, 0xd0, 0xa2, 0x99, 0x66, 0xe2, 0x2b, 0x74, 0xdd,
|
||||
0x5c, 0xaf, 0x9a, 0x03, 0xc4, 0x5d, 0x93, 0xfb, 0xcd, 0x45, 0x9a, 0xee,
|
||||
0xfb, 0x7b, 0x18, 0x94, 0xc1, 0x8c, 0x82, 0x34, 0x7f, 0x02, 0x12, 0x21,
|
||||
0xfc, 0x40, 0xc1, 0x50, 0xc9, 0xf4, 0x7c, 0xd5, 0x96, 0xbe, 0x55, 0x7f,
|
||||
0x3c, 0x1d, 0x70, 0x34, 0xb4, 0xa2, 0x03, 0xc4, 0x3f, 0x89, 0x60, 0xe4,
|
||||
0x24, 0x09, 0x1a, 0x74, 0xc4, 0xb6, 0x39, 0xf0, 0x34, 0x60, 0x8e, 0xa7,
|
||||
0x5f, 0x02, 0x7f, 0xb9, 0x2a, 0xc5, 0xaa, 0xb2, 0x4c, 0x34, 0xd3, 0x5a,
|
||||
0x5d, 0xfa, 0x07, 0xf2, 0xb9, 0xb3, 0xc1, 0xba, 0xab, 0xbe, 0x89, 0x99,
|
||||
0xe3, 0x6d, 0x9b, 0xa9, 0xd3, 0xaf, 0x2a, 0x08, 0x76, 0xf3, 0x0e, 0xc9,
|
||||
0xe0, 0xb3, 0xbf, 0x51, 0x0c, 0xc5, 0xf4, 0xf3, 0x15, 0x7b, 0x08, 0x11,
|
||||
0x8f, 0x61, 0x1f, 0x61, 0x64, 0xdb, 0x15, 0x84, 0x5b, 0x8a, 0xd1, 0x28,
|
||||
0x40, 0xde, 0xc5, 0x32, 0xb5, 0xad, 0xad, 0x65, 0x4c, 0xf5, 0xf7, 0xd1,
|
||||
0x90, 0x14, 0x5d, 0xc2, 0x85, 0x98, 0xcc, 0xe9, 0xe6, 0x95, 0x42, 0xe1,
|
||||
0x3e, 0xfc, 0x7f, 0xc4, 0x49, 0xed, 0x9c, 0xe4, 0x49, 0x3f, 0x03, 0x1b,
|
||||
0x0d, 0xa0, 0xfb, 0xf5, 0x38, 0x49, 0xd2, 0xdf, 0xa3, 0x88, 0xb2, 0x76,
|
||||
0x93, 0x08, 0x20, 0x18, 0xfe, 0xdc, 0x72, 0x6c, 0x6e, 0xbf, 0x61, 0x37,
|
||||
0x03, 0xdb, 0xe5, 0x72, 0x68, 0xe0, 0x99, 0x2f, 0xb9, 0xe0, 0x2e, 0xbb,
|
||||
0x9f, 0x96, 0x36, 0x61, 0xaa, 0x2d, 0xa4, 0x93, 0xe8, 0x50, 0x58, 0xe6,
|
||||
0x61, 0xe1, 0x14, 0xcf, 0xac, 0x86, 0x98, 0x7f, 0x3c, 0x67, 0x16, 0xce,
|
||||
0xb8, 0x70, 0x90, 0x3a, 0x5a, 0xd4, 0xe1, 0xe2, 0x35, 0x98, 0xbf, 0x93,
|
||||
0x41, 0x11, 0xb2, 0x44, 0xb2, 0x64, 0xc2, 0xe7, 0x09, 0x45, 0xb7, 0x6f,
|
||||
0xb0, 0xbd, 0x6e, 0xe8, 0x67, 0xfa, 0x8d, 0xd4, 0xfa, 0x4b, 0xef, 0xa8,
|
||||
0x9d, 0x8a, 0x0a, 0xd9, 0x14, 0x77, 0x09, 0x11, 0x9e, 0xc3, 0x50, 0x14,
|
||||
0x6c, 0x45, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x17, 0x01,
|
||||
0x60, 0x24, 0xe1, 0xfd, 0x75, 0x60, 0x17, 0x5c, 0x5e, 0x6f, 0x9f, 0x7f,
|
||||
0xdf, 0xee, 0xf0, 0xf7, 0x7d, 0xb2, 0x50, 0x65, 0x36, 0x26, 0x14, 0x19,
|
||||
0x01, 0x5e, 0x98, 0x94, 0x65, 0x97, 0x83, 0xaa, 0x4a, 0x2b, 0x98, 0x2e,
|
||||
0x02, 0xf3, 0xb2, 0xc9, 0xb2, 0xed, 0xd3, 0x1b, 0x20, 0x27, 0x9e, 0xe1,
|
||||
0x25, 0xc7, 0x86, 0xf0, 0x66, 0x68, 0x5d, 0xd2, 0x3d, 0xa7, 0xbb, 0xbc,
|
||||
0x22, 0xfc, 0x29, 0xfa, 0x17, 0x16, 0xf4, 0xa2, 0x00, 0x10, 0x87, 0xb4,
|
||||
0x5d, 0x51, 0x45, 0x6b, 0xc8, 0xf4, 0x6b, 0xcc, 0x92, 0x91, 0xe7, 0xa7,
|
||||
0x93, 0xbc, 0xc7, 0x2e, 0xdc, 0xac, 0x82, 0x2b, 0x85, 0x56, 0x7b, 0xae,
|
||||
0xf2, 0xd8, 0xda, 0xa6, 0xd7, 0xfa, 0x6d, 0x70, 0x2a, 0x2e, 0xcf, 0x69,
|
||||
0xef, 0x57, 0x91, 0xa7, 0xaa, 0x40, 0x15, 0x4a, 0x49, 0x1b, 0xbc, 0x36,
|
||||
0xbb, 0x1c, 0x94, 0x33, 0x36, 0x61, 0x22, 0x9d, 0x22, 0x66, 0xf0, 0x88,
|
||||
0x5e, 0x7c, 0x3c, 0xa5, 0xff, 0x81, 0xcf, 0x1a, 0x44, 0xa1, 0x2b, 0xdf,
|
||||
0xc9, 0x3d, 0xd5, 0xc7, 0xc7, 0x3a, 0x75, 0xac, 0x29, 0xfa, 0xfd, 0x5b,
|
||||
0xda, 0xf5, 0x8f, 0xd9, 0xdf, 0x08, 0xa4, 0x8d, 0x19, 0x4a, 0xa4, 0x79,
|
||||
0x6e, 0x47, 0xf6, 0x07, 0xe0, 0xbd, 0xbf, 0x30, 0x3a, 0xf9, 0xf5, 0xc0,
|
||||
0x90, 0x6d, 0x70, 0x27, 0x44, 0xa8, 0x5e, 0x70, 0xcd, 0x43, 0x3e, 0xaf,
|
||||
0xf0, 0xd7, 0x20, 0xd3, 0x5e, 0x97, 0x2d, 0x32, 0x1a, 0x3d, 0x2d, 0x0f,
|
||||
0x0f, 0xcf, 0xac, 0x4e, 0x88, 0x75, 0x98, 0x6c, 0xfa, 0xe8, 0x42, 0x58,
|
||||
0x99, 0xaa, 0x45, 0x0c, 0x41, 0x0c, 0x6e, 0x27, 0x58, 0x57, 0xd2, 0x5b,
|
||||
0x82, 0x3d, 0x75, 0x2f, 0x9e, 0xf3, 0xe4, 0x00, 0xcf, 0x91, 0x48, 0x25,
|
||||
0xca, 0x98, 0xf2, 0x91, 0x6b, 0x41, 0xa5, 0xe8, 0xcd, 0x64, 0xa7, 0x2e,
|
||||
0x78, 0xc7, 0x76, 0x82, 0x3f, 0xf8, 0x57, 0x8a, 0x9d, 0x78, 0x25, 0xad,
|
||||
0xf3, 0x1a, 0x8b, 0xfc, 0x83, 0x9a, 0x98, 0x87, 0xe4, 0x55, 0x3e, 0x1c,
|
||||
0xa7, 0x80, 0x8f, 0xd6, 0x76, 0xab, 0x03, 0xc7, 0x05, 0x66, 0xc3, 0xa0,
|
||||
0x4c, 0x33, 0x1f, 0x39, 0x74, 0x1b, 0x2a, 0xbf, 0xe6, 0xb0, 0x9f, 0x6b,
|
||||
0xc1, 0xd6, 0xd3, 0xf4, 0x46, 0x9b, 0xf3, 0xab, 0xca, 0x2e, 0x88, 0x3d,
|
||||
0x84, 0x5f, 0xc9, 0x9b, 0x47, 0xbb, 0x57, 0x64, 0x08, 0x0e, 0x18, 0x74,
|
||||
0x83, 0x44, 0xd4, 0xc3, 0x18, 0x97, 0xcf, 0x89, 0x6a, 0x49, 0x51, 0xc6,
|
||||
0xff, 0x8d, 0x39, 0xc5, 0x23, 0xf9, 0xd5, 0x01, 0xd7, 0x2f, 0xa9, 0xa5,
|
||||
0x5d, 0xa9, 0xf3, 0xc9, 0xfd, 0xc4, 0x52, 0x19, 0x7d, 0xf6, 0xa4, 0x2c,
|
||||
0x0c, 0xa0, 0x07, 0xdf, 0x7b, 0x44, 0xd7, 0xe5, 0xbf, 0x57, 0x87, 0xc9,
|
||||
0x8c, 0xfe, 0x30, 0xb2, 0x89, 0x5d, 0x00, 0x03, 0x3b, 0xe5};
|
||||
|
||||
static const unsigned char kProdRootCertificate[] = {
|
||||
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xdd, 0x94, 0x88,
|
||||
0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
|
||||
0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11,
|
||||
0x97, 0x09, 0xe8, 0x68, 0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d,
|
||||
0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0,
|
||||
0x57, 0x03, 0x53, 0x4c, 0xf6, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3,
|
||||
0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe,
|
||||
0x66, 0xdf, 0xc5, 0x21, 0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46,
|
||||
0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22,
|
||||
0xbe, 0x34, 0x23, 0x8b, 0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48,
|
||||
0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f,
|
||||
0x0f, 0x92, 0xd6, 0x4c, 0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10,
|
||||
0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53,
|
||||
0x9a, 0x97, 0xeb, 0x84, 0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26,
|
||||
0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e,
|
||||
0x72, 0xa0, 0xfa, 0xc1, 0xbd, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4,
|
||||
0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc,
|
||||
0xfd, 0x82, 0x48, 0x2b, 0xb7, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba,
|
||||
0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e,
|
||||
0x65, 0xf5, 0xc8, 0x6c, 0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca,
|
||||
0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98,
|
||||
0x79, 0x52, 0x5e, 0x45, 0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba,
|
||||
0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa,
|
||||
0x4f, 0x5e, 0x48, 0x77, 0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19,
|
||||
0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf,
|
||||
0xe4, 0x29, 0x06, 0x5e, 0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed,
|
||||
0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35,
|
||||
0xec, 0x1f, 0x11, 0xb3, 0x24, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c,
|
||||
0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d,
|
||||
0x68, 0x15, 0x84, 0xff, 0xa5, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4,
|
||||
0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12,
|
||||
0xd2, 0x88, 0x6d, 0x41, 0x3d, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4,
|
||||
0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5,
|
||||
0xce, 0xa6, 0x96, 0x55, 0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33,
|
||||
0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x58, 0xf1,
|
||||
0xd6, 0x4d, 0x04, 0x09, 0x7b, 0xdf, 0xd7, 0xef, 0x5d, 0x3b, 0x02, 0x39,
|
||||
0x17, 0xfa, 0x14, 0x36, 0x75, 0x4a, 0x38, 0x67, 0x85, 0x57, 0x12, 0xa7,
|
||||
0x14, 0xee, 0x35, 0x16, 0xd5, 0x3d, 0xbf, 0x42, 0x86, 0xf6, 0x69, 0x00,
|
||||
0x76, 0xcd, 0x93, 0xf4, 0x7c, 0xb2, 0xdf, 0x9e, 0x44, 0xcd, 0x4c, 0xd4,
|
||||
0xae, 0x09, 0x18, 0x53, 0x44, 0x32, 0xec, 0xe0, 0x61, 0x1b, 0xe5, 0xda,
|
||||
0x13, 0xd3, 0x55, 0xc5, 0xdd, 0x1a, 0xcb, 0x90, 0x1e, 0x7e, 0x5b, 0xc6,
|
||||
0xe9, 0x0f, 0x22, 0x9f, 0xbe, 0x85, 0x02, 0xfe, 0x90, 0x31, 0xcc, 0x6b,
|
||||
0x03, 0x84, 0xbd, 0x22, 0xc4, 0x55, 0xfa, 0xf5, 0xf2, 0x08, 0xcd, 0x65,
|
||||
0x41, 0x58, 0xe8, 0x7d, 0x29, 0xda, 0x04, 0x58, 0x82, 0xf5, 0x37, 0x69,
|
||||
0xbc, 0xf3, 0x5a, 0x57, 0x84, 0x17, 0x7b, 0x32, 0x87, 0x70, 0xb2, 0xb0,
|
||||
0x76, 0x9c, 0xb2, 0xc3, 0x15, 0xd1, 0x11, 0x26, 0x2a, 0x23, 0x75, 0x99,
|
||||
0x3e, 0xb9, 0x77, 0x22, 0x32, 0x0d, 0xbc, 0x1a, 0x19, 0xc1, 0xd5, 0x65,
|
||||
0x90, 0x76, 0x55, 0x74, 0x0f, 0x0e, 0x69, 0x4d, 0x5f, 0x4d, 0x8f, 0x19,
|
||||
0xaf, 0xdf, 0xd6, 0x16, 0x31, 0x94, 0xa8, 0x92, 0x5f, 0x4f, 0xbc, 0x7a,
|
||||
0x31, 0xf8, 0xae, 0x8e, 0xad, 0x33, 0xb7, 0xe9, 0x30, 0xd0, 0x8c, 0x0a,
|
||||
0x8a, 0x6c, 0x83, 0x35, 0xf8, 0x8a, 0x81, 0xb2, 0xfe, 0x1c, 0x88, 0xac,
|
||||
0x2a, 0x66, 0xc5, 0xff, 0xbd, 0xe6, 0x17, 0xd0, 0x62, 0x0b, 0xdc, 0x8a,
|
||||
0x45, 0xf7, 0xb0, 0x3e, 0x5a, 0xc8, 0x1e, 0x4a, 0x24, 0x2f, 0x6c, 0xa5,
|
||||
0xe3, 0x1c, 0x88, 0x14, 0x83, 0xd5, 0xc5, 0xef, 0x5e, 0x9f, 0x3d, 0x85,
|
||||
0x45, 0x73, 0xe2, 0x6b, 0x50, 0x52, 0x57, 0x4c, 0xfb, 0x92, 0x6c, 0x66,
|
||||
0x75, 0x8a, 0xd6, 0x0d, 0x1b, 0xae, 0xf3, 0xec, 0xaf, 0x51, 0x22, 0x03,
|
||||
0x5d, 0x0a, 0x2e, 0x63, 0x93, 0x9c, 0x0b, 0x01, 0x20, 0xa8, 0xa9, 0x84,
|
||||
0x2e, 0x17, 0xca, 0xae, 0x73, 0xec, 0x22, 0x1b, 0x79, 0xae, 0xf6, 0xa0,
|
||||
0x72, 0x2c, 0xdf, 0x07, 0x47, 0xdb, 0x88, 0x86, 0x30, 0x14, 0x78, 0x21,
|
||||
0x11, 0x22, 0x88, 0xac, 0xd7, 0x54, 0x74, 0xf9, 0xf3, 0x26, 0xc2, 0xa5,
|
||||
0x56, 0xc8, 0x56, 0x4f, 0x00, 0x29, 0x1d, 0x08, 0x7b, 0x7a, 0xfb, 0x95,
|
||||
0x89, 0xc3, 0xee, 0x98, 0x54, 0x9e, 0x3c, 0x6b, 0x94, 0x05, 0x13, 0x12,
|
||||
0xf6, 0x71, 0xb9, 0xab, 0x13, 0xc3, 0x0c, 0x9b, 0x46, 0x08, 0x7b, 0x3d,
|
||||
0x32, 0x6a, 0x68, 0xca, 0x1e, 0x9c, 0x90, 0x62, 0xc5, 0xed, 0x10, 0xb9,
|
||||
0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd, 0xc4, 0x46, 0xf5, 0xa3,
|
||||
0x62, 0x13, 0x74, 0x02, 0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf,
|
||||
0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5, 0x02, 0x0c};
|
||||
|
||||
util::Status DrmRootCertificate::Create(
|
||||
const std::string& signed_drm_certificate,
|
||||
std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
CHECK(cert);
|
||||
|
||||
SignedDrmCertificate signed_root_cert;
|
||||
if (!signed_root_cert.ParseFromString(signed_drm_certificate)) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signed-root-cert-deserialize-fail");
|
||||
}
|
||||
DrmCertificate root_cert;
|
||||
if (!signed_root_cert.has_drm_certificate()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-device-certificate");
|
||||
}
|
||||
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"root-cert-deserialize-fail");
|
||||
}
|
||||
if (!root_cert.has_public_key()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-cert-public-key");
|
||||
}
|
||||
if (!signed_root_cert.has_signature()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-certificate-signature");
|
||||
}
|
||||
std::unique_ptr<RsaPublicKey> public_key(
|
||||
RsaPublicKey::Create(root_cert.public_key()));
|
||||
if (!public_key) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-public-key");
|
||||
}
|
||||
if (!public_key->VerifySignature(signed_root_cert.drm_certificate(),
|
||||
signed_root_cert.signature())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-certificate-signature");
|
||||
}
|
||||
cert->reset(new DrmRootCertificate(root_cert.public_key()));
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status DrmRootCertificate::CreateByType(
|
||||
CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
CHECK(cert);
|
||||
return Create(GetDrmRootCertificate(cert_type), cert);
|
||||
}
|
||||
|
||||
std::string DrmRootCertificate::GetDrmRootCertificate(CertificateType cert_type) {
|
||||
std::string root_cert;
|
||||
switch (cert_type) {
|
||||
case kCertificateTypeProduction: {
|
||||
root_cert.assign(kProdRootCertificate,
|
||||
kProdRootCertificate + sizeof(kProdRootCertificate));
|
||||
break;
|
||||
}
|
||||
case kCertificateTypeDevelopment: {
|
||||
root_cert.assign(kDevRootCertificate,
|
||||
kDevRootCertificate + sizeof(kDevRootCertificate));
|
||||
break;
|
||||
}
|
||||
case kCertificateTypeTesting: {
|
||||
root_cert.assign(kTestRootCertificate,
|
||||
kTestRootCertificate + sizeof(kTestRootCertificate));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO(user): Consider returning util::Status indicating unsupported
|
||||
// cert type.
|
||||
break;
|
||||
}
|
||||
return root_cert;
|
||||
}
|
||||
|
||||
std::string DrmRootCertificate::GetDigest(CertificateType cert_type) {
|
||||
std::string cert(GetDrmRootCertificate(cert_type));
|
||||
if (cert.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
std::string hash(SHA256_DIGEST_LENGTH, 0);
|
||||
SHA256(reinterpret_cast<const unsigned char*>(cert.data()), cert.size(),
|
||||
reinterpret_cast<unsigned char*>(&hash[0]));
|
||||
return absl::BytesToHexString(hash);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
66
common/drm_root_certificate.h
Normal file
66
common/drm_root_certificate.h
Normal file
@@ -0,0 +1,66 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Root device certificate holder class which deserializes, validates,
|
||||
// and extracts the root certificate public key.
|
||||
|
||||
#ifndef COMMON_DRM_ROOT_CERTIFICATE_H_
|
||||
#define COMMON_DRM_ROOT_CERTIFICATE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "util/status.h"
|
||||
|
||||
#include "common/certificate_type.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class DrmRootCertificate {
|
||||
public:
|
||||
virtual ~DrmRootCertificate() {}
|
||||
// Creates a DrmRootCertificate object given a certificate type.
|
||||
// |cert| may not be nullptr, and it points to a
|
||||
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
|
||||
// created DrmRootCertificate* if successful. The caller assumes ownership of
|
||||
// the new DrmRootCertificate. This method returns util::Status::OK on
|
||||
// success, or appropriate error status otherwise.
|
||||
static util::Status CreateByType(CertificateType cert_type,
|
||||
std::unique_ptr<DrmRootCertificate>* cert);
|
||||
// Returns the hex-encoded SHA-256 digest for the specified root certificate.
|
||||
static std::string GetDigest(CertificateType cert_type);
|
||||
// Given |cert_type|, the appropiate root certificate is returned as
|
||||
// a serialized SignedDrmCertificates.
|
||||
static std::string GetDrmRootCertificate(CertificateType cert_type);
|
||||
const std::string& public_key() const { return public_key_; }
|
||||
|
||||
// Verifies a DRM certificate.
|
||||
private:
|
||||
friend class DrmRootCertificateTest;
|
||||
|
||||
// Creates a DrmRootCertificate object given a serialized
|
||||
// SignedDrmCertificate. |cert| may not be nullptr, and it points to a
|
||||
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
|
||||
// created DrmRootCertificate* if successful. The caller assumes ownership of
|
||||
// the new DrmRootCertificate. This method returns util::Status::OK on
|
||||
// success, or appropriate error status otherwise.
|
||||
// TODO(user): Consider moving to private.
|
||||
static util::Status Create(const std::string& signed_drm_certificate,
|
||||
std::unique_ptr<DrmRootCertificate>* cert);
|
||||
explicit DrmRootCertificate(const std::string& public_key)
|
||||
: public_key_(public_key) {}
|
||||
|
||||
std::string public_key_;
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmRootCertificate);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_DRM_ROOT_CERTIFICATE_H_
|
||||
100
common/drm_root_certificate_test.cc
Normal file
100
common/drm_root_certificate_test.cc
Normal file
@@ -0,0 +1,100 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Unit tests for drm_root_certificate.cc
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class DrmRootCertificateTest : public testing::Test {
|
||||
protected:
|
||||
DrmRootCertificateTest() {}
|
||||
util::Status DrmRootCertificateCreate(
|
||||
const std::string& signed_drm_certificate,
|
||||
std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
return DrmRootCertificate::Create(signed_drm_certificate, cert);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DrmRootCertificateTest, DrmRootCertificateCreation) {
|
||||
RsaTestKeys test_keys;
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
|
||||
// First, invalid serialized cert. Should fail.
|
||||
EXPECT_EQ(INVALID_DRM_CERTIFICATE,
|
||||
DrmRootCertificateCreate("bad_cert", &root_cert).error_code());
|
||||
SignedDrmCertificate signed_cert;
|
||||
std::string serialized;
|
||||
// Serialized empty cert. Should fail.
|
||||
ASSERT_TRUE(signed_cert.SerializeToString(&serialized));
|
||||
EXPECT_NE(util::OkStatus(),
|
||||
DrmRootCertificateCreate(serialized, &root_cert));
|
||||
// Add public key. Should still fail.
|
||||
DrmCertificate drm_cert;
|
||||
drm_cert.set_public_key(test_keys.public_test_key_1_3072_bits());
|
||||
ASSERT_TRUE(
|
||||
drm_cert.SerializeToString(signed_cert.mutable_drm_certificate()));
|
||||
ASSERT_TRUE(signed_cert.SerializeToString(&serialized));
|
||||
EXPECT_EQ(INVALID_DRM_CERTIFICATE,
|
||||
DrmRootCertificateCreate(serialized, &root_cert).error_code());
|
||||
// Now self-sign the cert. Should succeed.
|
||||
std::unique_ptr<RsaPrivateKey> private_key(
|
||||
RsaPrivateKey::Create(test_keys.private_test_key_1_3072_bits()));
|
||||
ASSERT_TRUE(private_key.get());
|
||||
ASSERT_TRUE(private_key->GenerateSignature(signed_cert.drm_certificate(),
|
||||
signed_cert.mutable_signature()));
|
||||
ASSERT_TRUE(signed_cert.SerializeToString(&serialized));
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
DrmRootCertificateCreate(serialized, &root_cert));
|
||||
ASSERT_TRUE(root_cert);
|
||||
// Verify the public key.
|
||||
EXPECT_EQ(test_keys.public_test_key_1_3072_bits(), root_cert->public_key());
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, DrmRootCertificateCreationByType) {
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeTesting, &root_cert));
|
||||
ASSERT_TRUE(root_cert != nullptr);
|
||||
EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeDevelopment, &root_cert));
|
||||
ASSERT_TRUE(root_cert != nullptr);
|
||||
EXPECT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeProduction, &root_cert));
|
||||
ASSERT_TRUE(root_cert != nullptr);
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, DrmRootCertificateDigest) {
|
||||
const std::string test_cert_hash(
|
||||
"49f917b1bdfed78002a58e799a58e940"
|
||||
"1fffaaed9d8d80752782b066757e2c8c");
|
||||
const std::string dev_cert_hash(
|
||||
"0e25ee95476a770f30b98ac5ef778b3f"
|
||||
"137b66c29385b84f547a361b4724b17d");
|
||||
const std::string prod_cert_hash(
|
||||
"d62fdabc9286648a81f7d3bedaf2f5a5"
|
||||
"27bbad39bc38da034ba98a21569adb9b");
|
||||
EXPECT_EQ(test_cert_hash,
|
||||
DrmRootCertificate::GetDigest(kCertificateTypeTesting));
|
||||
EXPECT_EQ(dev_cert_hash,
|
||||
DrmRootCertificate::GetDigest(kCertificateTypeDevelopment));
|
||||
EXPECT_EQ(prod_cert_hash,
|
||||
DrmRootCertificate::GetDigest(kCertificateTypeProduction));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
341
common/drm_service_certificate.cc
Normal file
341
common/drm_service_certificate.cc
Normal file
@@ -0,0 +1,341 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/drm_service_certificate.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "base/thread_annotations.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_util.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
|
||||
// Class used to hold global service certificate map.
|
||||
class DrmServiceCertificateMap {
|
||||
public:
|
||||
DrmServiceCertificateMap();
|
||||
~DrmServiceCertificateMap();
|
||||
|
||||
DrmServiceCertificateMap(const DrmServiceCertificateMap&) = delete;
|
||||
DrmServiceCertificateMap& operator=(const DrmServiceCertificateMap&) = delete;
|
||||
|
||||
void Reset();
|
||||
void AddCert(std::unique_ptr<DrmServiceCertificate> new_cert);
|
||||
void ClearDefaultDrmServiceCertificate();
|
||||
const DrmServiceCertificate* GetDefaultCert();
|
||||
const DrmServiceCertificate* GetCert(const std::string& serial_number);
|
||||
|
||||
static DrmServiceCertificateMap* GetInstance();
|
||||
|
||||
private:
|
||||
absl::Mutex mutex_;
|
||||
// Certificate serial number to certificate map.
|
||||
std::map<std::string, std::unique_ptr<DrmServiceCertificate>> map_
|
||||
GUARDED_BY(mutex_);
|
||||
DrmServiceCertificate* default_cert_ GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
DrmServiceCertificateMap::DrmServiceCertificateMap() : default_cert_(nullptr) {}
|
||||
DrmServiceCertificateMap::~DrmServiceCertificateMap() { Reset(); }
|
||||
|
||||
void DrmServiceCertificateMap::Reset() {
|
||||
absl::WriterMutexLock lock(&mutex_);
|
||||
map_.clear();
|
||||
default_cert_ = nullptr;
|
||||
}
|
||||
|
||||
void DrmServiceCertificateMap::AddCert(
|
||||
std::unique_ptr<DrmServiceCertificate> new_cert) {
|
||||
absl::WriterMutexLock lock(&mutex_);
|
||||
|
||||
std::unique_ptr<DrmServiceCertificate>* previous_cert =
|
||||
gtl::FindOrNull(map_, new_cert->serial_number());
|
||||
if (previous_cert != nullptr) {
|
||||
if (default_cert_ == previous_cert->get()) {
|
||||
default_cert_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (default_cert_ == nullptr) {
|
||||
default_cert_ = new_cert.get();
|
||||
}
|
||||
const std::string& serial_number = new_cert->serial_number();
|
||||
map_[serial_number] = std::move(new_cert);
|
||||
}
|
||||
|
||||
void DrmServiceCertificateMap::ClearDefaultDrmServiceCertificate() {
|
||||
absl::WriterMutexLock lock(&mutex_);
|
||||
default_cert_ = nullptr;
|
||||
}
|
||||
|
||||
const DrmServiceCertificate* DrmServiceCertificateMap::GetDefaultCert() {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
return default_cert_;
|
||||
}
|
||||
|
||||
const DrmServiceCertificate* DrmServiceCertificateMap::GetCert(
|
||||
const std::string& serial_number) {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
return map_[serial_number].get();
|
||||
}
|
||||
|
||||
DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
|
||||
static auto* const kInstance = new DrmServiceCertificateMap();
|
||||
return kInstance;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
util::Status DrmServiceCertificate::AddDrmServiceCertificate(
|
||||
const std::string& root_public_key, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase) {
|
||||
std::unique_ptr<RsaPublicKey> root_key(RsaPublicKey::Create(root_public_key));
|
||||
if (root_key == nullptr) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"root-certificate-rsa-public-key-failed");
|
||||
}
|
||||
SignedDrmCertificate signed_cert;
|
||||
if (!signed_cert.ParseFromString(service_certificate)) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"signed-certificate-parse-failed");
|
||||
}
|
||||
if (!root_key->VerifySignature(signed_cert.drm_certificate(),
|
||||
signed_cert.signature())) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"certificate-signature-verification-failed");
|
||||
}
|
||||
DrmCertificate drm_cert;
|
||||
if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"certificate-parse-failed");
|
||||
}
|
||||
if (drm_cert.type() != DrmCertificate::SERVICE) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"not-service-certificate");
|
||||
}
|
||||
if (drm_cert.serial_number().empty()) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"missing-certificate-serial-number");
|
||||
}
|
||||
if (drm_cert.provider_id().empty()) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"missing-certificate-service-id");
|
||||
}
|
||||
if (!drm_cert.has_creation_time_seconds()) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"missing-certificate-creation-time");
|
||||
}
|
||||
if (drm_cert.public_key().empty()) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"missing-certificate-public-key");
|
||||
}
|
||||
std::unique_ptr<RsaPublicKey> public_key(
|
||||
RsaPublicKey::Create(drm_cert.public_key()));
|
||||
if (!public_key) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"invalid-certificate-public-key");
|
||||
}
|
||||
std::string pkcs1_key;
|
||||
if (!rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey(
|
||||
service_private_key, service_private_key_passphrase, &pkcs1_key)) {
|
||||
return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
||||
"key-decryption-failed");
|
||||
}
|
||||
std::unique_ptr<RsaPrivateKey> private_key(RsaPrivateKey::Create(pkcs1_key));
|
||||
if (private_key == nullptr) {
|
||||
return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
||||
"invalid-private-key");
|
||||
}
|
||||
|
||||
std::unique_ptr<DrmServiceCertificate> new_cert(new DrmServiceCertificate(
|
||||
service_certificate, drm_cert.provider_id(), drm_cert.serial_number(),
|
||||
drm_cert.creation_time_seconds(), std::move(public_key),
|
||||
std::move(private_key)));
|
||||
DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert));
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status DrmServiceCertificate::AddDrmServiceCertificate(
|
||||
CertificateType root_cert_type, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase) {
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
util::Status status =
|
||||
DrmRootCertificate::CreateByType(root_cert_type, &root_cert);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
return AddDrmServiceCertificate(root_cert->public_key(), service_certificate,
|
||||
service_private_key,
|
||||
service_private_key_passphrase);
|
||||
}
|
||||
|
||||
const DrmServiceCertificate*
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificate() {
|
||||
return DrmServiceCertificateMap::GetInstance()->GetDefaultCert();
|
||||
}
|
||||
|
||||
const DrmServiceCertificate*
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() {
|
||||
const DrmServiceCertificate* default_cert =
|
||||
DrmServiceCertificateMap::GetInstance()->GetDefaultCert();
|
||||
CHECK(default_cert) << "Service Certificate not set!";
|
||||
return default_cert;
|
||||
}
|
||||
|
||||
const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate(
|
||||
const std::string& serial_number) {
|
||||
return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number);
|
||||
}
|
||||
|
||||
util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||
const std::string& root_public_key, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase) {
|
||||
DrmServiceCertificateMap::GetInstance()->ClearDefaultDrmServiceCertificate();
|
||||
return AddDrmServiceCertificate(root_public_key, service_certificate,
|
||||
service_private_key,
|
||||
service_private_key_passphrase);
|
||||
}
|
||||
|
||||
util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||
CertificateType root_cert_type, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase) {
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
util::Status status =
|
||||
DrmRootCertificate::CreateByType(root_cert_type, &root_cert);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
return SetDefaultDrmServiceCertificate(
|
||||
root_cert->public_key(), service_certificate, service_private_key,
|
||||
service_private_key_passphrase);
|
||||
}
|
||||
|
||||
util::Status DrmServiceCertificate::DecryptClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id) {
|
||||
DCHECK(client_id);
|
||||
if (encrypted_client_id.service_certificate_serial_number().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-service-certificate-serial-number");
|
||||
}
|
||||
if (encrypted_client_id.provider_id().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-service-id");
|
||||
}
|
||||
if (encrypted_client_id.encrypted_client_id().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id");
|
||||
}
|
||||
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id-iv");
|
||||
}
|
||||
if (encrypted_client_id.encrypted_privacy_key().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-privacy-key");
|
||||
}
|
||||
std::string privacy_key;
|
||||
std::string provider_id;
|
||||
const DrmServiceCertificate* cert = GetDrmServiceCertificate(
|
||||
encrypted_client_id.service_certificate_serial_number());
|
||||
if (!cert) {
|
||||
return util::Status(
|
||||
error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
"service-certificate-not-found (SN " +
|
||||
absl::BytesToHexString(
|
||||
encrypted_client_id.service_certificate_serial_number()) +
|
||||
")");
|
||||
}
|
||||
if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(),
|
||||
&privacy_key)) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"privacy-key-decryption-failed");
|
||||
}
|
||||
if (cert->provider_id() != encrypted_client_id.provider_id()) {
|
||||
return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
std::string("provider-id-mismatch (") + cert->provider_id() +
|
||||
" / " + encrypted_client_id.provider_id() + ")");
|
||||
}
|
||||
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
||||
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
||||
encrypted_client_id.encrypted_client_id()));
|
||||
if (serialized_client_id.empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-decryption-failed");
|
||||
}
|
||||
if (!client_id->ParseFromString(serialized_client_id)) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-parse-failed");
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
void DrmServiceCertificate::ResetServiceCertificates() {
|
||||
DrmServiceCertificateMap::GetInstance()->Reset();
|
||||
}
|
||||
|
||||
util::Status DrmServiceCertificate::ValidateDrmServiceCertificate() {
|
||||
const DrmServiceCertificate* service_certificate =
|
||||
GetDefaultDrmServiceCertificate();
|
||||
if (!service_certificate) {
|
||||
return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
"drm service certificate is not found.");
|
||||
}
|
||||
SignedDrmCertificate signed_cert;
|
||||
if (!signed_cert.ParseFromString(service_certificate->certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signed drm service certificate is failed to parse.");
|
||||
}
|
||||
DrmCertificate drm_cert;
|
||||
if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"Drm service certificate is failed to parse.");
|
||||
}
|
||||
if (!drm_cert.has_creation_time_seconds()) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"missing certificate creation time");
|
||||
}
|
||||
// TODO(user): Check creation_time_seconds field in DrmCertificate and also
|
||||
// export the absl/time dependency through moe.
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
DrmServiceCertificate::DrmServiceCertificate(
|
||||
const std::string& service_certificate, const std::string& provider_id,
|
||||
const std::string& serial_number, const uint32_t creation_time_seconds,
|
||||
std::unique_ptr<RsaPublicKey> public_key,
|
||||
std::unique_ptr<RsaPrivateKey> private_key)
|
||||
: certificate_(service_certificate),
|
||||
provider_id_(provider_id),
|
||||
serial_number_(serial_number),
|
||||
creation_time_seconds_(creation_time_seconds),
|
||||
public_key_(std::move(public_key)),
|
||||
private_key_(std::move(private_key)) {}
|
||||
|
||||
} // namespace widevine
|
||||
129
common/drm_service_certificate.h
Normal file
129
common/drm_service_certificate.h
Normal file
@@ -0,0 +1,129 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Service certificate holder used to decrypt encrypted client credentials.
|
||||
|
||||
#ifndef COMMON_DRM_SERVICE_CERTIFICATE_H_
|
||||
#define COMMON_DRM_SERVICE_CERTIFICATE_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/macros.h"
|
||||
#include "util/status.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
namespace widevine {
|
||||
class RequestInspectorTest;
|
||||
} // namespace widevine
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class ClientIdentification;
|
||||
class EncryptedClientIdentification;
|
||||
|
||||
class DrmServiceCertificate {
|
||||
public:
|
||||
// Create a new DrmServiceCertificate object and add it to the list of valid
|
||||
// service certificates. |root_cert_type| indicates which root public key to
|
||||
// use to verify |service_certificate|, |service_certificate| is a
|
||||
// Google-generated certificate used to authenticate the service provider for
|
||||
// purposes of device privacy, |service_private_key| is the encrypted PKCS#8
|
||||
// private RSA key corresponding to the service certificate,
|
||||
// |service_private_key_passphrase| is the password required to decrypt
|
||||
// |service_private_key|.
|
||||
// Returns status::OK if successful, or appropriate error code otherwise.
|
||||
// If the default service certificate is not set, this certificate will be
|
||||
// used as the default service certificate.
|
||||
// This method is thread-safe.
|
||||
static util::Status AddDrmServiceCertificate(
|
||||
CertificateType root_cert_type, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
|
||||
// Same as AddDrmServiceCertificate(), but will clear the default service
|
||||
// certificate if it's set. This will result in this service certificate
|
||||
// being set as the default service certificate.
|
||||
static util::Status SetDefaultDrmServiceCertificate(
|
||||
CertificateType root_cert_type, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
|
||||
// Returns the default service certificate. Will return null if no default
|
||||
// Service Certificate is set. This method is thread-safe.
|
||||
static const DrmServiceCertificate* GetDefaultDrmServiceCertificate();
|
||||
|
||||
// Returns the default service certificate. Will abort if no default Service
|
||||
// Certificate is set. This method is thread-safe.
|
||||
static const DrmServiceCertificate* GetDefaultDrmServiceCertificateOrDie();
|
||||
|
||||
// Returns the service certificate with the given serial number if found, or
|
||||
// null otherwise.
|
||||
static const DrmServiceCertificate* GetDrmServiceCertificate(
|
||||
const std::string& cert_serial_number);
|
||||
|
||||
// Decrypts the EncryptedClientIdentification message passed in
|
||||
// |encrypted_client_id| into |client_id| using the private key for the
|
||||
// certificate which was used to encrypt the information. |client_id| must
|
||||
// not be NULL. Returns status::OK if successful, or an appropriate error
|
||||
// otherwise. This method is thread-safe.
|
||||
static util::Status DecryptClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id);
|
||||
|
||||
const std::string& certificate() const { return certificate_; }
|
||||
const std::string& provider_id() const { return provider_id_; }
|
||||
const std::string& serial_number() const { return serial_number_; }
|
||||
const RsaPrivateKey* const private_key() const { return private_key_.get(); }
|
||||
const RsaPublicKey* const public_key() const { return public_key_.get(); }
|
||||
|
||||
// Returns the validation result of drm service certificate. Returns
|
||||
// status::OK if successful, or in case of error, contact
|
||||
// widevine-tam@google.com to get the next valid service certificate renewed
|
||||
// via get deviceCertificate StatusList.
|
||||
static util::Status ValidateDrmServiceCertificate();
|
||||
|
||||
private:
|
||||
friend class DrmServiceCertificateTest;
|
||||
friend class widevine::RequestInspectorTest;
|
||||
|
||||
static util::Status AddDrmServiceCertificate(
|
||||
const std::string& root_public_key, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
|
||||
static util::Status SetDefaultDrmServiceCertificate(
|
||||
const std::string& root_public_key, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
|
||||
DrmServiceCertificate(const std::string& service_certificate,
|
||||
const std::string& provider_id, const std::string& serial_number,
|
||||
const uint32_t creation_time_seconds,
|
||||
std::unique_ptr<RsaPublicKey> public_key,
|
||||
std::unique_ptr<RsaPrivateKey> private_key);
|
||||
|
||||
static void ResetServiceCertificates();
|
||||
|
||||
std::string certificate_;
|
||||
std::string provider_id_;
|
||||
std::string serial_number_;
|
||||
uint32_t creation_time_seconds_;
|
||||
std::unique_ptr<RsaPublicKey> public_key_;
|
||||
std::unique_ptr<RsaPrivateKey> private_key_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmServiceCertificate);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_DRM_SERVICE_CERTIFICATE_H_
|
||||
368
common/drm_service_certificate_test.cc
Normal file
368
common/drm_service_certificate_test.cc
Normal file
@@ -0,0 +1,368 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "google/protobuf/util/message_differencer.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/drm_service_certificate.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/rsa_util.h"
|
||||
#include "common/test_certificates.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h" // IWYU pragma: keep
|
||||
#include "protos/public/license_server_sdk.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
const char kPrivacyKey[] = "f7538b38acc78ec68c732ac665c55c65";
|
||||
const char kIv[] = "09e9cda133ff5140bd2793173a04b5a3";
|
||||
const char kPassphrase[] = "passphrase";
|
||||
|
||||
class DrmServiceCertificateTest : public ::testing::Test {
|
||||
public:
|
||||
DrmServiceCertificateTest()
|
||||
: privacy_key_(absl::HexStringToBytes(kPrivacyKey)),
|
||||
iv_(absl::HexStringToBytes(kIv)),
|
||||
root_private_key_(
|
||||
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())) {
|
||||
CHECK(root_private_key_ != nullptr);
|
||||
client_id_.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||
client_id_.set_token(test_certs_.test_user_device_certificate());
|
||||
}
|
||||
|
||||
void SetUp() override { DrmServiceCertificate::ResetServiceCertificates(); }
|
||||
|
||||
protected:
|
||||
std::string GenerateDrmServiceCertificate(const std::string& serial_number,
|
||||
const std::string& provider_id,
|
||||
uint32_t creation_time_seconds,
|
||||
const std::string& public_key) {
|
||||
DrmCertificate cert;
|
||||
cert.set_type(DrmCertificate::SERVICE);
|
||||
cert.set_serial_number(serial_number);
|
||||
cert.set_provider_id(provider_id);
|
||||
cert.set_public_key(public_key);
|
||||
cert.set_creation_time_seconds(creation_time_seconds);
|
||||
SignedDrmCertificate signed_cert;
|
||||
cert.SerializeToString(signed_cert.mutable_drm_certificate());
|
||||
root_private_key_->GenerateSignature(signed_cert.drm_certificate(),
|
||||
signed_cert.mutable_signature());
|
||||
std::string serialized_cert;
|
||||
signed_cert.SerializeToString(&serialized_cert);
|
||||
return serialized_cert;
|
||||
}
|
||||
|
||||
util::Status SetDefaultDrmServiceCertificate(const std::string& serial_number,
|
||||
const std::string& provider_id,
|
||||
uint32_t creation_time_seconds) {
|
||||
std::string signed_cert(GenerateDrmServiceCertificate(
|
||||
serial_number, provider_id, creation_time_seconds,
|
||||
test_keys_.public_test_key_2_2048_bits()));
|
||||
std::string encrypted_private_key;
|
||||
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
||||
&encrypted_private_key)) {
|
||||
return util::Status(util::error::INTERNAL, "");
|
||||
}
|
||||
return DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||
test_keys_.public_test_key_1_3072_bits(), signed_cert,
|
||||
encrypted_private_key, kPassphrase);
|
||||
}
|
||||
|
||||
util::Status AddDrmServiceCertificate(const std::string& serial_number,
|
||||
const std::string& provider_id,
|
||||
uint32_t creation_time_seconds) {
|
||||
std::string signed_cert(GenerateDrmServiceCertificate(
|
||||
serial_number, provider_id, creation_time_seconds,
|
||||
test_keys_.public_test_key_2_2048_bits()));
|
||||
std::string encrypted_private_key;
|
||||
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
||||
&encrypted_private_key)) {
|
||||
return util::Status(util::error::INTERNAL, "");
|
||||
}
|
||||
return DrmServiceCertificate::AddDrmServiceCertificate(
|
||||
test_keys_.public_test_key_1_3072_bits(), signed_cert,
|
||||
encrypted_private_key, kPassphrase);
|
||||
}
|
||||
|
||||
void EncryptClientIdentification(
|
||||
const std::string& serial_number, const std::string& provider_id,
|
||||
const std::string& public_key,
|
||||
EncryptedClientIdentification* encrypted_client_id) {
|
||||
CHECK(encrypted_client_id);
|
||||
encrypted_client_id->set_provider_id(provider_id);
|
||||
encrypted_client_id->set_service_certificate_serial_number(serial_number);
|
||||
std::string serial_client_id;
|
||||
client_id_.SerializeToString(&serial_client_id);
|
||||
encrypted_client_id->set_encrypted_client_id(
|
||||
crypto_util::EncryptAesCbc(privacy_key_, iv_, serial_client_id));
|
||||
encrypted_client_id->set_encrypted_client_id_iv(iv_);
|
||||
std::unique_ptr<RsaPublicKey> rsa_key(RsaPublicKey::Create(public_key));
|
||||
ASSERT_TRUE(rsa_key.get());
|
||||
rsa_key->Encrypt(privacy_key_,
|
||||
encrypted_client_id->mutable_encrypted_privacy_key());
|
||||
}
|
||||
|
||||
RsaTestKeys test_keys_;
|
||||
TestCertificates test_certs_;
|
||||
std::string privacy_key_;
|
||||
std::string iv_;
|
||||
std::unique_ptr<RsaPrivateKey> root_private_key_;
|
||||
ClientIdentification client_id_;
|
||||
};
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, BasicClientIdDecrypt) {
|
||||
std::string serial_number("serial_number");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||
creation_time_seconds));
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, NoDefaultDrmServiceCertificate) {
|
||||
ASSERT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate());
|
||||
const auto& get_default_sc_or_die = []() {
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie();
|
||||
};
|
||||
EXPECT_DEATH(get_default_sc_or_die(), "Service Certificate not set!");
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) {
|
||||
std::string serial_number1("serial_number1");
|
||||
std::string provider_id1("someservice.com");
|
||||
uint32_t creation_time_seconds1(1234);
|
||||
std::string serial_number2("serial_number2");
|
||||
uint32_t creation_time_seconds2(1234);
|
||||
std::string bogus_serial_number("bogus-serial-number2");
|
||||
|
||||
EXPECT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate());
|
||||
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id1,
|
||||
creation_time_seconds1));
|
||||
|
||||
// Expect this to pass because the serial number is allowed to change as long
|
||||
// as the service Id is the same as before.
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id1,
|
||||
creation_time_seconds2));
|
||||
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number1, provider_id1,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptClientIdentification(serial_number2, provider_id1,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptClientIdentification(bogus_serial_number, provider_id1,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) {
|
||||
std::string serial_number1("serial_number1");
|
||||
std::string serial_number2("serial_number2");
|
||||
std::string serial_number3("serial_number3");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id,
|
||||
creation_time_seconds));
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id,
|
||||
creation_time_seconds + 1));
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id,
|
||||
creation_time_seconds - 1));
|
||||
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number1, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptClientIdentification(serial_number2, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptClientIdentification(serial_number3, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
const DrmServiceCertificate* default_cert(
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificate());
|
||||
ASSERT_TRUE(default_cert);
|
||||
SignedDrmCertificate signed_cert;
|
||||
ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate()));
|
||||
DrmCertificate drm_cert;
|
||||
ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate()));
|
||||
EXPECT_EQ(serial_number1, drm_cert.serial_number());
|
||||
|
||||
EXPECT_OK(SetDefaultDrmServiceCertificate(serial_number2, provider_id,
|
||||
creation_time_seconds));
|
||||
default_cert = DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
||||
ASSERT_TRUE(default_cert);
|
||||
ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate()));
|
||||
ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate()));
|
||||
EXPECT_EQ(serial_number2, drm_cert.serial_number());
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, DrmServiceCertificateNotFound) {
|
||||
std::string serial_number("serial_number");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||
creation_time_seconds));
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification("invalid_serial_number", provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
||||
std::string serial_number("serial_number");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||
creation_time_seconds));
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptedClientIdentification invalid;
|
||||
invalid = encrypted_client_id;
|
||||
invalid.clear_encrypted_privacy_key();
|
||||
EXPECT_EQ(
|
||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||
"missing-encrypted-privacy-key",
|
||||
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
||||
&decrypted_client_id)
|
||||
.ToString());
|
||||
|
||||
invalid = encrypted_client_id;
|
||||
++(*invalid.mutable_encrypted_client_id_iv())[4];
|
||||
EXPECT_NE(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
invalid, &decrypted_client_id));
|
||||
|
||||
invalid.clear_encrypted_client_id_iv();
|
||||
EXPECT_EQ(
|
||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||
"missing-encrypted-client-id-iv",
|
||||
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
||||
&decrypted_client_id)
|
||||
.ToString());
|
||||
|
||||
invalid = encrypted_client_id;
|
||||
++(*invalid.mutable_encrypted_client_id())[0];
|
||||
EXPECT_NE(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
invalid, &decrypted_client_id));
|
||||
|
||||
invalid.clear_encrypted_client_id();
|
||||
EXPECT_EQ(
|
||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||
"missing-encrypted-client-id",
|
||||
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
||||
&decrypted_client_id)
|
||||
.ToString());
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, PrivateKeyDecryptError) {
|
||||
std::string serial_number("serial_number");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||
creation_time_seconds));
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptedClientIdentification corrupted;
|
||||
corrupted = encrypted_client_id;
|
||||
++(*corrupted.mutable_encrypted_privacy_key())[20];
|
||||
EXPECT_EQ(
|
||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||
"privacy-key-decryption-failed",
|
||||
DrmServiceCertificate::DecryptClientIdentification(corrupted,
|
||||
&decrypted_client_id)
|
||||
.ToString());
|
||||
}
|
||||
|
||||
// TODO(user): Add more unit tests for various fail cases (bad keys having
|
||||
// to do with bad keys and bad certs).
|
||||
|
||||
} // namespace widevine
|
||||
113
common/ecb_util.cc
Normal file
113
common/ecb_util.cc
Normal file
@@ -0,0 +1,113 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Implementation of ecb crypto routines used by Widevine services.
|
||||
|
||||
#include "common/ecb_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/des.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
const int kWvmDESKeySizeBytes = 16;
|
||||
|
||||
static bool EncryptOrDecrypt3DesCbc(absl::string_view key,
|
||||
absl::string_view src, std::string* dst,
|
||||
bool encrypt) {
|
||||
CHECK(dst);
|
||||
if (key.size() != kWvmDESKeySizeBytes) {
|
||||
LOG(WARNING) << "Invalid 3DES key size (" << key.size() << "!=16).";
|
||||
dst->clear();
|
||||
return false;
|
||||
}
|
||||
const int data_size = src.size();
|
||||
if (data_size % DES_KEY_SZ != 0) {
|
||||
// Data must be a multiple of block size
|
||||
LOG(WARNING) << "3DES data is not a multiple of 8 bytes.";
|
||||
dst->clear();
|
||||
return false;
|
||||
}
|
||||
const int data_size_blocks = data_size / DES_KEY_SZ;
|
||||
DES_key_schedule schedule[2];
|
||||
// const_DES_cblock (the type of the first argument to DES_ecb3_encrypt) isn't
|
||||
// actually const, so we have to cast away const.
|
||||
DES_cblock* keyblock =
|
||||
const_cast<DES_cblock*>(reinterpret_cast<const DES_cblock*>(key.data()));
|
||||
DES_set_key(keyblock + 0, schedule + 1);
|
||||
DES_set_key(keyblock + 1, schedule + 0);
|
||||
DES_cblock* srcblock =
|
||||
const_cast<DES_cblock*>(reinterpret_cast<const DES_cblock*>(src.data()));
|
||||
dst->resize(data_size);
|
||||
DES_cblock* dstblock = reinterpret_cast<DES_cblock*>(&*dst->begin());
|
||||
for (int i = 0; i < data_size_blocks; i++) {
|
||||
DES_ecb3_encrypt(srcblock + i, dstblock + i, schedule + 0, schedule + 1,
|
||||
schedule + 0, encrypt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
||||
return EncryptOrDecrypt3DesCbc(key, src, dst, DES_ENCRYPT);
|
||||
}
|
||||
|
||||
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
||||
return EncryptOrDecrypt3DesCbc(key, src, dst, DES_DECRYPT);
|
||||
}
|
||||
|
||||
bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
||||
CHECK(dst);
|
||||
dst->clear();
|
||||
if (src.size() % 16 != 0) {
|
||||
LOG(WARNING) << "AES-ECB data is not a multiple of 16 bytes.";
|
||||
return false;
|
||||
}
|
||||
int num_bits = key.size() * 8;
|
||||
AES_KEY aes_key;
|
||||
int aes_result = AES_set_encrypt_key(
|
||||
reinterpret_cast<const uint8_t*>(key.data()), num_bits, &aes_key);
|
||||
if (aes_result != 0) {
|
||||
LOG(WARNING) << "AES result is not zero.";
|
||||
return false;
|
||||
}
|
||||
dst->resize(src.size());
|
||||
for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) {
|
||||
AES_encrypt(reinterpret_cast<const uint8_t*>(src.data() + i),
|
||||
reinterpret_cast<uint8_t*>(&(*dst)[i]), &aes_key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
||||
CHECK(dst);
|
||||
dst->clear();
|
||||
if (src.size() % 16 != 0) {
|
||||
LOG(WARNING) << "AES-ECB data is not a multiple of 16 bytes.";
|
||||
return false;
|
||||
}
|
||||
int num_bits = key.size() * 8;
|
||||
AES_KEY aes_key;
|
||||
int aes_result = AES_set_decrypt_key(
|
||||
reinterpret_cast<const uint8_t*>(key.data()), num_bits, &aes_key);
|
||||
if (aes_result != 0) {
|
||||
LOG(WARNING) << "AES result is not zero.";
|
||||
return false;
|
||||
}
|
||||
dst->resize(src.size());
|
||||
for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) {
|
||||
AES_decrypt(reinterpret_cast<const uint8_t*>(src.data() + i),
|
||||
reinterpret_cast<uint8_t*>(&(*dst)[i]), &aes_key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
59
common/ecb_util.h
Normal file
59
common/ecb_util.h
Normal file
@@ -0,0 +1,59 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Contains ecb crypto routines for widevine protocols. These routines are used
|
||||
// as part of licensing and provisioning request handling.
|
||||
|
||||
#ifndef COMMON_ECB_UTIL_H_
|
||||
#define COMMON_ECB_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
// Encrypts |src| into |dst| using 3DES ECB2 mode with the given key. This is
|
||||
// used for protecting content keys on some older Widevine keyboxes, and is not
|
||||
// intended for any other purpose. Returns false and sets *|dst|="" if
|
||||
// unsuccessful. Key should be 16 bytes. The first 8 are key2 of the 3DES key
|
||||
// bundle, and the last 8 bytes are key1 and key3. |src| must be a multiple of
|
||||
// 8 bytes.
|
||||
bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
||||
|
||||
// Decrypts |src| into |dst| using 3DES ECB2 mode with the given (16-byte)
|
||||
// key. This is used for protecting content keys on some older Widevine
|
||||
// keyboxes, and is not intended for any other purpose.
|
||||
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
||||
// fail if invalid key or data sizes are passed in.
|
||||
// Key should be 16 bytes. The first 8 are key2 of the 3DES key bundle,
|
||||
// and the last 8 bytes are key1 and key3. |src| must be a multiple of
|
||||
// 8 bytes.
|
||||
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
||||
|
||||
// Encrypts |src| into |dst| using AES ECB mode with the given
|
||||
// key. This is used for protecting content keys on Widevine devices,
|
||||
// and is not intended for any other purpose.
|
||||
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
||||
// fail if invalid key or data sizes are passed in.
|
||||
// Key must be 16 bytes, and src must be a multiple of 16 bytes.
|
||||
bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
||||
|
||||
// Decrypts |src| into |dst| using AES ECB mode with the given
|
||||
// key. This is used for protecting content keys on Widevine devices,
|
||||
// and is not intended for any other purpose.
|
||||
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
||||
// fail if invalid key or data sizes are passed in.
|
||||
// Key must be 16 bytes, and src must be a multiple of 16 bytes.
|
||||
bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_ECB_UTIL_H_
|
||||
90
common/ecb_util_test.cc
Normal file
90
common/ecb_util_test.cc
Normal file
@@ -0,0 +1,90 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Unit tests for the ecb_util functions.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/ecb_util.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
TEST(CryptoUtilTest, TestEncrypt3DesEcb) {
|
||||
// Test vector generated by (Python):
|
||||
// c = M2Crypto.EVP.Cipher('des_ede3_ecb', '89abcdef0123456789abcdef',
|
||||
// iv='', op=1, padding=False)
|
||||
// (c.update('This is a test message. ')+c.final()).encode('hex')
|
||||
// TODO(user): find some Widevine test vectors and use those
|
||||
std::string key = "0123456789abcdef";
|
||||
std::string plain = "This is a test message. ";
|
||||
std::string cipher;
|
||||
std::string decrypted;
|
||||
EXPECT_TRUE(Encrypt3DesEcb(key, plain, &cipher));
|
||||
EXPECT_EQ("ae7c7accaf99a973e7f89bb7f6dc61a9aa9e226a5ba17376",
|
||||
absl::BytesToHexString(cipher));
|
||||
EXPECT_TRUE(Decrypt3DesEcb(key, cipher, &decrypted));
|
||||
EXPECT_EQ(plain, decrypted);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestEncrypt3DesEcbFail) {
|
||||
// Verify that 3DES fails with invalid key or data size
|
||||
std::string badkey = "0123456789abcde"; // 15 bytes
|
||||
std::string badplain = "This is a test message."; // 23 bytes
|
||||
std::string goodkey = "0123456789abcdef"; // 16 bytes
|
||||
std::string goodplain = "This is a test message. "; // 24 bytes
|
||||
|
||||
// Encryption failure should leave 'decrypted' as empty std::string
|
||||
std::string out = "Not empty";
|
||||
EXPECT_FALSE(Encrypt3DesEcb(badkey, goodplain, &out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
|
||||
out = "Not empty";
|
||||
EXPECT_FALSE(Decrypt3DesEcb(goodkey, badplain, &out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestEncryptAesEcb) {
|
||||
// Test vector generated by (Python):
|
||||
// c = M2Crypto.EVP.Cipher('aes_128_ecb', key, '', 1, padding=False)
|
||||
// encrypted = c.update(data) + c.final();
|
||||
// TODO(user): find some Widevine test vectors and use those
|
||||
std::string key = "0123456789abcdef";
|
||||
std::string plaintext = "This message has 32 bytes in it.";
|
||||
std::string encrypted;
|
||||
std::string decrypted;
|
||||
EXPECT_TRUE(EncryptAesEcb(key, plaintext, &encrypted));
|
||||
EXPECT_EQ("1f6a7d63e0645de25c56c6b39ba7723d640129d65f41e96b87be812bc94ad8a9",
|
||||
absl::BytesToHexString(encrypted));
|
||||
EXPECT_TRUE(DecryptAesEcb(key, encrypted, &decrypted));
|
||||
EXPECT_EQ(plaintext, decrypted);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestEncryptAesEcbFail) {
|
||||
// Verify that EncryptAesEcb fails with invalid key or data size
|
||||
std::string badkey = "0123456789abcde"; // 15 bytes
|
||||
std::string badplain = "This message has 31 bytes in it";
|
||||
std::string goodkey = "0123456789abcdef"; // 16 bytes
|
||||
std::string goodplain = "This message has 32 bytes in it.";
|
||||
|
||||
// Encryption failure should leave 'decrypted' as empty std::string
|
||||
std::string out = "Not empty";
|
||||
EXPECT_FALSE(EncryptAesEcb(badkey, goodplain, &out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
|
||||
out = "Not empty";
|
||||
EXPECT_FALSE(EncryptAesEcb(goodkey, badplain, &out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
19
common/error_space.cc
Normal file
19
common/error_space.cc
Normal file
@@ -0,0 +1,19 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/error_space.h"
|
||||
|
||||
#include "util/proto_status.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
const util::ErrorSpace* error_space =
|
||||
util::ProtoEnumErrorSpace<Errors>::Get();
|
||||
|
||||
} // namespace widevine
|
||||
20
common/error_space.h
Normal file
20
common/error_space.h
Normal file
@@ -0,0 +1,20 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_ERROR_SPACE_H_
|
||||
#define COMMON_ERROR_SPACE_H_
|
||||
|
||||
#include "util/error_space.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
extern const util::ErrorSpace* error_space;
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_ERROR_SPACE_H_
|
||||
63
common/file_util.cc
Normal file
63
common/file_util.cc
Normal file
@@ -0,0 +1,63 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/file_util.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
bool GetContents(const std::string& file_name, std::string* contents) {
|
||||
if (file_name.empty()) {
|
||||
LOG(WARNING) << "File name is empty.";
|
||||
return false;
|
||||
}
|
||||
FILE* file = fopen(file_name.c_str(), "r");
|
||||
if (!file) {
|
||||
LOG(WARNING) << "Unable to open file " << file_name;
|
||||
return false;
|
||||
}
|
||||
contents->clear();
|
||||
const size_t kReadSize = 0x1000;
|
||||
char buffer[kReadSize];
|
||||
while (true) {
|
||||
size_t size_read = fread(buffer, sizeof(char), kReadSize, file);
|
||||
if (size_read == 0) break;
|
||||
contents->append(buffer, size_read);
|
||||
}
|
||||
const bool eof = feof(file);
|
||||
fclose(file);
|
||||
if (!eof) {
|
||||
LOG(WARNING) << "Failed to read all file contents.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetContents(const std::string& file_name, const std::string& contents) {
|
||||
if (file_name.empty()) {
|
||||
LOG(WARNING) << "File name is empty.";
|
||||
return false;
|
||||
}
|
||||
FILE* file = fopen(file_name.c_str(), "w");
|
||||
if (!file) {
|
||||
LOG(WARNING) << "Unable to open file " << file_name;
|
||||
return false;
|
||||
}
|
||||
const size_t size_written =
|
||||
fwrite(contents.data(), sizeof(char), contents.size(), file);
|
||||
if (size_written != contents.size())
|
||||
LOG(WARNING) << "Failed to write to " << file_name;
|
||||
fclose(file);
|
||||
return size_written == contents.size();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
27
common/file_util.h
Normal file
27
common/file_util.h
Normal file
@@ -0,0 +1,27 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// File util wrapper to be used in partner sdks. Implemented using generic file
|
||||
// apis.
|
||||
|
||||
#ifndef COMMON_FILE_UTIL_H_
|
||||
#define COMMON_FILE_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Read file to string.
|
||||
bool GetContents(const std::string& file_name, std::string* contents);
|
||||
|
||||
// Write file.
|
||||
bool SetContents(const std::string& file_name, const std::string& contents);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_FILE_UTIL_H_
|
||||
29
common/file_util_test.cc
Normal file
29
common/file_util_test.cc
Normal file
@@ -0,0 +1,29 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
TEST(FileUtilTest, EmptyFileName) {
|
||||
std::string contents;
|
||||
EXPECT_FALSE(GetContents("", &contents));
|
||||
EXPECT_FALSE(SetContents("", "test content"));
|
||||
}
|
||||
|
||||
TEST(FileUtilTest, BasicTest) {
|
||||
const std::string file_path = absl::StrCat("/tmp", "/file_util_test");
|
||||
EXPECT_TRUE(SetContents(file_path, "test content"));
|
||||
std::string contents;
|
||||
EXPECT_TRUE(GetContents(file_path, &contents));
|
||||
EXPECT_EQ("test content", contents);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
73
common/mock_rsa_key.h
Normal file
73
common/mock_rsa_key.h
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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_MOCK_RSA_KEY_H_
|
||||
#define COMMON_MOCK_RSA_KEY_H_
|
||||
|
||||
#include <string>
|
||||
#include "testing/gmock.h"
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class MockRsaPrivateKey : public RsaPrivateKey {
|
||||
public:
|
||||
MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {}
|
||||
~MockRsaPrivateKey() override {}
|
||||
|
||||
MOCK_CONST_METHOD2(Decrypt, bool(const std::string& encrypted_message,
|
||||
std::string* decrypted_message));
|
||||
MOCK_CONST_METHOD2(GenerateSignature,
|
||||
bool(const std::string& message, std::string* signature));
|
||||
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
||||
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
||||
|
||||
private:
|
||||
MockRsaPrivateKey(const MockRsaPrivateKey&) = delete;
|
||||
MockRsaPrivateKey& operator=(const MockRsaPrivateKey&) = delete;
|
||||
};
|
||||
|
||||
class MockRsaPublicKey : public RsaPublicKey {
|
||||
public:
|
||||
MockRsaPublicKey() : RsaPublicKey(RSA_new()) {}
|
||||
~MockRsaPublicKey() override {}
|
||||
|
||||
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
|
||||
std::string* encrypted_message));
|
||||
MOCK_CONST_METHOD2(VerifySignature,
|
||||
bool(const std::string& message, const std::string& signature));
|
||||
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
||||
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
||||
|
||||
private:
|
||||
MockRsaPublicKey(const MockRsaPublicKey&) = delete;
|
||||
MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete;
|
||||
};
|
||||
|
||||
class MockRsaKeyFactory : public RsaKeyFactory {
|
||||
public:
|
||||
MockRsaKeyFactory() {}
|
||||
~MockRsaKeyFactory() override {}
|
||||
|
||||
MOCK_METHOD1(CreateFromPkcs1PrivateKey,
|
||||
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
|
||||
MOCK_METHOD2(
|
||||
CreateFromPkcs8PrivateKey,
|
||||
std::unique_ptr<RsaPrivateKey>(const std::string& private_key,
|
||||
const std::string& private_key_passphrase));
|
||||
MOCK_METHOD1(CreateFromPkcs1PublicKey,
|
||||
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
|
||||
|
||||
private:
|
||||
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;
|
||||
MockRsaKeyFactory& operator=(const MockRsaKeyFactory&) = delete;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_MOCK_RSA_KEY_H_
|
||||
77
common/openssl_util.h
Normal file
77
common/openssl_util.h
Normal file
@@ -0,0 +1,77 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RAII wrapper classes for cleaning up various OpenSSL dynamically allocated
|
||||
// structures.
|
||||
|
||||
#ifndef COMMON_OPENSSL_UTIL_H_
|
||||
#define COMMON_OPENSSL_UTIL_H_
|
||||
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/x509v3.h"
|
||||
|
||||
template <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>;
|
||||
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 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 // COMMON_OPENSSL_UTIL_H_
|
||||
27
common/random_util.cc
Normal file
27
common/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 "common/random_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "openssl/rand.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
bool RandomBytes(size_t num_bytes, std::string* output) {
|
||||
DCHECK(output);
|
||||
output->resize(num_bytes);
|
||||
return RAND_bytes(reinterpret_cast<uint8_t*>(&(*output)[0]), num_bytes);
|
||||
}
|
||||
|
||||
std::string Random16Bytes() {
|
||||
std::string output;
|
||||
CHECK(RandomBytes(16u, &output));
|
||||
return output;
|
||||
}
|
||||
} // namespace widevine
|
||||
25
common/random_util.h
Normal file
25
common/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 COMMON_RANDOM_UTIL_H_
|
||||
#define COMMON_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 // COMMON_RANDOM_UTIL_H_
|
||||
28
common/random_util_test.cc
Normal file
28
common/random_util_test.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/random_util.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
TEST(RandomUtilTest, Test) {
|
||||
std::string output;
|
||||
ASSERT_TRUE(RandomBytes(16u, &output));
|
||||
EXPECT_EQ(16u, output.size());
|
||||
|
||||
std::string output2;
|
||||
ASSERT_TRUE(RandomBytes(16u, &output2));
|
||||
EXPECT_EQ(16u, output2.size());
|
||||
EXPECT_NE(output, output2);
|
||||
|
||||
ASSERT_TRUE(RandomBytes(10u, &output2));
|
||||
EXPECT_EQ(10u, output2.size());
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
261
common/remote_attestation_verifier.cc
Normal file
261
common/remote_attestation_verifier.cc
Normal file
@@ -0,0 +1,261 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/remote_attestation_verifier.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "common/client_id_util.h"
|
||||
#include "common/drm_service_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
const char kTestRootCaDerCert[] =
|
||||
"30820403308202eba003020102020900a24f94af7ae6831f300d06092a86"
|
||||
"4886f70d0101050500308197310b30090603550406130255533113301106"
|
||||
"035504080c0a57617368696e67746f6e3111300f06035504070c084b6972"
|
||||
"6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f"
|
||||
"060355040b0c085769646576696e653115301306035504030c0c54657374"
|
||||
"20526f6f742043413121301f06092a864886f70d010901161274696e736b"
|
||||
"697040676f6f676c652e636f6d301e170d3133303831363030353731305a"
|
||||
"170d3333303831353030353731305a308197310b30090603550406130255"
|
||||
"533113301106035504080c0a57617368696e67746f6e3111300f06035504"
|
||||
"070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049"
|
||||
"6e633111300f060355040b0c085769646576696e65311530130603550403"
|
||||
"0c0c5465737420526f6f742043413121301f06092a864886f70d01090116"
|
||||
"1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648"
|
||||
"86f70d01010105000382010f003082010a0282010100c6eee629d99f7736"
|
||||
"2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c"
|
||||
"e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319"
|
||||
"0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3"
|
||||
"f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93"
|
||||
"52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7"
|
||||
"e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f"
|
||||
"d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20"
|
||||
"79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7"
|
||||
"3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30"
|
||||
"ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680"
|
||||
"144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405"
|
||||
"30030101ff300d06092a864886f70d01010505000382010100779e9b98d3"
|
||||
"ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee"
|
||||
"79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124"
|
||||
"ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436"
|
||||
"026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140"
|
||||
"ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053"
|
||||
"0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258"
|
||||
"e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82"
|
||||
"d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4"
|
||||
"c270d87191c13aefdd177b";
|
||||
|
||||
const char kProdRootCaDerCert[] =
|
||||
"30820408308202f0a003020102020101300d06092a864886f70d01010505"
|
||||
"00307d311830160603550403130f5072697661637920434120526f6f7431"
|
||||
"123010060355040b13094368726f6d65204f5331133011060355040a130a"
|
||||
"476f6f676c6520496e63311630140603550407130d4d6f756e7461696e20"
|
||||
"56696577311330110603550408130a43616c69666f726e6961310b300906"
|
||||
"0355040613025553301e170d3133303231383130313334325a170d333330"
|
||||
"3231333130313334325a307d311830160603550403130f50726976616379"
|
||||
"20434120526f6f7431123010060355040b13094368726f6d65204f533113"
|
||||
"3011060355040a130a476f6f676c6520496e63311630140603550407130d"
|
||||
"4d6f756e7461696e2056696577311330110603550408130a43616c69666f"
|
||||
"726e6961310b300906035504061302555330820122300d06092a864886f7"
|
||||
"0d01010105000382010f003082010a0282010100e10ea6819d3d066b421d"
|
||||
"d7612de3eef9599f5d9a2a24bfd09caab543511cf22f615e29f989425a65"
|
||||
"7396bf33603747719cfb0b4240cd682c7c558fec0176b4793be440752246"
|
||||
"83648f5b12d02a838a2a8e55a4b645ed0a4a52b19252a23d34bf64a17ac7"
|
||||
"11fe93a889086d943211b17d670f96442c9f367d38026000da79664e600e"
|
||||
"e9259348f4fd74108e973d561e624e9f5eda77a085a6eb15fadb2cc7787c"
|
||||
"7f30ef3b196f2a416a76fa9eb30d65753f5039d97bea70e82431d2962396"
|
||||
"a34864f33b74d60707fea794c03c82e547abc2407fa7bad67bd09cdab49b"
|
||||
"26e68754994d12a3845dbeceffe18de0d51fc6fa78676d89ea1e0fcff931"
|
||||
"59bfb809519b0203010001a3819230818f30290603551d0e042204204b1d"
|
||||
"148aa5380938812ed6a763f5dc2c318610d5fa9604d609cb2e0d8cec3289"
|
||||
"302b0603551d230424302280204b1d148aa5380938812ed6a763f5dc2c31"
|
||||
"8610d5fa9604d609cb2e0d8cec3289300e0603551d0f0101ff0404030201"
|
||||
"06300f0603551d130101ff040530030101ff30140603551d200101ff040a"
|
||||
"300830060604551d2000300d06092a864886f70d01010505000382010100"
|
||||
"c40d84bc8d609b1b68b3caa7e841021838d7e392557d40debab3e0685e72"
|
||||
"80541092dc913b0aa6150228d8fe5ab08cceefbac56952fa00ba614294d1"
|
||||
"ba4fa170c86b27f9bf58666c46940f740c4be2795501b25e40b9702af07c"
|
||||
"884926bd8beed036c503e5e42a223ff36271404ca4360a93dec92a02fd8d"
|
||||
"ae8f756fc68aaa647e2159f0a7a95d1446e92362bd512f59daec02c5d152"
|
||||
"c301b9807db998ba70c616364762a0a497aaa92eb7d92f3635169d3f74c6"
|
||||
"40c738941759a8ab43677b80329d015bdcf8922b779a80f85f1e4a677659"
|
||||
"c60de80152e8c526a7de46cac143a75af58f0806de81e15c97f616e1bffa"
|
||||
"1c1c6b0d2438543bdfb2a21bd9bc7ae4";
|
||||
|
||||
const char kServiceIdFieldName[] = "OU";
|
||||
const char kDeviceModeFieldName[] = "O";
|
||||
const char kExpectedDeviceMode[] = "Chrome Device Content Protection";
|
||||
|
||||
RemoteAttestationVerifier& RemoteAttestationVerifier::get() {
|
||||
static RemoteAttestationVerifier instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void RemoteAttestationVerifier::EnableTestCertificates(bool enable) {
|
||||
absl::WriterMutexLock lock(&ca_mutex_);
|
||||
enable_test_certificates_ = enable;
|
||||
ca_.reset();
|
||||
}
|
||||
|
||||
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
std::string* remote_attestation_cert_sn) {
|
||||
DCHECK(remote_attestation_cert_sn);
|
||||
|
||||
// Sanity check RemoteAttestation.
|
||||
if (!remote_attestation.has_certificate()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-certificate-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_salt()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-salt-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_signature()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-signature-missing"));
|
||||
}
|
||||
// Decrypt ClientIdentification containing remote attestation certificate.
|
||||
// A service cert would be looked up first, then that cert will be used
|
||||
// to decrypt the ClientIdentification.
|
||||
ClientIdentification client_id;
|
||||
util::Status status = DrmServiceCertificate::DecryptClientIdentification(
|
||||
remote_attestation.certificate(), &client_id);
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (client_id.type() !=
|
||||
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
std::string("remote-attestation-invalid-client-id-type (") +
|
||||
absl::StrCat(client_id.type()) + ")"));
|
||||
}
|
||||
return VerifyRemoteAttestation(message, remote_attestation, client_id,
|
||||
remote_attestation_cert_sn);
|
||||
}
|
||||
|
||||
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
const std::string& privacy_key) {
|
||||
// Sanity check RemoteAttestation.
|
||||
if (!remote_attestation.has_certificate()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-certificate-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_salt()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-salt-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_signature()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-signature-missing"));
|
||||
}
|
||||
// Decrypt ClientIdentification containing remote attestation certificate,
|
||||
// directly using an explicitly provided key |privacy_key|.
|
||||
ClientIdentification client_id;
|
||||
util::Status status = DecryptEncryptedClientIdentification(
|
||||
remote_attestation.certificate(), privacy_key, &client_id);
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (client_id.type() !=
|
||||
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
std::string("remote-attestation-invalid-client-id-type (") +
|
||||
absl::StrCat(client_id.type()) + ")"));
|
||||
}
|
||||
std::string remote_attestation_cert_sn;
|
||||
return VerifyRemoteAttestation(message, remote_attestation, client_id,
|
||||
&remote_attestation_cert_sn);
|
||||
}
|
||||
|
||||
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) {
|
||||
if (!client_id.has_token()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-token-missing"));
|
||||
}
|
||||
// Load and verify the certificate chain.
|
||||
std::unique_ptr<X509CertChain> cert_chain(new X509CertChain);
|
||||
util::Status status = cert_chain->LoadPem(client_id.token());
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (cert_chain->GetNumCerts() < 1) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-empty-certificate-chain"));
|
||||
}
|
||||
std::string device_mode_string =
|
||||
cert_chain->GetCert(0)->GetSubjectNameField(kDeviceModeFieldName);
|
||||
if (device_mode_string != kExpectedDeviceMode) {
|
||||
return (util::Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
std::string("remote-attestation-device-not-verified (") +
|
||||
device_mode_string + " / " + kDeviceModeFieldName +
|
||||
")"));
|
||||
}
|
||||
ca_mutex_.ReaderLock();
|
||||
if (ca_ == NULL) {
|
||||
ca_mutex_.ReaderUnlock();
|
||||
status = LoadCa();
|
||||
if (!status.ok()) return status;
|
||||
ca_mutex_.ReaderLock();
|
||||
}
|
||||
status = ca_->VerifyCertChain(*cert_chain);
|
||||
ca_mutex_.ReaderUnlock();
|
||||
if (!status.ok()) {
|
||||
return (util::Status(
|
||||
error_space, REMOTE_ATTESTATION_FAILED,
|
||||
std::string("remote-attestation-cert-chain-validation-failed: ") +
|
||||
status.error_message()));
|
||||
}
|
||||
// Verify the remote attestation signature.
|
||||
std::unique_ptr<RsaPublicKey> leaf_key;
|
||||
std::string message_with_salt = message + remote_attestation.salt();
|
||||
for (size_t idx = 0; idx < cert_chain->GetNumCerts(); ++idx) {
|
||||
if (!cert_chain->GetCert(idx)->IsCaCertificate()) {
|
||||
leaf_key = cert_chain->GetCert(idx)->GetRsaPublicKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!leaf_key) {
|
||||
return util::Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
"remote-attestation-cert-chain-no-leaf");
|
||||
}
|
||||
|
||||
if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt,
|
||||
remote_attestation.signature())) {
|
||||
return (util::Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
"remote-attestation-signature-verification-failed: "));
|
||||
}
|
||||
|
||||
*remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber();
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status RemoteAttestationVerifier::LoadCa() {
|
||||
absl::WriterMutexLock lock(&ca_mutex_);
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
util::Status status = ca_cert->LoadDer(absl::HexStringToBytes(
|
||||
enable_test_certificates_ ? kTestRootCaDerCert : kProdRootCaDerCert));
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
ca_.reset(new X509CA(ca_cert.release()));
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
92
common/remote_attestation_verifier.h
Normal file
92
common/remote_attestation_verifier.h
Normal file
@@ -0,0 +1,92 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Functionality used to verifier ChromeOS remote attestation.
|
||||
|
||||
#ifndef COMMON_REMOTE_ATTESTATION_VERIFIER_H_
|
||||
#define COMMON_REMOTE_ATTESTATION_VERIFIER_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/thread_annotations.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/status.h"
|
||||
#include "common/x509_cert.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/remote_attestation.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Singleton class used to do remote attestation. Access singleton instance via
|
||||
// the get() method.
|
||||
// TODO(user): This class is tested as part of the Session unit tests, but
|
||||
// finer unit tests should be implemented for the failure cases.
|
||||
class RemoteAttestationVerifier {
|
||||
public:
|
||||
RemoteAttestationVerifier() : enable_test_certificates_(false) {}
|
||||
virtual ~RemoteAttestationVerifier() {}
|
||||
|
||||
// Singleton accessor.
|
||||
static RemoteAttestationVerifier& get();
|
||||
|
||||
// Call to use the test (non-production) remote attestation root certificate.
|
||||
// This method is thread-safe.
|
||||
void EnableTestCertificates(bool enable);
|
||||
|
||||
// Call to verify a RemoteAttestation challenge response, used in licensing
|
||||
// protocol.
|
||||
// |message| is the challenge message,
|
||||
// |remote_attestation| is the remote attestation response to verify,
|
||||
// |remote_attestation_cert_sn| is a pointer to a std::string which on successful
|
||||
// return will contain the serial number for the client's remote attestation
|
||||
// certificate.
|
||||
// This method is thread-safe.
|
||||
util::Status VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
std::string* remote_attestation_cert_sn);
|
||||
|
||||
// Call to verify a RemoteAttestation challenge response, used in certificate
|
||||
// provisioning protocol.
|
||||
// |message| is the challenge message,
|
||||
// |remote_attestation| is the remote attestation response to verify,
|
||||
// |privacy_key| is used to decrypt the EncryptedClientIdentification within
|
||||
// the |remote_attestation| message.
|
||||
// This method is thread-safe.
|
||||
util::Status VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
const std::string& privacy_key);
|
||||
|
||||
private:
|
||||
// Common subroutine to perform the verification.
|
||||
// |message| is the challenge message,
|
||||
// |remote_attestation| is the remote attestation response to verify,
|
||||
// |client_id| is the decrypted client identification carrying the token,
|
||||
// |remote_attestation_cert_sn| is a pointer to a std::string which on successful
|
||||
// return will contain the serial number for the client's remote attestation
|
||||
// certificate.
|
||||
util::Status VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
const ClientIdentification& client_id,
|
||||
std::string* remote_attestation_cert_sn);
|
||||
|
||||
util::Status LoadCa();
|
||||
|
||||
bool enable_test_certificates_;
|
||||
absl::Mutex ca_mutex_;
|
||||
std::unique_ptr<X509CA> ca_ GUARDED_BY(ca_mutex_);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RemoteAttestationVerifier);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_REMOTE_ATTESTATION_VERIFIER_H_
|
||||
313
common/rsa_key.cc
Normal file
313
common/rsa_key.cc
Normal file
@@ -0,0 +1,313 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Description:
|
||||
// Definition of classes representing RSA private and public keys used
|
||||
// for message signing, signature verification, encryption and decryption.
|
||||
//
|
||||
// RSA signature details:
|
||||
// Algorithm: RSASSA-PSS
|
||||
// Hash algorithm: SHA1
|
||||
// Mask generation function: mgf1SHA1
|
||||
// Salt length: 20 bytes
|
||||
// Trailer field: 0xbc
|
||||
//
|
||||
// RSA encryption details:
|
||||
// Algorithm: RSA-OAEP
|
||||
// Mask generation function: mgf1SHA1
|
||||
// Label (encoding paramter): empty std::string
|
||||
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "openssl/bn.h"
|
||||
#include "openssl/err.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "common/rsa_util.h"
|
||||
#include "common/sha_util.h"
|
||||
|
||||
static const int kPssSaltLength = 20;
|
||||
|
||||
namespace {
|
||||
|
||||
// Check if two RSA keys match. If matches, they are either a public-private key
|
||||
// pair or the same public key or the same private key.
|
||||
bool RsaKeyMatch(const RSA* key1, const RSA* key2) {
|
||||
if (!key1 || !key2) return false;
|
||||
return BN_cmp(key1->n, key2->n) == 0;
|
||||
}
|
||||
|
||||
std::string OpenSSLErrorString(uint32_t error) {
|
||||
char buf[ERR_ERROR_STRING_BUF_LEN];
|
||||
ERR_error_string_n(error, buf, sizeof(buf));
|
||||
return buf;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
|
||||
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); }
|
||||
|
||||
RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key)
|
||||
: key_(RSAPrivateKey_dup(rsa_key.key_)) {
|
||||
CHECK(key_ != nullptr);
|
||||
}
|
||||
|
||||
RsaPrivateKey::~RsaPrivateKey() { RSA_free(key_); }
|
||||
|
||||
RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) {
|
||||
RSA* key;
|
||||
if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key)) return nullptr;
|
||||
if (RSA_check_key(key) != 1) {
|
||||
LOG(ERROR) << "Invalid private RSA key: "
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
RSA_free(key);
|
||||
}
|
||||
return new RsaPrivateKey(key);
|
||||
}
|
||||
|
||||
bool RsaPrivateKey::Decrypt(const std::string& encrypted_message,
|
||||
std::string* decrypted_message) const {
|
||||
DCHECK(decrypted_message);
|
||||
|
||||
size_t rsa_size = RSA_size(key_);
|
||||
if (encrypted_message.size() != rsa_size) {
|
||||
LOG(ERROR) << "Encrypted RSA message has the wrong size (expected "
|
||||
<< rsa_size << ", actual " << encrypted_message.size() << ")";
|
||||
return false;
|
||||
}
|
||||
decrypted_message->assign(rsa_size, 0);
|
||||
int decrypted_size = RSA_private_decrypt(
|
||||
rsa_size,
|
||||
const_cast<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() { 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) {
|
||||
return std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key));
|
||||
}
|
||||
|
||||
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
|
||||
const std::string& private_key, const std::string& private_key_passphrase) {
|
||||
std::string pkcs1_key;
|
||||
const bool result =
|
||||
private_key_passphrase.empty()
|
||||
? rsa_util::PrivateKeyInfoToRsaPrivateKey(private_key, &pkcs1_key)
|
||||
: rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey(
|
||||
private_key, private_key_passphrase, &pkcs1_key);
|
||||
if (!result) {
|
||||
LOG(WARNING) << "Failed to get pkcs1_key.";
|
||||
return std::unique_ptr<RsaPrivateKey>();
|
||||
}
|
||||
return std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(pkcs1_key));
|
||||
}
|
||||
|
||||
std::unique_ptr<RsaPublicKey> RsaKeyFactory::CreateFromPkcs1PublicKey(
|
||||
const std::string& public_key) {
|
||||
return std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
150
common/rsa_key.h
Normal file
150
common/rsa_key.h
Normal file
@@ -0,0 +1,150 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Description:
|
||||
// Declaration of classes representing RSA private and public keys used
|
||||
// for message signing, signature verification, encryption and decryption.
|
||||
|
||||
#ifndef COMMON_RSA_KEY_H_
|
||||
#define COMMON_RSA_KEY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/macros.h"
|
||||
#include "openssl/rsa.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class RsaPublicKey;
|
||||
|
||||
class RsaPrivateKey {
|
||||
public:
|
||||
explicit RsaPrivateKey(RSA* key);
|
||||
RsaPrivateKey(const RsaPrivateKey&);
|
||||
virtual ~RsaPrivateKey();
|
||||
|
||||
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
|
||||
// Returns NULL on failure.
|
||||
static RsaPrivateKey* Create(const std::string& serialized_key);
|
||||
|
||||
// Decrypt a message using RSA-OAEP. Caller retains ownership of all
|
||||
// parameters. Returns true if successful, false otherwise.
|
||||
virtual bool Decrypt(const std::string& encrypted_message,
|
||||
std::string* decrypted_message) const;
|
||||
|
||||
// Generate RSSASSA-PSS signature. Caller retains ownership of all parameters.
|
||||
// Returns true if successful, false otherwise.
|
||||
virtual bool GenerateSignature(const std::string& message,
|
||||
std::string* signature) const;
|
||||
|
||||
// Generate SHA256 digest, PKCS#7 padded signature. Caller retains ownership
|
||||
// of all parameters. Returns true if successful, false otherwise.
|
||||
virtual bool GenerateSignatureSha256Pkcs7(const std::string& message,
|
||||
std::string* signature) const;
|
||||
|
||||
// Return true if the underlying key matches with |private_key|.
|
||||
virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const;
|
||||
|
||||
// Return true if the underlying key is a public-private key pair with
|
||||
// |public_key|.
|
||||
virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const;
|
||||
|
||||
// Returns the RSA key size (modulus) in bytes.
|
||||
virtual uint32_t KeySize() const;
|
||||
|
||||
const RSA* key() const { return key_; }
|
||||
|
||||
private:
|
||||
RSA* key_;
|
||||
|
||||
// SWIG appears to think this declaration is a syntax error. Excluding it for
|
||||
// python SWIG wrapping.
|
||||
#ifndef SWIG
|
||||
// Disallow assignment operator.
|
||||
RsaPrivateKey& operator=(const RsaPrivateKey&) = delete;
|
||||
#endif // SWIG
|
||||
};
|
||||
|
||||
class RsaPublicKey {
|
||||
public:
|
||||
explicit RsaPublicKey(RSA* key);
|
||||
RsaPublicKey(const RsaPublicKey&);
|
||||
virtual ~RsaPublicKey();
|
||||
|
||||
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
|
||||
// Returns NULL on failure.
|
||||
static RsaPublicKey* Create(const std::string& serialized_key);
|
||||
|
||||
// Encrypt a message using RSA-OAEP. Caller retains ownership of all
|
||||
// parameters. Returns true if successful, false otherwise.
|
||||
virtual bool Encrypt(const std::string& clear_message,
|
||||
std::string* encrypted_message) const;
|
||||
|
||||
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters.
|
||||
// Returns true if validation succeeds, false otherwise.
|
||||
virtual bool VerifySignature(const std::string& message,
|
||||
const std::string& signature) const;
|
||||
|
||||
// Verify a signature. This method takes two parameters: |message| which is a
|
||||
// std::string containing the data which was signed, and |signature| which is a
|
||||
// std::string containing the message SHA256 digest signature with PKCS#7
|
||||
// padding. Returns true if verification succeeds, false otherwise.
|
||||
virtual bool VerifySignatureSha256Pkcs7(const std::string& message,
|
||||
const std::string& signature) const;
|
||||
|
||||
// Return true if the underlying key is a public-private key pair with
|
||||
// |private_key|.
|
||||
virtual bool MatchesPrivateKey(const RsaPrivateKey& private_key) const;
|
||||
|
||||
// Return true if the underlying key matches with |public_key|.
|
||||
virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const;
|
||||
|
||||
// Returns the RSA key size (modulus) in bytes.
|
||||
virtual uint32_t KeySize() const;
|
||||
|
||||
const RSA* key() const { return key_; }
|
||||
|
||||
private:
|
||||
RSA* key_;
|
||||
|
||||
// SWIG appears to think this declaration is a syntax error. Excluding it for
|
||||
// python SWIG wrapping.
|
||||
#ifndef SWIG
|
||||
// Disallow assignment operator.
|
||||
RsaPublicKey& operator=(const RsaPublicKey&) = delete;
|
||||
#endif // SWIG
|
||||
};
|
||||
|
||||
class RsaKeyFactory {
|
||||
public:
|
||||
RsaKeyFactory();
|
||||
virtual ~RsaKeyFactory();
|
||||
|
||||
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
|
||||
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs1PrivateKey(
|
||||
const std::string& private_key);
|
||||
|
||||
// Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or
|
||||
// EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty).
|
||||
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs8PrivateKey(
|
||||
const std::string& private_key, const std::string& private_key_passphrase);
|
||||
|
||||
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
|
||||
virtual std::unique_ptr<RsaPublicKey> CreateFromPkcs1PublicKey(
|
||||
const std::string& public_key);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_RSA_KEY_H_
|
||||
255
common/rsa_key_test.cc
Normal file
255
common/rsa_key_test.cc
Normal file
@@ -0,0 +1,255 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Description:
|
||||
// Unit test for rsa_key RSA encryption and signing.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/rsa_util.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
static const char kTestMessage[] =
|
||||
"A fool thinks himself to be wise, but a wise man knows himself to be a "
|
||||
"fool.";
|
||||
|
||||
class RsaKeyTest : public ::testing::Test {
|
||||
protected:
|
||||
void TestEncryption(std::unique_ptr<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, 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
common/rsa_test_keys.cc
Normal file
786
common/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 "common/rsa_test_keys.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
static const unsigned char kTestRsaPrivateKey1_3072[] = {
|
||||
0x30, 0x82, 0x06, 0xe3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x81, 0x00,
|
||||
0xa5, 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0,
|
||||
0x78, 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3,
|
||||
0x0f, 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2,
|
||||
0xca, 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55,
|
||||
0x1b, 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29,
|
||||
0x46, 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17,
|
||||
0x01, 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37,
|
||||
0x5c, 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1,
|
||||
0x14, 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86,
|
||||
0xcd, 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3,
|
||||
0xb6, 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4,
|
||||
0xde, 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb,
|
||||
0x31, 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee,
|
||||
0x3a, 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb,
|
||||
0x90, 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca,
|
||||
0x49, 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0,
|
||||
0x5f, 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6,
|
||||
0xda, 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb,
|
||||
0x17, 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab,
|
||||
0x30, 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26,
|
||||
0xaf, 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10,
|
||||
0x16, 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b,
|
||||
0x42, 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21,
|
||||
0xf9, 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64,
|
||||
0xe4, 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05,
|
||||
0x4a, 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1,
|
||||
0x72, 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab,
|
||||
0xde, 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50,
|
||||
0x04, 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85,
|
||||
0x1b, 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0,
|
||||
0x33, 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b,
|
||||
0x67, 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87,
|
||||
0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x80, 0x5a, 0x09, 0x3f,
|
||||
0x9e, 0x2e, 0x4d, 0x26, 0x50, 0x7b, 0x70, 0x21, 0xb0, 0x0c, 0x25, 0x21,
|
||||
0x1f, 0xd9, 0x89, 0x5a, 0xca, 0x35, 0x23, 0x0b, 0x58, 0xa9, 0x7d, 0xf6,
|
||||
0x19, 0xc4, 0x29, 0x87, 0xc7, 0xd4, 0x94, 0x85, 0xb4, 0x2c, 0xaf, 0x62,
|
||||
0xb1, 0xe8, 0x62, 0x5b, 0xda, 0xdb, 0x70, 0x40, 0x37, 0xb1, 0x4e, 0x0c,
|
||||
0xc8, 0x62, 0xee, 0xa2, 0xfc, 0x3c, 0xd2, 0x39, 0x90, 0x15, 0x2c, 0xba,
|
||||
0x20, 0x50, 0xb7, 0x82, 0x2a, 0xa0, 0x76, 0x83, 0x20, 0x7f, 0x56, 0x73,
|
||||
0x43, 0x8a, 0x9b, 0xa7, 0x6c, 0x63, 0xb6, 0xad, 0x56, 0xb2, 0x8a, 0xb2,
|
||||
0xbc, 0x8f, 0xe2, 0xef, 0x83, 0x9d, 0x98, 0x0b, 0xc7, 0x62, 0x0e, 0x51,
|
||||
0x6e, 0x57, 0x1d, 0x1b, 0x0e, 0x3a, 0xea, 0x3b, 0x76, 0x63, 0x35, 0xd0,
|
||||
0xd1, 0xcf, 0xbe, 0xad, 0xbb, 0x1d, 0xde, 0x0f, 0x05, 0x48, 0x55, 0x29,
|
||||
0xc1, 0xbc, 0x21, 0xc7, 0x87, 0xf2, 0x75, 0x12, 0x7d, 0x92, 0x9e, 0xbf,
|
||||
0xad, 0x04, 0x68, 0xc4, 0xc9, 0x9d, 0x35, 0xd6, 0xa8, 0x62, 0xc1, 0x69,
|
||||
0x6a, 0xb6, 0x41, 0xb7, 0x37, 0x66, 0xdf, 0xb2, 0xb9, 0x8c, 0x8b, 0x15,
|
||||
0x08, 0x4c, 0x3d, 0xf1, 0xed, 0x82, 0x0f, 0xe3, 0xd5, 0xff, 0x46, 0xbd,
|
||||
0xf7, 0x85, 0x43, 0xc0, 0x8b, 0xba, 0x47, 0xf1, 0x41, 0x57, 0xc3, 0x7f,
|
||||
0x8b, 0x0d, 0x48, 0xea, 0xc2, 0xed, 0xc0, 0x69, 0x84, 0xb6, 0x32, 0x08,
|
||||
0x49, 0x74, 0x14, 0x84, 0xa4, 0x1b, 0x48, 0x5b, 0xec, 0xd3, 0x0b, 0x12,
|
||||
0x2b, 0x4c, 0x9e, 0x5c, 0x01, 0x60, 0xad, 0xef, 0xcb, 0x2b, 0x56, 0x84,
|
||||
0x07, 0xfa, 0x62, 0xc6, 0x08, 0x92, 0x98, 0x70, 0xc9, 0x5b, 0x18, 0xc8,
|
||||
0xfa, 0x27, 0x0c, 0xe2, 0xbd, 0xfb, 0x3e, 0x43, 0xa5, 0xb7, 0x06, 0x2c,
|
||||
0x4e, 0xf1, 0x07, 0x5d, 0x8d, 0xdd, 0x53, 0xc5, 0x8c, 0x4a, 0xf2, 0x2f,
|
||||
0x8e, 0x80, 0x96, 0x16, 0xc0, 0xfc, 0xf9, 0x20, 0x4f, 0x35, 0xc7, 0x53,
|
||||
0x8b, 0x2d, 0x37, 0x43, 0x93, 0x3d, 0x74, 0x3f, 0x63, 0xf7, 0x0b, 0xbd,
|
||||
0x46, 0xe4, 0x51, 0x67, 0x33, 0x57, 0x15, 0xf5, 0x59, 0x27, 0x66, 0xe8,
|
||||
0xe2, 0x4b, 0xa3, 0x93, 0x03, 0x8a, 0x9c, 0x05, 0x13, 0xf2, 0xcb, 0xf7,
|
||||
0x9c, 0x68, 0xe7, 0x16, 0x4b, 0x8e, 0x59, 0x71, 0x2b, 0x73, 0x9b, 0xb9,
|
||||
0xae, 0x50, 0xfa, 0xd7, 0xd3, 0x34, 0x17, 0x1d, 0x62, 0x88, 0xbd, 0x8c,
|
||||
0xba, 0x5a, 0x6b, 0x6a, 0x5e, 0xb3, 0xa5, 0x80, 0xca, 0xbb, 0xb9, 0xb5,
|
||||
0xa8, 0x2e, 0xb1, 0x61, 0x6e, 0xd5, 0xd6, 0x62, 0x98, 0x4a, 0xb0, 0xb8,
|
||||
0x76, 0xa9, 0x19, 0x5c, 0xe2, 0xbe, 0xb3, 0x9b, 0x4a, 0x39, 0xf5, 0xe6,
|
||||
0xbb, 0x11, 0x6e, 0x13, 0x13, 0x38, 0xb8, 0x1f, 0x21, 0x19, 0xf5, 0xa7,
|
||||
0x76, 0x93, 0xb3, 0x56, 0xfa, 0xcc, 0x74, 0xbc, 0x19, 0x02, 0x81, 0xc1,
|
||||
0x00, 0xd1, 0xd1, 0x72, 0x57, 0xe5, 0xb0, 0x1c, 0x09, 0x05, 0xbb, 0x55,
|
||||
0x89, 0x3c, 0x4a, 0x81, 0x90, 0x9a, 0xf9, 0x32, 0x63, 0x41, 0xad, 0x6a,
|
||||
0x5f, 0x65, 0x94, 0x92, 0xcc, 0xf7, 0xc7, 0x53, 0x93, 0xa0, 0xf7, 0xbe,
|
||||
0x48, 0x82, 0x63, 0x31, 0x7b, 0xd0, 0x82, 0x09, 0xbb, 0x0a, 0xbc, 0x60,
|
||||
0xc9, 0x4d, 0x83, 0xe4, 0x5d, 0x50, 0xe6, 0x5f, 0x8b, 0x47, 0x07, 0xa3,
|
||||
0x3a, 0x36, 0x97, 0xaa, 0x21, 0x70, 0x7f, 0xd5, 0x6c, 0xb0, 0x56, 0xf5,
|
||||
0x5c, 0x48, 0x74, 0x2a, 0xdd, 0xfe, 0x94, 0x83, 0x05, 0xe0, 0x3d, 0x5d,
|
||||
0xdd, 0x5a, 0x05, 0xcb, 0x47, 0xd7, 0xf9, 0x89, 0x55, 0xaa, 0x0b, 0x21,
|
||||
0xc0, 0x71, 0x5d, 0xe1, 0x4c, 0x6a, 0x45, 0x86, 0x86, 0xf2, 0xb9, 0x38,
|
||||
0x6a, 0x56, 0x51, 0x0d, 0x7d, 0xac, 0x30, 0x31, 0xca, 0x2d, 0xaa, 0xaa,
|
||||
0xba, 0xcc, 0x12, 0x40, 0xc1, 0x0d, 0xa6, 0xc1, 0x7d, 0x22, 0xec, 0xb6,
|
||||
0x51, 0x45, 0xfe, 0x4e, 0xbb, 0x4a, 0xd2, 0xba, 0x9b, 0xa2, 0xcc, 0x28,
|
||||
0x2b, 0x01, 0x53, 0x53, 0xf3, 0xa9, 0x5a, 0x8f, 0xeb, 0xb7, 0xb8, 0x62,
|
||||
0x6b, 0x8a, 0x79, 0x24, 0xcc, 0x86, 0x34, 0x45, 0xe2, 0xad, 0x1d, 0xd0,
|
||||
0x4c, 0xc9, 0x77, 0x2a, 0xf9, 0x1a, 0xe8, 0x58, 0x78, 0x51, 0x8a, 0xea,
|
||||
0x3f, 0x90, 0x36, 0x46, 0x2a, 0xc0, 0x71, 0x41, 0x83, 0x2c, 0x48, 0xee,
|
||||
0xc5, 0x02, 0x81, 0xc1, 0x00, 0xc9, 0xc8, 0xce, 0xc4, 0x50, 0xb2, 0x26,
|
||||
0xcb, 0x35, 0x78, 0x55, 0x3c, 0xcc, 0xf0, 0x7e, 0xba, 0xad, 0xeb, 0x58,
|
||||
0xe9, 0xb5, 0x78, 0x2f, 0x43, 0x5f, 0x07, 0x47, 0x56, 0x05, 0x41, 0x38,
|
||||
0x71, 0xe1, 0x58, 0x62, 0xb1, 0x8e, 0xbc, 0xf9, 0x80, 0x04, 0x22, 0x39,
|
||||
0x22, 0x24, 0x28, 0x86, 0x9c, 0x00, 0x44, 0x5f, 0xc4, 0x97, 0xe6, 0x71,
|
||||
0x5f, 0x1f, 0x58, 0xea, 0x75, 0x18, 0x0c, 0x23, 0x63, 0x09, 0xc5, 0x98,
|
||||
0xc4, 0x6d, 0x23, 0xc2, 0x2c, 0x93, 0x6a, 0x26, 0xe4, 0x3d, 0x8d, 0xa1,
|
||||
0x39, 0x70, 0x34, 0x25, 0xcd, 0xbc, 0x82, 0x78, 0x2b, 0xf3, 0x7e, 0x81,
|
||||
0xb6, 0x5f, 0xc5, 0x69, 0xd0, 0x81, 0x69, 0x50, 0x2f, 0x17, 0x0c, 0x17,
|
||||
0x3c, 0x0b, 0x45, 0x38, 0xce, 0xe3, 0xbf, 0x8a, 0x50, 0x0a, 0x00, 0x74,
|
||||
0x7e, 0x7a, 0xd8, 0x55, 0x52, 0x6b, 0x82, 0xfb, 0x34, 0x15, 0x73, 0x6a,
|
||||
0xf4, 0x51, 0x9b, 0x9f, 0xa0, 0x45, 0xb9, 0x76, 0xe5, 0xd3, 0xd5, 0xf4,
|
||||
0xa9, 0xa4, 0xcd, 0x42, 0x2f, 0x29, 0x89, 0xec, 0x28, 0x5f, 0x03, 0x45,
|
||||
0x27, 0xaf, 0x8c, 0x39, 0x3e, 0x59, 0x9d, 0xaf, 0x27, 0x5d, 0x17, 0x53,
|
||||
0x17, 0xeb, 0x8d, 0x7f, 0x3d, 0xb8, 0x2a, 0x50, 0x1e, 0xb5, 0xc5, 0x04,
|
||||
0xab, 0x9c, 0xa7, 0xaa, 0x86, 0x41, 0xb9, 0x36, 0x29, 0x9e, 0xd2, 0xd8,
|
||||
0xde, 0x5f, 0xde, 0x80, 0xdb, 0x02, 0x81, 0xc0, 0x03, 0xf3, 0x5f, 0xa5,
|
||||
0xcc, 0x0b, 0x5e, 0xdb, 0xc4, 0xa1, 0xdc, 0x60, 0x73, 0x24, 0x2c, 0x00,
|
||||
0x5f, 0x0a, 0xa6, 0x2a, 0x3c, 0x48, 0x59, 0xa2, 0x66, 0x35, 0x3f, 0xf6,
|
||||
0x60, 0x0b, 0xfe, 0xc4, 0xde, 0xd9, 0x0b, 0x5a, 0x2e, 0x2a, 0x53, 0xfa,
|
||||
0x32, 0xd8, 0xdf, 0xfa, 0x07, 0x9f, 0xb8, 0x6a, 0xd1, 0xec, 0xd3, 0xd5,
|
||||
0xf5, 0xfa, 0x00, 0x7e, 0x8c, 0xdd, 0xd5, 0xf2, 0xf8, 0xa8, 0x2e, 0x69,
|
||||
0xe6, 0xc6, 0x61, 0x6c, 0x64, 0x7d, 0x9e, 0xad, 0x18, 0x28, 0x27, 0xce,
|
||||
0x7a, 0x46, 0xad, 0x98, 0xe4, 0xba, 0x03, 0x14, 0x71, 0xe7, 0x7e, 0x06,
|
||||
0x62, 0x48, 0xae, 0x8f, 0x50, 0x5e, 0x59, 0x4a, 0x58, 0x58, 0x1e, 0x2f,
|
||||
0xe4, 0x28, 0x5e, 0xfa, 0x17, 0x83, 0xe9, 0x4e, 0x07, 0x46, 0x0b, 0x6c,
|
||||
0xfc, 0x5b, 0x03, 0xf4, 0xfc, 0x9b, 0x24, 0x0f, 0xd4, 0x5b, 0xdb, 0xa0,
|
||||
0x46, 0xf3, 0x86, 0xdd, 0x26, 0x55, 0x32, 0xb1, 0xa1, 0x11, 0xc2, 0xc5,
|
||||
0xc0, 0x08, 0xeb, 0xbe, 0x96, 0x78, 0x25, 0xa1, 0x79, 0xaa, 0xe9, 0xff,
|
||||
0xc2, 0x86, 0x94, 0x03, 0x2a, 0x38, 0x6c, 0x91, 0xfd, 0xcf, 0x7e, 0x23,
|
||||
0xe3, 0xbb, 0x04, 0x3d, 0xda, 0x68, 0x9f, 0x4d, 0x72, 0xd5, 0xad, 0x97,
|
||||
0x77, 0x2c, 0x3c, 0xce, 0x37, 0x2a, 0xd8, 0x72, 0x4d, 0xf2, 0xd7, 0xab,
|
||||
0x62, 0x68, 0x3f, 0x85, 0x8a, 0xc5, 0xec, 0xc9, 0x02, 0x81, 0xc1, 0x00,
|
||||
0x92, 0x43, 0x0c, 0x1d, 0x20, 0xa1, 0x01, 0x9d, 0xaa, 0x54, 0x5e, 0xf4,
|
||||
0x83, 0x58, 0x8f, 0x83, 0xa1, 0x2d, 0x46, 0x75, 0xa1, 0x24, 0x4c, 0x9d,
|
||||
0xf8, 0xf3, 0xbd, 0xb1, 0x8c, 0x7d, 0x89, 0xfc, 0x81, 0xeb, 0x1f, 0x1e,
|
||||
0xb4, 0xe8, 0x25, 0xb1, 0xb5, 0x4d, 0x59, 0x3c, 0x76, 0x19, 0x29, 0xf9,
|
||||
0x49, 0xf8, 0x45, 0xb2, 0xaa, 0xa8, 0x4e, 0xe5, 0x34, 0x43, 0xaf, 0x2e,
|
||||
0xd1, 0x0f, 0x7b, 0x56, 0xfe, 0x6e, 0x4c, 0x1d, 0x95, 0x3e, 0xa6, 0x30,
|
||||
0xc9, 0x69, 0xd8, 0x66, 0xf8, 0x77, 0x00, 0xb6, 0x31, 0xae, 0x9a, 0xf8,
|
||||
0x55, 0xfb, 0xfc, 0x3f, 0x5f, 0x70, 0x03, 0x75, 0xbe, 0x55, 0xca, 0x2d,
|
||||
0x68, 0xa0, 0x7d, 0x8e, 0xa4, 0x96, 0x0f, 0x01, 0x66, 0xe9, 0xf6, 0x13,
|
||||
0x80, 0xe2, 0x05, 0xcf, 0x9e, 0x70, 0x56, 0x00, 0x97, 0xea, 0xd7, 0x6d,
|
||||
0xb6, 0xa0, 0x6a, 0x95, 0x86, 0x36, 0xf2, 0xff, 0xc5, 0x67, 0x98, 0x7d,
|
||||
0x04, 0x0d, 0x3b, 0x31, 0xbc, 0x2b, 0x09, 0xfd, 0x2d, 0x87, 0xda, 0xc1,
|
||||
0x74, 0xca, 0x94, 0x73, 0x6e, 0xeb, 0x5f, 0xe5, 0x34, 0x49, 0xdf, 0xf4,
|
||||
0x61, 0xe0, 0xfa, 0x64, 0xfe, 0x05, 0x3a, 0x25, 0xcc, 0x87, 0xf4, 0x03,
|
||||
0x38, 0xca, 0xf2, 0xe8, 0x4f, 0xb9, 0x4f, 0x79, 0x55, 0x43, 0xf3, 0x46,
|
||||
0xfd, 0xbc, 0xd2, 0x95, 0xb8, 0x99, 0xfc, 0xb8, 0xb3, 0xa5, 0x04, 0xa1,
|
||||
0x02, 0x81, 0xc0, 0x47, 0xc6, 0x9c, 0x18, 0x54, 0xe5, 0xbb, 0xf9, 0xf4,
|
||||
0x38, 0xd2, 0xc0, 0xd1, 0x1a, 0xcc, 0xdb, 0x06, 0x87, 0x75, 0x1f, 0x13,
|
||||
0xa2, 0x7f, 0x8b, 0x45, 0x54, 0xcb, 0x43, 0xf8, 0xbb, 0x94, 0xd6, 0x2e,
|
||||
0x56, 0x5c, 0x69, 0x6d, 0x83, 0xb5, 0x45, 0x46, 0x68, 0x5c, 0x76, 0x1e,
|
||||
0x6c, 0x0c, 0x53, 0x59, 0xcc, 0x19, 0xc7, 0x81, 0x62, 0x66, 0x92, 0x02,
|
||||
0x8f, 0xa6, 0xdb, 0x50, 0x1c, 0x67, 0xfc, 0x82, 0x56, 0x2b, 0x4b, 0x1f,
|
||||
0x97, 0x87, 0xc4, 0x7d, 0x20, 0xda, 0xd3, 0x3f, 0x28, 0xf9, 0x55, 0xfe,
|
||||
0x84, 0x50, 0xc5, 0x3b, 0xd4, 0xaf, 0xf5, 0x3d, 0x43, 0xce, 0xdc, 0x55,
|
||||
0x11, 0x87, 0xdb, 0x72, 0x66, 0xcc, 0x83, 0xc4, 0x8b, 0x20, 0xae, 0x59,
|
||||
0x4d, 0xeb, 0xac, 0xb5, 0x4a, 0xec, 0x66, 0x09, 0x37, 0x55, 0x14, 0x21,
|
||||
0x57, 0xff, 0x0a, 0xac, 0xda, 0xb1, 0xae, 0x31, 0xab, 0x41, 0x30, 0x65,
|
||||
0x02, 0x83, 0xd1, 0xdb, 0x65, 0xb7, 0x52, 0xa7, 0x21, 0x9f, 0x1f, 0x8f,
|
||||
0x69, 0x23, 0x3b, 0xb8, 0xf9, 0x6d, 0xe7, 0xc1, 0x53, 0x9f, 0x8f, 0x67,
|
||||
0xfc, 0x6e, 0x20, 0x18, 0x31, 0x89, 0xe7, 0xbb, 0xd4, 0xc1, 0x03, 0x67,
|
||||
0xd6, 0xa5, 0x76, 0xc9, 0xea, 0x97, 0x93, 0x02, 0xca, 0x44, 0x52, 0x55,
|
||||
0x0f, 0xed, 0x55, 0xb5, 0x49, 0xd6, 0x94, 0x59, 0xee, 0xcc, 0x1b, 0x5a,
|
||||
0x00, 0x3d, 0xcd};
|
||||
|
||||
static const unsigned char kTestRsaPublicKey1_3072[] = {
|
||||
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5, 0x62, 0x07,
|
||||
0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, 0x76, 0xbe,
|
||||
0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f, 0xe9, 0x61,
|
||||
0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, 0xe4, 0xdd,
|
||||
0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b, 0x83, 0xbe,
|
||||
0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, 0xc2, 0x65,
|
||||
0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01, 0xb5, 0x11,
|
||||
0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, 0xb4, 0x7a,
|
||||
0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14, 0xf1, 0x22,
|
||||
0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, 0xa0, 0x0a,
|
||||
0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6, 0x0e, 0x85,
|
||||
0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, 0xf2, 0x59,
|
||||
0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31, 0xa0, 0xee,
|
||||
0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, 0xae, 0xb2,
|
||||
0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90, 0x97, 0x2c,
|
||||
0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, 0x94, 0x53,
|
||||
0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f, 0x07, 0x53,
|
||||
0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, 0xea, 0x1e,
|
||||
0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17, 0xe8, 0xc9,
|
||||
0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, 0xb7, 0xf5,
|
||||
0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf, 0x39, 0xf3,
|
||||
0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, 0x9c, 0xee,
|
||||
0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42, 0x72, 0x85,
|
||||
0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, 0xa6, 0x82,
|
||||
0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4, 0x3a, 0x3b,
|
||||
0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, 0xe9, 0x4c,
|
||||
0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72, 0x71, 0x04,
|
||||
0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, 0x8f, 0xe1,
|
||||
0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04, 0xb5, 0xd7,
|
||||
0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, 0x38, 0xff,
|
||||
0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33, 0x61, 0x53,
|
||||
0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, 0xd5, 0xf0,
|
||||
0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02, 0x03, 0x01,
|
||||
0x00, 0x01};
|
||||
|
||||
static const unsigned char kTestRsaPrivateKey2_2048[] = {
|
||||
0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
|
||||
0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4,
|
||||
0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f,
|
||||
0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e,
|
||||
0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa,
|
||||
0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7,
|
||||
0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b,
|
||||
0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68,
|
||||
0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8,
|
||||
0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2,
|
||||
0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73,
|
||||
0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e,
|
||||
0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd,
|
||||
0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88,
|
||||
0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88,
|
||||
0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8,
|
||||
0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2,
|
||||
0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e,
|
||||
0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f,
|
||||
0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29,
|
||||
0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e,
|
||||
0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f,
|
||||
0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01,
|
||||
0x00, 0x5e, 0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, 0x45, 0x0f,
|
||||
0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, 0xd5, 0xde, 0x33, 0x63, 0xd8, 0xb8,
|
||||
0xac, 0x97, 0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, 0x3b, 0x5c,
|
||||
0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, 0x46, 0xf5, 0xca, 0x2d, 0x8b, 0x3a,
|
||||
0x7e, 0xdc, 0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, 0x5e, 0x79,
|
||||
0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, 0xc1, 0x6b, 0x78, 0x04, 0x4e, 0x8e,
|
||||
0x79, 0xf9, 0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, 0x68, 0x7b,
|
||||
0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, 0xa7, 0x79, 0x5c, 0x7a, 0x81, 0xd1,
|
||||
0x71, 0xe7, 0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, 0xbe, 0x09,
|
||||
0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, 0x0e, 0x97, 0x8d, 0x89, 0x6e, 0xf1,
|
||||
0xe8, 0x88, 0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, 0x25, 0x0b,
|
||||
0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, 0xb6, 0xda, 0xc6, 0x24, 0x5a, 0xd0,
|
||||
0x37, 0x14, 0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, 0xde, 0x00,
|
||||
0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, 0x68, 0x71, 0x17, 0x66, 0x12, 0x1a,
|
||||
0x87, 0x27, 0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, 0x6f, 0x35,
|
||||
0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, 0x13, 0x86, 0x9c, 0x79, 0xd0, 0xb7,
|
||||
0xb6, 0x64, 0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, 0x1f, 0x51,
|
||||
0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, 0x70, 0x98, 0x0f, 0xee, 0xa8, 0x96,
|
||||
0x07, 0x5f, 0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, 0xf6, 0x06,
|
||||
0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, 0x96, 0xa9, 0x03, 0x17, 0xbb, 0x4e,
|
||||
0xc9, 0x21, 0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, 0xb2, 0x51,
|
||||
0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, 0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c,
|
||||
0xbe, 0x6d, 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, 0x78, 0xcc,
|
||||
0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85,
|
||||
0xab, 0x04, 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, 0x84, 0x68,
|
||||
0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86,
|
||||
0x7b, 0xd0, 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, 0x3a, 0x83,
|
||||
0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7,
|
||||
0x1e, 0x64, 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, 0x18, 0x6c,
|
||||
0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a,
|
||||
0xcc, 0x48, 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, 0x3c, 0x2d,
|
||||
0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, 0x26, 0x60, 0x48, 0x16,
|
||||
0xcb, 0xef, 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, 0x15, 0x43,
|
||||
0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49,
|
||||
0xc2, 0x19, 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, 0xcb, 0x43,
|
||||
0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17,
|
||||
0x68, 0x78, 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, 0xa7, 0x5b,
|
||||
0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d,
|
||||
0xeb, 0x3f, 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, 0xc1, 0xee,
|
||||
0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d,
|
||||
0x8e, 0x87, 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, 0x0d, 0x6c,
|
||||
0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, 0xed, 0x83, 0x51, 0x67,
|
||||
0xa9, 0x55, 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, 0x53, 0xa8,
|
||||
0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, 0x67, 0x9c, 0x32, 0x83,
|
||||
0x39, 0x57, 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, 0x0a, 0x2d,
|
||||
0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b,
|
||||
0x1b, 0x43, 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, 0xeb, 0xdb,
|
||||
0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25,
|
||||
0x92, 0xe4, 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, 0x37, 0x43,
|
||||
0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d,
|
||||
0x45, 0xdc, 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, 0xf9, 0xca,
|
||||
0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71,
|
||||
0x16, 0xd8, 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, 0xb1, 0x31,
|
||||
0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, 0x41, 0x29, 0x40, 0x19,
|
||||
0x83, 0x35, 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, 0xcc, 0x3b,
|
||||
0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, 0x63, 0x94, 0x49, 0x4c,
|
||||
0xad, 0x10, 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, 0x6a, 0xab,
|
||||
0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec,
|
||||
0x5e, 0x61, 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, 0x5f, 0xdf,
|
||||
0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22,
|
||||
0xfa, 0x34, 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, 0x77, 0xe6,
|
||||
0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a,
|
||||
0x2d, 0x0c, 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, 0xd9, 0xbe,
|
||||
0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96,
|
||||
0x76, 0xe8, 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, 0xac, 0x21,
|
||||
0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6,
|
||||
0x69, 0x1d, 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, 0x28, 0x66,
|
||||
0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9,
|
||||
0xe4, 0xc7, 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, 0x70, 0xcf,
|
||||
0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6,
|
||||
0x2b, 0xad, 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, 0x50, 0xcb,
|
||||
0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7,
|
||||
0xab, 0x30, 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, 0xa2, 0x0d,
|
||||
0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae,
|
||||
0x84, 0x6b, 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, 0x72, 0x2c,
|
||||
0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0,
|
||||
0xdb, 0x03};
|
||||
|
||||
static const unsigned char kTestRsaPublicKey2_2048[] = {
|
||||
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36,
|
||||
0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94,
|
||||
0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0,
|
||||
0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f,
|
||||
0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4,
|
||||
0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9,
|
||||
0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10,
|
||||
0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a,
|
||||
0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8,
|
||||
0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a,
|
||||
0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54,
|
||||
0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82,
|
||||
0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed,
|
||||
0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66,
|
||||
0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18,
|
||||
0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2,
|
||||
0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21,
|
||||
0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce,
|
||||
0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07,
|
||||
0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97,
|
||||
0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39,
|
||||
0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a,
|
||||
0x05, 0x02, 0x03, 0x01, 0x00, 0x01};
|
||||
|
||||
static const unsigned char kTestRsaPrivateKey3_2048[] = {
|
||||
0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
|
||||
0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40,
|
||||
0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83, 0xa7,
|
||||
0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44,
|
||||
0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a, 0x91,
|
||||
0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08,
|
||||
0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74, 0x10,
|
||||
0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d,
|
||||
0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47, 0x94,
|
||||
0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d,
|
||||
0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03, 0x68,
|
||||
0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55,
|
||||
0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c, 0xe8,
|
||||
0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7,
|
||||
0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0, 0x35,
|
||||
0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06,
|
||||
0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a, 0x21,
|
||||
0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae,
|
||||
0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87, 0x3c,
|
||||
0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84,
|
||||
0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f, 0x87,
|
||||
0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b,
|
||||
0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01,
|
||||
0x00, 0x43, 0x8f, 0x19, 0x83, 0xb1, 0x27, 0x4e, 0xee, 0x98, 0xba, 0xcb,
|
||||
0x54, 0xa0, 0x77, 0x11, 0x6d, 0xd4, 0x25, 0x31, 0x8c, 0xb0, 0x01, 0xcf,
|
||||
0xe6, 0x80, 0x83, 0x14, 0x40, 0x67, 0x39, 0x33, 0x67, 0x03, 0x1e, 0xa0,
|
||||
0x8b, 0xd1, 0x1d, 0xfd, 0x80, 0xa4, 0xb9, 0xe7, 0x57, 0x5e, 0xc8, 0x8e,
|
||||
0x79, 0x71, 0xd5, 0x6b, 0x09, 0xe9, 0x2b, 0x41, 0xa0, 0x33, 0x64, 0xc9,
|
||||
0x66, 0x33, 0xa1, 0xb1, 0x55, 0x07, 0x55, 0x98, 0x53, 0x10, 0xe6, 0xc0,
|
||||
0x39, 0x6d, 0x61, 0xd9, 0xe8, 0x16, 0x52, 0x28, 0xe4, 0x2b, 0xda, 0x27,
|
||||
0x01, 0xaf, 0x21, 0x4a, 0xe8, 0x55, 0x1d, 0x0b, 0xd1, 0x1c, 0xdc, 0xfd,
|
||||
0xb3, 0x0b, 0xa6, 0x5c, 0xcc, 0x6e, 0x77, 0xb8, 0xe0, 0xd1, 0x4e, 0x0a,
|
||||
0xd7, 0x7a, 0x5e, 0x18, 0xc3, 0xfb, 0xe9, 0xa1, 0x9c, 0xc3, 0x9c, 0xd4,
|
||||
0x4a, 0x7e, 0x70, 0x72, 0x11, 0x18, 0x24, 0x56, 0x24, 0xdf, 0xf8, 0xba,
|
||||
0xac, 0x5b, 0x54, 0xd3, 0xc4, 0x65, 0x69, 0xc8, 0x79, 0x94, 0x16, 0x88,
|
||||
0x9a, 0x68, 0x1c, 0xbc, 0xd4, 0xca, 0xec, 0x5e, 0x07, 0x4a, 0xc9, 0x54,
|
||||
0x7a, 0x4b, 0xdb, 0x19, 0x88, 0xf6, 0xbe, 0x50, 0x9d, 0x9e, 0x9d, 0x88,
|
||||
0x5b, 0x4a, 0x23, 0x86, 0x2b, 0xa9, 0xa6, 0x6c, 0x70, 0x7d, 0xe1, 0x11,
|
||||
0xba, 0xbf, 0x03, 0x2e, 0xf1, 0x46, 0x7e, 0x1b, 0xed, 0x06, 0x11, 0x57,
|
||||
0xad, 0x4a, 0xcb, 0xe5, 0xb1, 0x11, 0x05, 0x0a, 0x30, 0xb1, 0x73, 0x79,
|
||||
0xcd, 0x7a, 0x04, 0xcc, 0x70, 0xe9, 0x95, 0xe4, 0x27, 0xc2, 0xd5, 0x2d,
|
||||
0x92, 0x44, 0xdf, 0xb4, 0x94, 0xa8, 0x73, 0xa1, 0x4a, 0xc3, 0xcc, 0xc4,
|
||||
0x0e, 0x8d, 0xa1, 0x6a, 0xc2, 0xd8, 0x03, 0x7f, 0xfa, 0xa7, 0x76, 0x0d,
|
||||
0xad, 0x87, 0x88, 0xa0, 0x77, 0xaf, 0x3b, 0x23, 0xd1, 0x66, 0x0b, 0x31,
|
||||
0x2b, 0xaf, 0xef, 0xd5, 0x41, 0x02, 0x81, 0x81, 0x00, 0xdb, 0xc1, 0xe7,
|
||||
0xdd, 0xba, 0x3c, 0x1f, 0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb, 0xd2, 0x47,
|
||||
0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5, 0xda, 0xc2, 0x25, 0x64, 0x0a, 0x02,
|
||||
0xbc, 0x7d, 0x7f, 0x50, 0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d, 0x77, 0x6d,
|
||||
0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd, 0x18, 0xe9, 0x53, 0x0b, 0x5c, 0xa2,
|
||||
0x71, 0x14, 0x02, 0xfd, 0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c, 0x2a, 0x4e,
|
||||
0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26, 0xd0, 0xd8, 0x3d, 0xcf, 0x8c, 0x89,
|
||||
0x65, 0x6c, 0x35, 0x19, 0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49, 0xcd, 0x6d,
|
||||
0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9, 0xba, 0x43, 0xe5, 0xdd, 0x39, 0x3a,
|
||||
0x78, 0x8f, 0x07, 0xb8, 0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8, 0x07, 0x02,
|
||||
0x9b, 0x79, 0x26, 0x32, 0x22, 0x38, 0x91, 0x01, 0x90, 0x81, 0x29, 0x94,
|
||||
0xad, 0x77, 0xeb, 0x86, 0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x29, 0x88,
|
||||
0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77, 0x5d, 0x32, 0x12, 0x5e, 0xdf, 0x28,
|
||||
0x0c, 0x96, 0x0d, 0xa8, 0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0, 0x41, 0xcb,
|
||||
0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2, 0xfb, 0xd2, 0xf3, 0xf2, 0x95, 0xff,
|
||||
0xd8, 0x33, 0x3f, 0x8c, 0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe, 0x32, 0x66,
|
||||
0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe, 0x05, 0xb1, 0xbc, 0xbe, 0x44, 0x09,
|
||||
0xb4, 0xfe, 0x95, 0x06, 0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d, 0x22, 0xa0,
|
||||
0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09, 0x2c, 0xb8, 0xcc, 0xeb, 0x10, 0x4d,
|
||||
0xa7, 0xd0, 0x4b, 0x46, 0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47, 0x55, 0xd3,
|
||||
0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e, 0xf9, 0x5b, 0x7b, 0xae, 0x6d, 0x08,
|
||||
0x9f, 0x0c, 0x2a, 0xe1, 0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55, 0xa3, 0x6d,
|
||||
0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02, 0x81, 0x80, 0x61, 0x73, 0x3d, 0x64,
|
||||
0xff, 0xdf, 0x05, 0x8d, 0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d, 0x7d, 0x53,
|
||||
0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4, 0x72, 0x8f, 0xae, 0x2b, 0x1a, 0x47,
|
||||
0x87, 0xc7, 0x5b, 0x78, 0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3, 0x8c, 0x0c,
|
||||
0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a, 0x88, 0x7d, 0x40, 0xfe, 0x95, 0x32,
|
||||
0x5b, 0xd3, 0x6f, 0x90, 0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d, 0xc5, 0xc7,
|
||||
0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b, 0xcb, 0x78, 0xad, 0x7e, 0xfe, 0xb6,
|
||||
0xb1, 0x23, 0x63, 0x01, 0x94, 0x9a, 0x99, 0x05, 0x63, 0xda, 0xea, 0xf1,
|
||||
0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b, 0x35, 0xec, 0xcb, 0x0b, 0x43, 0xb8,
|
||||
0x8e, 0x84, 0xb8, 0x09, 0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c, 0x7c, 0x95,
|
||||
0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08, 0x09, 0x8f, 0x91, 0x17, 0x65, 0x4c,
|
||||
0xff, 0x6e, 0xbc, 0x51, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d, 0x9d, 0xd8,
|
||||
0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f, 0x85, 0xd7, 0xce, 0x72, 0x02, 0x38,
|
||||
0xf2, 0x0f, 0x9c, 0x27, 0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d, 0x02, 0x19,
|
||||
0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb, 0x61, 0x8e, 0xe6, 0xb8, 0xfc, 0x07,
|
||||
0x3c, 0xd1, 0x1b, 0x16, 0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26, 0xb2, 0xbd,
|
||||
0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11, 0xc5, 0xdd, 0xf9, 0xdb, 0x4e, 0x48,
|
||||
0x52, 0xd8, 0xe6, 0x4b, 0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1, 0x7c, 0xd3,
|
||||
0x51, 0x6b, 0x45, 0x46, 0x54, 0x11, 0x1a, 0x71, 0xd3, 0xcd, 0x6b, 0x8f,
|
||||
0x79, 0x22, 0x83, 0x02, 0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed, 0x32, 0xd8,
|
||||
0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec, 0x2c, 0x7e, 0xa4, 0x89, 0xdc, 0xbf,
|
||||
0xf9, 0x0d, 0x32, 0xc8, 0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc, 0x70, 0x4e,
|
||||
0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81, 0x81, 0x00, 0xd3, 0x35, 0x1b, 0x19,
|
||||
0x75, 0x3f, 0x61, 0xf2, 0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf, 0x0c, 0x0a,
|
||||
0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b, 0x13, 0x4d, 0x1f, 0x86, 0x58, 0x4f,
|
||||
0xd8, 0xee, 0xfa, 0x76, 0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2, 0xd2, 0x62,
|
||||
0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08, 0xa9, 0x83, 0x07, 0xac, 0x8c, 0xa3,
|
||||
0x7e, 0x18, 0xc0, 0x1c, 0x57, 0x63, 0x8d, 0x05, 0x17, 0x47, 0x1b, 0xd3,
|
||||
0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8, 0x1a, 0x43, 0x04, 0x36, 0xc8, 0x19,
|
||||
0xbe, 0xdc, 0xa6, 0xe5, 0x0f, 0x25, 0x62, 0x24, 0x96, 0x92, 0xb6, 0xb3,
|
||||
0x97, 0xad, 0x57, 0x9a, 0x90, 0x37, 0x4e, 0x31, 0x44, 0x74, 0xfa, 0x7c,
|
||||
0xb4, 0xea, 0xfc, 0x15, 0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e, 0xed, 0x5b,
|
||||
0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b, 0x21, 0xdc, 0x86, 0x17,
|
||||
0x6e, 0xc8, 0xee, 0x24};
|
||||
|
||||
static const unsigned char kTestRsaPublicKey3_2048[] = {
|
||||
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7,
|
||||
0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89,
|
||||
0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41,
|
||||
0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44, 0xb3, 0x08, 0x05,
|
||||
0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8,
|
||||
0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10,
|
||||
0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1,
|
||||
0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b,
|
||||
0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47, 0x94, 0x40, 0x70, 0xac,
|
||||
0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95,
|
||||
0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03, 0x68, 0x16, 0x31, 0xaa,
|
||||
0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4,
|
||||
0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf,
|
||||
0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b,
|
||||
0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66,
|
||||
0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06, 0xd9, 0x84, 0x51,
|
||||
0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd,
|
||||
0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee,
|
||||
0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89,
|
||||
0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a,
|
||||
0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1,
|
||||
0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69,
|
||||
0x6d, 0x02, 0x03, 0x01, 0x00, 0x01};
|
||||
|
||||
unsigned char kTestRsaPrivateKey2CarmichaelTotient_2048[] = {
|
||||
0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
|
||||
0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4,
|
||||
0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f,
|
||||
0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e,
|
||||
0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa,
|
||||
0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7,
|
||||
0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b,
|
||||
0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68,
|
||||
0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8,
|
||||
0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2,
|
||||
0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73,
|
||||
0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e,
|
||||
0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd,
|
||||
0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88,
|
||||
0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88,
|
||||
0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8,
|
||||
0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2,
|
||||
0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e,
|
||||
0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f,
|
||||
0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29,
|
||||
0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e,
|
||||
0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f,
|
||||
0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01,
|
||||
0x00, 0x0a, 0xf9, 0x4a, 0x19, 0x72, 0x88, 0x1b, 0x4e, 0xd8, 0x2f, 0xef,
|
||||
0x99, 0x93, 0x32, 0xda, 0x51, 0x21, 0x2e, 0x14, 0x06, 0xf4, 0xe9, 0x65,
|
||||
0x1c, 0xf9, 0xd4, 0xcf, 0x1a, 0x51, 0x53, 0xcd, 0x48, 0x33, 0x8c, 0x30,
|
||||
0xed, 0xdd, 0x53, 0x6f, 0x29, 0x82, 0xf9, 0xe0, 0x74, 0xde, 0xb1, 0x13,
|
||||
0x01, 0x88, 0x8f, 0xce, 0x14, 0xc1, 0x3b, 0x90, 0xb7, 0xcc, 0x6c, 0xdf,
|
||||
0x35, 0xa1, 0xf2, 0x1a, 0x3d, 0xbe, 0x19, 0xd7, 0x0a, 0xe4, 0x67, 0x75,
|
||||
0xbb, 0xfa, 0x87, 0xf4, 0x03, 0xb5, 0x7f, 0x69, 0xe4, 0x0b, 0x6a, 0xdc,
|
||||
0x92, 0x82, 0x54, 0x64, 0x1a, 0x94, 0x2d, 0xe4, 0x63, 0x40, 0xb2, 0xb4,
|
||||
0x85, 0x6b, 0xc8, 0x34, 0xba, 0xa2, 0x14, 0x30, 0x47, 0x1a, 0xeb, 0x90,
|
||||
0x62, 0x30, 0x43, 0x44, 0x02, 0xc7, 0x0c, 0x30, 0xc0, 0x7f, 0xa9, 0x47,
|
||||
0xae, 0xde, 0x68, 0x27, 0x92, 0xaa, 0x11, 0x95, 0xf5, 0x6f, 0xfc, 0x19,
|
||||
0x8b, 0x49, 0xa0, 0x77, 0x9d, 0xc6, 0x13, 0x5d, 0x73, 0xff, 0x45, 0xa2,
|
||||
0x4c, 0x3b, 0xf3, 0xe1, 0x2d, 0xd7, 0xc4, 0x70, 0xe2, 0x6c, 0x37, 0x99,
|
||||
0x4c, 0x7a, 0xa9, 0x27, 0xf8, 0x3a, 0xd6, 0xfd, 0xc5, 0xd8, 0xfa, 0x2d,
|
||||
0x0e, 0x71, 0x4b, 0x85, 0x7e, 0xce, 0xcb, 0x1c, 0x79, 0x71, 0xbd, 0xff,
|
||||
0x63, 0x03, 0x6b, 0x58, 0x68, 0xe0, 0x14, 0xca, 0x5e, 0x85, 0xfd, 0xd0,
|
||||
0xb7, 0xe0, 0x68, 0x14, 0xff, 0x2c, 0x82, 0x22, 0x26, 0x8a, 0x3f, 0xbf,
|
||||
0xb0, 0x2a, 0x90, 0xff, 0xc7, 0x72, 0xfc, 0x66, 0x51, 0x3e, 0x51, 0x9f,
|
||||
0x82, 0x68, 0x0e, 0xf3, 0x65, 0x74, 0x88, 0xab, 0xb7, 0xe5, 0x97, 0x5f,
|
||||
0x0f, 0x3e, 0xe5, 0x3a, 0xbc, 0xa4, 0xa1, 0x50, 0xdd, 0x5c, 0x94, 0x4b,
|
||||
0x0c, 0x70, 0x71, 0x48, 0x4e, 0xd0, 0xec, 0x46, 0x8f, 0xdf, 0xa2, 0x9a,
|
||||
0xfe, 0xd8, 0x35, 0x1a, 0x2f, 0x02, 0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c,
|
||||
0xbe, 0x6d, 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, 0x78, 0xcc,
|
||||
0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85,
|
||||
0xab, 0x04, 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, 0x84, 0x68,
|
||||
0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86,
|
||||
0x7b, 0xd0, 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, 0x3a, 0x83,
|
||||
0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7,
|
||||
0x1e, 0x64, 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, 0x18, 0x6c,
|
||||
0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a,
|
||||
0xcc, 0x48, 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, 0x3c, 0x2d,
|
||||
0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, 0x26, 0x60, 0x48, 0x16,
|
||||
0xcb, 0xef, 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, 0x15, 0x43,
|
||||
0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49,
|
||||
0xc2, 0x19, 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, 0xcb, 0x43,
|
||||
0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17,
|
||||
0x68, 0x78, 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, 0xa7, 0x5b,
|
||||
0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d,
|
||||
0xeb, 0x3f, 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, 0xc1, 0xee,
|
||||
0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d,
|
||||
0x8e, 0x87, 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, 0x0d, 0x6c,
|
||||
0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, 0xed, 0x83, 0x51, 0x67,
|
||||
0xa9, 0x55, 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, 0x53, 0xa8,
|
||||
0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, 0x67, 0x9c, 0x32, 0x83,
|
||||
0x39, 0x57, 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, 0x0a, 0x2d,
|
||||
0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b,
|
||||
0x1b, 0x43, 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, 0xeb, 0xdb,
|
||||
0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25,
|
||||
0x92, 0xe4, 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, 0x37, 0x43,
|
||||
0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d,
|
||||
0x45, 0xdc, 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, 0xf9, 0xca,
|
||||
0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71,
|
||||
0x16, 0xd8, 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, 0xb1, 0x31,
|
||||
0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, 0x41, 0x29, 0x40, 0x19,
|
||||
0x83, 0x35, 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, 0xcc, 0x3b,
|
||||
0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, 0x63, 0x94, 0x49, 0x4c,
|
||||
0xad, 0x10, 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, 0x6a, 0xab,
|
||||
0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec,
|
||||
0x5e, 0x61, 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, 0x5f, 0xdf,
|
||||
0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22,
|
||||
0xfa, 0x34, 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, 0x77, 0xe6,
|
||||
0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a,
|
||||
0x2d, 0x0c, 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, 0xd9, 0xbe,
|
||||
0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96,
|
||||
0x76, 0xe8, 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, 0xac, 0x21,
|
||||
0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6,
|
||||
0x69, 0x1d, 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, 0x28, 0x66,
|
||||
0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9,
|
||||
0xe4, 0xc7, 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, 0x70, 0xcf,
|
||||
0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6,
|
||||
0x2b, 0xad, 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, 0x50, 0xcb,
|
||||
0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7,
|
||||
0xab, 0x30, 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, 0xa2, 0x0d,
|
||||
0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae,
|
||||
0x84, 0x6b, 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, 0x72, 0x2c,
|
||||
0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0,
|
||||
0xdb, 0x03};
|
||||
|
||||
static const unsigned char kTestRsaPrivateKey3CarmichaelTotient_2048[] = {
|
||||
0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
|
||||
0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40,
|
||||
0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83, 0xa7,
|
||||
0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44,
|
||||
0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a, 0x91,
|
||||
0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08,
|
||||
0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74, 0x10,
|
||||
0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d,
|
||||
0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47, 0x94,
|
||||
0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d,
|
||||
0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03, 0x68,
|
||||
0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55,
|
||||
0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c, 0xe8,
|
||||
0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7,
|
||||
0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0, 0x35,
|
||||
0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06,
|
||||
0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a, 0x21,
|
||||
0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae,
|
||||
0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87, 0x3c,
|
||||
0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84,
|
||||
0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f, 0x87,
|
||||
0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b,
|
||||
0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01,
|
||||
0x00, 0x1a, 0x1a, 0xe3, 0xb4, 0x2d, 0x9b, 0xd0, 0x1d, 0xc4, 0x54, 0x50,
|
||||
0xc4, 0x98, 0xeb, 0xae, 0xf4, 0xab, 0x95, 0x72, 0x78, 0x60, 0xbe, 0x2e,
|
||||
0xfc, 0x88, 0x59, 0xc3, 0xff, 0x5f, 0xb4, 0x01, 0xfd, 0x2c, 0x06, 0x52,
|
||||
0xfa, 0xa4, 0x5b, 0xfc, 0x29, 0xdf, 0x82, 0x67, 0x14, 0x52, 0x39, 0x67,
|
||||
0xd5, 0x31, 0xc1, 0x41, 0x02, 0x76, 0x03, 0x5d, 0xd2, 0xc5, 0x74, 0x2c,
|
||||
0x24, 0x26, 0xfe, 0xed, 0x46, 0x65, 0x97, 0x22, 0x74, 0xe7, 0xff, 0x63,
|
||||
0x35, 0x3b, 0xd8, 0xad, 0x88, 0x2c, 0xe2, 0x50, 0xf3, 0x76, 0x14, 0xbe,
|
||||
0x3a, 0x37, 0x1b, 0xf0, 0x21, 0x91, 0x8e, 0xdd, 0x43, 0xed, 0xb7, 0xab,
|
||||
0xcd, 0xfb, 0x8a, 0x31, 0xa6, 0x26, 0xb4, 0xd5, 0x4b, 0x2c, 0x80, 0x7f,
|
||||
0xfc, 0x39, 0x24, 0x33, 0x7d, 0x6d, 0xf3, 0x1c, 0x06, 0xdd, 0x21, 0x53,
|
||||
0x70, 0x78, 0xe4, 0x07, 0x60, 0x2a, 0x3f, 0xb8, 0xd0, 0x47, 0xf6, 0x0e,
|
||||
0xbd, 0xde, 0x31, 0xf3, 0x66, 0xfe, 0x6e, 0x4b, 0x50, 0x75, 0x0b, 0x49,
|
||||
0x3a, 0x96, 0xeb, 0x63, 0xb9, 0x24, 0xbb, 0xfc, 0xcd, 0xf5, 0x49, 0x12,
|
||||
0xa9, 0x6d, 0x39, 0xd4, 0x18, 0x15, 0x14, 0x50, 0x82, 0xa9, 0x75, 0xeb,
|
||||
0x9f, 0x1a, 0xaa, 0x52, 0x1d, 0x0d, 0x55, 0x74, 0x30, 0x45, 0x3b, 0xd2,
|
||||
0xd3, 0xe1, 0xdb, 0x8d, 0xec, 0x38, 0x2b, 0xb0, 0xdd, 0xda, 0x10, 0xe3,
|
||||
0x40, 0x87, 0x27, 0xbe, 0x0b, 0xbf, 0x08, 0x9e, 0x25, 0x95, 0x14, 0xf4,
|
||||
0xd7, 0xfe, 0x8c, 0x4f, 0x23, 0xfd, 0x1b, 0xad, 0x83, 0x6b, 0x05, 0x3a,
|
||||
0x83, 0xfa, 0x65, 0x1e, 0x65, 0x12, 0xe3, 0x9f, 0xea, 0x52, 0xd7, 0xed,
|
||||
0x01, 0x7d, 0xc5, 0xf1, 0x96, 0x2d, 0xf5, 0x4a, 0xa3, 0xcb, 0x69, 0x6c,
|
||||
0x9a, 0x48, 0xe9, 0xf5, 0x01, 0xef, 0x1d, 0x2e, 0x90, 0x64, 0x6c, 0x0b,
|
||||
0x79, 0xe0, 0xeb, 0x64, 0x29, 0x02, 0x81, 0x81, 0x00, 0xdb, 0xc1, 0xe7,
|
||||
0xdd, 0xba, 0x3c, 0x1f, 0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb, 0xd2, 0x47,
|
||||
0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5, 0xda, 0xc2, 0x25, 0x64, 0x0a, 0x02,
|
||||
0xbc, 0x7d, 0x7f, 0x50, 0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d, 0x77, 0x6d,
|
||||
0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd, 0x18, 0xe9, 0x53, 0x0b, 0x5c, 0xa2,
|
||||
0x71, 0x14, 0x02, 0xfd, 0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c, 0x2a, 0x4e,
|
||||
0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26, 0xd0, 0xd8, 0x3d, 0xcf, 0x8c, 0x89,
|
||||
0x65, 0x6c, 0x35, 0x19, 0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49, 0xcd, 0x6d,
|
||||
0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9, 0xba, 0x43, 0xe5, 0xdd, 0x39, 0x3a,
|
||||
0x78, 0x8f, 0x07, 0xb8, 0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8, 0x07, 0x02,
|
||||
0x9b, 0x79, 0x26, 0x32, 0x22, 0x38, 0x91, 0x01, 0x90, 0x81, 0x29, 0x94,
|
||||
0xad, 0x77, 0xeb, 0x86, 0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x29, 0x88,
|
||||
0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77, 0x5d, 0x32, 0x12, 0x5e, 0xdf, 0x28,
|
||||
0x0c, 0x96, 0x0d, 0xa8, 0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0, 0x41, 0xcb,
|
||||
0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2, 0xfb, 0xd2, 0xf3, 0xf2, 0x95, 0xff,
|
||||
0xd8, 0x33, 0x3f, 0x8c, 0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe, 0x32, 0x66,
|
||||
0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe, 0x05, 0xb1, 0xbc, 0xbe, 0x44, 0x09,
|
||||
0xb4, 0xfe, 0x95, 0x06, 0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d, 0x22, 0xa0,
|
||||
0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09, 0x2c, 0xb8, 0xcc, 0xeb, 0x10, 0x4d,
|
||||
0xa7, 0xd0, 0x4b, 0x46, 0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47, 0x55, 0xd3,
|
||||
0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e, 0xf9, 0x5b, 0x7b, 0xae, 0x6d, 0x08,
|
||||
0x9f, 0x0c, 0x2a, 0xe1, 0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55, 0xa3, 0x6d,
|
||||
0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02, 0x81, 0x80, 0x61, 0x73, 0x3d, 0x64,
|
||||
0xff, 0xdf, 0x05, 0x8d, 0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d, 0x7d, 0x53,
|
||||
0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4, 0x72, 0x8f, 0xae, 0x2b, 0x1a, 0x47,
|
||||
0x87, 0xc7, 0x5b, 0x78, 0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3, 0x8c, 0x0c,
|
||||
0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a, 0x88, 0x7d, 0x40, 0xfe, 0x95, 0x32,
|
||||
0x5b, 0xd3, 0x6f, 0x90, 0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d, 0xc5, 0xc7,
|
||||
0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b, 0xcb, 0x78, 0xad, 0x7e, 0xfe, 0xb6,
|
||||
0xb1, 0x23, 0x63, 0x01, 0x94, 0x9a, 0x99, 0x05, 0x63, 0xda, 0xea, 0xf1,
|
||||
0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b, 0x35, 0xec, 0xcb, 0x0b, 0x43, 0xb8,
|
||||
0x8e, 0x84, 0xb8, 0x09, 0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c, 0x7c, 0x95,
|
||||
0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08, 0x09, 0x8f, 0x91, 0x17, 0x65, 0x4c,
|
||||
0xff, 0x6e, 0xbc, 0x51, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d, 0x9d, 0xd8,
|
||||
0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f, 0x85, 0xd7, 0xce, 0x72, 0x02, 0x38,
|
||||
0xf2, 0x0f, 0x9c, 0x27, 0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d, 0x02, 0x19,
|
||||
0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb, 0x61, 0x8e, 0xe6, 0xb8, 0xfc, 0x07,
|
||||
0x3c, 0xd1, 0x1b, 0x16, 0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26, 0xb2, 0xbd,
|
||||
0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11, 0xc5, 0xdd, 0xf9, 0xdb, 0x4e, 0x48,
|
||||
0x52, 0xd8, 0xe6, 0x4b, 0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1, 0x7c, 0xd3,
|
||||
0x51, 0x6b, 0x45, 0x46, 0x54, 0x11, 0x1a, 0x71, 0xd3, 0xcd, 0x6b, 0x8f,
|
||||
0x79, 0x22, 0x83, 0x02, 0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed, 0x32, 0xd8,
|
||||
0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec, 0x2c, 0x7e, 0xa4, 0x89, 0xdc, 0xbf,
|
||||
0xf9, 0x0d, 0x32, 0xc8, 0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc, 0x70, 0x4e,
|
||||
0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81, 0x81, 0x00, 0xd3, 0x35, 0x1b, 0x19,
|
||||
0x75, 0x3f, 0x61, 0xf2, 0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf, 0x0c, 0x0a,
|
||||
0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b, 0x13, 0x4d, 0x1f, 0x86, 0x58, 0x4f,
|
||||
0xd8, 0xee, 0xfa, 0x76, 0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2, 0xd2, 0x62,
|
||||
0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08, 0xa9, 0x83, 0x07, 0xac, 0x8c, 0xa3,
|
||||
0x7e, 0x18, 0xc0, 0x1c, 0x57, 0x63, 0x8d, 0x05, 0x17, 0x47, 0x1b, 0xd3,
|
||||
0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8, 0x1a, 0x43, 0x04, 0x36, 0xc8, 0x19,
|
||||
0xbe, 0xdc, 0xa6, 0xe5, 0x0f, 0x25, 0x62, 0x24, 0x96, 0x92, 0xb6, 0xb3,
|
||||
0x97, 0xad, 0x57, 0x9a, 0x90, 0x37, 0x4e, 0x31, 0x44, 0x74, 0xfa, 0x7c,
|
||||
0xb4, 0xea, 0xfc, 0x15, 0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e, 0xed, 0x5b,
|
||||
0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b, 0x21, 0xdc, 0x86, 0x17,
|
||||
0x6e, 0xc8, 0xee, 0x24};
|
||||
|
||||
static const unsigned char kTestRsaPrivateKey4CarmichaelTotient_2048[] = {
|
||||
0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
|
||||
0xb5, 0xed, 0x90, 0x77, 0xfe, 0xb4, 0xac, 0x2c, 0x7a, 0x53, 0x0a, 0xe0,
|
||||
0x94, 0x87, 0xfe, 0xc7, 0xc4, 0xa8, 0x3f, 0x09, 0x5b, 0xdd, 0xa7, 0x6e,
|
||||
0x70, 0xc9, 0xf0, 0xef, 0x9e, 0x47, 0xad, 0xcf, 0xbe, 0xdc, 0x9b, 0x89,
|
||||
0xc7, 0xbd, 0xb5, 0x2f, 0xa9, 0x43, 0x97, 0xd5, 0x5b, 0xae, 0x73, 0x46,
|
||||
0xa9, 0x10, 0x47, 0x67, 0x35, 0xa0, 0x6e, 0xfc, 0xfb, 0x45, 0xf8, 0x85,
|
||||
0xb0, 0xf1, 0xd3, 0x67, 0x84, 0x91, 0x03, 0x1b, 0x90, 0xb1, 0x18, 0x12,
|
||||
0xa4, 0x15, 0x7a, 0x65, 0xfa, 0x74, 0x4f, 0x73, 0x34, 0x70, 0x9b, 0x64,
|
||||
0x91, 0x1f, 0x89, 0x4f, 0x40, 0x2d, 0x37, 0x2f, 0xbf, 0x2b, 0x1e, 0x54,
|
||||
0x95, 0x7c, 0x16, 0x14, 0xd3, 0x60, 0xf4, 0xd7, 0x86, 0xf7, 0x0c, 0x2c,
|
||||
0xbb, 0x3f, 0xe7, 0x49, 0xdf, 0x74, 0x8b, 0xc9, 0x8f, 0x7c, 0x22, 0x23,
|
||||
0xd9, 0xbd, 0x2a, 0x57, 0xfe, 0xfb, 0x7a, 0x77, 0x91, 0xce, 0xa9, 0xd8,
|
||||
0xa7, 0xfb, 0xf5, 0xe7, 0x39, 0xbe, 0xfb, 0xf5, 0x47, 0x57, 0x48, 0xe7,
|
||||
0x71, 0x37, 0xda, 0x53, 0xe7, 0x89, 0xd3, 0x37, 0x31, 0x98, 0xba, 0xd8,
|
||||
0xd0, 0xd9, 0x74, 0xb2, 0xda, 0x06, 0x99, 0x3b, 0x97, 0x87, 0xc4, 0x22,
|
||||
0x3c, 0xf7, 0xa3, 0x13, 0x84, 0x49, 0x27, 0x74, 0x50, 0x03, 0x51, 0xea,
|
||||
0xc9, 0x57, 0xeb, 0x16, 0xa9, 0x7a, 0xea, 0x3c, 0x26, 0xda, 0xcb, 0xc6,
|
||||
0x86, 0x85, 0xfc, 0xd0, 0x73, 0x26, 0xce, 0xe8, 0xd9, 0x92, 0x09, 0xaf,
|
||||
0x68, 0x0c, 0x01, 0x8d, 0x95, 0xfd, 0x8d, 0x01, 0xde, 0x26, 0x0f, 0x25,
|
||||
0xb9, 0xd5, 0x80, 0x99, 0x62, 0xb1, 0x71, 0xcd, 0xa1, 0x5f, 0x52, 0xab,
|
||||
0x97, 0x97, 0x3f, 0xa7, 0x93, 0x40, 0x89, 0x4f, 0xbc, 0x7d, 0x99, 0x7a,
|
||||
0x7e, 0x35, 0xa5, 0xfc, 0x74, 0xfb, 0xe4, 0x5a, 0x0b, 0xed, 0xf0, 0xef,
|
||||
0x65, 0xed, 0x22, 0x95, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01,
|
||||
0x00, 0x35, 0x4a, 0x9d, 0x36, 0xa1, 0x3c, 0x50, 0x71, 0x08, 0xf6, 0x19,
|
||||
0x6a, 0x16, 0xe8, 0x4d, 0x68, 0x3c, 0x41, 0xa2, 0x91, 0x7d, 0xc1, 0x0c,
|
||||
0xa2, 0x2b, 0xd4, 0xe3, 0xc8, 0x75, 0x3f, 0x7e, 0xf9, 0x2b, 0x6a, 0x18,
|
||||
0xff, 0xbf, 0xac, 0x61, 0x0e, 0x50, 0x91, 0x55, 0xc1, 0x30, 0x85, 0x86,
|
||||
0x0c, 0x0d, 0x4b, 0x10, 0xf7, 0x79, 0x3c, 0x81, 0x36, 0x86, 0xee, 0x84,
|
||||
0xb1, 0xb8, 0xd6, 0xe5, 0xbb, 0xdd, 0x97, 0xd2, 0xe6, 0xb8, 0xb8, 0x3f,
|
||||
0x9a, 0x7a, 0x49, 0x36, 0x5c, 0xf8, 0x04, 0x29, 0x1f, 0xd0, 0x9d, 0x29,
|
||||
0xcf, 0xc8, 0x39, 0x0a, 0x32, 0x56, 0x54, 0xc8, 0x65, 0x2a, 0xa5, 0x19,
|
||||
0x51, 0xe2, 0xa6, 0x02, 0x1b, 0xe0, 0x9d, 0x76, 0xab, 0x49, 0xc4, 0x45,
|
||||
0x63, 0x37, 0x08, 0xad, 0x9a, 0x34, 0xa4, 0x41, 0xac, 0x6d, 0xe5, 0x09,
|
||||
0x65, 0x22, 0x0b, 0xa9, 0x03, 0x34, 0xd4, 0x7a, 0x97, 0x5c, 0x5d, 0xdc,
|
||||
0xa3, 0xae, 0xfd, 0xe8, 0xd6, 0xe8, 0xf8, 0x52, 0x44, 0x9c, 0xf6, 0x00,
|
||||
0xe3, 0x8e, 0xad, 0xee, 0xa3, 0x82, 0x8a, 0x85, 0x07, 0x8b, 0x46, 0x12,
|
||||
0x0f, 0xa1, 0xca, 0x07, 0x0d, 0x50, 0x02, 0x96, 0x71, 0x2f, 0x1b, 0x2b,
|
||||
0x59, 0x17, 0x95, 0xec, 0x90, 0xa6, 0xb9, 0x05, 0xff, 0xa2, 0x25, 0x75,
|
||||
0xc5, 0x57, 0x42, 0x37, 0x12, 0xa0, 0xb6, 0x09, 0xa1, 0xe0, 0xef, 0x70,
|
||||
0x85, 0xa7, 0x39, 0x47, 0xb1, 0x72, 0x78, 0x0d, 0xf4, 0x4a, 0xa7, 0xca,
|
||||
0x92, 0x68, 0x2c, 0xe8, 0x53, 0x54, 0x52, 0xe2, 0x21, 0xce, 0xaf, 0x7e,
|
||||
0x56, 0x05, 0x74, 0xe1, 0xb7, 0x18, 0xed, 0x7f, 0x83, 0xf6, 0x7d, 0x99,
|
||||
0x33, 0xf9, 0x60, 0x24, 0xae, 0x59, 0x09, 0xc0, 0x95, 0x90, 0x5a, 0x19,
|
||||
0x25, 0xb0, 0x79, 0xfd, 0x0b, 0x2c, 0x73, 0x85, 0xc5, 0xa0, 0xe5, 0x7b,
|
||||
0x3a, 0x47, 0xce, 0x8d, 0x03, 0x02, 0x81, 0x81, 0x00, 0xe2, 0xd7, 0xeb,
|
||||
0x7f, 0xc1, 0x1f, 0x13, 0x8f, 0x52, 0x49, 0xa2, 0x83, 0x22, 0xb7, 0x3d,
|
||||
0x93, 0xfe, 0xe7, 0x20, 0x03, 0xf6, 0x6c, 0xd8, 0x95, 0x25, 0xef, 0x5f,
|
||||
0xa4, 0xd4, 0x8f, 0xb1, 0x38, 0x39, 0x7c, 0xe0, 0xdd, 0xff, 0x56, 0xa7,
|
||||
0xdf, 0x59, 0xe1, 0x26, 0x36, 0x72, 0x78, 0xdf, 0xd4, 0x19, 0xc0, 0x37,
|
||||
0x58, 0x75, 0x85, 0xdd, 0x8b, 0x2e, 0x73, 0xfa, 0xf5, 0x65, 0x93, 0xc7,
|
||||
0x36, 0x3d, 0xb7, 0x8e, 0xa9, 0x5e, 0x41, 0x71, 0x55, 0x1d, 0x22, 0x83,
|
||||
0x13, 0x91, 0x76, 0xa4, 0x80, 0xa0, 0xac, 0xf6, 0xd5, 0xbb, 0x6a, 0xf3,
|
||||
0x47, 0x01, 0x81, 0x3d, 0x34, 0xd6, 0x32, 0x49, 0xb2, 0xc9, 0xa7, 0xb3,
|
||||
0x93, 0x64, 0x47, 0x6d, 0x30, 0x8b, 0x23, 0xbd, 0xf0, 0xe8, 0x3c, 0xb5,
|
||||
0x4e, 0x33, 0x74, 0x4b, 0x60, 0x38, 0x25, 0x89, 0xaf, 0xf8, 0x86, 0x53,
|
||||
0x73, 0x62, 0x74, 0x35, 0xb7, 0x02, 0x81, 0x81, 0x00, 0xcd, 0x4f, 0xbd,
|
||||
0xe1, 0x43, 0xb4, 0xfe, 0x9a, 0x50, 0xb8, 0xeb, 0xf3, 0xd6, 0x3f, 0x2a,
|
||||
0xbe, 0x19, 0x7a, 0x2c, 0xf4, 0x63, 0x35, 0x29, 0x47, 0x39, 0x17, 0xb4,
|
||||
0xc7, 0x58, 0xc0, 0x5a, 0xbf, 0x5c, 0xa4, 0xcf, 0x9e, 0x4b, 0xad, 0x5a,
|
||||
0x95, 0xfd, 0xfa, 0x50, 0xbe, 0x54, 0x12, 0xbe, 0xd8, 0x8e, 0xaf, 0x8b,
|
||||
0xb2, 0x15, 0xa1, 0xb5, 0x78, 0x03, 0xa6, 0x3b, 0x18, 0x13, 0x1d, 0xfe,
|
||||
0x38, 0xb7, 0x65, 0x1e, 0x20, 0x39, 0xa9, 0x25, 0x37, 0xed, 0xe8, 0x5b,
|
||||
0xc7, 0xd0, 0x60, 0xe2, 0x1f, 0xf2, 0x06, 0x5d, 0x3c, 0x5c, 0x36, 0xe0,
|
||||
0xc6, 0x83, 0x52, 0xd3, 0xd3, 0x3f, 0x7c, 0x43, 0x82, 0x4a, 0x13, 0xf8,
|
||||
0x8d, 0x48, 0x03, 0x4f, 0xb8, 0xad, 0x64, 0x85, 0xa9, 0xee, 0xde, 0x3d,
|
||||
0x0c, 0x23, 0xb6, 0xe9, 0x8d, 0xad, 0x02, 0x62, 0x32, 0x58, 0x39, 0xbf,
|
||||
0x5f, 0x3c, 0x6c, 0x0a, 0x13, 0x02, 0x81, 0x80, 0x7f, 0x87, 0x3c, 0x12,
|
||||
0x3a, 0x94, 0x29, 0xfe, 0xed, 0x18, 0x10, 0x91, 0x00, 0xb7, 0x5b, 0x9b,
|
||||
0x14, 0x37, 0x03, 0xbc, 0xb6, 0x91, 0x42, 0xc1, 0xb6, 0xed, 0xf8, 0x2b,
|
||||
0x46, 0x84, 0xf1, 0xf0, 0xd6, 0x00, 0xea, 0xba, 0x63, 0x8e, 0x68, 0x1a,
|
||||
0x1d, 0x01, 0x82, 0xe6, 0x21, 0x3c, 0xeb, 0x38, 0xe6, 0xb1, 0x35, 0x6c,
|
||||
0x39, 0xc5, 0xe4, 0x63, 0x16, 0xde, 0x85, 0x3b, 0xe8, 0xbb, 0x47, 0xc7,
|
||||
0xaa, 0xb2, 0xc3, 0x35, 0x5c, 0x94, 0x16, 0x0e, 0xef, 0xae, 0x33, 0x5a,
|
||||
0x90, 0xf0, 0xce, 0x52, 0xb2, 0x02, 0x0b, 0x52, 0xe5, 0x66, 0x9f, 0x16,
|
||||
0x50, 0x36, 0x44, 0x1e, 0xac, 0x3c, 0xe1, 0x49, 0xee, 0x2c, 0xa5, 0xbc,
|
||||
0x3b, 0x28, 0x1c, 0xae, 0xa9, 0xca, 0x92, 0x42, 0x19, 0x8d, 0xe7, 0xaf,
|
||||
0xeb, 0x25, 0x7a, 0x2a, 0xc1, 0xe5, 0xf8, 0x9e, 0x41, 0x6d, 0xe3, 0x04,
|
||||
0x7f, 0x59, 0x2d, 0xc9, 0x02, 0x81, 0x81, 0x00, 0xbe, 0xc3, 0x51, 0xdd,
|
||||
0x25, 0x48, 0xdd, 0xab, 0xca, 0x47, 0x17, 0xcd, 0x57, 0x0b, 0x18, 0x0e,
|
||||
0xcb, 0xa3, 0x4e, 0x73, 0xc0, 0x5e, 0x1b, 0xbd, 0x76, 0x99, 0xc5, 0x39,
|
||||
0xd8, 0x07, 0xda, 0x09, 0xa5, 0xed, 0xe8, 0x8e, 0xdf, 0x27, 0xf2, 0x5c,
|
||||
0x1d, 0x40, 0xe0, 0x97, 0x07, 0x8c, 0xe7, 0x50, 0x55, 0xbb, 0x5c, 0x24,
|
||||
0x1a, 0x9f, 0x46, 0xfa, 0x7d, 0x01, 0x8e, 0x34, 0xbf, 0x46, 0x85, 0xf8,
|
||||
0x72, 0xc6, 0x7c, 0x68, 0x5a, 0xcb, 0x03, 0xae, 0xe4, 0xd9, 0x99, 0xb5,
|
||||
0x9d, 0xb2, 0xf7, 0x47, 0xd1, 0x5c, 0x02, 0x73, 0x5c, 0x07, 0x0d, 0x70,
|
||||
0xc5, 0x82, 0x47, 0x19, 0x28, 0x0a, 0xb0, 0xbb, 0x35, 0x53, 0x3b, 0x05,
|
||||
0x22, 0x9d, 0x19, 0x0c, 0xb1, 0xe7, 0x0d, 0x9e, 0xa8, 0x38, 0x4c, 0x26,
|
||||
0xa4, 0x64, 0x86, 0xbb, 0x41, 0xbe, 0x4e, 0x39, 0x12, 0xea, 0x8d, 0x1a,
|
||||
0xd3, 0x0c, 0x5b, 0x8b, 0x02, 0x81, 0x81, 0x00, 0xa9, 0x2d, 0x4b, 0x43,
|
||||
0x55, 0x7f, 0x90, 0xb4, 0xea, 0xf9, 0xc2, 0x4c, 0x8c, 0x4d, 0xee, 0x62,
|
||||
0x85, 0xd2, 0x58, 0x71, 0xc7, 0xb3, 0xfb, 0x80, 0x37, 0xd9, 0xc5, 0x20,
|
||||
0x61, 0xeb, 0x39, 0x7f, 0x4d, 0x09, 0x7c, 0x32, 0x46, 0x8b, 0x49, 0xa4,
|
||||
0xd6, 0x99, 0xb4, 0xdc, 0xb6, 0xe3, 0x2a, 0x77, 0x53, 0xa3, 0xee, 0xdb,
|
||||
0x4d, 0xf9, 0x59, 0x20, 0x8a, 0x83, 0x94, 0x29, 0x72, 0x96, 0x76, 0x36,
|
||||
0x63, 0xbe, 0xa1, 0xe8, 0x94, 0xf9, 0x50, 0x71, 0x56, 0xaa, 0xeb, 0x90,
|
||||
0x06, 0xf2, 0x05, 0xb1, 0x19, 0xf0, 0x13, 0x89, 0x2d, 0x7b, 0x94, 0xb7,
|
||||
0xf2, 0x18, 0xfa, 0x52, 0x09, 0x82, 0x99, 0x20, 0xe9, 0x7a, 0x1c, 0x68,
|
||||
0xfe, 0x44, 0xe5, 0xf8, 0xc5, 0xd3, 0xc6, 0x66, 0x3d, 0xe3, 0x43, 0xc3,
|
||||
0x63, 0x81, 0xff, 0x78, 0x91, 0x5c, 0x48, 0x16, 0xbe, 0x92, 0x90, 0x2f,
|
||||
0x10, 0x9f, 0x2d, 0x17};
|
||||
|
||||
RsaTestKeys::RsaTestKeys()
|
||||
: private_key_1_3072_bits_(std::begin(kTestRsaPrivateKey1_3072),
|
||||
std::end(kTestRsaPrivateKey1_3072)),
|
||||
public_key_1_3072_bits_(std::begin(kTestRsaPublicKey1_3072),
|
||||
std::end(kTestRsaPublicKey1_3072)),
|
||||
private_key_2_2048_bits_(std::begin(kTestRsaPrivateKey2_2048),
|
||||
std::end(kTestRsaPrivateKey2_2048)),
|
||||
public_key_2_2048_bits_(std::begin(kTestRsaPublicKey2_2048),
|
||||
std::end(kTestRsaPublicKey2_2048)),
|
||||
private_key_3_2048_bits_(std::begin(kTestRsaPrivateKey3_2048),
|
||||
std::end(kTestRsaPrivateKey3_2048)),
|
||||
public_key_3_2048_bits_(std::begin(kTestRsaPublicKey3_2048),
|
||||
std::end(kTestRsaPublicKey3_2048)),
|
||||
private_key_2_carmichael_totient_2048_bits_(
|
||||
std::begin(kTestRsaPrivateKey2CarmichaelTotient_2048),
|
||||
std::end(kTestRsaPrivateKey2CarmichaelTotient_2048)),
|
||||
private_key_3_carmichael_totient_2048_bits_(
|
||||
std::begin(kTestRsaPrivateKey3CarmichaelTotient_2048),
|
||||
std::end(kTestRsaPrivateKey3CarmichaelTotient_2048)),
|
||||
private_key_4_carmichael_totient_2048_bits_(
|
||||
std::begin(kTestRsaPrivateKey4CarmichaelTotient_2048),
|
||||
std::end(kTestRsaPrivateKey4CarmichaelTotient_2048)) {}
|
||||
|
||||
} // namespace widevine
|
||||
92
common/rsa_test_keys.h
Normal file
92
common/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 COMMON_RSA_TEST_KEYS_H_
|
||||
#define COMMON_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 // COMMON_RSA_TEST_KEYS_H_
|
||||
516
common/rsa_util.cc
Normal file
516
common/rsa_util.cc
Normal file
@@ -0,0 +1,516 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Description:
|
||||
// RSA utility functions for serializing and deserializing RSA keys,
|
||||
// encryption, and signing.
|
||||
|
||||
#include "common/rsa_util.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/x509.h"
|
||||
|
||||
namespace {
|
||||
int BigNumGreaterThanPow2(const BIGNUM* b, int n) {
|
||||
if (BN_is_negative(b) || n == INT_MAX) {
|
||||
return 0;
|
||||
}
|
||||
int b_bits = BN_num_bits(b);
|
||||
return b_bits > n + 1 || (b_bits == n + 1 && !BN_is_pow2(b));
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
namespace widevine {
|
||||
namespace rsa_util {
|
||||
|
||||
static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
|
||||
bool serialize_private_key) {
|
||||
if (key == nullptr) {
|
||||
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
|
||||
<< " RSA key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (serialized_key == nullptr) {
|
||||
LOG(ERROR) << "Pointer to hold serialized RSA"
|
||||
<< (serialize_private_key ? "Private" : "Public")
|
||||
<< "Key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new returned nullptr";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
if ((serialize_private_key
|
||||
? i2d_RSAPrivateKey_bio(bio, const_cast<RSA*>(key))
|
||||
: i2d_RSAPublicKey_bio(bio, const_cast<RSA*>(key))) != 0) {
|
||||
int serialized_size = BIO_pending(bio);
|
||||
serialized_key->assign(serialized_size, 0);
|
||||
if (BIO_read(bio, &(*serialized_key)[0], serialized_size) ==
|
||||
serialized_size) {
|
||||
success = true;
|
||||
} else {
|
||||
LOG(ERROR) << "BIO_read failure";
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
|
||||
<< " key serialization failure";
|
||||
}
|
||||
BIO_free(bio);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key,
|
||||
bool deserialize_private_key) {
|
||||
if (serialized_key.empty()) {
|
||||
LOG(ERROR) << "Serialized RSA"
|
||||
<< (deserialize_private_key ? "Private" : "Public")
|
||||
<< "Key is empty.";
|
||||
return false;
|
||||
}
|
||||
if (key == nullptr) {
|
||||
LOG(ERROR) << "Pointer to hold new RSA "
|
||||
<< (deserialize_private_key ? "private" : "public")
|
||||
<< " key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
|
||||
serialized_key.size());
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
|
||||
return false;
|
||||
}
|
||||
*key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, nullptr)
|
||||
: d2i_RSAPublicKey_bio(bio, nullptr);
|
||||
BIO_free(bio);
|
||||
if (*key == nullptr) {
|
||||
LOG(ERROR) << (deserialize_private_key ? "Private" : "Public")
|
||||
<< " RSA key deserialization failure";
|
||||
}
|
||||
return *key != nullptr;
|
||||
}
|
||||
|
||||
bool SerializeRsaPrivateKey(const RSA* private_key,
|
||||
std::string* serialized_private_key) {
|
||||
return SerializeRsaKey(private_key, serialized_private_key, true);
|
||||
}
|
||||
|
||||
bool DeserializeRsaPrivateKey(const std::string& serialized_private_key,
|
||||
RSA** private_key) {
|
||||
return DeserializeRsaKey(serialized_private_key, private_key, true);
|
||||
}
|
||||
|
||||
bool SerializeRsaPublicKey(const RSA* public_key,
|
||||
std::string* serialized_public_key) {
|
||||
return SerializeRsaKey(public_key, serialized_public_key, false);
|
||||
}
|
||||
|
||||
bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
|
||||
RSA** public_key) {
|
||||
return DeserializeRsaKey(serialized_public_key, public_key, false);
|
||||
}
|
||||
|
||||
bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||
std::string* serialized_private_key) {
|
||||
if (private_key == nullptr) {
|
||||
LOG(ERROR) << "Private RSA key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (serialized_private_key == nullptr) {
|
||||
LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is nullptr.";
|
||||
return false;
|
||||
}
|
||||
// The following method of serializing a PKCS#8 PrivateKeyInfo object
|
||||
// was obtained from analyzing the openssl utility code, as the official
|
||||
// mechanism via i2d_PKCS8PrivateKey_bio is broken in the current openssl
|
||||
// version (1.0.0c). Please refer to b/8560683.
|
||||
EVP_PKEY* evp = EVP_PKEY_new();
|
||||
if (evp == nullptr) {
|
||||
LOG(ERROR) << "EVP_PKEY_new returned nullptr.";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
PKCS8_PRIV_KEY_INFO* pkcs8_pki = nullptr;
|
||||
BIO* bio = nullptr;
|
||||
if (EVP_PKEY_set1_RSA(evp, const_cast<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
|
||||
201
common/rsa_util.h
Normal file
201
common/rsa_util.h
Normal file
@@ -0,0 +1,201 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Description:
|
||||
// RSA utility functions for serializing and deserializing RSA keys,
|
||||
// encryption, and signing.
|
||||
|
||||
#ifndef COMMON_RSA_UTIL_H_
|
||||
#define COMMON_RSA_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "openssl/rsa.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace rsa_util {
|
||||
|
||||
// Serialize RSA private key into DER encoded PKCS#1 RSAPrivateKey.
|
||||
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||
// - serialized_private_key is a pointer to the std::string to hold the serialized
|
||||
// PKCS#1 RSAPrivateKey object. Caller retains ownership of the string. This
|
||||
// parameter must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool SerializeRsaPrivateKey(const RSA* private_key,
|
||||
std::string* serialized_private_key);
|
||||
|
||||
// Deserialize RSA private key from DER encoded PKCS#1 RSAPrivateKey.
|
||||
// - serialized_private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
|
||||
// deserialized.
|
||||
// - private_key is a pointer to an RSA structure pointer to point to a newly
|
||||
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||
// which is not allocated if the method fails. This parameter must not be
|
||||
// NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DeserializeRsaPrivateKey(const std::string& serialized_private_key,
|
||||
RSA** private_key);
|
||||
|
||||
// Serialize RSA key into DER encoded PKCS#1 RSAPublicKey.
|
||||
// - public_key is the RSA key to be serialized, which must not be NULL.
|
||||
// - serialized_public_key is a pointer to the std::string to hold the serialized
|
||||
// PKCS#1 RSAPublicKey object. Caller retains ownership of the string. This
|
||||
// parameter must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool SerializeRsaPublicKey(const RSA* public_key,
|
||||
std::string* serialized_public_key);
|
||||
|
||||
// Deserialize RSA public key from DER encoded PKCS#1 RSAPublicKey.
|
||||
// - serialized_public_key is the DER-encoded PKCS#1 RSAPublicKey to be
|
||||
// deserialized.
|
||||
// - public_key is a pointer to an RSA structure pointer to point to a newly
|
||||
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||
// which is not allocated if the method fails. This parameter must not be
|
||||
// NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
|
||||
RSA** public_key);
|
||||
|
||||
// Serialize RSA private key into DER encoded PKCS#8 PrivateKeyInfo.
|
||||
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||
// - serialized_private_key is a pointer to the std::string to hold the serialized
|
||||
// PKCS#8 PrivateKeyInfo object. Caller retains ownership of the string. This
|
||||
// parameter must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||
std::string* serialized_private_key);
|
||||
|
||||
// Deserialize RSA private key from DER encoded PKCS#8 PrivateKeyInfo.
|
||||
// - serialized_private_key is the DER-encoded PKCS#8 PrivateKeyInfo to be
|
||||
// deserialized.
|
||||
// - private_key is a pointer to an RSA structure pointer to point to a newly
|
||||
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||
// which is not allocated if the method fails. This parameter must not be
|
||||
// NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
|
||||
RSA** private_key);
|
||||
|
||||
// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8
|
||||
// PrivateKeyInfo.
|
||||
// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted.
|
||||
// - private_key_info is a pointer to std::string to hold the PKCS#8 PrivateKeyInfo.
|
||||
// The caller retains ownership of this parameter, which must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
std::string* private_key_info);
|
||||
|
||||
// Convert DER-encoded PKCS#8 PrivateKeyInfo to DER-encoded PKCS#1
|
||||
// RSAPrivateKey.
|
||||
// - private_key_info is the PKCS#8 PrivateKeyInfo to be converted.
|
||||
// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey.
|
||||
// The caller retains ownership of this parameter, which must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
std::string* rsa_private_key);
|
||||
|
||||
// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo.
|
||||
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||
// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption.
|
||||
// - serialized_private_key is a pointer to the std::string to hold the serialized
|
||||
// PKCS#8 EncryptedPrivateKeyInfo object. Caller retains ownership of the
|
||||
// string. This parameter must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
|
||||
const std::string& passphrase,
|
||||
std::string* serialized_private_key);
|
||||
|
||||
// Deserialize RSA private key from DER encoded PKCS#8 EncryptedPrivateKeyInfo.
|
||||
// - serialized_private_key is the DER-encoded PKCS#8 EncryptedPrivateKeyInfo to
|
||||
// be deserialized.
|
||||
// - passphrase is the password to use for key decryption.
|
||||
// - private_key is a pointer to an RSA structure pointer to point to a newly
|
||||
// allocated RSA structure. Caller assumes ownership of the new RSA pointer,
|
||||
// which is not allocated if the method fails. This parameter must not be
|
||||
// NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key,
|
||||
const std::string& passphrase,
|
||||
RSA** private_key);
|
||||
|
||||
// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8
|
||||
// EncryptedPrivateKeyInfo.
|
||||
// - rsa_private_key is the PKCS#1 RSAPrivateKey to be converted.
|
||||
// - passphrase is the password to use for PKCS#5 v2.1 AES-256-CBC encryption.
|
||||
// - private_key_info is a pointer to std::string to hold the PKCS#8
|
||||
// EncryptedPrivateKeyInfo.
|
||||
// The caller retains ownership of this parameter, which must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
const std::string& passphrase,
|
||||
std::string* private_key_info);
|
||||
|
||||
// Convert DER-encoded PKCS#8 EncryptedPrivateKeyInfo to DER-encoded PKCS#1
|
||||
// RSAPrivateKey.
|
||||
// - private_key_info is the PKCS#8 EncryptedPrivateKeyInfo to be converted.
|
||||
// - passphrase is the password to use for key decryption.
|
||||
// - rsa_private_key is a pointer to std::string to hold the PKCS#1 RSAPrivateKey.
|
||||
// The caller retains ownership of this parameter, which must not be NULL.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
const std::string& passphrase,
|
||||
std::string* rsa_private_key);
|
||||
|
||||
// This method changes |rsa| to use the more common, but not FIPS 186-4
|
||||
// compliant, computation for the RSA private exponent (d). This used to be the
|
||||
// form produced by the BoringSSL method RSA_generate_key. This changed in this
|
||||
// CL: https://boringssl-review.googlesource.com/c/boringssl/+/15944
|
||||
//
|
||||
// This method is used to produce a "backward-compatible" version. Some vendor
|
||||
// RSA implementations do not handle the new computation properly.
|
||||
//
|
||||
// - rsa is the openssl RSA structure. Must not be null. Caller retains
|
||||
// ownership.
|
||||
//
|
||||
// Returns true if the key was successfully updated, false otherwise.
|
||||
bool ConvertToEulerTotient(RSA* rsa);
|
||||
|
||||
// This is wrapper to the other SwitchToEulerTotient that supports deserializing
|
||||
// and serializing from and to a DER encoded PKCS#1 RSAPrivateKey.
|
||||
//
|
||||
// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
|
||||
// deserialized and converted.
|
||||
// - euler_private_key is a pointer to the std::string to hold the converted and
|
||||
// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the
|
||||
// string. This parameter must not be NULL.
|
||||
// Returns true if the key was successfully updated, false otherwise.
|
||||
bool ConvertToEulerTotient(const std::string& private_key,
|
||||
std::string* euler_private_key);
|
||||
|
||||
// This method changes |rsa| to use the FIPS 186-4 compliant computation for d.
|
||||
// This uses the Carmichael totient. This is equivalent to the way the key
|
||||
// is generated in BoringSSL as of this change:
|
||||
// https://boringssl-review.googlesource.com/c/boringssl/+/15944
|
||||
//
|
||||
// This method is mostly used for testing. It allows tests to convert back and
|
||||
// forth between forms and verify the output.
|
||||
//
|
||||
// Returns true if the key can be successfully updated, false otherwise.
|
||||
bool ConvertToCarmichaelTotient(RSA* rsa);
|
||||
|
||||
// This is wrapper to the other SwitchToCarmichaelTotient that supports
|
||||
// deserializing and serializing from and to a DER encoded PKCS#1 RSAPrivateKey.
|
||||
//
|
||||
// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
|
||||
// deserialized and converted.
|
||||
// - carmichael_private_key is a pointer to the std::string to hold the converted and
|
||||
// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the
|
||||
// string. This parameter must not be NULL.
|
||||
// Returns true if the key was successfully updated, false otherwise.
|
||||
bool ConvertToCarmichaelTotient(const std::string& private_key,
|
||||
std::string* carmichael_private_key);
|
||||
|
||||
} // namespace rsa_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_RSA_UTIL_H_
|
||||
353
common/rsa_util_test.cc
Normal file
353
common/rsa_util_test.cc
Normal file
@@ -0,0 +1,353 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <stddef.h>
|
||||
#include <memory>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "openssl/bn.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/rsa_util.h"
|
||||
|
||||
using ::testing::NotNull;
|
||||
|
||||
namespace {
|
||||
const uint32_t kRsaPublicExponent = 65537;
|
||||
const int kTestRsaBits = 2048;
|
||||
} // anonymous namespace
|
||||
|
||||
namespace widevine {
|
||||
namespace rsa_util {
|
||||
|
||||
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
|
||||
63
common/sha_util.cc
Normal file
63
common/sha_util.cc
Normal file
@@ -0,0 +1,63 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/sha_util.h"
|
||||
|
||||
#include <cstdint>
|
||||
#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 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
|
||||
31
common/sha_util.h
Normal file
31
common/sha_util.h
Normal file
@@ -0,0 +1,31 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_SHA_UTIL_H_
|
||||
#define COMMON_SHA_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Calculates SHA1 hash.
|
||||
std::string Sha1_Hash(const std::string& message);
|
||||
|
||||
// Calculates SHA256 hash.
|
||||
std::string Sha256_Hash(const std::string& message);
|
||||
|
||||
// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based,
|
||||
// 16-byte binary representation. Name_space is a GUID prefix; name is a unique
|
||||
// name in the namespace.
|
||||
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_SHA_UTIL_H_
|
||||
59
common/sha_util_test.cc
Normal file
59
common/sha_util_test.cc
Normal file
@@ -0,0 +1,59 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/sha_util.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
TEST(ShaUtilTest, Sha1Empty) {
|
||||
const uint8_t kExpected[] = {
|
||||
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
|
||||
0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha1_Hash(""));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, Sha256Empty) {
|
||||
const uint8_t kExpected[] = {
|
||||
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
|
||||
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
|
||||
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha256_Hash(""));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, Sha1) {
|
||||
const uint8_t kExpected[] = {
|
||||
0x6b, 0x63, 0xa5, 0xe3, 0x6b, 0xfe, 0x54, 0x19, 0x4e, 0xfe,
|
||||
0x9f, 0xe0, 0x29, 0x7e, 0xd8, 0x11, 0x0f, 0x10, 0x0d, 0x1d,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha1_Hash("random data"));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, Sha256) {
|
||||
const uint8_t kExpected[] = {
|
||||
0x62, 0x4b, 0x56, 0xb3, 0x38, 0x1b, 0xbc, 0xe0, 0x4f, 0x58, 0x88,
|
||||
0x83, 0xb4, 0x2f, 0x4e, 0x27, 0xfe, 0xc0, 0x95, 0x56, 0xf8, 0x61,
|
||||
0xcf, 0x94, 0x49, 0xe6, 0x5f, 0x26, 0xea, 0x70, 0xad, 0x88,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha256_Hash("random data"));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, GenerateSha1Uuid) {
|
||||
std::string name_space =
|
||||
absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774");
|
||||
std::string name = "some seed value";
|
||||
EXPECT_EQ("730621a55c675c4086be38e72aefa03e",
|
||||
absl::BytesToHexString(GenerateSha1Uuid(name_space, name)));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
66
common/signature_util.cc
Normal file
66
common/signature_util.cc
Normal file
@@ -0,0 +1,66 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/signature_util.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "util/status.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/sha_util.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace signature_util {
|
||||
|
||||
util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
||||
const std::string& aes_iv, std::string* signature) {
|
||||
if (signature == nullptr) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "signature is nullptr");
|
||||
}
|
||||
std::string hash = Sha1_Hash(message);
|
||||
if (hash.empty()) {
|
||||
return util::Status(util::error::INTERNAL, "Computed hash is empty");
|
||||
}
|
||||
std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash);
|
||||
if (sig.empty()) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Computed AES signature is empty");
|
||||
}
|
||||
*signature = sig;
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status GenerateRsaSignature(const std::string& message,
|
||||
const std::string& private_key,
|
||||
std::string* signature) {
|
||||
if (signature == nullptr) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "signature is nullptr");
|
||||
}
|
||||
std::unique_ptr<RsaPrivateKey> rsa_private_key(
|
||||
RsaPrivateKey::Create(private_key));
|
||||
if (rsa_private_key == nullptr) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Failed to construct a RsaPrivateKey");
|
||||
}
|
||||
std::string sig;
|
||||
if (!rsa_private_key->GenerateSignature(message, &sig)) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Failed to generate a RSA signature");
|
||||
}
|
||||
if (sig.empty()) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Computed RSA signature is empty");
|
||||
}
|
||||
*signature = sig;
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace signature_util
|
||||
} // namespace widevine
|
||||
34
common/signature_util.h
Normal file
34
common/signature_util.h
Normal file
@@ -0,0 +1,34 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_SIGNATURE_UTIL_H_
|
||||
#define COMMON_SIGNATURE_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/status.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace signature_util {
|
||||
|
||||
// Generates an AES signature of |message| using |aes_key| and |aes_iv|.
|
||||
// Signature is returned via |signature| if generation was successful.
|
||||
// Returns a Status that carries the details of error if generation failed.
|
||||
util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
||||
const std::string& aes_iv, std::string* signature);
|
||||
|
||||
// Generates a RSA signature of |message| using |private_key|.
|
||||
// Signature is returned via |sigature| if generation was successful.
|
||||
// Returns a Status that carries the details of error if generation failed.
|
||||
util::Status GenerateRsaSignature(const std::string& message,
|
||||
const std::string& private_key, std::string* signature);
|
||||
|
||||
} // namespace signature_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_SIGNATURE_UTIL_H_
|
||||
44
common/signing_key_util.cc
Normal file
44
common/signing_key_util.cc
Normal file
@@ -0,0 +1,44 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/signing_key_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using crypto_util::kSigningKeySizeBits;
|
||||
|
||||
uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version) {
|
||||
if (protocol_version <= VERSION_2_0) {
|
||||
return kSigningKeySizeBits;
|
||||
} else {
|
||||
return kSigningKeySizeBits * 2;
|
||||
}
|
||||
}
|
||||
|
||||
using crypto_util::kSigningKeySizeBytes;
|
||||
std::string GetClientSigningKey(const std::string& derived_key,
|
||||
ProtocolVersion protocol_version) {
|
||||
if (protocol_version == VERSION_2_0) {
|
||||
DCHECK(derived_key.size() >= kSigningKeySizeBytes);
|
||||
return derived_key.substr(0, kSigningKeySizeBytes);
|
||||
} else {
|
||||
DCHECK(derived_key.size() >= kSigningKeySizeBytes * 2);
|
||||
return derived_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetServerSigningKey(const std::string& derived_key) {
|
||||
DCHECK(derived_key.size() >= kSigningKeySizeBytes);
|
||||
return derived_key.substr(0, kSigningKeySizeBytes);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
55
common/signing_key_util.h
Normal file
55
common/signing_key_util.h
Normal file
@@ -0,0 +1,55 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Contains functions that are used to create and parse derived keys created
|
||||
// using the NIST 800-108 KDF recommendation, using AES-CMAC PRF.
|
||||
// NIST 800-108:
|
||||
// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf
|
||||
// AES-CMAC:
|
||||
// http://tools.ietf.org/html/rfc4493
|
||||
//
|
||||
// Example usage:
|
||||
// using video::widevine::common::crypto_util::DeriveKey;
|
||||
// using widevine_server::sdk::VERSION_2_1;
|
||||
//
|
||||
// std::string derived_key = DeriveKey(key_str,
|
||||
// label,
|
||||
// context,
|
||||
// SigningKeyMaterialSize(VERSION_2_1));
|
||||
// std::string server_derived_key = GetServerSigningKey(derived_key);
|
||||
// std::string client_derived_key = GetClientSigninKey(derived_key);
|
||||
#ifndef COMMON_SIGNING_KEY_UTIL_H_
|
||||
#define COMMON_SIGNING_KEY_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Returns the size of the signing key based on the License Protocol
|
||||
// Version. Signing keys for version 2.0 have a length of 256. Signing
|
||||
// keys for version 2.1 have a length of 512.
|
||||
uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version);
|
||||
|
||||
// Returns the client portion of the derived_key. The client portion
|
||||
// depend on the size of the key. Keys that are 512 bits in length
|
||||
// are assumed to be version 2.1 keys. The last 256 bits of those
|
||||
// keys are returned. Keys that are 256 bits in length are returned
|
||||
// in there entirety, version 2.0 keys.
|
||||
std::string GetClientSigningKey(const std::string& derived_key,
|
||||
ProtocolVersion protocol_version);
|
||||
|
||||
// Returns the server portion of the derived_key. The server portion
|
||||
// is the first 256 bits of the key.
|
||||
std::string GetServerSigningKey(const std::string& derived_key);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_SIGNING_KEY_UTIL_H_
|
||||
64
common/signing_key_util_test.cc
Normal file
64
common/signing_key_util_test.cc
Normal file
@@ -0,0 +1,64 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/signing_key_util.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace signing_key_util {
|
||||
namespace {
|
||||
const char* kFrontKeyHex =
|
||||
"0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b";
|
||||
const char* kBackKeyHex =
|
||||
"0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d";
|
||||
|
||||
std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) {
|
||||
if (protocol_version == widevine::VERSION_2_0) {
|
||||
return absl::HexStringToBytes(kFrontKeyHex);
|
||||
} else {
|
||||
return absl::HexStringToBytes(kFrontKeyHex) +
|
||||
absl::HexStringToBytes(kBackKeyHex);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_0) {
|
||||
ASSERT_EQ(crypto_util::kSigningKeySizeBits,
|
||||
SigningKeyMaterialSize(VERSION_2_0));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_1) {
|
||||
ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2,
|
||||
SigningKeyMaterialSize(VERSION_2_1));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) {
|
||||
ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey(
|
||||
GenerateDerivedKey(VERSION_2_1))));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_1) {
|
||||
ASSERT_EQ(kBackKeyHex, absl::BytesToHexString(GetClientSigningKey(
|
||||
GenerateDerivedKey(VERSION_2_1), VERSION_2_1)));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_0) {
|
||||
ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey(
|
||||
GenerateDerivedKey(VERSION_2_0))));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_0) {
|
||||
ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetClientSigningKey(
|
||||
GenerateDerivedKey(VERSION_2_0), VERSION_2_0)));
|
||||
}
|
||||
|
||||
} // namespace signing_key_util
|
||||
} // namespace widevine
|
||||
36
common/string_util.cc
Normal file
36
common/string_util.cc
Normal file
@@ -0,0 +1,36 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "util/status.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace string_util {
|
||||
|
||||
util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) {
|
||||
if (output == nullptr) {
|
||||
return util::Status(util::error::INTERNAL, "output is nullptr.");
|
||||
}
|
||||
|
||||
std::stringstream sstream(bitset);
|
||||
for (size_t i = 0; i < bitset.size(); i += 8) {
|
||||
std::bitset<8> bits;
|
||||
sstream >> bits;
|
||||
char c = static_cast<char>(bits.to_ulong());
|
||||
*output += c;
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace string_util
|
||||
} // namespace widevine
|
||||
25
common/string_util.h
Normal file
25
common/string_util.h
Normal file
@@ -0,0 +1,25 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_STRING_UTIL_H_
|
||||
#define COMMON_STRING_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
#include "util/status.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace string_util {
|
||||
|
||||
// Converts std::string representation of a bitset to its binary equivalent string.
|
||||
// For example, converts "01110100011001010111001101110100" to "test".
|
||||
util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output);
|
||||
|
||||
} // namespace string_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_STRING_UTIL_H_
|
||||
26
common/string_util_test.cc
Normal file
26
common/string_util_test.cc
Normal file
@@ -0,0 +1,26 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace string_util {
|
||||
|
||||
TEST(StringUtilTest, BitsetStringToBinaryString) {
|
||||
std::string input = "01110100011001010111001101110100";
|
||||
std::string output;
|
||||
|
||||
EXPECT_OK(BitsetStringToBinaryString(input, &output));
|
||||
|
||||
EXPECT_EQ("test", output);
|
||||
}
|
||||
|
||||
} // namespace string_util
|
||||
} // namespace widevine
|
||||
328
common/test_certificates.cc
Normal file
328
common/test_certificates.cc
Normal file
@@ -0,0 +1,328 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
|
||||
#include "common/test_certificates.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
static const unsigned char kTestRootCertificate[] = {
|
||||
0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
|
||||
0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5,
|
||||
0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78,
|
||||
0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f,
|
||||
0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca,
|
||||
0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b,
|
||||
0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46,
|
||||
0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01,
|
||||
0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c,
|
||||
0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14,
|
||||
0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd,
|
||||
0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6,
|
||||
0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde,
|
||||
0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31,
|
||||
0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a,
|
||||
0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90,
|
||||
0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49,
|
||||
0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f,
|
||||
0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda,
|
||||
0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17,
|
||||
0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30,
|
||||
0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf,
|
||||
0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16,
|
||||
0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42,
|
||||
0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9,
|
||||
0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4,
|
||||
0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a,
|
||||
0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72,
|
||||
0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde,
|
||||
0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04,
|
||||
0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b,
|
||||
0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33,
|
||||
0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67,
|
||||
0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02,
|
||||
0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a,
|
||||
0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35,
|
||||
0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe,
|
||||
0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49,
|
||||
0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6,
|
||||
0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58,
|
||||
0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce,
|
||||
0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27,
|
||||
0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5,
|
||||
0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c,
|
||||
0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e,
|
||||
0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12,
|
||||
0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54,
|
||||
0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8,
|
||||
0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1,
|
||||
0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1,
|
||||
0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8,
|
||||
0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4,
|
||||
0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9,
|
||||
0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd,
|
||||
0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9,
|
||||
0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f,
|
||||
0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92,
|
||||
0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15,
|
||||
0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0,
|
||||
0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9,
|
||||
0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1,
|
||||
0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2,
|
||||
0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c,
|
||||
0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36,
|
||||
0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35,
|
||||
0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7,
|
||||
0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44};
|
||||
|
||||
const unsigned char kTestIntermediateCertificate[] = {
|
||||
0x0a, 0xaf, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
|
||||
0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
|
||||
0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a,
|
||||
0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd,
|
||||
0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67,
|
||||
0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d,
|
||||
0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f,
|
||||
0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f,
|
||||
0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf,
|
||||
0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73,
|
||||
0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5,
|
||||
0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13,
|
||||
0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad,
|
||||
0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57,
|
||||
0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24,
|
||||
0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06,
|
||||
0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61,
|
||||
0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8,
|
||||
0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb,
|
||||
0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b,
|
||||
0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b,
|
||||
0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d,
|
||||
0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb,
|
||||
0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01,
|
||||
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40,
|
||||
0xa8, 0xd0, 0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf,
|
||||
0xc9, 0x26, 0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4,
|
||||
0x33, 0x8e, 0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b,
|
||||
0xc3, 0x4f, 0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51,
|
||||
0xce, 0x45, 0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4,
|
||||
0x5a, 0x02, 0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9,
|
||||
0xee, 0xc9, 0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb,
|
||||
0x1f, 0x7a, 0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9,
|
||||
0x26, 0xbe, 0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25,
|
||||
0xdb, 0x85, 0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba,
|
||||
0xb9, 0x3d, 0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80,
|
||||
0x12, 0x72, 0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98,
|
||||
0xab, 0x73, 0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1,
|
||||
0x75, 0x79, 0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98,
|
||||
0xe8, 0x40, 0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f,
|
||||
0x6f, 0x23, 0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30,
|
||||
0xaa, 0xcf, 0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b,
|
||||
0x7f, 0x91, 0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a,
|
||||
0x98, 0xad, 0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e,
|
||||
0x6e, 0x9d, 0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04,
|
||||
0x4f, 0x32, 0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52,
|
||||
0xfc, 0xcc, 0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa,
|
||||
0xb3, 0x77, 0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d,
|
||||
0x3c, 0x74, 0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f,
|
||||
0x13, 0x97, 0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94,
|
||||
0x3b, 0x31, 0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b,
|
||||
0x65, 0xe9, 0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d,
|
||||
0x0a, 0xfd, 0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4,
|
||||
0x83, 0xcf, 0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16,
|
||||
0x7b, 0x66, 0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d,
|
||||
0x94, 0x5d, 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b,
|
||||
0x55, 0xf8, 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
|
||||
|
||||
const unsigned char kTestUserDrmCertificate[] = {
|
||||
0x0a, 0xc1, 0x02, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42,
|
||||
0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18,
|
||||
0x91, 0xab, 0x4b, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
|
||||
0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51,
|
||||
0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d,
|
||||
0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c,
|
||||
0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32,
|
||||
0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7,
|
||||
0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3,
|
||||
0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7,
|
||||
0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc,
|
||||
0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93,
|
||||
0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99,
|
||||
0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc,
|
||||
0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6,
|
||||
0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a,
|
||||
0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44,
|
||||
0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec,
|
||||
0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea,
|
||||
0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e,
|
||||
0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94,
|
||||
0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8,
|
||||
0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16,
|
||||
0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd,
|
||||
0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01,
|
||||
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65,
|
||||
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x12, 0x80, 0x02, 0x62, 0xd5, 0x8b, 0xb6, 0x52, 0x94, 0xcb, 0x25, 0xba,
|
||||
0x68, 0x44, 0xdc, 0x6f, 0x03, 0xf6, 0x24, 0xc5, 0xba, 0x46, 0xd1, 0xa1,
|
||||
0x83, 0xe7, 0xaf, 0x94, 0x96, 0x8b, 0x57, 0x89, 0xd6, 0xa0, 0x99, 0x6f,
|
||||
0xed, 0xac, 0xfe, 0x6c, 0x9d, 0x80, 0x1c, 0xae, 0x34, 0xda, 0x49, 0x4f,
|
||||
0x10, 0x22, 0x3c, 0xdd, 0x77, 0xa0, 0x9a, 0x79, 0x73, 0x68, 0x66, 0xa4,
|
||||
0x6d, 0x1e, 0x82, 0xbf, 0xce, 0x06, 0x1a, 0x83, 0xcd, 0xa3, 0xed, 0x91,
|
||||
0xbe, 0xb1, 0xfe, 0xf3, 0xde, 0x63, 0x96, 0xd5, 0x24, 0x44, 0x46, 0x94,
|
||||
0x7f, 0xc2, 0x14, 0x19, 0x42, 0x08, 0x64, 0xef, 0x93, 0x81, 0x7a, 0x54,
|
||||
0x8b, 0x6e, 0xd9, 0xf5, 0x14, 0x88, 0x6c, 0x39, 0x6f, 0x0f, 0x70, 0x91,
|
||||
0x97, 0xd4, 0x24, 0x73, 0x9d, 0x12, 0x7a, 0xc8, 0x83, 0xd7, 0x2b, 0xc7,
|
||||
0xb7, 0xe1, 0x20, 0x6c, 0x28, 0x11, 0x6f, 0x56, 0x82, 0xf6, 0x1c, 0x4f,
|
||||
0x2d, 0x51, 0x0f, 0xd6, 0xd4, 0x14, 0xea, 0xac, 0x28, 0x66, 0xeb, 0x37,
|
||||
0xca, 0x00, 0x49, 0xff, 0xed, 0x8e, 0x8c, 0x3e, 0x4b, 0x9b, 0x12, 0x0e,
|
||||
0xbf, 0xcd, 0xb7, 0xe6, 0xed, 0xd6, 0x1f, 0x88, 0xe8, 0x99, 0x68, 0x1a,
|
||||
0xf8, 0xbb, 0xa2, 0x33, 0xfa, 0xb6, 0x21, 0xdf, 0xba, 0x24, 0x5c, 0x19,
|
||||
0xa2, 0xe7, 0x6f, 0x61, 0x90, 0x78, 0x21, 0xca, 0x2f, 0x84, 0xab, 0x9f,
|
||||
0xff, 0x37, 0x14, 0x33, 0x83, 0x43, 0x98, 0xeb, 0xa9, 0x88, 0xde, 0xad,
|
||||
0x3a, 0xd9, 0xe2, 0x5c, 0x26, 0xd3, 0x95, 0x72, 0xba, 0x8c, 0x77, 0xdf,
|
||||
0x90, 0x67, 0x4e, 0xbc, 0xda, 0x83, 0x09, 0x22, 0x70, 0x51, 0x84, 0x70,
|
||||
0x31, 0x25, 0x8b, 0xae, 0x5e, 0x19, 0xba, 0x97, 0xd7, 0x1f, 0x6a, 0xd7,
|
||||
0x95, 0xcf, 0xde, 0x8f, 0x93, 0x69, 0x88, 0x11, 0xbe, 0x8c, 0x6a, 0xfb,
|
||||
0x3c, 0x13, 0x87, 0x0e, 0x6c, 0xa5, 0xa0, 0x1a, 0xb5, 0x05, 0x0a, 0xaf,
|
||||
0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
|
||||
0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18, 0xb2, 0x92,
|
||||
0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
|
||||
0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40,
|
||||
0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7,
|
||||
0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56,
|
||||
0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e,
|
||||
0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34,
|
||||
0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31,
|
||||
0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e,
|
||||
0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39,
|
||||
0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2,
|
||||
0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54,
|
||||
0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3,
|
||||
0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71,
|
||||
0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96,
|
||||
0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a,
|
||||
0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c,
|
||||
0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f,
|
||||
0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca,
|
||||
0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77,
|
||||
0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27,
|
||||
0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c,
|
||||
0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c,
|
||||
0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x28, 0xd2,
|
||||
0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40, 0xa8, 0xd0,
|
||||
0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf, 0xc9, 0x26,
|
||||
0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4, 0x33, 0x8e,
|
||||
0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b, 0xc3, 0x4f,
|
||||
0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51, 0xce, 0x45,
|
||||
0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4, 0x5a, 0x02,
|
||||
0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9, 0xee, 0xc9,
|
||||
0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb, 0x1f, 0x7a,
|
||||
0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9, 0x26, 0xbe,
|
||||
0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25, 0xdb, 0x85,
|
||||
0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba, 0xb9, 0x3d,
|
||||
0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80, 0x12, 0x72,
|
||||
0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98, 0xab, 0x73,
|
||||
0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1, 0x75, 0x79,
|
||||
0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98, 0xe8, 0x40,
|
||||
0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f, 0x6f, 0x23,
|
||||
0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30, 0xaa, 0xcf,
|
||||
0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b, 0x7f, 0x91,
|
||||
0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a, 0x98, 0xad,
|
||||
0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e, 0x6e, 0x9d,
|
||||
0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04, 0x4f, 0x32,
|
||||
0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52, 0xfc, 0xcc,
|
||||
0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa, 0xb3, 0x77,
|
||||
0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d, 0x3c, 0x74,
|
||||
0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f, 0x13, 0x97,
|
||||
0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94, 0x3b, 0x31,
|
||||
0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b, 0x65, 0xe9,
|
||||
0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d, 0x0a, 0xfd,
|
||||
0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4, 0x83, 0xcf,
|
||||
0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16, 0x7b, 0x66,
|
||||
0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d, 0x94, 0x5d,
|
||||
0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, 0x55, 0xf8,
|
||||
0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
|
||||
|
||||
const unsigned char kTestDrmServiceCertificate[] = {
|
||||
0x0a, 0xbc, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32,
|
||||
0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18,
|
||||
0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02,
|
||||
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54,
|
||||
0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58,
|
||||
0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57,
|
||||
0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa,
|
||||
0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e,
|
||||
0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda,
|
||||
0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9,
|
||||
0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2,
|
||||
0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e,
|
||||
0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a,
|
||||
0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67,
|
||||
0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56,
|
||||
0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b,
|
||||
0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83,
|
||||
0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1,
|
||||
0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f,
|
||||
0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40,
|
||||
0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46,
|
||||
0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86,
|
||||
0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b,
|
||||
0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b,
|
||||
0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00,
|
||||
0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0x6e, 0xc3,
|
||||
0x5a, 0x17, 0xa8, 0xf9, 0xef, 0xee, 0x67, 0x4d, 0x0a, 0xef, 0x57, 0x5e,
|
||||
0xbc, 0x59, 0x3d, 0x22, 0x84, 0xa0, 0x0a, 0xf5, 0x84, 0x26, 0xb7, 0x8b,
|
||||
0xab, 0x91, 0x3e, 0x4b, 0xb9, 0x91, 0x3c, 0x50, 0xc9, 0x08, 0x2f, 0x97,
|
||||
0x0a, 0x91, 0xb5, 0x48, 0xe4, 0xba, 0xfd, 0x7b, 0xbd, 0xf0, 0xba, 0x08,
|
||||
0xb3, 0x29, 0xb4, 0x23, 0x74, 0xaf, 0x3f, 0xe9, 0x77, 0x78, 0x3f, 0xdc,
|
||||
0x3d, 0x8a, 0x37, 0xec, 0x1c, 0x3a, 0xff, 0x60, 0x8e, 0x10, 0x72, 0xaa,
|
||||
0x97, 0x98, 0x56, 0xa0, 0x35, 0xa9, 0xbf, 0x43, 0x21, 0x6a, 0x15, 0x88,
|
||||
0xba, 0xc0, 0x68, 0x01, 0x7b, 0xd7, 0x88, 0x2f, 0x1a, 0xc5, 0x1f, 0x54,
|
||||
0xf0, 0xea, 0x36, 0xb7, 0xed, 0x49, 0x78, 0x09, 0xb1, 0x07, 0x46, 0xfe,
|
||||
0xf4, 0xfa, 0x16, 0x0c, 0x46, 0x91, 0xe2, 0xa9, 0xe0, 0x8e, 0x97, 0xe5,
|
||||
0xea, 0x2f, 0xd9, 0x94, 0x1e, 0xe7, 0xba, 0x28, 0x98, 0x92, 0xae, 0xb8,
|
||||
0xb6, 0x6e, 0xf6, 0xd2, 0x50, 0xd3, 0x5b, 0x25, 0x12, 0x68, 0x5e, 0x07,
|
||||
0x82, 0x64, 0x27, 0xfe, 0x1a, 0xcd, 0x38, 0xa8, 0x00, 0x53, 0x8c, 0x69,
|
||||
0x51, 0x75, 0x71, 0xc2, 0x6a, 0x5f, 0x05, 0x13, 0x77, 0x2b, 0xc8, 0x6c,
|
||||
0xab, 0xd2, 0x64, 0x27, 0xbd, 0x21, 0xfc, 0x33, 0x0a, 0x3a, 0x53, 0xa6,
|
||||
0x28, 0x1c, 0x2a, 0xad, 0x23, 0x0a, 0x95, 0xe4, 0x38, 0x6b, 0x9b, 0x3e,
|
||||
0x77, 0x7d, 0x96, 0x20, 0x42, 0xf5, 0x18, 0xbe, 0xb0, 0x78, 0xe4, 0xf0,
|
||||
0x95, 0x6c, 0xd5, 0x30, 0xd6, 0xfc, 0x04, 0xe2, 0xf7, 0xff, 0x06, 0x6b,
|
||||
0xaf, 0xf1, 0x9c, 0x10, 0xa6, 0xdb, 0xed, 0x4a, 0x18, 0x68, 0x87, 0xda,
|
||||
0x43, 0x2c, 0x60, 0xc6, 0x0a, 0x72, 0x1e, 0x9f, 0x4b, 0x05, 0x80, 0x15,
|
||||
0x17, 0x84, 0xf1, 0xee, 0xcc, 0x80, 0x25, 0x33, 0x87, 0x74, 0x02, 0x8c,
|
||||
0xa1, 0xbb, 0xd9, 0x29, 0x33, 0x97, 0xbd, 0x5b, 0x1c, 0xed, 0xcc, 0x47,
|
||||
0xda, 0x73, 0xae, 0xb1, 0x75, 0xac, 0xf7, 0x39, 0xbe, 0x67, 0xc3, 0xaf,
|
||||
0x60, 0x07, 0xf5, 0xba, 0x81, 0xf4, 0x42, 0xad, 0x28, 0x8d, 0xe6, 0x63,
|
||||
0xea, 0x8a, 0x0e, 0x71, 0x53, 0x6e, 0x62, 0x8a, 0x23, 0x4f, 0xad, 0x2a,
|
||||
0x9a, 0xf6, 0xeb, 0xa8, 0x82, 0x83, 0xbb, 0x5f, 0xc9, 0x86, 0xd8, 0x76,
|
||||
0xb9, 0xf3, 0xe7, 0x32, 0xdd, 0xe0, 0x44, 0x6a, 0xab, 0x78, 0xa0, 0x8c,
|
||||
0xa4, 0x99, 0x6f, 0x71, 0x42, 0x8b, 0x31, 0x32, 0xbb, 0x80, 0x36, 0x61,
|
||||
0x1c, 0xe5, 0x6d, 0x87, 0xf2, 0x68, 0xca, 0xcd, 0xe0, 0x5f, 0xa2, 0x68,
|
||||
0x5b, 0xfc, 0x73, 0xc9, 0x26, 0x2b, 0x13, 0x05, 0x1c, 0xde, 0x19, 0xdf,
|
||||
0x34, 0xba, 0xf5, 0xec, 0xaf, 0x26, 0xfb, 0x64, 0xc4, 0x38, 0x7e, 0xdb,
|
||||
0x51, 0x28, 0x49, 0xa7, 0x12, 0x88, 0xa5, 0x6d, 0xa2, 0xfa};
|
||||
|
||||
TestCertificates::TestCertificates()
|
||||
: test_root_certificate_(
|
||||
kTestRootCertificate,
|
||||
kTestRootCertificate + sizeof(kTestRootCertificate)),
|
||||
test_intermediate_certificate_(
|
||||
kTestIntermediateCertificate,
|
||||
kTestIntermediateCertificate + sizeof(kTestIntermediateCertificate)),
|
||||
test_user_device_certificate_(
|
||||
kTestUserDrmCertificate,
|
||||
kTestUserDrmCertificate + sizeof(kTestUserDrmCertificate)),
|
||||
test_service_certificate_(
|
||||
kTestDrmServiceCertificate,
|
||||
kTestDrmServiceCertificate + sizeof(kTestDrmServiceCertificate)) {}
|
||||
|
||||
} // namespace widevine
|
||||
54
common/test_certificates.h
Normal file
54
common/test_certificates.h
Normal file
@@ -0,0 +1,54 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Class contains certificates that can be used for testing. Provides methods
|
||||
// to retrieve a test root certificate, a test intermediate certificate and a
|
||||
// test user device certificate.
|
||||
#ifndef COMMON_TEST_CERTIFICATES_H_
|
||||
#define COMMON_TEST_CERTIFICATES_H_
|
||||
|
||||
#include <string>
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class TestCertificates {
|
||||
public:
|
||||
TestCertificates();
|
||||
virtual ~TestCertificates() {}
|
||||
|
||||
// returns a test root certificate
|
||||
const std::string& test_root_certificate() const { return test_root_certificate_; }
|
||||
|
||||
// returns a test intermediate certificate
|
||||
const std::string& test_intermediate_certificate() const {
|
||||
return test_intermediate_certificate_;
|
||||
}
|
||||
|
||||
// returns an user device certificate
|
||||
const std::string& test_user_device_certificate() const {
|
||||
return test_user_device_certificate_;
|
||||
}
|
||||
|
||||
// returns a service certificate
|
||||
const std::string& test_service_certificate() const {
|
||||
return test_service_certificate_;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string test_root_certificate_;
|
||||
const std::string test_intermediate_certificate_;
|
||||
const std::string test_user_device_certificate_;
|
||||
const std::string test_service_certificate_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestCertificates);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
#endif // COMMON_TEST_CERTIFICATES_H_
|
||||
73
common/test_utils.cc
Normal file
73
common/test_utils.cc
Normal file
@@ -0,0 +1,73 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/test_utils.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/sha.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||
const std::string& message,
|
||||
std::string* signature) {
|
||||
CHECK(signature);
|
||||
if (pem_private_key.empty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM private key");
|
||||
}
|
||||
if (message.empty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty message");
|
||||
}
|
||||
BIO* bio(NULL);
|
||||
bio = BIO_new_mem_buf(const_cast<char*>(pem_private_key.data()),
|
||||
pem_private_key.size());
|
||||
if (bio == NULL) {
|
||||
return util::Status(util::error::INTERNAL, "BIO allocation failed");
|
||||
}
|
||||
util::Status status;
|
||||
RSA* key(NULL);
|
||||
std::unique_ptr<char[]> sig_buffer;
|
||||
unsigned int sig_size;
|
||||
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||
key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
|
||||
if (key == NULL) {
|
||||
status = util::Status(util::Status::canonical_space(),
|
||||
util::error::INVALID_ARGUMENT,
|
||||
"PEM RSA private key load failed");
|
||||
goto cleanup;
|
||||
}
|
||||
SHA256(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
|
||||
digest);
|
||||
sig_size = RSA_size(key);
|
||||
sig_buffer.reset(new char[sig_size]);
|
||||
if (RSA_sign(NID_sha256, digest, sizeof(digest),
|
||||
reinterpret_cast<unsigned char*>(sig_buffer.get()), &sig_size,
|
||||
key) != 1) {
|
||||
status =
|
||||
util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
||||
"RSA private encrypt failed");
|
||||
goto cleanup;
|
||||
}
|
||||
signature->assign(sig_buffer.get(), sig_size);
|
||||
|
||||
cleanup:
|
||||
if (key != NULL) {
|
||||
RSA_free(key);
|
||||
}
|
||||
if (bio != NULL) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
32
common/test_utils.h
Normal file
32
common/test_utils.h
Normal file
@@ -0,0 +1,32 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Auxiliary functions for license server SDK testing.
|
||||
|
||||
#ifndef COMMON_TEST_UTILS_H_
|
||||
#define COMMON_TEST_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/status.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Generate RSA signature using the specified RSA private key, SHA256 digest,
|
||||
// and PKCS#1 1.5 padding. |pem_private_key| is a PEM-encoded private RSA key,
|
||||
// |message| is the message to be signed, and |signature| is a pointer to a
|
||||
// std::string where the signature will be stored. The caller returns ownership of
|
||||
// all paramters.
|
||||
util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||
const std::string& message,
|
||||
std::string* signature);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_TEST_UTILS_H_
|
||||
45
common/verified_media_pipeline.cc
Normal file
45
common/verified_media_pipeline.cc
Normal file
@@ -0,0 +1,45 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Helper methods for verifying VMP (Verified Media Pipeline) data.
|
||||
|
||||
#include "common/verified_media_pipeline.h"
|
||||
|
||||
#include "common/vmp_checker.h"
|
||||
|
||||
namespace widevine {
|
||||
util::Status VerifyVmpData(
|
||||
const std::string& vmp_data,
|
||||
PlatformVerificationStatus* platform_verification_status) {
|
||||
*platform_verification_status = PLATFORM_UNVERIFIED;
|
||||
VmpChecker::Result vmp_result;
|
||||
util::Status status =
|
||||
VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result);
|
||||
if (status.ok()) {
|
||||
switch (vmp_result) {
|
||||
case VmpChecker::kUnverified:
|
||||
*platform_verification_status = PLATFORM_UNVERIFIED;
|
||||
break;
|
||||
case VmpChecker::kVerified:
|
||||
*platform_verification_status = PLATFORM_SOFTWARE_VERIFIED;
|
||||
break;
|
||||
case VmpChecker::kSecureStorageVerified:
|
||||
*platform_verification_status =
|
||||
PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED;
|
||||
break;
|
||||
case VmpChecker::kTampered:
|
||||
*platform_verification_status = PLATFORM_TAMPERED;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
*platform_verification_status = PLATFORM_TAMPERED;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
} // namespace widevine
|
||||
28
common/verified_media_pipeline.h
Normal file
28
common/verified_media_pipeline.h
Normal file
@@ -0,0 +1,28 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Helper methods for verifying VMP (Verified Media Pipeline) data.
|
||||
|
||||
#ifndef COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
||||
#define COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
||||
|
||||
#include <string>
|
||||
#include "util/status.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Retrieve the PlatformVerificationStatus for |vmp_data|. The
|
||||
// PlatformVerificationStatus is defined at
|
||||
util::Status VerifyVmpData(
|
||||
const std::string& vmp_data,
|
||||
PlatformVerificationStatus* platform_verification_status);
|
||||
|
||||
} // namespace widevine
|
||||
#endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
||||
359
common/vmp_checker.cc
Normal file
359
common/vmp_checker.cc
Normal file
@@ -0,0 +1,359 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Singleton object which validates VMP (Verified Media Pipeline) data for
|
||||
// purposes of platform software verification. Thread safe.
|
||||
|
||||
#include "common/vmp_checker.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/x509_cert.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/verified_media_pipeline.pb.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kBlessedBinaryFlag = 0x00000001;
|
||||
|
||||
const uint8_t kDevVmpCodeSigningDrmRootCertificate[] = {
|
||||
0x30, 0x82, 0x04, 0xb8, 0x30, 0x82, 0x03, 0x20, 0x02, 0x09, 0x00, 0xc5,
|
||||
0xf8, 0x2f, 0x03, 0x8f, 0xac, 0xf1, 0x58, 0x30, 0x0d, 0x06, 0x09, 0x2a,
|
||||
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
|
||||
0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
|
||||
0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
|
||||
0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31,
|
||||
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69,
|
||||
0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
|
||||
0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31,
|
||||
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69,
|
||||
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03,
|
||||
0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e,
|
||||
0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69,
|
||||
0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
||||
0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73, 0x6b,
|
||||
0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x30, 0x32, 0x38, 0x30,
|
||||
0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x31, 0x36,
|
||||
0x31, 0x30, 0x30, 0x34, 0x30, 0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x30,
|
||||
0x81, 0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
|
||||
0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
|
||||
0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e,
|
||||
0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b,
|
||||
0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06,
|
||||
0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||
0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57,
|
||||
0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06,
|
||||
0x03, 0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69,
|
||||
0x6e, 0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73,
|
||||
0x69, 0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48,
|
||||
0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73,
|
||||
0x6b, 0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x30, 0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
|
||||
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
|
||||
0x8f, 0x00, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xd2,
|
||||
0x6f, 0x60, 0x7f, 0xff, 0x7c, 0xbd, 0xa4, 0xe5, 0x8c, 0xa6, 0xcf, 0xde,
|
||||
0x22, 0x6d, 0x3a, 0x5e, 0x83, 0xa9, 0x0e, 0x9b, 0xd4, 0x93, 0xb8, 0xb0,
|
||||
0xe0, 0x5d, 0x03, 0x3d, 0xc1, 0x00, 0xb8, 0x1a, 0xcc, 0x84, 0x31, 0xfb,
|
||||
0x9e, 0x97, 0x79, 0x17, 0x04, 0x48, 0xe7, 0x13, 0x98, 0x0a, 0x47, 0x41,
|
||||
0xac, 0x6c, 0x52, 0xb0, 0xca, 0x9e, 0xfb, 0xfd, 0x78, 0x65, 0xd0, 0xd6,
|
||||
0x12, 0x07, 0x7e, 0x24, 0x65, 0x46, 0x6c, 0xb9, 0x23, 0xbb, 0xdc, 0x02,
|
||||
0x03, 0xc7, 0xb0, 0x02, 0xc1, 0xd3, 0x10, 0x59, 0xe7, 0x0b, 0x45, 0x13,
|
||||
0x73, 0x5f, 0xae, 0x58, 0xcd, 0xbf, 0x42, 0x8a, 0xac, 0xf5, 0x6a, 0x1e,
|
||||
0x75, 0x26, 0xb1, 0x69, 0x07, 0xad, 0xf5, 0xfd, 0x4c, 0xaa, 0x66, 0x55,
|
||||
0x74, 0x56, 0x9a, 0x9e, 0x40, 0x78, 0x77, 0x9a, 0x39, 0x7a, 0x37, 0xe4,
|
||||
0x25, 0x6b, 0x07, 0x09, 0xbe, 0xe2, 0x0d, 0x23, 0x83, 0xfc, 0x94, 0x9f,
|
||||
0x26, 0x98, 0x0e, 0x49, 0x81, 0x7b, 0xf4, 0xe6, 0xd4, 0xda, 0x7a, 0xc9,
|
||||
0xa4, 0x14, 0x5a, 0xa9, 0xaf, 0x0c, 0xd9, 0xf1, 0xbc, 0xd8, 0x6c, 0xd2,
|
||||
0xd4, 0x1b, 0x82, 0x10, 0x3d, 0x87, 0xf1, 0x81, 0xe6, 0x1a, 0xb7, 0xfa,
|
||||
0xfa, 0x1f, 0x9c, 0xde, 0xa1, 0x3f, 0x11, 0xb1, 0xd8, 0x26, 0xd1, 0x86,
|
||||
0x21, 0xdc, 0x03, 0xcb, 0xd6, 0x40, 0xfb, 0x5f, 0xb0, 0x84, 0x7f, 0x69,
|
||||
0x9e, 0xa3, 0xbc, 0xfb, 0x03, 0xb6, 0xc1, 0xb9, 0x23, 0xb1, 0x20, 0x6f,
|
||||
0x71, 0xf5, 0x7a, 0x3b, 0x84, 0x30, 0xa8, 0x59, 0xc0, 0x8f, 0x75, 0xfd,
|
||||
0xcb, 0xe1, 0xc3, 0x5f, 0xe7, 0x8a, 0xb0, 0xe9, 0xf8, 0xef, 0x04, 0x4b,
|
||||
0x4a, 0xf6, 0xc3, 0x7d, 0x08, 0xfe, 0x08, 0x52, 0x2e, 0xbc, 0x1f, 0x65,
|
||||
0xf6, 0x51, 0xb7, 0xd8, 0x24, 0x21, 0x49, 0x1d, 0x7f, 0x16, 0x28, 0x14,
|
||||
0xd9, 0xc2, 0x19, 0xdb, 0xa2, 0xc6, 0xf0, 0x3a, 0x2d, 0x98, 0x70, 0x72,
|
||||
0x45, 0xf7, 0x80, 0x37, 0x56, 0x0b, 0x0c, 0x6f, 0x80, 0xf1, 0x8c, 0xe2,
|
||||
0xf3, 0x4d, 0x16, 0xc5, 0x74, 0x90, 0x34, 0x1e, 0x57, 0x3c, 0xde, 0xf1,
|
||||
0xc4, 0x8c, 0x17, 0x09, 0xd3, 0xc5, 0x92, 0x9d, 0xcf, 0xdc, 0x7b, 0x4f,
|
||||
0xae, 0x20, 0x10, 0xd7, 0x04, 0x56, 0x9d, 0x9a, 0xa9, 0xd8, 0x06, 0x4a,
|
||||
0xd6, 0x68, 0xd4, 0x83, 0xcb, 0x7d, 0xe2, 0x62, 0xd1, 0x99, 0xb8, 0x9d,
|
||||
0x81, 0xc7, 0xfc, 0x94, 0x69, 0x0d, 0x2a, 0x1c, 0x99, 0xcf, 0x40, 0xc3,
|
||||
0xfd, 0xe9, 0x64, 0x5b, 0xc3, 0x1d, 0xda, 0x1c, 0x89, 0xab, 0x34, 0x1b,
|
||||
0x53, 0x6e, 0xad, 0xf0, 0x6e, 0x97, 0x87, 0xe8, 0xfb, 0x0c, 0x96, 0x93,
|
||||
0x1b, 0x52, 0x82, 0x6a, 0xba, 0x0f, 0xe3, 0x5d, 0xc4, 0x17, 0xdc, 0xe4,
|
||||
0x31, 0x78, 0x12, 0x26, 0x10, 0x74, 0x14, 0x7c, 0x45, 0xb2, 0xb9, 0x02,
|
||||
0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
||||
0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00,
|
||||
0x8f, 0x5b, 0x2a, 0x40, 0xce, 0xae, 0xa2, 0x79, 0xfc, 0xc5, 0x5e, 0x94,
|
||||
0xc8, 0x10, 0x4b, 0xf1, 0x21, 0x7f, 0x4b, 0xeb, 0x81, 0x3d, 0xb8, 0x26,
|
||||
0x83, 0x84, 0x76, 0x79, 0xda, 0x35, 0xfc, 0xdf, 0xfe, 0x10, 0x7a, 0xd5,
|
||||
0x17, 0xc0, 0xad, 0x0d, 0xf9, 0x3f, 0xa6, 0xa1, 0xcd, 0x6c, 0x9c, 0x3b,
|
||||
0x52, 0xbd, 0x04, 0xf9, 0xe2, 0x9e, 0x86, 0x83, 0x98, 0x60, 0x01, 0x99,
|
||||
0xb7, 0xbd, 0x02, 0x87, 0xd8, 0xea, 0x65, 0xaa, 0x60, 0x6f, 0x33, 0x50,
|
||||
0x25, 0x84, 0xb7, 0x42, 0x63, 0x39, 0xfd, 0x17, 0x67, 0x74, 0x88, 0x66,
|
||||
0xe2, 0x38, 0x59, 0xf6, 0x9b, 0x98, 0x95, 0xdd, 0x54, 0x2c, 0x69, 0x6a,
|
||||
0x0a, 0x51, 0x66, 0x4d, 0x65, 0xc0, 0x58, 0x3e, 0xcf, 0x15, 0x63, 0x7a,
|
||||
0x32, 0xc8, 0xfb, 0xe7, 0x11, 0x2e, 0x25, 0x17, 0x52, 0x4d, 0x8e, 0x6b,
|
||||
0x6d, 0x58, 0x4e, 0xf6, 0xd6, 0xb0, 0xfa, 0x0d, 0x7a, 0xb1, 0x44, 0x52,
|
||||
0x9a, 0x6c, 0x90, 0x38, 0x68, 0xa5, 0xa9, 0x9b, 0xc5, 0x45, 0x09, 0xfa,
|
||||
0xaa, 0x8c, 0xfe, 0x91, 0x55, 0x93, 0x35, 0x52, 0x45, 0xbb, 0xaa, 0x5b,
|
||||
0xf0, 0x63, 0x53, 0x13, 0xcf, 0x48, 0x7b, 0xaa, 0x20, 0xa0, 0x07, 0x43,
|
||||
0x1d, 0xd7, 0xb3, 0x4a, 0x1b, 0x8c, 0x51, 0xb5, 0xf6, 0xb9, 0x5b, 0x13,
|
||||
0x02, 0x74, 0x3e, 0x48, 0xde, 0xec, 0xeb, 0x65, 0xfe, 0xf2, 0x61, 0xe5,
|
||||
0x68, 0xd5, 0xea, 0xd9, 0x79, 0xa6, 0x71, 0xb1, 0x57, 0x0f, 0xbc, 0xd0,
|
||||
0x31, 0x4f, 0xff, 0xc5, 0x95, 0xe8, 0xee, 0x70, 0x18, 0xb9, 0xbc, 0x19,
|
||||
0xcd, 0x3a, 0x06, 0x75, 0xe4, 0x57, 0xc1, 0x2e, 0x32, 0x19, 0xdd, 0x2e,
|
||||
0x45, 0xc0, 0x19, 0xe6, 0x72, 0x81, 0x2c, 0xb6, 0xed, 0x1c, 0xd4, 0xef,
|
||||
0x42, 0x18, 0x44, 0x44, 0x75, 0xd6, 0x29, 0x81, 0xe1, 0xf7, 0x5b, 0x48,
|
||||
0xa3, 0xf8, 0x92, 0x54, 0xd0, 0x79, 0xa1, 0xe1, 0x8e, 0xa8, 0x98, 0x2d,
|
||||
0x57, 0x5d, 0xb5, 0x5a, 0x01, 0x1b, 0xb3, 0xcf, 0x5f, 0x64, 0x2e, 0x70,
|
||||
0xba, 0xa0, 0x41, 0xbb, 0xd4, 0x82, 0x28, 0x3c, 0xb1, 0x81, 0x76, 0xd6,
|
||||
0x85, 0x2e, 0xc6, 0x01, 0x7f, 0xae, 0xc3, 0x17, 0x2f, 0xed, 0xbe, 0xad,
|
||||
0xa2, 0x7c, 0x53, 0xc6, 0x77, 0x73, 0x1d, 0x19, 0x90, 0x2a, 0xf8, 0xd5,
|
||||
0x50, 0x65, 0xc1, 0x22, 0x3c, 0x24, 0x96, 0xeb, 0x7b, 0x53, 0x8f, 0xbf,
|
||||
0xd9, 0xf7, 0x80, 0xa8, 0x76, 0x72, 0xea, 0xb7, 0x7f, 0xf7, 0xa2, 0x52,
|
||||
0xc7, 0xa7, 0xd6, 0x88, 0xa1, 0x38, 0x40, 0x5d, 0xcd, 0xdb, 0xe3, 0x8e,
|
||||
0xc7, 0xf9, 0x39, 0xbe, 0xfa, 0x27, 0x41, 0x73, 0x3a, 0x1c, 0xb3, 0x03,
|
||||
0xf1, 0x36, 0xea, 0xe8, 0xb3, 0xe1, 0x6e, 0x59, 0xcc, 0xe2, 0x75, 0x2b,
|
||||
0xf9, 0x55, 0xb9, 0xc2, 0xdf, 0x0a, 0x8d, 0x8d, 0xd1, 0x62, 0x3b, 0x86};
|
||||
|
||||
const uint8_t kProdVmpCodeSigningDrmRootCertificate[] = {
|
||||
0x30, 0x82, 0x04, 0xd7, 0x30, 0x82, 0x03, 0x3f, 0xa0, 0x03, 0x02, 0x01,
|
||||
0x02, 0x02, 0x11, 0x00, 0xca, 0xa4, 0xbd, 0x6b, 0x93, 0x56, 0x4a, 0xf1,
|
||||
0x84, 0xef, 0x5e, 0xed, 0xe8, 0xf7, 0xe2, 0x0b, 0x30, 0x0d, 0x06, 0x09,
|
||||
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
|
||||
0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
|
||||
0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
|
||||
0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31,
|
||||
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69,
|
||||
0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
|
||||
0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31,
|
||||
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69,
|
||||
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
|
||||
0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e,
|
||||
0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x72,
|
||||
0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x37,
|
||||
0x30, 0x31, 0x32, 0x34, 0x32, 0x33, 0x33, 0x38, 0x31, 0x34, 0x5a, 0x18,
|
||||
0x0f, 0x33, 0x30, 0x31, 0x36, 0x30, 0x35, 0x32, 0x37, 0x32, 0x33, 0x33,
|
||||
0x38, 0x31, 0x34, 0x5a, 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
|
||||
0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06,
|
||||
0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e,
|
||||
0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
|
||||
0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31,
|
||||
0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
|
||||
0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31,
|
||||
0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69,
|
||||
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73,
|
||||
0x69, 0x67, 0x6e, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30,
|
||||
0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
||||
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30,
|
||||
0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xe0, 0xb5, 0xc5, 0x6d,
|
||||
0x04, 0xc1, 0x97, 0xaf, 0xe7, 0x62, 0xfb, 0x84, 0xdc, 0xd1, 0xf4, 0xb1,
|
||||
0xb5, 0xa2, 0x7c, 0xca, 0x31, 0xf8, 0xce, 0xa7, 0x7a, 0x92, 0xc2, 0xbe,
|
||||
0x14, 0xdc, 0x85, 0x9f, 0x18, 0x9e, 0x78, 0xba, 0x65, 0x05, 0x56, 0x88,
|
||||
0x88, 0xdc, 0x1f, 0x4f, 0x24, 0x7f, 0xf5, 0x26, 0x6f, 0x6e, 0xcc, 0x04,
|
||||
0x2f, 0x38, 0xb8, 0xcd, 0x27, 0xd7, 0x9e, 0x07, 0xd3, 0xa9, 0xd4, 0x6b,
|
||||
0x84, 0xfe, 0xf8, 0xac, 0x9c, 0x53, 0xdf, 0x7a, 0x45, 0x5e, 0x77, 0xf8,
|
||||
0x4e, 0x88, 0x00, 0x5c, 0x6f, 0xb6, 0xa7, 0x0b, 0x4b, 0x63, 0x57, 0x92,
|
||||
0x7a, 0x7b, 0x3d, 0x20, 0x88, 0x3e, 0x7b, 0xb4, 0x28, 0x2b, 0x63, 0x81,
|
||||
0xd4, 0x0c, 0x4c, 0xb7, 0x54, 0x68, 0x68, 0x2c, 0x0d, 0xf5, 0xa6, 0x9e,
|
||||
0x16, 0x93, 0x76, 0xdd, 0xc0, 0xd5, 0x93, 0x65, 0x99, 0x90, 0x17, 0x2d,
|
||||
0x2b, 0xdc, 0x6f, 0xaf, 0x58, 0xfd, 0x78, 0xe9, 0xf5, 0xde, 0x2e, 0x36,
|
||||
0x95, 0xf0, 0xcf, 0x25, 0x41, 0x3d, 0x4f, 0x37, 0xd1, 0x70, 0x5b, 0xb5,
|
||||
0xc0, 0xc8, 0xf3, 0x63, 0xa3, 0xda, 0x9f, 0x94, 0xdb, 0xf8, 0x51, 0xf2,
|
||||
0xa9, 0xe5, 0x67, 0x0e, 0x29, 0xb3, 0x45, 0x12, 0xc5, 0x42, 0xf9, 0x3b,
|
||||
0x38, 0xf9, 0xa5, 0x7b, 0x41, 0x88, 0x6f, 0x32, 0x62, 0x03, 0x5f, 0xfd,
|
||||
0x35, 0x97, 0xc2, 0x83, 0x15, 0xb6, 0x56, 0x4f, 0xbb, 0x81, 0x39, 0x37,
|
||||
0xf2, 0x9c, 0x2a, 0x61, 0xa8, 0x63, 0x5f, 0xa0, 0x27, 0x30, 0x06, 0xd4,
|
||||
0xcb, 0x9d, 0xb7, 0xe9, 0xf2, 0xae, 0xd6, 0xc9, 0xcd, 0x72, 0xa3, 0xe6,
|
||||
0xf8, 0x54, 0x03, 0x6e, 0xe1, 0x95, 0x03, 0xdd, 0x7a, 0x85, 0xb3, 0x5c,
|
||||
0xa7, 0xca, 0x99, 0xec, 0xa6, 0xe8, 0x1a, 0xb2, 0x72, 0xe1, 0x91, 0x2d,
|
||||
0x97, 0xe3, 0x2a, 0x9c, 0x42, 0xaa, 0x45, 0xf2, 0x8e, 0x51, 0xc0, 0xd8,
|
||||
0x21, 0x83, 0x66, 0x07, 0xb5, 0x20, 0xb8, 0x28, 0xa5, 0xde, 0xfb, 0x4e,
|
||||
0x2e, 0xc7, 0x70, 0x9b, 0x3d, 0x52, 0x66, 0x24, 0xc5, 0xa2, 0x2e, 0x49,
|
||||
0x54, 0x5c, 0xfd, 0xc0, 0xde, 0xf6, 0x9d, 0xb4, 0x70, 0x31, 0x2b, 0xac,
|
||||
0x14, 0xfb, 0x19, 0x9e, 0x89, 0xd5, 0x07, 0x87, 0xa0, 0xd8, 0x15, 0x96,
|
||||
0xe9, 0xf7, 0x91, 0x36, 0x52, 0x83, 0x3c, 0x2c, 0xfa, 0xb5, 0xc4, 0xc6,
|
||||
0x1a, 0x34, 0xf0, 0x53, 0x94, 0x15, 0x82, 0xa2, 0x2f, 0x98, 0xbb, 0x49,
|
||||
0xca, 0xf7, 0xe0, 0xcb, 0x9e, 0x3c, 0xa6, 0x64, 0x59, 0x77, 0x63, 0xd1,
|
||||
0x05, 0x03, 0x99, 0x6c, 0x50, 0x08, 0xec, 0x64, 0x86, 0xf7, 0x97, 0xaf,
|
||||
0xf8, 0xcc, 0xdf, 0x91, 0xc7, 0x2c, 0x15, 0x0f, 0xa7, 0x0e, 0x02, 0x33,
|
||||
0x63, 0x84, 0xb7, 0x7e, 0xd3, 0x10, 0x89, 0x05, 0x2d, 0xb4, 0x68, 0x38,
|
||||
0xe0, 0x00, 0x49, 0xda, 0xaa, 0xb9, 0xf9, 0xbf, 0x02, 0x03, 0x01, 0x00,
|
||||
0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
|
||||
0x04, 0x16, 0x04, 0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f,
|
||||
0xd0, 0x9a, 0xd9, 0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86,
|
||||
0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
|
||||
0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f, 0xd0, 0x9a, 0xd9,
|
||||
0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86, 0x30, 0x0c, 0x06,
|
||||
0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
|
||||
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
|
||||
0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00, 0xc1, 0x81, 0x17, 0x08, 0x0a,
|
||||
0xcb, 0xae, 0x54, 0x92, 0xed, 0x0f, 0xc0, 0xf0, 0x83, 0xb8, 0xe9, 0x2a,
|
||||
0xd5, 0x65, 0x18, 0xb5, 0x92, 0xfc, 0x67, 0x9c, 0x39, 0x9e, 0x3a, 0x93,
|
||||
0x1f, 0x91, 0x7b, 0x35, 0x6d, 0x09, 0x6c, 0x3c, 0x02, 0x85, 0xe8, 0xc6,
|
||||
0x0c, 0x6b, 0x35, 0xeb, 0x8c, 0xe4, 0x44, 0x80, 0xc5, 0x4e, 0x65, 0xa6,
|
||||
0xbc, 0x25, 0x93, 0x5a, 0xed, 0x5a, 0xd1, 0x5a, 0xcf, 0xf1, 0xbe, 0xd5,
|
||||
0x46, 0x7c, 0x78, 0xfe, 0xb6, 0xa6, 0x9c, 0x85, 0xa9, 0xe2, 0x13, 0x70,
|
||||
0x99, 0x03, 0x5a, 0x3a, 0xa8, 0x7b, 0xdd, 0x50, 0x76, 0x83, 0xfe, 0x49,
|
||||
0xc0, 0x5e, 0xc7, 0xf1, 0x18, 0xc0, 0xe6, 0xb7, 0xc7, 0xe4, 0x9d, 0x54,
|
||||
0xaf, 0x25, 0xdf, 0xe4, 0x81, 0xc1, 0xe9, 0xaa, 0x7c, 0x05, 0x20, 0xfa,
|
||||
0x91, 0x47, 0xd1, 0x4a, 0xe2, 0x24, 0x6f, 0x72, 0x22, 0x69, 0xd0, 0x89,
|
||||
0x78, 0x2c, 0x9a, 0x16, 0x7d, 0x92, 0xdd, 0x64, 0x6c, 0xc8, 0xbf, 0x33,
|
||||
0xe3, 0x91, 0xb9, 0xb5, 0x16, 0xfe, 0xfd, 0xa2, 0xdb, 0x82, 0xf0, 0xec,
|
||||
0xac, 0xe7, 0xf9, 0x2e, 0xac, 0x50, 0xf0, 0xcf, 0xba, 0xe1, 0x55, 0xa9,
|
||||
0xb0, 0xd9, 0x66, 0x3f, 0xb6, 0xee, 0x57, 0xff, 0x8d, 0x43, 0xb7, 0xc3,
|
||||
0xb4, 0x44, 0xa9, 0xcc, 0x99, 0xa2, 0xbf, 0xac, 0x4f, 0xec, 0xed, 0xb5,
|
||||
0xd0, 0x3f, 0xa9, 0x50, 0x3b, 0xc1, 0x24, 0xf2, 0xd0, 0xef, 0x5c, 0xbf,
|
||||
0x5c, 0xc2, 0x41, 0x29, 0xbe, 0xb6, 0x76, 0x5a, 0x19, 0xde, 0x67, 0x1c,
|
||||
0x2a, 0x67, 0xae, 0x07, 0xe8, 0xfa, 0x49, 0xf1, 0x81, 0xbc, 0x22, 0xa1,
|
||||
0xe6, 0x5d, 0x27, 0x76, 0x0f, 0x4d, 0x41, 0x57, 0xcf, 0x0f, 0x12, 0x2f,
|
||||
0xdd, 0x20, 0x88, 0xcf, 0xc8, 0xa7, 0xc0, 0xe5, 0xec, 0xcd, 0xb9, 0xa7,
|
||||
0x1c, 0x29, 0x55, 0xed, 0x67, 0xf9, 0x38, 0x33, 0xea, 0x85, 0xe9, 0x69,
|
||||
0x5a, 0x7c, 0xfe, 0x37, 0x3b, 0xdd, 0x61, 0x5f, 0xaa, 0xc3, 0x18, 0xbc,
|
||||
0x58, 0x95, 0x39, 0x61, 0x79, 0xa1, 0x46, 0xcc, 0xc0, 0xe7, 0xd6, 0x52,
|
||||
0x3c, 0xc7, 0xfa, 0xed, 0x89, 0x06, 0xeb, 0xd4, 0x5e, 0x9c, 0xa5, 0x55,
|
||||
0x8c, 0xe3, 0x5f, 0xe6, 0xb4, 0x0a, 0xf4, 0xf6, 0x7d, 0xeb, 0x64, 0x74,
|
||||
0xa9, 0x1a, 0x8d, 0x6e, 0xf1, 0x41, 0xc7, 0x7e, 0xc6, 0x26, 0x3a, 0x47,
|
||||
0x70, 0x49, 0x07, 0x27, 0xa2, 0xb9, 0xc6, 0x79, 0x9d, 0x94, 0xf5, 0x51,
|
||||
0x69, 0xdf, 0xbd, 0x84, 0xee, 0xaa, 0x46, 0xea, 0x4b, 0x27, 0xb6, 0x5c,
|
||||
0xac, 0xcf, 0x4a, 0x48, 0x12, 0x40, 0x86, 0x80, 0xd4, 0xb8, 0x0a, 0xb1,
|
||||
0x9f, 0xdc, 0x68, 0x60, 0x14, 0x33, 0x1d, 0x88, 0xfb, 0xa2, 0xfc, 0x49,
|
||||
0x0c, 0xa9, 0x76, 0x2d, 0xd7, 0x32, 0x3f, 0x77, 0xdb, 0x62, 0x8c, 0x35,
|
||||
0x88, 0x5d, 0x66, 0xc9, 0x8d, 0x07, 0xe5};
|
||||
|
||||
const char kCodeSigningDevelopmentFlagOid[] = "1.3.6.1.4.1.11129.4.1.2";
|
||||
const char kSecureStorageFlagOid[] = "1.3.6.1.4.1.11129.4.1.3";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
|
||||
VmpChecker::VmpChecker() : allow_development_vmp_(false) {}
|
||||
|
||||
VmpChecker::~VmpChecker() {}
|
||||
|
||||
util::Status VmpChecker::SelectDrmCertificateType(CertificateType cert_type) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
util::Status status = ca_cert->LoadDer(
|
||||
cert_type == kCertificateTypeProduction
|
||||
? std::string(reinterpret_cast<const char*>(
|
||||
kProdVmpCodeSigningDrmRootCertificate),
|
||||
sizeof(kProdVmpCodeSigningDrmRootCertificate))
|
||||
: std::string(reinterpret_cast<const char*>(
|
||||
kDevVmpCodeSigningDrmRootCertificate),
|
||||
sizeof(kDevVmpCodeSigningDrmRootCertificate)));
|
||||
if (!status.ok()) return status;
|
||||
|
||||
ca_.reset(new X509CA(ca_cert.release()));
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
VmpChecker* VmpChecker::Instance() {
|
||||
static VmpChecker instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
// Verify VMP data and return appropriate result.
|
||||
util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) {
|
||||
DCHECK(!vmp_data.empty());
|
||||
DCHECK(result);
|
||||
|
||||
if (!ca_) return util::Status(error_space, CERT_CHAIN_NOT_SELECTED, "");
|
||||
|
||||
vmp::VmpData vmp_data_obj;
|
||||
if (!vmp_data_obj.ParseFromString(vmp_data)) {
|
||||
LOG(INFO) << "Error deserializing VmpData.";
|
||||
return util::Status(error_space, INVALID_MESSAGE,
|
||||
"vmp-data-deserialize-failed");
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<X509Cert>> code_signing_certs;
|
||||
const std::string kDevelopmentFlagOid(kCodeSigningDevelopmentFlagOid);
|
||||
bool secure_storage_verified(true);
|
||||
for (int cert_idx = 0; cert_idx < vmp_data_obj.certificates_size();
|
||||
++cert_idx) {
|
||||
code_signing_certs.emplace_back(new X509Cert);
|
||||
util::Status status(code_signing_certs.back()->LoadDer(
|
||||
vmp_data_obj.certificates(cert_idx)));
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (!allow_development_vmp_) {
|
||||
bool dev_flag;
|
||||
if (code_signing_certs.back()->GetV3BooleanExtension(kDevelopmentFlagOid,
|
||||
&dev_flag) &&
|
||||
dev_flag) {
|
||||
return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
"development-vmp-certificate-not-allowed");
|
||||
}
|
||||
}
|
||||
status = ca_->VerifyCert(*code_signing_certs.back());
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (secure_storage_verified) {
|
||||
bool secure_storage_flag;
|
||||
if (!code_signing_certs.back()->GetV3BooleanExtension(
|
||||
kSecureStorageFlagOid, &secure_storage_flag) ||
|
||||
!secure_storage_flag) {
|
||||
secure_storage_verified = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t num_blessed_binaries(0);
|
||||
for (int binary_idx = 0; binary_idx < vmp_data_obj.signed_binary_info_size();
|
||||
++binary_idx) {
|
||||
const vmp::VmpData::SignedBinaryInfo& binary_info(
|
||||
vmp_data_obj.signed_binary_info(binary_idx));
|
||||
if (binary_info.signature().empty()) {
|
||||
LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\".";
|
||||
*result = kTampered;
|
||||
return util::OkStatus();
|
||||
}
|
||||
if (binary_info.certificate_index() >= code_signing_certs.size()) {
|
||||
LOG(INFO) << "Invalid code signing certificate index.";
|
||||
*result = kTampered;
|
||||
return util::OkStatus();
|
||||
}
|
||||
X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get();
|
||||
std::unique_ptr<RsaPublicKey> key(cert->GetRsaPublicKey());
|
||||
std::string message(binary_info.binary_hash());
|
||||
message += binary_info.flags() & 0xff;
|
||||
if (!key->VerifySignature(message, binary_info.signature())) {
|
||||
LOG(INFO) << "Code signature verification failed for file \""
|
||||
<< binary_info.file_name() << "\".";
|
||||
*result = kTampered;
|
||||
return util::OkStatus();
|
||||
}
|
||||
if (binary_info.flags() & kBlessedBinaryFlag) ++num_blessed_binaries;
|
||||
}
|
||||
if (num_blessed_binaries != 1) {
|
||||
LOG(INFO) << "Invalid number of blessed binaries (" << num_blessed_binaries
|
||||
<< ").";
|
||||
*result = kTampered;
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
VLOG(2) << "VMP verification success. Secure storage: "
|
||||
<< secure_storage_verified;
|
||||
*result = secure_storage_verified ? kSecureStorageVerified : kVerified;
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
57
common/vmp_checker.h
Normal file
57
common/vmp_checker.h
Normal file
@@ -0,0 +1,57 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Singleton object which validates VMP (Verified Media Pipeline) data for
|
||||
// purposes of platform software verification.
|
||||
|
||||
#ifndef COMMON_VMP_CHECKER_H_
|
||||
#define COMMON_VMP_CHECKER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "util/status.h"
|
||||
#include "common/certificate_type.h"
|
||||
|
||||
namespace widevine {
|
||||
class X509CA;
|
||||
|
||||
class VmpChecker {
|
||||
public:
|
||||
enum Result {
|
||||
kUnverified = 0,
|
||||
kVerified = 1,
|
||||
kSecureStorageVerified = 2,
|
||||
kTampered = 3
|
||||
};
|
||||
|
||||
// Singleton accessor.
|
||||
static VmpChecker* Instance();
|
||||
|
||||
// Select the type of root to use. Not thread-safe.
|
||||
virtual util::Status SelectDrmCertificateType(CertificateType root_type);
|
||||
|
||||
// Verify VMP data and return appropriate result.
|
||||
virtual util::Status VerifyVmpData(const std::string& vmp_data, Result* result);
|
||||
|
||||
// Enable/disable development code signing certificates.
|
||||
void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; }
|
||||
bool allow_development_vmp() const { return allow_development_vmp_; }
|
||||
|
||||
private:
|
||||
VmpChecker();
|
||||
~VmpChecker();
|
||||
|
||||
std::unique_ptr<X509CA> ca_;
|
||||
bool allow_development_vmp_ = false;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_VMP_CHECKER_H_
|
||||
321
common/vmp_checker_test.cc
Normal file
321
common/vmp_checker_test.cc
Normal file
@@ -0,0 +1,321 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/vmp_checker.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/verified_media_pipeline.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kBlessedBinaryFlag = 0x00000001;
|
||||
|
||||
const char kDevVmpCodeSigningKey[] =
|
||||
"308204a50201000282010100b3a7da87309390688388d614a5a37e7ee73a"
|
||||
"c3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd"
|
||||
"8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6"
|
||||
"d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b"
|
||||
"b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51"
|
||||
"957dc4608fd34c999a7b35245bce2795715d8a5957a0872398473eed4860"
|
||||
"29286f51d4118927e88634ad040034e061fd3f58632961761b1fbb8faf45"
|
||||
"391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706"
|
||||
"424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203"
|
||||
"01000102820101008befeb1ff28e7ea56a0f63f1a133c08c48c0553efe86"
|
||||
"07cfd9d216d981aef81a81db226b47277a6d32d09207df88e03316247ae7"
|
||||
"39325456a00644bbfacd6dc2990851f047ccdc1226bec21afac9eacfb957"
|
||||
"1e51889cfb6dac853fcdd1bb1593bd5528cdd3cbbb32c69183ef018fd3f5"
|
||||
"3153f097fd3de9aca7998a6f4522e60c5e76ecc717033a2be16c233fe3e2"
|
||||
"94c0ae68179a1ae5d33d51f2c0bcda35cffa21e1814374af1c014c280cfb"
|
||||
"09e294a099f9a6f003d3da026af0f53029cad3d401178b0db53ca97fd166"
|
||||
"a5eddf1384ddd92c55ab31758e0651e41dd7b24f2c438d58de00f55343cc"
|
||||
"a461b94ff6358fc37f404ecbda2365d3e75391566248d3e102818100e4c8"
|
||||
"783343b43fc127f5077fb35b37145ab6ee5f9a802e8100161ce441cbdbdc"
|
||||
"1eb15f50bbef1f5088ba83ff914977862dffc63bd364172c9044e1131dbb"
|
||||
"ff98755daa49670a014163b3c121395d880ad874b9ebdf66ff72a245c2b5"
|
||||
"4f8fe1e233e72fd9cc253cbcd144f52537e9b7ed16b8a6d328de61c41f86"
|
||||
"ea1d9ef326fb02818100c90738b2cf61af785e53393d9e96bc2f4d79358b"
|
||||
"7bd487a242d170b8372af0a26af4980534a099647a4f38efb1a47d9dcdcf"
|
||||
"a4ea6036396ef333e4f1de62ec529c3122f0d3b6abc818918494a52b028b"
|
||||
"2cdf06bf2b450d5b99da7f203ff011318c030a01659c4c56244c2c312528"
|
||||
"e30dd3b7709022d8fd14a2dba23a855da02f0281804f8ebeede4cf5b9449"
|
||||
"d6d582bcd62d73309088984a5be4d00b3da55262e7074fa684bbc69173f8"
|
||||
"09c36248e0a89f49a7297bd66d9b7724efe4436f997c2f92146c4be4199e"
|
||||
"71463a7cf75763bc552027d559d2058a2c810c560db845e0a30243ed14a9"
|
||||
"f92d1a8de2834b5d8c51c33ea87dcc3c8715a12f9249fc5a916e62d3dd02"
|
||||
"8181008f74c7d1528cb35b82748174a7a789c377d5f790025e382c62e273"
|
||||
"3e02a071f875baf681407d1af9c90e9fe2ed322532679cb6634b2566f6f6"
|
||||
"37223a3828ffdc33fa1ca51f704c460ec2498a8a13974d1a484dd83e5898"
|
||||
"9fb5bb66dcecc3b4815719141acb182ea18a659163c0d0dcb7114ee6d4f5"
|
||||
"09441165e6b66e6c9dd3a102818100c449003eee6d9a0b724b66027a791e"
|
||||
"3b86a72b2098ab085c2e69c276627fd6beeee98918de14ce15d17e059906"
|
||||
"a178015d04e205918203ffa6c1324868d5c0a019f6fe82335403743322ee"
|
||||
"14b1159692194ac22d9056c0c39309d249b33abaddb0d08ee87cc053a12a"
|
||||
"83f12d261525e1b37df643c3e55770ef247d5d094816a0";
|
||||
|
||||
const char kDevVmpCodeSigningCert[] =
|
||||
"3082056a308203d2a003020102020f112233445566778899aabbccddeeff300d06092a86"
|
||||
"4886f70d01010b050030819c310b30090603550406130255533113301106035504080c0a"
|
||||
"57617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d060355"
|
||||
"040a0c06476f6f676c653111300f060355040b0c085769646576696e65311e301c060355"
|
||||
"04030c157769646576696e652d6465762d636f64657369676e3121301f06092a864886f7"
|
||||
"0d010901161274696e736b697040676f6f676c652e636f6d301e170d3137313030393231"
|
||||
"323835385a170d3237313030373231323835385a3081a0310b3009060355040613025553"
|
||||
"3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b"
|
||||
"6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465"
|
||||
"76696e653122302006035504030c197769646576696e652d6465762d766d702d636f6465"
|
||||
"7369676e3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e"
|
||||
"636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100"
|
||||
"b3a7da87309390688388d614a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5d"
|
||||
"e2f7b409b1eac2dc578b2bfd8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c"
|
||||
"62c71d894fe6d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b"
|
||||
"b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd3"
|
||||
"4c999a7b35245bce2795715d8a5957a0872398473eed486029286f51d4118927e88634ad"
|
||||
"040034e061fd3f58632961761b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be"
|
||||
"2fd258331e7a86baa7394706424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3b"
|
||||
"b73008150203010001a38201213082011d301d0603551d0e041604147266b4ce84aafd02"
|
||||
"b1159cd2fa04c2553c6c02463081bb0603551d230481b33081b0a181a2a4819f30819c31"
|
||||
"0b30090603550406130255533113301106035504080c0a57617368696e67746f6e311130"
|
||||
"0f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c65311130"
|
||||
"0f060355040b0c085769646576696e65311e301c06035504030c157769646576696e652d"
|
||||
"6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b6970"
|
||||
"40676f6f676c652e636f6d820900c5f82f038facf15830090603551d1304023000300b06"
|
||||
"03551d0f04040302078030130603551d25040c300a06082b060105050703033011060a2b"
|
||||
"06010401d67904010204030101ff300d06092a864886f70d01010b05000382018100aa23"
|
||||
"6a5c0e23d5bf67c9f80f893d8347ba489541cf7f4ab7dfffda0ca21a3372e8ee8cfea863"
|
||||
"628b9e0795904bc0e7495517246143c7b8555884e82fe1c305f0f4c3575447d4e7ce3243"
|
||||
"4e1e0cf11712d537cd434c11d1328b814c94dbd0bab802e8fed5390da5f0cd719ce0e366"
|
||||
"47620bcf40ed3945c80ab19beb7728080a74d4ff5d62564f47b32c4915c1f14890d379c8"
|
||||
"8060f0bac73301defda06275a2e1a2024f92f0b4700d4e50d2d6f8d033715a362d7ca5ab"
|
||||
"6d2dae20d8cefa2d3fc4f61e1734802984e5078dd6d957719fa75ea10dd02d983f7e383b"
|
||||
"10fa92be7add70238388e63ec2c7ec49a37aa0c8a2566c46e9755cd9ce654c6d42053d1e"
|
||||
"1cd9555f8a3bc5e426857072a8e8b44a756543893b1d29dabf31a2301597df2666612f23"
|
||||
"a442613526e19f2aa9b2ea49f16f14b16794e053967a21b821c7b2495b2e02b01344a339"
|
||||
"9d7e31dd71982cf21a546b1947bbce236381d717070c27096b6a91413abc69c3c0759574"
|
||||
"4b7e91daf24b2a0acfd85924669f00292a4bd0c57b4c";
|
||||
|
||||
const char kDevSecureStorageVmpCodeSigningCert[] =
|
||||
"30820571308203d9a0030201020203112233300d06092a864886f70d01010b050030819c"
|
||||
"310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3111"
|
||||
"300f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c653111"
|
||||
"300f060355040b0c085769646576696e65311e301c06035504030c157769646576696e65"
|
||||
"2d6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b69"
|
||||
"7040676f6f676c652e636f6d301e170d3138303232323231323834395a170d3238303232"
|
||||
"303231323834395a3081a0310b30090603550406130255533113301106035504080c0a57"
|
||||
"617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d06035504"
|
||||
"0a0c06476f6f676c653111300f060355040b0c085769646576696e653122302006035504"
|
||||
"030c197769646576696e652d6465762d766d702d636f64657369676e3121301f06092a86"
|
||||
"4886f70d010901161274696e736b697040676f6f676c652e636f6d30820122300d06092a"
|
||||
"864886f70d01010105000382010f003082010a0282010100b3a7da87309390688388d614"
|
||||
"a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd"
|
||||
"8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6d5524e1b28af"
|
||||
"1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646bb50d3a13d8687ad304005b0b"
|
||||
"0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd34c999a7b35245bce2795715d"
|
||||
"8a5957a0872398473eed486029286f51d4118927e88634ad040034e061fd3f5863296176"
|
||||
"1b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706"
|
||||
"424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203010001a38201"
|
||||
"3430820130301d0603551d0e041604147266b4ce84aafd02b1159cd2fa04c2553c6c0246"
|
||||
"3081bb0603551d230481b33081b0a181a2a4819f30819c310b3009060355040613025553"
|
||||
"3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b"
|
||||
"6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465"
|
||||
"76696e65311e301c06035504030c157769646576696e652d6465762d636f64657369676e"
|
||||
"3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e636f6d82"
|
||||
"0900c5f82f038facf15830090603551d1304023000300b0603551d0f0404030207803013"
|
||||
"0603551d25040c300a06082b060105050703033011060a2b06010401d679040102040301"
|
||||
"01ff3011060a2b06010401d67904010304030101ff300d06092a864886f70d01010b0500"
|
||||
"038201810069482a9bb125b17c76c77f86004e36ca4fc36bbde9c86901481c70d165e3cf"
|
||||
"1c8541264190b3e7106c33ff4920a99bc9f939298091dc72ff898d22f5017b04d213eb60"
|
||||
"626382656fdd66b9cd1830d98ef89d8e405b2d7ce9445db8de2ca365438b848f85d5266b"
|
||||
"c211c8934154bb88ddb9e6f9aaff761814c1c0da90dd499f174507b5f4e89339e8abd157"
|
||||
"8b3238d8f5c9dcb1f76e4d810679c2eca3583144f1ac0ce955b7c6a2cc9fc9f3d6c87069"
|
||||
"28e301ebc3844e53f4905ff60803110dfc3b4f74b50ae1baffd091daad3ea29925f8009e"
|
||||
"adc471a9ae673d9a9a003901d962f58fede85b2f65d9a470725459b19a69b06ae49179a3"
|
||||
"395286b7d7039b32985a6db5b30bcd5cbed975d9a68de44fbcd1dbc8b6fbf67746b87d30"
|
||||
"122c1cd8ed303a8bec7e23d284c0de35cfdccf261c317efe192efa84d2600bba3f9af846"
|
||||
"8f89953a98f92c97b13d320f484627790d5b7b407f29f343c41154efbcf06e98632a3f3d"
|
||||
"7138184a6f40af2435b3d98054ed9f4c3aa9ecf95c5c3014d3aa4d12f2";
|
||||
|
||||
const char kSameAsPrevious[] = "";
|
||||
|
||||
} // namespace
|
||||
|
||||
class VmpCheckerTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
ASSERT_OK(VmpChecker::Instance()->SelectDrmCertificateType(
|
||||
kCertificateTypeTesting));
|
||||
vmp_data_.Clear();
|
||||
VmpChecker::Instance()->set_allow_development_vmp(true);
|
||||
signing_key_.reset(
|
||||
RsaPrivateKey::Create(absl::HexStringToBytes(kDevVmpCodeSigningKey)));
|
||||
ASSERT_TRUE(signing_key_);
|
||||
}
|
||||
|
||||
// Adds a binary to the VMP data to be verified. If |signing_cert| is
|
||||
// |kSameAsPrevious| (empty), then the binary is signed using the same
|
||||
// certificate as the previously added binary. This means that the first
|
||||
// call to this function should not use |kSameAsPrevious|.
|
||||
void AddVmpBinary(const std::string& signing_cert, const std::string& file_name,
|
||||
const std::string& binary_hash, uint32_t flags) {
|
||||
DCHECK(!signing_cert.empty() || !vmp_data_.certificates().empty());
|
||||
|
||||
if (!signing_cert.empty()) {
|
||||
*vmp_data_.add_certificates() = absl::HexStringToBytes(signing_cert);
|
||||
}
|
||||
vmp::VmpData::SignedBinaryInfo* new_binary =
|
||||
vmp_data_.add_signed_binary_info();
|
||||
new_binary->set_file_name(file_name);
|
||||
new_binary->set_certificate_index(vmp_data_.certificates_size() - 1);
|
||||
new_binary->set_binary_hash(binary_hash);
|
||||
new_binary->set_flags(flags);
|
||||
std::string message(binary_hash);
|
||||
message += flags & 0xff;
|
||||
std::string signature;
|
||||
ASSERT_TRUE(signing_key_->GenerateSignature(message, &signature));
|
||||
new_binary->set_signature(signature);
|
||||
}
|
||||
|
||||
const std::string GetVmpData() {
|
||||
std::string result;
|
||||
vmp_data_.SerializeToString(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
vmp::VmpData vmp_data_;
|
||||
std::unique_ptr<RsaPrivateKey> signing_key_;
|
||||
};
|
||||
|
||||
TEST_F(VmpCheckerTest, Success) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kVerified, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, SecureStorageSuccess) {
|
||||
AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kSecureStorageVerified, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailDevelopmentCert) {
|
||||
VmpChecker::Instance()->set_allow_development_vmp(false);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
VmpChecker::Result result;
|
||||
EXPECT_EQ(DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
VmpChecker::Instance()
|
||||
->VerifyVmpData(GetVmpData(), &result)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailTwoBlessedBinaries) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
vmp_data_.mutable_signed_binary_info(0)->set_flags(kBlessedBinaryFlag);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailNoBlessedBinaries) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
vmp_data_.mutable_signed_binary_info(3)->set_flags(0);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailBadSignature) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
++((*vmp_data_.mutable_signed_binary_info(2)->mutable_signature())[10]);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailSignatureVerification) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
++((*vmp_data_.mutable_signed_binary_info(3)->mutable_binary_hash())[16]);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailUnsignedBinary) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
vmp_data_.mutable_signed_binary_info(2)->clear_binary_hash();
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
19
common/widevine_system_id.cc
Normal file
19
common/widevine_system_id.cc
Normal file
@@ -0,0 +1,19 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Common Encryption (CENC) system ID for Widevine DRM.
|
||||
|
||||
#include "common/widevine_system_id.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
const uint8_t kWidevineSystemId[16] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
||||
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
||||
0xd5, 0x1d, 0x21, 0xed};
|
||||
|
||||
} // namespace widevine
|
||||
22
common/widevine_system_id.h
Normal file
22
common/widevine_system_id.h
Normal file
@@ -0,0 +1,22 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Common Encryption (CENC) system ID for Widevine DRM.
|
||||
|
||||
#ifndef COMMON_WIDEVINE_SYSTEM_ID_H_
|
||||
#define COMMON_WIDEVINE_SYSTEM_ID_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
extern const uint8_t kWidevineSystemId[16];
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_WIDEVINE_SYSTEM_ID_H_
|
||||
86
common/wvm_test_keys.cc
Normal file
86
common/wvm_test_keys.cc
Normal file
@@ -0,0 +1,86 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/wvm_test_keys.h"
|
||||
#include "common/wvm_token_handler.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace wvm_test_keys {
|
||||
|
||||
// Preprov key and keyboxes in this header use system ID 0x112.
|
||||
const char kTestPreprovKeyHex[] = "f7538b38acc78ec68c732ac665c55c65";
|
||||
const char kBadPreprovKey1Hex[] = "1badbadbadbadbadbadbadbadbadbad1";
|
||||
const char kBadPreprovKey2Hex[] = "2badbadbadbadbadbadbadbadbadbad2";
|
||||
|
||||
const char kTestToken1Hex[] =
|
||||
"00000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e";
|
||||
|
||||
// The device key corresponding to kTestToken1.
|
||||
const char kTestDeviceKey1Hex[] = "4071197f1f8910d9bf10c6bc4c987638";
|
||||
|
||||
// Base64Encode(kTestToken1)
|
||||
const char kTestToken1Base64[] =
|
||||
"AAAAAgAAARKOHr/gN4KAlsplOLT29Ly1HCtxkc8Dfpi+qiSSSQfhKPn/SbVKFlzZ"
|
||||
"wz5lR1N+tNKft+jfPCwc2SUXoS9JIpU+";
|
||||
|
||||
const char kTestToken2Hex[] =
|
||||
"0000000200000112d906feebe1750c5886ff77c2dfa31bb40e002f3adbc0fa5b"
|
||||
"eb2486cf5f419549cdaa23230e5165ac2ffab56d53b692b7ba0c1857400c6add"
|
||||
"3af3ff3d5cb24985";
|
||||
|
||||
// The device key corresponding to kTestToken2.
|
||||
const char kTestDeviceKey2Hex[] = "42cfb1765201042302a404d1e0fac8ed";
|
||||
|
||||
// Base64Encode(kTestToken2)
|
||||
const char kTestToken2Base64[] =
|
||||
"AAAAAgAAARLZBv7r4XUMWIb/d8Lfoxu0DgAvOtvA+lvrJIbPX0GVSc2qIyMOUWWs"
|
||||
"L/q1bVO2kre6DBhXQAxq3Trz/z1cskmF";
|
||||
|
||||
const char kTestToken3DesHex[] =
|
||||
"00000002100000138e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e";
|
||||
|
||||
const char kTestDeviceKey3DesHex[] = "4071197f1f8910d9bf10c6bc4c987638";
|
||||
|
||||
std::vector<WvmTokenHandler::PreprovKey> GetPreprovKeyVector() {
|
||||
return {
|
||||
// Return multiple preprov keys for the same device.
|
||||
WvmTokenHandler::PreprovKey(kTestSystemId,
|
||||
absl::HexStringToBytes(kBadPreprovKey1Hex)),
|
||||
WvmTokenHandler::PreprovKey(kTestSystemId,
|
||||
absl::HexStringToBytes(kTestPreprovKeyHex)),
|
||||
WvmTokenHandler::PreprovKey(kTestSystemId,
|
||||
absl::HexStringToBytes(kBadPreprovKey2Hex)),
|
||||
|
||||
// Add another device that uses 3DES encryption for asset keys. Tokens
|
||||
// the same except for the system ID portion.
|
||||
WvmTokenHandler::PreprovKey(kTestSystemId3Des,
|
||||
absl::HexStringToBytes(kTestPreprovKeyHex),
|
||||
WvmTokenHandler::DES3),
|
||||
};
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::string> GetPreprovKeyTable() {
|
||||
return {{kTestSystemId, std::string(kTestPreprovKeyHex)}};
|
||||
}
|
||||
|
||||
std::multimap<uint32_t, std::string> GetPreprovKeyMultimap() {
|
||||
return {{kTestSystemId, kBadPreprovKey1Hex},
|
||||
{kTestSystemId, kTestPreprovKeyHex},
|
||||
{kTestSystemId, kBadPreprovKey2Hex}};
|
||||
}
|
||||
|
||||
} // namespace wvm_test_keys
|
||||
} // namespace widevine
|
||||
58
common/wvm_test_keys.h
Normal file
58
common/wvm_test_keys.h
Normal file
@@ -0,0 +1,58 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Sample keys for use in tests of Widevine WVM crypto code.
|
||||
|
||||
#ifndef COMMON_WVM_TEST_KEYS_H_
|
||||
#define COMMON_WVM_TEST_KEYS_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "common/wvm_token_handler.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace wvm_test_keys {
|
||||
|
||||
// Most preprov key and keyboxes in this header use system ID 0x112.
|
||||
const uint32_t kTestSystemId = 0x112;
|
||||
|
||||
// One oddball system that uses 3DES. (We'll re-use the system and device keys).
|
||||
const uint32_t kTestSystemId3Des = 0x10000013;
|
||||
|
||||
extern const char kTestPreprovKeyHex[];
|
||||
|
||||
extern const char kTestToken1Hex[];
|
||||
extern const char kTestDeviceKey1Hex[];
|
||||
|
||||
extern const char kTestToken2Hex[];
|
||||
extern const char kTestDeviceKey2Hex[];
|
||||
|
||||
extern const char kTestToken3DesHex[];
|
||||
extern const char kTestDeviceKey3DesHex[];
|
||||
|
||||
// Return a list of preprovisioning keys, suitable for initializing
|
||||
// the preprov key table for tests.
|
||||
std::vector<WvmTokenHandler::PreprovKey> GetPreprovKeyVector();
|
||||
|
||||
// Old version of GetPreprovKeyTable() which uses a simple int->std::string
|
||||
// map. Doesn't support multiple devices per system ID.
|
||||
// TODO(user): get rid of this once other code as been migrated off of it.
|
||||
std::map<uint32_t, std::string> GetPreprovKeyTable();
|
||||
|
||||
// Version of GetPreprovKeyTable() which uses a simple int->hex_string
|
||||
// multimap, useful for modular DRM code which doesn't care about the asset
|
||||
// key cipher.
|
||||
std::multimap<uint32_t, std::string> GetPreprovKeyMultimap();
|
||||
|
||||
} // namespace wvm_test_keys
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_WVM_TEST_KEYS_H_
|
||||
317
common/wvm_token_handler.cc
Normal file
317
common/wvm_token_handler.cc
Normal file
@@ -0,0 +1,317 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/wvm_token_handler.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/endian/endian.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "util/status.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/ecb_util.h"
|
||||
#include "common/sha_util.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
const int kKeyboxFlagInsecure = 1;
|
||||
const int kSystemIdSamsungTVFactory = 8;
|
||||
const int kPreProvisioningKeySizeBytes = 16;
|
||||
const int kKeyboxSizeBytes = 72;
|
||||
const std::string kZeroIV(16, '\0');
|
||||
|
||||
class PreprovKeysMap {
|
||||
public:
|
||||
void Update(const std::vector<WvmTokenHandler::PreprovKey>& key_vector);
|
||||
std::vector<WvmTokenHandler::PreprovKey> GetPreprovKeys(uint32_t system_id);
|
||||
bool IsSystemIdKnown(uint32_t system_id);
|
||||
bool IsEmpty();
|
||||
|
||||
static PreprovKeysMap* GetSingleton();
|
||||
|
||||
private:
|
||||
absl::Mutex mutex_;
|
||||
std::multimap<uint32_t, WvmTokenHandler::PreprovKey> preprov_keys_;
|
||||
};
|
||||
|
||||
void PreprovKeysMap::Update(
|
||||
const std::vector<WvmTokenHandler::PreprovKey>& key_vector) {
|
||||
absl::WriterMutexLock lock(&mutex_);
|
||||
preprov_keys_.clear();
|
||||
for (const WvmTokenHandler::PreprovKey& ppk : key_vector) {
|
||||
if (ppk.key_bytes.size() != kPreProvisioningKeySizeBytes) {
|
||||
LOG(WARNING) << "Invalid preprov key for system id: " << ppk.system_id;
|
||||
continue;
|
||||
}
|
||||
preprov_keys_.insert(std::make_pair(ppk.system_id, ppk));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<WvmTokenHandler::PreprovKey> PreprovKeysMap::GetPreprovKeys(
|
||||
uint32_t system_id) {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
std::vector<WvmTokenHandler::PreprovKey> key_vector;
|
||||
auto range = preprov_keys_.equal_range(system_id);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
key_vector.push_back(it->second);
|
||||
return key_vector;
|
||||
}
|
||||
|
||||
bool PreprovKeysMap::IsSystemIdKnown(uint32_t system_id) {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
return gtl::ContainsKey(preprov_keys_, system_id);
|
||||
}
|
||||
|
||||
bool PreprovKeysMap::IsEmpty() {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
return preprov_keys_.empty();
|
||||
}
|
||||
|
||||
PreprovKeysMap* PreprovKeysMap::GetSingleton() {
|
||||
static auto* const kInstance = new PreprovKeysMap();
|
||||
return kInstance;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
||||
const std::string& key_bytes, Cipher cipher,
|
||||
const std::string& model_filter)
|
||||
: system_id(system_id),
|
||||
key_bytes(key_bytes),
|
||||
cipher(cipher),
|
||||
model_filter(model_filter) {}
|
||||
|
||||
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
||||
const std::string& key_bytes, Cipher cipher)
|
||||
: system_id(system_id), key_bytes(key_bytes), cipher(cipher) {}
|
||||
|
||||
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
||||
const std::string& key_bytes)
|
||||
: system_id(system_id), key_bytes(key_bytes), cipher(AES) {}
|
||||
|
||||
void WvmTokenHandler::SetPreprovKeys(const std::vector<PreprovKey>& keyvec) {
|
||||
PreprovKeysMap::GetSingleton()->Update(keyvec);
|
||||
}
|
||||
|
||||
bool WvmTokenHandler::IsSystemIdKnown(uint32_t system_id) {
|
||||
return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id);
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out,
|
||||
bool* insecure_out) {
|
||||
const std::string default_make_model;
|
||||
return DecryptDeviceKey(token, default_make_model, device_key_out, cipher_out,
|
||||
insecure_out);
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
const std::string& make_model,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out,
|
||||
bool* insecure_out) {
|
||||
DCHECK(device_key_out);
|
||||
// DCHECK below is commented out because preprov_keys_ being nullptr
|
||||
// is a valid test in wvm_token_handler_test.cc. If we have
|
||||
// DCHECK(preprov_keys_) here it would thrown an failure in Kokoro
|
||||
// presubmit because evidently Kokoro does debug build.
|
||||
// DCHECK(preprov_keys_);
|
||||
if (token.size() < kKeyboxSizeBytes) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Keybox token is too short.");
|
||||
}
|
||||
if (PreprovKeysMap::GetSingleton()->IsEmpty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Pre-provisioning key map is nullptr.");
|
||||
}
|
||||
|
||||
uint32_t system_id = GetSystemId(token);
|
||||
|
||||
// There may be multiple preprov keys for a system ID; try them all.
|
||||
std::vector<PreprovKey> key_vector =
|
||||
PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id);
|
||||
|
||||
util::Status status;
|
||||
// First pass through the matching system Ids is an attempt to find an
|
||||
// alternate preprov key specific to this make/model.
|
||||
const PreprovKey* preferred_ppk = NULL;
|
||||
for (const PreprovKey& ppk : key_vector) {
|
||||
if (!ppk.model_filter.empty() && ppk.model_filter == make_model) {
|
||||
preferred_ppk = &ppk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const PreprovKey& ppk : key_vector) {
|
||||
if (preferred_ppk && preferred_ppk != &ppk) {
|
||||
continue;
|
||||
}
|
||||
uint32_t version;
|
||||
status = DecryptDeviceKeyWithPreprovKey(
|
||||
ppk.key_bytes, token, device_key_out, insecure_out, &version);
|
||||
if (version != 2) {
|
||||
// Only version 2 keyboxes supported.
|
||||
return util::Status(util::error::PERMISSION_DENIED,
|
||||
absl::StrCat("invalid-keybox-version ", version));
|
||||
}
|
||||
if (status.ok()) {
|
||||
if (cipher_out) {
|
||||
*cipher_out = ppk.cipher;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
}
|
||||
if (!status.ok()) {
|
||||
// Return error from last attempt.
|
||||
return status;
|
||||
}
|
||||
return util::Status(
|
||||
util::error::NOT_FOUND,
|
||||
absl::StrCat("Unknown system id: ", system_id).c_str()); // NOLINT
|
||||
}
|
||||
|
||||
// Decrypt a token using the preprov key for its system ID, and use the
|
||||
// decrypted device key to encrypt the given asset key. Returns the encrypted
|
||||
// asset key in |result|.
|
||||
// On failure, returns an error from the Widevine Server SDK error space.
|
||||
util::Status WvmTokenHandler::GetEncryptedAssetKey(
|
||||
absl::string_view token, absl::string_view raw_asset_key,
|
||||
const std::string& make_model, std::string* result) {
|
||||
std::string device_key;
|
||||
Cipher cipher = AES;
|
||||
util::Status status =
|
||||
DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
return EncryptAssetKey(device_key, raw_asset_key, cipher, result);
|
||||
}
|
||||
|
||||
uint32_t WvmTokenHandler::GetSystemId(absl::string_view token) {
|
||||
uint32_t system_id = 0;
|
||||
if (token.size() >= 8) {
|
||||
// Bytes 4-8 contain the little-endian system ID.
|
||||
system_id = BigEndian::Load32(token.data() + 4);
|
||||
}
|
||||
return system_id;
|
||||
}
|
||||
|
||||
std::string WvmTokenHandler::GetEncryptedUniqueId(absl::string_view token) {
|
||||
std::string encrypted_unique_id("");
|
||||
if (token.size() >= 24) {
|
||||
encrypted_unique_id = std::string(token.substr(8, 16));
|
||||
}
|
||||
return encrypted_unique_id;
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
absl::string_view preprov_key, absl::string_view token,
|
||||
std::string* device_key_out) {
|
||||
return DecryptDeviceKeyWithPreprovKey(preprov_key, token, device_key_out,
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
absl::string_view preprov_key, absl::string_view token,
|
||||
std::string* device_key_out, bool* insecure_out, uint32_t* version) {
|
||||
CHECK(device_key_out);
|
||||
if (token.size() < kKeyboxSizeBytes) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Keybox token is too short.");
|
||||
}
|
||||
if (version) {
|
||||
*version = BigEndian::Load32(token.data());
|
||||
}
|
||||
// This was checked at initialization, so if it fails now something is wrong.
|
||||
CHECK_EQ(preprov_key.size(), kPreProvisioningKeySizeBytes);
|
||||
|
||||
absl::string_view encrypted_unique_id = token.substr(8, 16);
|
||||
std::string unique_id = crypto_util::DecryptAesCbcNoPad(
|
||||
std::string(preprov_key), kZeroIV, std::string(encrypted_unique_id));
|
||||
|
||||
if (unique_id.size() != 16) {
|
||||
// Decrypting 16 bytes should result in 16 bytes.
|
||||
LOG(WARNING) << "Internal error decrypting unique id from token.";
|
||||
return util::Status(util::error::INTERNAL, "Wrong size after decrypt/16.");
|
||||
}
|
||||
|
||||
absl::string_view encrypted_bits = token.substr(24, 48);
|
||||
std::string decrypted_bits = crypto_util::DecryptAesCbcNoPad(
|
||||
unique_id, kZeroIV, std::string(encrypted_bits));
|
||||
if (decrypted_bits.size() != 48) {
|
||||
// Decrypting 48 bytes should result in 48 bytes.
|
||||
LOG(WARNING) << "Internal error decrypting device key from token.";
|
||||
return util::Status(util::error::INTERNAL, "Wrong size after decrypt/48.");
|
||||
}
|
||||
uint8_t keybox_flags = decrypted_bits[36];
|
||||
absl::string_view device_key =
|
||||
absl::string_view(decrypted_bits).substr(0, 16);
|
||||
absl::string_view expected_hash =
|
||||
absl::string_view(decrypted_bits).substr(16, 20);
|
||||
std::string actual_hash = Sha1_Hash(std::string(device_key));
|
||||
|
||||
if (GetSystemId(token) == kSystemIdSamsungTVFactory) {
|
||||
// Keyboxes with this system ID have corrupted bytes starting after the
|
||||
// first 16 bytes of the hash, so we use only the uncorrupted part.
|
||||
expected_hash = expected_hash.substr(0, 16);
|
||||
actual_hash.resize(16);
|
||||
keybox_flags = 0;
|
||||
}
|
||||
if (expected_hash != actual_hash) {
|
||||
return util::Status(util::error::PERMISSION_DENIED,
|
||||
"Keybox validation failed.");
|
||||
}
|
||||
*device_key_out = std::string(device_key);
|
||||
if (insecure_out) {
|
||||
*insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0;
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key,
|
||||
absl::string_view raw_asset_key,
|
||||
Cipher cipher, std::string* result) {
|
||||
CHECK(result);
|
||||
if (device_key.size() != 16) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Invalid device key: size != 16");
|
||||
}
|
||||
if (raw_asset_key.size() < 16) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Invalid asset key: size < 16");
|
||||
}
|
||||
// Truncate extra characters in the key; wvm always uses 16.
|
||||
absl::string_view asset_key = raw_asset_key.substr(0, 16);
|
||||
switch (cipher) {
|
||||
case DES3:
|
||||
if (!crypto_util::Encrypt3DesEcb(device_key, asset_key, result)) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Error encrypting asset key with 3DES.");
|
||||
}
|
||||
return util::OkStatus();
|
||||
case AES:
|
||||
if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Error encrypting asset key with AES.");
|
||||
}
|
||||
return util::OkStatus();
|
||||
case PASS_THRU:
|
||||
result->assign(raw_asset_key.data(), raw_asset_key.size());
|
||||
return util::OkStatus();
|
||||
default:
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Unknown cipher type");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
126
common/wvm_token_handler.h
Normal file
126
common/wvm_token_handler.h
Normal file
@@ -0,0 +1,126 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_WVM_TOKEN_HANDLER_H_
|
||||
#define COMMON_WVM_TOKEN_HANDLER_H_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "util/status.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Class for decoding the encrypted token that comes from a WVM classic keybox.
|
||||
//
|
||||
// Internally, this class keeps a multimap from system ID to the preprov key(s)
|
||||
// for that system, so construction is relatively expensive; don't create an
|
||||
// instance of this class per-request.
|
||||
//
|
||||
// Errors in this file are returned in the canonical space, but are chosen so
|
||||
// that it's possible to map different failure modes to the appropriate codes
|
||||
// in the WVM or server SDK error spaces:
|
||||
// OK - success.
|
||||
// NOT_FOUND - system id from token wasn't in the preprov key table.
|
||||
// PERMISSION_DENIED - hash of device key didn't match.
|
||||
// INVALID_ARGUMENT - token or a key is wrong size or otherwise invalid.
|
||||
// INTERNAL_ERROR - something went wrong that shouldn't have been able to.
|
||||
class WvmTokenHandler {
|
||||
public:
|
||||
// Cipher type to use for encrypting asset keys. This matches the enum in
|
||||
// video/widevine/lockbox/public/key.proto.
|
||||
enum Cipher {
|
||||
DES3 = 0,
|
||||
AES = 1,
|
||||
PASS_THRU = 2,
|
||||
};
|
||||
|
||||
struct PreprovKey {
|
||||
// Utility constructor.
|
||||
PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher,
|
||||
const std::string& model_filter);
|
||||
PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher);
|
||||
// Constructor if the cipher isn't needed (i.e. modular DRM).
|
||||
PreprovKey(uint32_t system_id, const std::string& key_bytes);
|
||||
uint32_t system_id;
|
||||
std::string key_bytes;
|
||||
Cipher cipher;
|
||||
// If set, the make/model in the license request must match this value.
|
||||
std::string model_filter;
|
||||
};
|
||||
|
||||
// Set pre-provisioning keys from the given vector. This may be called
|
||||
// concurrently with other methods.
|
||||
static void SetPreprovKeys(const std::vector<PreprovKey>& preprov_keys);
|
||||
|
||||
// Returns true if system_id is in the preprov key table.
|
||||
static bool IsSystemIdKnown(uint32_t system_id);
|
||||
|
||||
// Decrypt a token using the preprov key for its system ID, and
|
||||
// return the decrypted device key in result.
|
||||
// On failure, returns one of the errors listed above.
|
||||
// cipher_out may be null; if not, *cipher_out will be set to the cipher type
|
||||
// to use with the device key.
|
||||
// insecure_out may be null; if not, *insecure_out will be set to the
|
||||
// decrypted value of the 'insecure keybox' flag.
|
||||
static util::Status DecryptDeviceKey(absl::string_view token,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out, bool* insecure_out);
|
||||
// Same as above, except takes in the make/model from the license request.
|
||||
// For legacy WVM license, we have some special cases where we need to inspect
|
||||
// the make/model as we apply alternate keys.
|
||||
static util::Status DecryptDeviceKey(absl::string_view token,
|
||||
const std::string& make_model,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out, bool* insecure_out);
|
||||
|
||||
// Decrypt a token using the preprov key for its system ID, and use the
|
||||
// decrypted device key to encrypt the given asset key. Returns the encrypted
|
||||
// asset key in result.
|
||||
static util::Status GetEncryptedAssetKey(absl::string_view token,
|
||||
absl::string_view raw_asset_key,
|
||||
const std::string& make_model,
|
||||
std::string* result);
|
||||
|
||||
// Extract the system ID component of a token (bytes 4-8).
|
||||
static uint32_t GetSystemId(absl::string_view token);
|
||||
|
||||
// Extract the encrypted unique ID component of a token (bytes 8-24).
|
||||
static std::string GetEncryptedUniqueId(absl::string_view token);
|
||||
|
||||
// Try to decrypt a token using the provided preprov key, and return the
|
||||
// decrypted device key in result.
|
||||
//
|
||||
// Note that the if the input std::string lengths are correct (16 and 72 bytes),
|
||||
// the only possible cause of failure is the decrypted device key hash
|
||||
// being incorrect.
|
||||
static util::Status DecryptDeviceKeyWithPreprovKey(
|
||||
absl::string_view preprov_key_bytes, absl::string_view token,
|
||||
std::string* device_key_out);
|
||||
|
||||
// Same as above, but allows extracting the 'insecure keybox' flag and keybox
|
||||
// version.
|
||||
static util::Status DecryptDeviceKeyWithPreprovKey(
|
||||
absl::string_view preprov_key_bytes, absl::string_view token,
|
||||
std::string* device_key_out, bool* insecure_out, uint32_t* version);
|
||||
|
||||
// Given a decrypted device key as returned by DecryptToken(), use it to
|
||||
// encrypt an asset key with the given cipher.
|
||||
static util::Status EncryptAssetKey(absl::string_view device_key,
|
||||
absl::string_view raw_asset_key,
|
||||
Cipher cipher, std::string* result);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_WVM_TOKEN_HANDLER_H_
|
||||
235
common/wvm_token_handler_test.cc
Normal file
235
common/wvm_token_handler_test.cc
Normal file
@@ -0,0 +1,235 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/wvm_token_handler.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/wvm_test_keys.h"
|
||||
|
||||
using widevine::wvm_test_keys::kTestSystemId;
|
||||
using widevine::wvm_test_keys::kTestSystemId3Des;
|
||||
using widevine::wvm_test_keys::kTestPreprovKeyHex;
|
||||
using widevine::wvm_test_keys::kTestDeviceKey1Hex;
|
||||
using widevine::wvm_test_keys::kTestDeviceKey2Hex;
|
||||
using widevine::wvm_test_keys::kTestDeviceKey3DesHex;
|
||||
using widevine::wvm_test_keys::kTestToken1Hex;
|
||||
using widevine::wvm_test_keys::kTestToken2Hex;
|
||||
using widevine::wvm_test_keys::kTestToken3DesHex;
|
||||
using widevine::wvm_test_keys::GetPreprovKeyVector;
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using absl::BytesToHexString;
|
||||
using absl::HexStringToBytes;
|
||||
|
||||
// TODO(user): Add EXPECT_OK macro to testing/gmock.h.
|
||||
// (b/37545268).
|
||||
#define EXPECT_OK(expression) \
|
||||
EXPECT_EQ(util::error::OK, expression.error_code())
|
||||
|
||||
|
||||
TEST(WvmTokenHandlerTest, GetSystemId) {
|
||||
EXPECT_EQ(kTestSystemId,
|
||||
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex)));
|
||||
EXPECT_EQ(kTestSystemId,
|
||||
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken2Hex)));
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, GetEncryptedUniqueId) {
|
||||
EXPECT_EQ(
|
||||
HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"),
|
||||
WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken1Hex)));
|
||||
EXPECT_EQ(
|
||||
HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"),
|
||||
WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken2Hex)));
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
|
||||
util::Status status;
|
||||
std::string device_key;
|
||||
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken1Hex),
|
||||
&device_key);
|
||||
EXPECT_OK(status) << status;
|
||||
EXPECT_EQ(kTestDeviceKey1Hex, BytesToHexString(device_key));
|
||||
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken2Hex),
|
||||
&device_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ(kTestDeviceKey2Hex, BytesToHexString(device_key));
|
||||
|
||||
// Test with invalid token. Hash failure should produce PERMISSION_DENIED.
|
||||
device_key.clear();
|
||||
std::string token = HexStringToBytes(
|
||||
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e");
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(kTestPreprovKeyHex), token, &device_key);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
}
|
||||
|
||||
// See b/68798704 for background of this test.
|
||||
// It is important to keep this test *before* all the DecryptDeviceKey* tests
|
||||
// below, in which WvmTokenHandler::SetPreprovKeys() would be called.
|
||||
TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) {
|
||||
// Not calling WvmTokenHandler::SetPreprovKeys()
|
||||
// So preprov_keys_ would be nullptr.
|
||||
util::Status status;
|
||||
std::string device_key;
|
||||
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
|
||||
&device_key, nullptr, nullptr);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(util::error::INVALID_ARGUMENT, status.error_code());
|
||||
}
|
||||
|
||||
// Same tests as DecryptDeviceKeyWithPreprovKey(), but we use the handler's
|
||||
// table of preprov keys instead of providing our own.
|
||||
TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
|
||||
util::Status status;
|
||||
std::string device_key;
|
||||
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
|
||||
|
||||
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
|
||||
&device_key, nullptr, nullptr);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ(HexStringToBytes(kTestDeviceKey1Hex), device_key);
|
||||
|
||||
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken2Hex),
|
||||
&device_key, nullptr, nullptr);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ(HexStringToBytes(kTestDeviceKey2Hex), device_key);
|
||||
|
||||
// Test with invalid token. Hash failure should produce PERMISSION_DENIED.
|
||||
device_key.clear();
|
||||
std::string token = HexStringToBytes(
|
||||
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e");
|
||||
status =
|
||||
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
|
||||
// Test with nonexistent system id. Should produce NOT_FOUND.
|
||||
device_key.clear();
|
||||
token = HexStringToBytes(
|
||||
"00000002555555559e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e");
|
||||
status =
|
||||
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(util::error::NOT_FOUND, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, GetEncryptedAssetKey) {
|
||||
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
|
||||
|
||||
std::string raw_asset_key = "asset-key-000000";
|
||||
std::string asset_key;
|
||||
std::string make_model;
|
||||
util::Status status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key));
|
||||
|
||||
status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken2Hex), raw_asset_key, make_model, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("091802159bf8da12aecfcdfb092075c8", BytesToHexString(asset_key));
|
||||
|
||||
// Check 3DES encryption of asset keys
|
||||
status = WvmTokenHandler::EncryptAssetKey(
|
||||
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
|
||||
WvmTokenHandler::DES3, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
|
||||
asset_key.clear();
|
||||
status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model,
|
||||
&asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
|
||||
// Test with pass-thru (Cipher=PASS_THRU).
|
||||
asset_key.clear();
|
||||
status = WvmTokenHandler::EncryptAssetKey(
|
||||
HexStringToBytes(kTestDeviceKey1Hex), raw_asset_key,
|
||||
WvmTokenHandler::PASS_THRU, &asset_key);
|
||||
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ(BytesToHexString(raw_asset_key), BytesToHexString(asset_key));
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, FilterOnMakeModel) {
|
||||
// Use all good keys, but only the key for LG:BD572 should work. It works
|
||||
// by setting only that one to DES3. All other AES ppks will encrypt the
|
||||
// asset key incorrectly.
|
||||
std::vector<WvmTokenHandler::PreprovKey> ppks;
|
||||
ppks.push_back(WvmTokenHandler::PreprovKey(
|
||||
kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex),
|
||||
WvmTokenHandler::AES, ""));
|
||||
ppks.push_back(WvmTokenHandler::PreprovKey(
|
||||
kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex),
|
||||
WvmTokenHandler::DES3, "LG:BD572"));
|
||||
ppks.push_back(WvmTokenHandler::PreprovKey(
|
||||
kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex),
|
||||
WvmTokenHandler::AES, ""));
|
||||
WvmTokenHandler::SetPreprovKeys(ppks);
|
||||
std::string raw_asset_key = "asset-key-000000";
|
||||
std::string asset_key;
|
||||
// Check 3DES encryption of asset keys
|
||||
util::Status status = WvmTokenHandler::EncryptAssetKey(
|
||||
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
|
||||
WvmTokenHandler::DES3, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
|
||||
asset_key.clear();
|
||||
std::string make_model;
|
||||
status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model,
|
||||
&asset_key);
|
||||
EXPECT_OK(status);
|
||||
// Should fail because the asset key was encrypted with AES instead of DES3.
|
||||
EXPECT_NE("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
|
||||
// Set the make/model so we find and use the correct ppk.
|
||||
make_model = "LG:BD572";
|
||||
status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model,
|
||||
&asset_key);
|
||||
EXPECT_OK(status);
|
||||
// Should work because the asset key was encrypted with DES3.
|
||||
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, AncientKeybox) {
|
||||
util::Status status;
|
||||
std::string device_key;
|
||||
|
||||
std::string v1_token(
|
||||
std::string(kTestPreprovKeyHex).replace(0, 16, "0000000100000001"));
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex),
|
||||
&device_key);
|
||||
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
363
common/x509_cert.cc
Normal file
363
common/x509_cert.cc
Normal file
@@ -0,0 +1,363 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/x509_cert.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/pkcs7.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "openssl/x509v3.h"
|
||||
#include "common/openssl_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
namespace {
|
||||
// Serializes the X509 |certificate| into a PEM-encoded string. Returns true
|
||||
// on success, false otherwise. The caller retains ownership of
|
||||
// |certificate| and |serialized_certificate|. |serialized_certificate| must
|
||||
// not be NULL.
|
||||
bool PemEncodeX509Certificate(const X509& certificate,
|
||||
std::string* serialized_certificate) {
|
||||
CHECK(serialized_certificate) << "serialized_certificate can not be null.";
|
||||
|
||||
ScopedBIO bio(BIO_new(BIO_s_mem()));
|
||||
if (bio == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The const_cast is necessary for the openssl call.
|
||||
PEM_write_bio_X509(bio.get(), const_cast<X509*>(&certificate));
|
||||
int serialized_size = BIO_pending(bio.get());
|
||||
serialized_certificate->resize(serialized_size);
|
||||
if (BIO_read(bio.get(), &(*serialized_certificate)[0], serialized_size) !=
|
||||
serialized_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // anonymous namespace.
|
||||
|
||||
namespace widevine {
|
||||
|
||||
std::unique_ptr<X509Cert> X509Cert::FromOpenSslCert(ScopedX509 certificate) {
|
||||
return std::unique_ptr<X509Cert>(new X509Cert(certificate.release()));
|
||||
}
|
||||
|
||||
X509Cert::X509Cert() : openssl_cert_(NULL) {}
|
||||
|
||||
X509Cert::~X509Cert() {
|
||||
if (openssl_cert_ != NULL) {
|
||||
X509_free(openssl_cert_);
|
||||
}
|
||||
}
|
||||
|
||||
X509Cert::X509Cert(X509* openssl_cert) : openssl_cert_(openssl_cert) {}
|
||||
|
||||
util::Status X509Cert::LoadPem(const std::string& pem_cert) {
|
||||
if (pem_cert.empty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM certificate");
|
||||
}
|
||||
BIO* bio(NULL);
|
||||
X509* new_cert(NULL);
|
||||
bio = BIO_new_mem_buf(const_cast<char*>(pem_cert.data()), pem_cert.size());
|
||||
if (bio == NULL) {
|
||||
return util::Status(util::error::INTERNAL, "BIO allocation failed");
|
||||
}
|
||||
util::Status status;
|
||||
new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
|
||||
if (new_cert == NULL) {
|
||||
status = util::Status(util::Status::canonical_space(),
|
||||
util::error::INVALID_ARGUMENT,
|
||||
"PEM certificate load failed");
|
||||
goto cleanup;
|
||||
}
|
||||
if (openssl_cert_ != NULL) {
|
||||
X509_free(openssl_cert_);
|
||||
}
|
||||
openssl_cert_ = new_cert;
|
||||
|
||||
cleanup:
|
||||
if (bio != NULL) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
util::Status X509Cert::LoadDer(const std::string& der_cert) {
|
||||
if (der_cert.empty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty DER certificate");
|
||||
}
|
||||
const unsigned char* cert_data =
|
||||
reinterpret_cast<const unsigned char*>(der_cert.data());
|
||||
X509* new_cert = d2i_X509(NULL, &cert_data, der_cert.size());
|
||||
if (new_cert == NULL) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"DER certificate load failed");
|
||||
}
|
||||
if (openssl_cert_ != NULL) {
|
||||
X509_free(openssl_cert_);
|
||||
}
|
||||
openssl_cert_ = new_cert;
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
std::string X509Cert::GetPem() const {
|
||||
std::string serialized_certificate;
|
||||
if (!PemEncodeX509Certificate(*openssl_cert_, &serialized_certificate)) {
|
||||
return "";
|
||||
}
|
||||
return serialized_certificate;
|
||||
}
|
||||
|
||||
std::unique_ptr<RsaPublicKey> X509Cert::GetRsaPublicKey() const {
|
||||
ScopedPKEY pkey(X509_get_pubkey(openssl_cert_));
|
||||
return std::unique_ptr<RsaPublicKey>(
|
||||
new RsaPublicKey(EVP_PKEY_get1_RSA(pkey.get())));
|
||||
}
|
||||
|
||||
const std::string& X509Cert::GetSubjectName() {
|
||||
if (subject_name_.empty() && (openssl_cert_ != NULL)) {
|
||||
X509_NAME* subject = X509_get_subject_name(openssl_cert_);
|
||||
if (subject != NULL) {
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
if (bio != NULL) {
|
||||
X509_NAME_print_ex(bio, subject, 0, 0);
|
||||
int size = BIO_pending(bio);
|
||||
std::unique_ptr<char[]> buffer(new char[size]);
|
||||
int bytes_read = BIO_read(bio, buffer.get(), size);
|
||||
if (bytes_read == size) {
|
||||
subject_name_.assign(buffer.get(), bytes_read);
|
||||
}
|
||||
BIO_free(bio);
|
||||
}
|
||||
}
|
||||
}
|
||||
return subject_name_;
|
||||
}
|
||||
|
||||
std::string X509Cert::GetSubjectNameField(const std::string& field) {
|
||||
if (field.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
const std::string& subject = GetSubjectName();
|
||||
size_t start_pos = subject.find(field + "=");
|
||||
if (start_pos == std::string::npos) {
|
||||
return std::string();
|
||||
}
|
||||
start_pos += field.size() + 1;
|
||||
size_t end_pos = subject.find(",", start_pos);
|
||||
if (end_pos == std::string::npos) {
|
||||
end_pos = subject.size();
|
||||
}
|
||||
return subject.substr(start_pos, end_pos - start_pos);
|
||||
}
|
||||
|
||||
std::string X509Cert::GetSerialNumber() const {
|
||||
if (openssl_cert_ == NULL) {
|
||||
return std::string();
|
||||
}
|
||||
BIGNUM* bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(openssl_cert_), NULL);
|
||||
if (bn == NULL) {
|
||||
return std::string();
|
||||
}
|
||||
std::string result;
|
||||
char* openssl_sn = BN_bn2hex(bn);
|
||||
if (openssl_sn != NULL) {
|
||||
result = absl::HexStringToBytes(openssl_sn);
|
||||
OPENSSL_free(openssl_sn);
|
||||
}
|
||||
BN_free(bn);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool X509Cert::IsCaCertificate() const {
|
||||
return X509_check_ca(openssl_cert_) != 0;
|
||||
}
|
||||
|
||||
bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const {
|
||||
ScopedAsn1Object extension_name(OBJ_txt2obj(oid.c_str(), 1));
|
||||
int ext_pos = X509_get_ext_by_OBJ(openssl_cert_, extension_name.get(), -1);
|
||||
if (ext_pos < 0) return false;
|
||||
X509_EXTENSION* extension(X509_get_ext(openssl_cert_, ext_pos));
|
||||
if (!extension) return false;
|
||||
ASN1_OCTET_STRING* extension_data(X509_EXTENSION_get_data(extension));
|
||||
if (!extension_data) return false;
|
||||
if ((extension_data->length != 3) || (extension_data->data[0] != 1) ||
|
||||
(extension_data->data[1] != 1))
|
||||
return false;
|
||||
|
||||
*value = extension_data->data[2] != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
X509CertChain::~X509CertChain() { Reset(); }
|
||||
|
||||
void X509CertChain::Reset() {
|
||||
for (auto certp : cert_chain_) {
|
||||
delete certp;
|
||||
}
|
||||
cert_chain_.clear();
|
||||
}
|
||||
|
||||
util::Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
||||
static const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----";
|
||||
static const char kEndCertificate[] = "-----END CERTIFICATE-----";
|
||||
|
||||
Reset();
|
||||
size_t begin_pos = pem_cert_chain.find(kBeginCertificate);
|
||||
while (begin_pos != std::string::npos) {
|
||||
size_t end_pos = pem_cert_chain.find(
|
||||
kEndCertificate, begin_pos + sizeof(kBeginCertificate) - 1);
|
||||
if (end_pos != std::string::npos) {
|
||||
end_pos += sizeof(kEndCertificate) - 1;
|
||||
std::unique_ptr<X509Cert> new_cert(new X509Cert);
|
||||
util::Status status = new_cert->LoadPem(
|
||||
pem_cert_chain.substr(begin_pos, end_pos - begin_pos));
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
cert_chain_.push_back(new_cert.release());
|
||||
begin_pos = pem_cert_chain.find(kBeginCertificate, end_pos);
|
||||
}
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) {
|
||||
ScopedX509Stack cert_stack(sk_X509_new_null());
|
||||
CBS cbs;
|
||||
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pk7_cert_chain.data()),
|
||||
pk7_cert_chain.size());
|
||||
if (!PKCS7_get_certificates(cert_stack.get(), &cbs)) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Unable to load PKCS#7 certificate chain");
|
||||
}
|
||||
|
||||
while (sk_X509_num(cert_stack.get()) > 0) {
|
||||
cert_chain_.insert(cert_chain_.begin(),
|
||||
new X509Cert(sk_X509_pop(cert_stack.get())));
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
X509Cert* X509CertChain::GetCert(size_t cert_index) const {
|
||||
if (cert_index >= cert_chain_.size()) {
|
||||
return NULL;
|
||||
}
|
||||
return cert_chain_[cert_index];
|
||||
}
|
||||
|
||||
X509CA::X509CA(X509Cert* ca_cert) : ca_cert_(ca_cert), openssl_store_(NULL) {}
|
||||
|
||||
X509CA::~X509CA() {
|
||||
if (openssl_store_ != NULL) {
|
||||
X509_STORE_free(openssl_store_);
|
||||
}
|
||||
}
|
||||
|
||||
util::Status X509CA::InitializeStore() {
|
||||
absl::WriterMutexLock lock(&openssl_store_mutex_);
|
||||
if (openssl_store_ == NULL) {
|
||||
if (ca_cert_ == NULL) {
|
||||
return util::Status(util::error::INTERNAL, "CA X.509Cert is NULL");
|
||||
}
|
||||
openssl_store_ = X509_STORE_new();
|
||||
if (openssl_store_ == NULL) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Failed to allocate X.509 store");
|
||||
}
|
||||
if (X509_STORE_add_cert(openssl_store_,
|
||||
const_cast<X509*>(ca_cert_->openssl_cert())) == 0) {
|
||||
X509_STORE_free(openssl_store_);
|
||||
openssl_store_ = NULL;
|
||||
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Failed to add X.509 CA certificate to store");
|
||||
}
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status X509CA::VerifyCert(const X509Cert& cert) {
|
||||
return OpenSslX509Verify(cert.openssl_cert(), nullptr);
|
||||
}
|
||||
|
||||
util::Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) {
|
||||
if (cert_chain.GetNumCerts() < 1) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Cannot verify empty certificate chain");
|
||||
}
|
||||
|
||||
ScopedX509StackOnly intermediates(sk_X509_new_null());
|
||||
if (!intermediates) {
|
||||
return util::Status(
|
||||
util::Status::canonical_space(), util::error::INTERNAL,
|
||||
"Failed to allocate X.509 intermediate certificate stack");
|
||||
}
|
||||
const X509Cert* leaf_cert(nullptr);
|
||||
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
|
||||
if (cert_chain.GetCert(idx)->IsCaCertificate()) {
|
||||
sk_X509_push(intermediates.get(),
|
||||
const_cast<X509*>(cert_chain.GetCert(idx)->openssl_cert()));
|
||||
} else {
|
||||
leaf_cert = cert_chain.GetCert(idx);
|
||||
}
|
||||
}
|
||||
if (!leaf_cert) {
|
||||
return util::Status(util::Status::canonical_space(),
|
||||
util::error::INVALID_ARGUMENT,
|
||||
"X.509 certificate chain without leaf certificate.");
|
||||
}
|
||||
return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get());
|
||||
}
|
||||
|
||||
util::Status X509CA::OpenSslX509Verify(const X509* cert,
|
||||
STACK_OF(X509) * intermediates) {
|
||||
DCHECK(cert);
|
||||
|
||||
absl::ReaderMutexLock lock(&openssl_store_mutex_);
|
||||
if (openssl_store_ == NULL) {
|
||||
openssl_store_mutex_.ReaderUnlock();
|
||||
util::Status status = InitializeStore();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
openssl_store_mutex_.ReaderLock();
|
||||
}
|
||||
ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new());
|
||||
if (!store_ctx) {
|
||||
return util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
||||
"Failed to allocate X.509 store context");
|
||||
}
|
||||
if (X509_STORE_CTX_init(store_ctx.get(), openssl_store_,
|
||||
const_cast<X509*>(cert), intermediates) == 0) {
|
||||
return util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
||||
"Failed to initialize X.509 store context");
|
||||
}
|
||||
int x509_status = X509_verify_cert(store_ctx.get());
|
||||
if (x509_status != 1) {
|
||||
return util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
||||
std::string("X.509 certificate chain validation failed: ") +
|
||||
X509_verify_cert_error_string(
|
||||
X509_STORE_CTX_get_error(store_ctx.get())));
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
154
common/x509_cert.h
Normal file
154
common/x509_cert.h
Normal file
@@ -0,0 +1,154 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// X.509 certificate classes used by the license server SDK.
|
||||
|
||||
#ifndef COMMON_X509_CERT_H_
|
||||
#define COMMON_X509_CERT_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/thread_annotations.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "openssl/x509v3.h"
|
||||
#include "util/status.h"
|
||||
#include "common/openssl_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// NOTE: All util::Status codes are in the canonical error space.
|
||||
|
||||
// Class which holds a single X.509 certificates.
|
||||
class X509Cert {
|
||||
public:
|
||||
// Load the certificate from an openssl X509 certificate instance.
|
||||
static std::unique_ptr<X509Cert> FromOpenSslCert(ScopedX509 openssl_cert_);
|
||||
|
||||
X509Cert();
|
||||
virtual ~X509Cert();
|
||||
|
||||
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
||||
// a PEM-encoded certificate.
|
||||
util::Status LoadPem(const std::string& pem_cert);
|
||||
|
||||
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
||||
// a DER-encoded certificate.
|
||||
util::Status LoadDer(const std::string& der_cert);
|
||||
|
||||
// Return a std::string containing the PEM-encoded certificate.
|
||||
std::string GetPem() const;
|
||||
|
||||
// Returns certificate RSA public key. nullptr if not found, or if key type
|
||||
// is not RSA.
|
||||
std::unique_ptr<RsaPublicKey> GetRsaPublicKey() const;
|
||||
|
||||
// Returns the internal OpenSSL X509 certificate.
|
||||
const X509* openssl_cert() const { return openssl_cert_; }
|
||||
|
||||
// Returns the certificate subject name.
|
||||
const std::string& GetSubjectName();
|
||||
|
||||
// Returns a field within the certificate subject name, or an empty std::string
|
||||
// if the field is not found.
|
||||
std::string GetSubjectNameField(const std::string& field);
|
||||
|
||||
// Returns the certificate serial number, binary encoded, or an empty std::string
|
||||
// if an error occurs.
|
||||
std::string GetSerialNumber() const;
|
||||
|
||||
// Returns true if the certificate is a CA (root or intermediate) certificate.
|
||||
bool IsCaCertificate() const;
|
||||
|
||||
// Gets the value of an X.509 V3 extension encoded as a boolean. |oid| is the
|
||||
// object identifier sequence for the extension, and |value| is a pointer to
|
||||
// an integer which will contain the extension value upon successful return.
|
||||
// Returns true if successful, or false otherwise.
|
||||
bool GetV3BooleanExtension(const std::string& oid, bool* value) const;
|
||||
|
||||
private:
|
||||
explicit X509Cert(X509* openssl_cert);
|
||||
|
||||
X509* openssl_cert_;
|
||||
std::string subject_name_;
|
||||
|
||||
friend class X509CertChain;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(X509Cert);
|
||||
};
|
||||
|
||||
// Class which holds a chain of X.509 certificates.
|
||||
class X509CertChain {
|
||||
public:
|
||||
X509CertChain() {}
|
||||
virtual ~X509CertChain();
|
||||
|
||||
// Loads a chain of PEM-encoded X.509 certificates. Takes a single parameter,
|
||||
// |pem_cert_chain|, which is the concatenation of a number of PEM X.509
|
||||
// certificates, beginning with the leaf certificate, and ending with the
|
||||
// certificate signed by the root CA.
|
||||
util::Status LoadPem(const std::string& pem_cert_chain);
|
||||
|
||||
// Loads a chain of DER-encoded PKCS#7 certificates. Takes a single parameter,
|
||||
// |pk7_cert_chain|, which is a DER-encoded PKCS#7 X.509 certificate
|
||||
// container.
|
||||
util::Status LoadPkcs7(const std::string& pk7_cert_chain);
|
||||
|
||||
// Returns the number of certificates in the chain.
|
||||
size_t GetNumCerts() const { return cert_chain_.size(); }
|
||||
|
||||
// Returns the X509Cert at the specified chain index (0 start). Returns
|
||||
// NULL if cert_index is out of range. The X509CertChain retains ownwership
|
||||
// of the object returned.
|
||||
X509Cert* GetCert(size_t cert_index) const;
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
|
||||
std::vector<X509Cert*> cert_chain_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(X509CertChain);
|
||||
};
|
||||
|
||||
// CA class which holds the root CA cert, and verifies certificate chains.
|
||||
class X509CA {
|
||||
public:
|
||||
// New object assumes ownership of |ca_cert|.
|
||||
explicit X509CA(X509Cert* ca_cert);
|
||||
virtual ~X509CA();
|
||||
|
||||
// Does X.509 PKI validation of |cert| against the root CA certificate
|
||||
// used when constructing X509CA. This method is thread-safe.
|
||||
util::Status VerifyCert(const X509Cert& cert);
|
||||
|
||||
// Does X.509 PKI validation of |cert_chain| against the root CA certificate
|
||||
// used when constructing X509CA. This method is thread-safe.
|
||||
util::Status VerifyCertChain(const X509CertChain& cert_chain);
|
||||
|
||||
private:
|
||||
util::Status InitializeStore();
|
||||
util::Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * stack);
|
||||
|
||||
std::unique_ptr<X509Cert> ca_cert_;
|
||||
absl::Mutex openssl_store_mutex_;
|
||||
X509_STORE* openssl_store_ GUARDED_BY(openssl_store_mutex_);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(X509CA);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_X509_CERT_H_
|
||||
403
common/x509_cert_test.cc
Normal file
403
common/x509_cert_test.cc
Normal file
@@ -0,0 +1,403 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/test_utils.h"
|
||||
#include "common/x509_cert.h"
|
||||
|
||||
namespace widevine {
|
||||
const char kTestRootCaDerCert[] =
|
||||
"30820403308202eba003020102020900a24f94af7ae6831f300d06092a86"
|
||||
"4886f70d0101050500308197310b30090603550406130255533113301106"
|
||||
"035504080c0a57617368696e67746f6e3111300f06035504070c084b6972"
|
||||
"6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f"
|
||||
"060355040b0c085769646576696e653115301306035504030c0c54657374"
|
||||
"20526f6f742043413121301f06092a864886f70d010901161274696e736b"
|
||||
"697040676f6f676c652e636f6d301e170d3133303831363030353731305a"
|
||||
"170d3333303831353030353731305a308197310b30090603550406130255"
|
||||
"533113301106035504080c0a57617368696e67746f6e3111300f06035504"
|
||||
"070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049"
|
||||
"6e633111300f060355040b0c085769646576696e65311530130603550403"
|
||||
"0c0c5465737420526f6f742043413121301f06092a864886f70d01090116"
|
||||
"1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648"
|
||||
"86f70d01010105000382010f003082010a0282010100c6eee629d99f7736"
|
||||
"2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c"
|
||||
"e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319"
|
||||
"0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3"
|
||||
"f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93"
|
||||
"52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7"
|
||||
"e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f"
|
||||
"d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20"
|
||||
"79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7"
|
||||
"3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30"
|
||||
"ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680"
|
||||
"144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405"
|
||||
"30030101ff300d06092a864886f70d01010505000382010100779e9b98d3"
|
||||
"ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee"
|
||||
"79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124"
|
||||
"ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436"
|
||||
"026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140"
|
||||
"ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053"
|
||||
"0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258"
|
||||
"e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82"
|
||||
"d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4"
|
||||
"c270d87191c13aefdd177b";
|
||||
|
||||
const char kTestPemCert[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n"
|
||||
"VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n"
|
||||
"Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n"
|
||||
"ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n"
|
||||
"MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n"
|
||||
"BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n"
|
||||
"aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n"
|
||||
"Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n"
|
||||
"aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n"
|
||||
"DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n"
|
||||
"l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n"
|
||||
"krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n"
|
||||
"wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n"
|
||||
"fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n"
|
||||
"FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n"
|
||||
"qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n"
|
||||
"EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n"
|
||||
"f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n"
|
||||
"QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n"
|
||||
"7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n"
|
||||
"3rDeUI+Odg==\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
const char kTestPemCertSubjectField_C[] = "US";
|
||||
const char kTestPemCertSubjectField_CN[] =
|
||||
"stable id/emailAddress=tinskip@google.com";
|
||||
const char kTestPemCertSerialNumber[] = "\002";
|
||||
|
||||
const char kTestPemCertChain[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n"
|
||||
"VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n"
|
||||
"Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n"
|
||||
"ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n"
|
||||
"MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n"
|
||||
"BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n"
|
||||
"aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n"
|
||||
"Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n"
|
||||
"aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n"
|
||||
"DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n"
|
||||
"l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n"
|
||||
"krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n"
|
||||
"wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n"
|
||||
"fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n"
|
||||
"FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n"
|
||||
"qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n"
|
||||
"EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n"
|
||||
"f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n"
|
||||
"QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n"
|
||||
"7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n"
|
||||
"3rDeUI+Odg==\n"
|
||||
"-----END CERTIFICATE-----\n"
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n"
|
||||
"EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n"
|
||||
"DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n"
|
||||
"b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n"
|
||||
"ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n"
|
||||
"BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n"
|
||||
"bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n"
|
||||
"ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n"
|
||||
"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n"
|
||||
"f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n"
|
||||
"oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n"
|
||||
"UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n"
|
||||
"gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n"
|
||||
"9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n"
|
||||
"AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n"
|
||||
"FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n"
|
||||
"BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n"
|
||||
"xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n"
|
||||
"HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n"
|
||||
"ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n"
|
||||
"qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n"
|
||||
"6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
const char kTestPk7CertChain[] =
|
||||
"308207fb06092a864886f70d010702a08207ec308207e80201013100300b"
|
||||
"06092a864886f70d010701a08207ce308203c3308202ab020102300d0609"
|
||||
"2a864886f70d010105050030819f310b3009060355040613025553311330"
|
||||
"1106035504080c0a57617368696e67746f6e3111300f06035504070c084b"
|
||||
"69726b6c616e6431133011060355040a0c0a476f6f676c6520496e633111"
|
||||
"300f060355040b0c085769646576696e65311d301b06035504030c145465"
|
||||
"737420496e7465726d6564696174652043413121301f06092a864886f70d"
|
||||
"010901161274696e736b697040676f6f676c652e636f6d301e170d313330"
|
||||
"3831363231343430305a170d3333303831353231343430305a3081ae310b"
|
||||
"30090603550406130255533113301106035504080c0a57617368696e6774"
|
||||
"6f6e3111300f06035504070c084b69726b6c616e6431293027060355040a"
|
||||
"0c204368726f6d652044657669636520436f6e74656e742050726f746563"
|
||||
"74696f6e31153013060355040b0c0c74657374696e672e74657374311230"
|
||||
"1006035504030c09737461626c652069643121301f06092a864886f70d01"
|
||||
"0901161274696e736b697040676f6f676c652e636f6d30820122300d0609"
|
||||
"2a864886f70d01010105000382010f003082010a0282010100a95b0eab2d"
|
||||
"38ad1394bb4964e1b3cac8c3e3c657127a70b62d1c4002ba25c37d86dc29"
|
||||
"1de98197301b22e3888de636b5fbe4976b8838e9cd3200222acfc3a6ef55"
|
||||
"6de7570150a7bb113bff2ad6230feb835666a9dc505c5a93a3ee7612bb5e"
|
||||
"32bbb892b39a36043add213ddf8c91d0c485cee49bbd381314b3fb847b9e"
|
||||
"69e83cf8252f4115345a80b66a4317cd8d46e80915c3263f96e7c0fd7110"
|
||||
"5e03db86f3faed8c51f9ec7009fcd01a8941e6192588abb1d2b9b30df708"
|
||||
"88730cd080da13a9ee7db9bc04a3f19594bf42bcd3640afd43918cca372e"
|
||||
"d174c8d5f927195ac4e2964c87c81efa89dd720210c5704e85f24a142a1f"
|
||||
"6123fbac1c6d6ac2bee2770203010001300d06092a864886f70d01010505"
|
||||
"0003820101004dc35f68ba5f2db5faab3d6a28c2d868d7b8388d17f2df19"
|
||||
"357a8476ac8d7780bb91275a424c0dba7540ab0d4269d5332d18bc39fd7c"
|
||||
"73095011704de1b3d378e0c2a1b3e34bbd587183e10ecbc641c4371900ba"
|
||||
"04ec872824d8a6a81c2083c4185cc8f85ac009e75e7f8b7221ec6aeb7888"
|
||||
"c75229993067a589e77e073cbf86a7a6134e0c728c4411f2f0125170aa53"
|
||||
"145168e7bd5ce4e8c442384a10ea7d783ef61ae120b4ad1fee35d81f66d1"
|
||||
"4f896648bc60d8bd636f0837a888e85f68237882f2cd417e153cceec6e49"
|
||||
"5909f20e377f649ba577b151b099aecc080849ea957ab6985d4d69e2eb5b"
|
||||
"aaeb404e698705c62191724a04a91fdeb0de508f8e7630820403308202eb"
|
||||
"a003020102020101300d06092a864886f70d0101050500308197310b3009"
|
||||
"0603550406130255533113301106035504080c0a57617368696e67746f6e"
|
||||
"3111300f06035504070c084b69726b6c616e6431133011060355040a0c0a"
|
||||
"476f6f676c6520496e633111300f060355040b0c085769646576696e6531"
|
||||
"15301306035504030c0c5465737420526f6f742043413121301f06092a86"
|
||||
"4886f70d010901161274696e736b697040676f6f676c652e636f6d301e17"
|
||||
"0d3133303831363231343134365a170d3333303831353231343134365a30"
|
||||
"819f310b30090603550406130255533113301106035504080c0a57617368"
|
||||
"696e67746f6e3111300f06035504070c084b69726b6c616e643113301106"
|
||||
"0355040a0c0a476f6f676c6520496e633111300f060355040b0c08576964"
|
||||
"6576696e65311d301b06035504030c145465737420496e7465726d656469"
|
||||
"6174652043413121301f06092a864886f70d010901161274696e736b6970"
|
||||
"40676f6f676c652e636f6d30820122300d06092a864886f70d0101010500"
|
||||
"0382010f003082010a0282010100da28062eb1dc8f489dcb3acad94f8c1b"
|
||||
"bbc84cb97fa04b7f9351104e67426ef846decf000ed8543b711cffa98b5b"
|
||||
"b2c6c2f77d4af1f74e2ce2d7573512bce7fdb39fbfa8a193d86fc7c7e9d8"
|
||||
"25ee872661ec82d234a683bfbe662692e8eb233d25131366846c07ba43e6"
|
||||
"6162c01b7642fb67674d50947d6a484b12a96a7d7e2ae5b580d0cdcfff6f"
|
||||
"dfa8d60c4b4c34d74370a5df8ac9d6dc793d2b1d04e64c656ca8208b8266"
|
||||
"8ff5af1a4113123f4767e4839c73f2b54279dfc358319e3185e1a04feca6"
|
||||
"c2331341adb8013b0893c6010c21c299f481accc07297ed297e6b6b75de3"
|
||||
"f315609c9f08a5a6969a13fd9759791c01e85cf1ddf1b64740619c952015"
|
||||
"0203010001a350304e301d0603551d0e0416041439b7e60cccea5975dc78"
|
||||
"5951b5b9b83c3b062793301f0603551d230418301680144d30ff181ac4f1"
|
||||
"0da99e6a12c01e02accadf840a300c0603551d13040530030101ff300d06"
|
||||
"092a864886f70d01010505000382010100b8fefd9f3281f595367715d0df"
|
||||
"54b0073094651ee4c13e5685d417fd123a676b73c2ed3dcd8391cf1bc624"
|
||||
"0dd701f113f385b1bb0e7686cfcf038187aecac98c955adc73cec2ced0c1"
|
||||
"c6c6c6ce9b06b501eaeca0fe31f31a021da054f7472f898ab3e64eab6720"
|
||||
"4894522b33996a1b5241dd4d244dc786ff5b2ac11000bbde0162a316d625"
|
||||
"b2afd32a0124425413ab92c59d6b8a599022e9eb6be4ec8b4e72cd8fac77"
|
||||
"fa5166c03530c7fbc43ea48c36c2e27995ffe29aa28eaab602266022e950"
|
||||
"5ada3c87a9754f324ea52f5466764e1480eb11bec6e62b36f6bd87962308"
|
||||
"6c5d8e52a68dd65532efea42241990853ffc2cedcb64f9f4328e3b6d9dc2"
|
||||
"1d2f69a1003100";
|
||||
|
||||
const char kTestCertPrivateKey[] =
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"MIIEowIBAAKCAQEAqVsOqy04rROUu0lk4bPKyMPjxlcSenC2LRxAArolw32G3Ckd\n"
|
||||
"6YGXMBsi44iN5ja1++SXa4g46c0yACIqz8Om71Vt51cBUKe7ETv/KtYjD+uDVmap\n"
|
||||
"3FBcWpOj7nYSu14yu7iSs5o2BDrdIT3fjJHQxIXO5Ju9OBMUs/uEe55p6Dz4JS9B\n"
|
||||
"FTRagLZqQxfNjUboCRXDJj+W58D9cRBeA9uG8/rtjFH57HAJ/NAaiUHmGSWIq7HS\n"
|
||||
"ubMN9wiIcwzQgNoTqe59ubwEo/GVlL9CvNNkCv1DkYzKNy7RdMjV+ScZWsTilkyH\n"
|
||||
"yB76id1yAhDFcE6F8koUKh9hI/usHG1qwr7idwIDAQABAoIBADdwlZa30QvnkxLU\n"
|
||||
"be/s+X9LkS8GpgfrCdgunU3HPkGGwDUmSKJ+R835tCwkMb+hPWXeaStMhsUS5UFh\n"
|
||||
"7f3hoK5MmxPWSZnrrrNvnpKZUxUNFgucxBJZREJqfom7oVow9g6511xwKSqtUmJl\n"
|
||||
"bN8JhPwwiZAQ45qNtINO3QnSy/y4IGrUPgjMpmJa26a+JhduTRq+LMPu2wz+HxS1\n"
|
||||
"Vf2q0H1IOJr/kimMFMaBRYErNclFa8VIFjwjz5reH5lJyptajGhruor6EK1qqhNc\n"
|
||||
"zPSRY4TZH5QcjM46zui6l3tL9e32j6oUd4mAp4HhH0fws/pwawFYECI+M+7OCjgK\n"
|
||||
"y+qSJ1ECgYEA1g+L0yN4i+uScs7EpsYJfaRP1PMtGnUsof64Pg6i9IKcuf5mi5Kp\n"
|
||||
"aIgZdXAZIzsACH5XbfuC5Srs4565k/9XrHehLcuBzodulrzwmOUDbJAxIDw4uTUX\n"
|
||||
"95W0uK9UqyGLyM8wNYs/EzhveSFL8fnFWzOAL/+HshQpKCBzedSU+G0CgYEAyolH\n"
|
||||
"xws2mim7rSrYyRz1Vj02rLZuBUR7cPaHDxjjuuSUbI2nsDRsm6ZUCNlJtReHBkpH\n"
|
||||
"eW5iClBGkksVsJJYJBmyDw6a3mnj0mfxBnh9zGaHQi0RCuOwmYlu2L/XVQXiMFKT\n"
|
||||
"gffazuvysg7N/bz7CJjm8PRRx/cAxxFfAozdf/MCgYEAtBagLCHLaOvnaW9LQoOZ\n"
|
||||
"uHpkL2PmrjumMSN7HbpyngLEmDXPT90zaR4XTRXiECGzBXJFW+IdXW+fnGANANXx\n"
|
||||
"jMeYck6kBn0qLOcIA5moJ82nhtcjYa2pXEI2qKnZMaAnWen1RRbBGgqAvgelPQ5F\n"
|
||||
"W1UYo0j3gHo1peynOff+3IECgYAsP53M4KhHOgLkrE28cnUvKCR/y0NyJyoI3fNX\n"
|
||||
"2wo11KaQqMoP9wQbZVVKsZ4m0EMRnrzKzNDii/M/FuRgNTjIekyqeXhgSyYY29iO\n"
|
||||
"n1hshaHbVVk51dDJWns7I3559tUZ1ZCgfnPxbR8Sw6VBYD4//JfH4LjVRSOIWkU1\n"
|
||||
"m2zw/QKBgGE55o0xrCywF3wDUtFa6vgpsOfZu9IblsWktSbD/lk1YOqGpU//B0O4\n"
|
||||
"GqihOQT7E9kDNusspFUGpZrE0T0B+GW1T9iTR0zd+lC+qExv2ggDJoH063DnH5OU\n"
|
||||
"Qz2M8LESeFxf6ZlBxkcyrk6G1RAy7lUs9fHhfmpEJLVv4DTCuWDl\n"
|
||||
"-----END RSA PRIVATE KEY-----\n";
|
||||
|
||||
const char kTestMessage[] =
|
||||
"c8635a17ccc672c941d0cc287715411a0a0222613a04d47693a53eb7f32c"
|
||||
"1ebae1f5d916a815b880426362c42f5f18f694a380756e0452018c70b3e4"
|
||||
"f72ebb5269cb7233a3b8a2a1840e33ca9d473224d17ff91bae6b8d4ff2d1"
|
||||
"8e5c89b5fc8a52c4f791c2063ab1a29ffd3372db483e4975c1c9c7408bf6"
|
||||
"dfe5696e256e86b75313c501ab781175971b9411a73c444592afb1ec1667"
|
||||
"2bfb935715ef5302f3bef712d2296be4f64ef2dc861f0611b06c35d0a5c2"
|
||||
"5ff9f4a2563f265f109d2fa8f8165d7891b8a83c84520eaa284d49a4f76e"
|
||||
"ac158204a5bdf018edd9401ae6593092ba97970be9a58b10720a235c9158"
|
||||
"b9f235f9dda3de05990cef8c2fd04920a2a434bd5b6aa75767762d89b964"
|
||||
"90e42524855a7eab49a8f82ac593e4df01990206d3fa98329aa50e31db89"
|
||||
"b46b82ee0073851826f77aabb3779738a6f311b79f54d036a98dca4881ef"
|
||||
"88c3cbfc86ac358c7bd107dc234d3772fc707df01637354dcb9270c7aefa"
|
||||
"852dd21818ede33ab7154c32f25268b82f89b344e6469b81b6699df68c56"
|
||||
"a6e61f1dd8f140f3be4edce755ceee8ee7868f45a17f8b4b4b0988f45815"
|
||||
"1b43d07dcb0cd80b1ffa37b824e0abc25897cb41c242a3db845bedd37adf"
|
||||
"88a13c0b2f0b158464b02f9fd97ad6e87b92c13cbeee5e69d183cc898c4e"
|
||||
"0cfa9c59abde74a437d030cb966137ffe9abe6be71ed21ef751cdea73625"
|
||||
"7cff9e378718f7d7e9c4d567cbec8e0afdfab0585b8ed0d5f8de159b6524"
|
||||
"22c90737b44c84603ba1131f557604fe4e6b4d91e45363903b8db179cee0"
|
||||
"a50f2ae73394973c8671df7a7b2eeb8341a3417727cfe43290a67ac3ad02"
|
||||
"a52c3d1698c2c28a46268518aea66cecb40f43f50bb9cea4ed1d49ceb51d"
|
||||
"9967fabccccc7237a36b6cecda5916234730d7b3ca3295519d77b7516824"
|
||||
"10e8a238b6345e8d28132f60423a13fdf4b6a6cf272cef9a0833abb4b86d"
|
||||
"9828af45442a390e241b2b8c3290671da4a163d7e55fea7828098c0749ca"
|
||||
"ff65145dd6b4a6e4c65d214801bb8302d8914864e99c4d0b390b8126d4bc"
|
||||
"0353e376e69aba56cf71b9943a47dcffa07c6a24986a077f69b7bec6bd9c"
|
||||
"357e211875453bdadd9bfc4526f96c458e0052d27a903611c09a9c7b5f51"
|
||||
"83daad078aec0e79ef991d102d4af492773f1509a265c5644cbab3253e34"
|
||||
"3015e4305fffd17ce0261bcb232cfa0e1dcc71f83dc1aac490e526f6269f"
|
||||
"606d0e0e556bb30b774c2208ed3771474be23f39b7fc21dcbf304a923d9c";
|
||||
|
||||
const char kTestDevCodeSigningCert[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIFDjCCA3agAwIBAgIPESIzRFVmd4iZqrvM3e7/MA0GCSqGSIb3DQEBCwUAMIGc\n"
|
||||
"MQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2ly\n"
|
||||
"a2xhbmQxDzANBgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNV\n"
|
||||
"BAMMFXdpZGV2aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tp\n"
|
||||
"cEBnb29nbGUuY29tMB4XDTE3MTAwOTIwMjUwNloXDTI3MTAwNzIwMjUwNlowRTEL\n"
|
||||
"MAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy\n"
|
||||
"bmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
|
||||
"ggEBAObsg/w+dJedH3x5KEsXdA/5sunWc8G+iZl0wMcngh2DiwmOSkKf68uCK/iW\n"
|
||||
"T0a2XGgk13zl1HuKrjatgc7n6E1j/sqDZBGkr0q1wQgsdzm3qvGZoDG/+Z2U23WU\n"
|
||||
"kX6ZcyIYUbpO2VtQELEl6DgNwoUi/9Yp+vCb6lsItpSZ1WRD9NhbWh1MxZxj1s18\n"
|
||||
"OYcEzpEYg4/vHTVhocUR/1Rp9M9yn0nH1MUdtjhgBM3BmlRH7TA/nF111A4+GzMN\n"
|
||||
"qyqfb0/6yXE64Ca3+fGg1hstfUUXkpmjjNPhYJ6QTgA3Xfrz04a4uwB+pSliF3SD\n"
|
||||
"gip7O3rDyK0ES55lGpZ7B3s3TakCAwEAAaOCASEwggEdMB0GA1UdDgQWBBQ2jJme\n"
|
||||
"0BuaGrhgFGJR2i59HR+DizCBuwYDVR0jBIGzMIGwoYGipIGfMIGcMQswCQYDVQQG\n"
|
||||
"EwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxDzAN\n"
|
||||
"BgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNVBAMMFXdpZGV2\n"
|
||||
"aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tpcEBnb29nbGUu\n"
|
||||
"Y29tggkAxfgvA4+s8VgwCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAww\n"
|
||||
"CgYIKwYBBQUHAwMwEQYKKwYBBAHWeQQBAgQDAQH/MA0GCSqGSIb3DQEBCwUAA4IB\n"
|
||||
"gQAtan04ZGie7rRsKpb1F6t7xs48KE6cj6L99B5dgl37fZaZIQ3XE2vbmmmY5YTx\n"
|
||||
"wofCkvOZMXHeQfJEK5GIK49TW/lAR+3kJUJzSh+N67f0X8O1pUl97IUFsbi6PTw/\n"
|
||||
"mjhu197Kdy/OxPu/csOkEChuOfJLagRxXtIXeIyaeVOmn6fkFTOMOL2BusWOPuIs\n"
|
||||
"9OmOQ+UHXpMuX4c2x9iO4NzZwwI/MgULLCrd/c73q199H+ttdPFoNs8+xGdodqA/\n"
|
||||
"NFlHtMHMLMKVGpazAf+JW1/c3nb8L3S0nw4q7vPWi216RdZTfKfSIs/f/IW3CYJh\n"
|
||||
"/IAuHOYvlD0GdSOFZHfhrnAvKhJ2iRu32psN87L9rL5EL22LT8csV/gLMc3SZ35n\n"
|
||||
"/viuYcTDnMbe9S/Mge3mMJ9XHD5XBhN3hzmGDQEUdRS5MXrYdY32viPE7f+GAO9s\n"
|
||||
"5MXS+h+FxQ6QUar2q1zHc/0Gr1hLzA6HYBmI0/AF8LsHs799XjrMKHkSBN6UQkC1\n"
|
||||
"hRk=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
const char kDevCertFlagOid[] = "1.3.6.1.4.1.11129.4.1.2";
|
||||
const bool kTestDevCodeSigningCertFlagValue = true;
|
||||
|
||||
|
||||
TEST(X509CertTest, LoadCert) {
|
||||
X509Cert test_cert;
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||
EXPECT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
// TODO(user): Add more specific status checks to failure tests.
|
||||
EXPECT_NE(util::OkStatus(), test_cert.LoadDer("bad cert"));
|
||||
EXPECT_NE(util::OkStatus(), test_cert.LoadPem("bad cert"));
|
||||
EXPECT_NE(util::OkStatus(), test_cert.LoadDer(""));
|
||||
EXPECT_NE(util::OkStatus(), test_cert.LoadPem(""));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, VerifySignature) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
std::string message(absl::HexStringToBytes(kTestMessage));
|
||||
std::string signature;
|
||||
ASSERT_EQ(util::OkStatus(), GenerateRsaSignatureSha256Pkcs1(
|
||||
kTestCertPrivateKey, message, &signature));
|
||||
std::unique_ptr<RsaPublicKey> pub_key(test_cert.GetRsaPublicKey());
|
||||
ASSERT_TRUE(pub_key);
|
||||
EXPECT_TRUE(pub_key->VerifySignatureSha256Pkcs7(message, signature));
|
||||
|
||||
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, "bad signature"));
|
||||
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("bad digest", signature));
|
||||
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, ""));
|
||||
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("", signature));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, GetSubjectNameField) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_EQ(kTestPemCertSubjectField_C, test_cert.GetSubjectNameField("C"));
|
||||
EXPECT_EQ(kTestPemCertSubjectField_CN, test_cert.GetSubjectNameField("CN"));
|
||||
EXPECT_EQ("", test_cert.GetSubjectNameField("invalid_field"));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, GetSerialNumber) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber());
|
||||
}
|
||||
|
||||
TEST(X509CertTest, CertChain) {
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
ASSERT_EQ(2, test_chain.GetNumCerts());
|
||||
EXPECT_FALSE(test_chain.GetCert(0) == NULL);
|
||||
EXPECT_FALSE(test_chain.GetCert(1) == NULL);
|
||||
EXPECT_TRUE(test_chain.GetCert(2) == NULL);
|
||||
}
|
||||
|
||||
TEST(X509CertTest, IsCaCertificate) {
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
ASSERT_EQ(2, test_chain.GetNumCerts());
|
||||
EXPECT_FALSE(test_chain.GetCert(0)->IsCaCertificate());
|
||||
EXPECT_TRUE(test_chain.GetCert(1)->IsCaCertificate());
|
||||
}
|
||||
|
||||
TEST(X509CertTest, ChainVerificationPem) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||
X509CA ca(ca_cert.release());
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, ChainVerificationPkcs7) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||
X509CA ca(ca_cert.release());
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain)));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, BooleanExtension) {
|
||||
std::unique_ptr<X509Cert> cert1(new X509Cert);
|
||||
ASSERT_EQ(util::OkStatus(), cert1->LoadPem(kTestPemCert));
|
||||
bool extension_value;
|
||||
EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
|
||||
|
||||
std::unique_ptr<X509Cert> cert2(new X509Cert);
|
||||
ASSERT_EQ(util::OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert));
|
||||
ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
|
||||
EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
183
glog.BUILD
Normal file
183
glog.BUILD
Normal file
@@ -0,0 +1,183 @@
|
||||
################################################################################
|
||||
# Copyright 2016 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
# Bazel build file for glog.
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
# Common options required by all library targets.
|
||||
glog_copts = [
|
||||
"-Isrc",
|
||||
"-D_START_GOOGLE_NAMESPACE_='namespace google {'",
|
||||
"-D_END_GOOGLE_NAMESPACE_=}",
|
||||
"-DGOOGLE_NAMESPACE=google",
|
||||
"-DGOOGLE_GLOG_DLL_DECL=",
|
||||
"-DHAVE_LIB_GFLAGS",
|
||||
"-DHAVE_PTHREAD",
|
||||
"-DHAVE_RWLOCK",
|
||||
"-DHAVE_PREAD",
|
||||
"-DHAVE_PWRITE",
|
||||
"-DHAVE_SYS_TIME_H",
|
||||
"-DHAVE_SYS_UTSNAME_H",
|
||||
"-DHAVE_UNISTD_H",
|
||||
"-DHAVE_GLOB_H",
|
||||
"-DHAVE___ATTRIBUTE__",
|
||||
"-D__NR_gettid",
|
||||
"-Wno-sign-compare",
|
||||
]
|
||||
|
||||
cc_library(
|
||||
name = "glog",
|
||||
srcs = [
|
||||
"src/base/commandlineflags.h",
|
||||
"src/base/googleinit.h",
|
||||
"src/base/mutex.h",
|
||||
"src/demangle.cc",
|
||||
"src/demangle.h",
|
||||
"src/glog/log_severity.h",
|
||||
"src/logging.cc",
|
||||
"src/raw_logging.cc",
|
||||
"src/signalhandler.cc",
|
||||
"src/stacktrace.h",
|
||||
"src/stacktrace_generic-inl.h",
|
||||
"src/stacktrace_libunwind-inl.h",
|
||||
"src/stacktrace_powerpc-inl.h",
|
||||
"src/stacktrace_x86-inl.h",
|
||||
"src/stacktrace_x86_64-inl.h",
|
||||
"src/symbolize.cc",
|
||||
"src/symbolize.h",
|
||||
"src/utilities.cc",
|
||||
"src/utilities.h",
|
||||
"src/vlog_is_on.cc",
|
||||
":config_h",
|
||||
":logging_h",
|
||||
":raw_logging_h",
|
||||
":stl_logging_h",
|
||||
":vlog_is_on_h",
|
||||
],
|
||||
hdrs = glob(["glog/*.h"]),
|
||||
copts = glog_copts,
|
||||
includes = ["src/"],
|
||||
linkopts = ["-pthread"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//external:gflags"],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "config_h",
|
||||
srcs = ["src/config.h.cmake.in"],
|
||||
outs = ["config.h"],
|
||||
cmd = "awk '{ gsub(/^#cmakedefine/, \"//cmakedefine\"); print; }' $(<) > $(@)",
|
||||
)
|
||||
|
||||
sub_cmd = ("awk '{ " +
|
||||
"gsub(/@ac_google_start_namespace@/, \"namespace google {\"); " +
|
||||
"gsub(/@ac_google_end_namespace@/, \"} // namespace google\"); " +
|
||||
"gsub(/@ac_google_namespace@/, \"google\"); " +
|
||||
("gsub(/@(ac_cv_have_stdint_h|ac_cv_have_uint16_t|" +
|
||||
"ac_cv_have_libgflags|ac_cv_have_unistd_h|" +
|
||||
"ac_cv_have___builtin_expect|" +
|
||||
"ac_cv_cxx_using_operator)@/, \"1\"); ") +
|
||||
"gsub(/@ac_cv___attribute___noreturn@/, \"__attribute__ ((noreturn))\"); " +
|
||||
"gsub(/@ac_cv___attribute___noinline@/, \"__attribute__ ((noinline))\"); " +
|
||||
"gsub(/@(ac_cv___attribute___[a-z0-9_]+)@/, \"\"); " +
|
||||
"gsub(/@([a-z0-9_]+)@/, \"0\"); " +
|
||||
"print; }' $(<) > $(@)")
|
||||
|
||||
genrule(
|
||||
name = "logging_h",
|
||||
srcs = ["src/glog/logging.h.in"],
|
||||
outs = ["glog/logging.h"],
|
||||
cmd = sub_cmd,
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "raw_logging_h",
|
||||
srcs = ["src/glog/raw_logging.h.in"],
|
||||
outs = ["glog/raw_logging.h"],
|
||||
cmd = sub_cmd,
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "stl_logging_h",
|
||||
srcs = ["src/glog/stl_logging.h.in"],
|
||||
outs = ["glog/stl_logging.h"],
|
||||
cmd = sub_cmd,
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "vlog_is_on_h",
|
||||
srcs = ["src/glog/vlog_is_on.h.in"],
|
||||
outs = ["glog/vlog_is_on.h"],
|
||||
cmd = sub_cmd,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "test_hdrs",
|
||||
hdrs = [
|
||||
"src/config_for_unittests.h",
|
||||
"src/googletest.h",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "demangle_unittest",
|
||||
size = "small",
|
||||
srcs = ["src/demangle_unittest.cc"],
|
||||
copts = glog_copts,
|
||||
data = ["src/demangle_unittest.txt"],
|
||||
deps = [
|
||||
":glog",
|
||||
":test_hdrs",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "logging_unittest",
|
||||
size = "small",
|
||||
srcs = ["src/logging_unittest.cc"],
|
||||
copts = glog_copts,
|
||||
data = ["src/logging_unittest.err"],
|
||||
deps = [
|
||||
":glog",
|
||||
":test_hdrs",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "stacktrace_unittest",
|
||||
size = "small",
|
||||
srcs = ["src/stacktrace_unittest.cc"],
|
||||
copts = glog_copts,
|
||||
deps = [
|
||||
":glog",
|
||||
":test_hdrs",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "symbolize_unittest",
|
||||
size = "small",
|
||||
srcs = ["src/symbolize_unittest.cc"],
|
||||
copts = glog_copts,
|
||||
deps = [
|
||||
":glog",
|
||||
":test_hdrs",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "utilities_unittest",
|
||||
size = "small",
|
||||
srcs = ["src/utilities_unittest.cc"],
|
||||
copts = glog_copts,
|
||||
deps = [
|
||||
":glog",
|
||||
":test_hdrs",
|
||||
],
|
||||
)
|
||||
202
license_server_sdk/internal/BUILD
Normal file
202
license_server_sdk/internal/BUILD
Normal file
@@ -0,0 +1,202 @@
|
||||
################################################################################
|
||||
# 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.
|
||||
################################################################################
|
||||
|
||||
# This package is the Widevine SDK used by DRM license servers. It supports
|
||||
# Widevine modular DRM clients, e.g. Widevine CDM in Encryption Media Extension
|
||||
# in Chrome.
|
||||
|
||||
package(default_visibility = [":friends"])
|
||||
|
||||
# friends is a package_group which own the visibility for video widevine license_server_sdk lib
|
||||
package_group(
|
||||
name = "friends",
|
||||
packages = [
|
||||
"//common/...",
|
||||
"//license_server_sdk/...",
|
||||
"//media_cas_proxy_sdk/...",
|
||||
"//proxy_sdk/...",
|
||||
"//sdk/...",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
filegroup(
|
||||
name = "binary_release_files",
|
||||
srcs = [
|
||||
"client_cert.h",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "session_impl",
|
||||
srcs = [
|
||||
"session_impl.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"session_impl.h",
|
||||
],
|
||||
deps = [
|
||||
":sdk",
|
||||
"//base",
|
||||
"//strings",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//util/endian",
|
||||
"//util/random:global_id",
|
||||
"//util:status",
|
||||
"//common:aes_cbc_util",
|
||||
"//common:certificate_type",
|
||||
"//common:certificate_util",
|
||||
"//common:crypto_util",
|
||||
"//common:drm_root_certificate",
|
||||
"//common:drm_service_certificate",
|
||||
"//common:error_space",
|
||||
"//common:random_util",
|
||||
"//common:remote_attestation_verifier",
|
||||
"//common:rsa_key",
|
||||
"//common:signing_key_util",
|
||||
"//common:verified_media_pipeline",
|
||||
"//common:vmp_checker",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:license_protocol_proto",
|
||||
"//protos/public:license_server_sdk_proto",
|
||||
"//protos/public:provisioned_device_info_proto",
|
||||
"//protos/public:widevine_pssh_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "sdk",
|
||||
srcs = [
|
||||
"client_cert.cc",
|
||||
"device_status_list.cc",
|
||||
"key_control_block.cc",
|
||||
"parse_content_id.cc",
|
||||
"generate_error_response.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"client_cert.h",
|
||||
"device_status_list.h",
|
||||
"generate_error_response.h",
|
||||
"key_control_block.h",
|
||||
"parse_content_id.h",
|
||||
"session_usage_report.h",
|
||||
],
|
||||
deps = [
|
||||
"//base",
|
||||
"//strings",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//external:openssl",
|
||||
"//util/endian",
|
||||
"//util/gtl:map_util",
|
||||
"//util:status",
|
||||
"//common:crypto_util",
|
||||
"//common:drm_service_certificate",
|
||||
"//common:error_space",
|
||||
"//common:random_util",
|
||||
"//common:rsa_key",
|
||||
"//common:signing_key_util",
|
||||
"//common:wvm_token_handler",
|
||||
"//sdk/external/common/wvpl:wvpl_types",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:device_certificate_status_proto",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:license_protocol_proto",
|
||||
"//protos/public:license_server_sdk_proto",
|
||||
"//protos/public:provisioned_device_info_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
"//protos/public:widevine_pssh_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "session_impl_test",
|
||||
timeout = "short",
|
||||
srcs = ["session_impl_test.cc"],
|
||||
deps = [
|
||||
":sdk",
|
||||
":session_impl",
|
||||
"//base",
|
||||
"//external:protobuf",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:aes_cbc_util",
|
||||
"//common:crypto_util",
|
||||
"//common:drm_root_certificate",
|
||||
"//common:error_space",
|
||||
"//common:remote_attestation_verifier",
|
||||
"//common:rsa_key",
|
||||
"//common:rsa_test_keys",
|
||||
"//common:rsa_util",
|
||||
"//common:signing_key_util",
|
||||
"//common:test_certificates",
|
||||
"//common:test_utils",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:device_certificate_status_proto",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:license_protocol_proto",
|
||||
"//protos/public:license_server_sdk_proto",
|
||||
"//protos/public:provisioned_device_info_proto",
|
||||
"//protos/public:remote_attestation_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
"//protos/public:widevine_pssh_proto",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
cc_test(
|
||||
name = "key_control_block_test",
|
||||
timeout = "short",
|
||||
srcs = ["key_control_block_test.cc"],
|
||||
deps = [
|
||||
":sdk",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:license_protocol_proto",
|
||||
"//protos/public:license_server_sdk_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "device_status_list_test",
|
||||
timeout = "short",
|
||||
srcs = ["device_status_list_test.cc"],
|
||||
deps = [
|
||||
":sdk",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:rsa_key",
|
||||
"//common:rsa_test_keys",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:provisioned_device_info_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "parse_content_id_test",
|
||||
timeout = "short",
|
||||
srcs = ["parse_content_id_test.cc"],
|
||||
deps = [
|
||||
":sdk",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"//util/endian",
|
||||
"//common:error_space",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:license_server_sdk_proto",
|
||||
],
|
||||
)
|
||||
408
license_server_sdk/internal/client_cert.cc
Normal file
408
license_server_sdk/internal/client_cert.cc
Normal file
@@ -0,0 +1,408 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "license_server_sdk/internal/client_cert.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "strings/serialize.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "util/status.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/random_util.h"
|
||||
#include "common/signing_key_util.h"
|
||||
#include "common/wvm_token_handler.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace {
|
||||
|
||||
const int kPreProvisioningKeySizeBytes = 16;
|
||||
const int kKeyboxSizeBytes = 72;
|
||||
|
||||
struct ValidatedSignerCertificate {
|
||||
std::string certificate;
|
||||
std::string signature;
|
||||
};
|
||||
|
||||
class ValidatedSignerCache {
|
||||
public:
|
||||
void SetRootPublicKey(std::unique_ptr<RsaPublicKey> new_root_key);
|
||||
bool Exist(const std::string& serial_number, const std::string& certificate,
|
||||
const std::string& signature);
|
||||
util::Status ValidateSigner(const std::string& serial_number,
|
||||
const std::string& certificate,
|
||||
const std::string& signature);
|
||||
|
||||
void ResetSignerCache();
|
||||
size_t SignerCacheSize();
|
||||
|
||||
static ValidatedSignerCache* GetSingleton();
|
||||
|
||||
private:
|
||||
absl::Mutex rsa_root_public_key_mutex_;
|
||||
std::unique_ptr<RsaPublicKey> rsa_root_public_key_
|
||||
GUARDED_BY(&rsa_root_public_key_mutex_);
|
||||
absl::Mutex validated_signer_cache_mutex_;
|
||||
std::map<std::string, ValidatedSignerCertificate> validated_signer_cache_
|
||||
GUARDED_BY(&validated_signer_cache_mutex_);
|
||||
};
|
||||
|
||||
void ValidatedSignerCache::SetRootPublicKey(
|
||||
std::unique_ptr<RsaPublicKey> new_root_key) {
|
||||
absl::WriterMutexLock lock(&rsa_root_public_key_mutex_);
|
||||
rsa_root_public_key_ = std::move(new_root_key);
|
||||
}
|
||||
|
||||
bool ValidatedSignerCache::Exist(const std::string& serial_number,
|
||||
const std::string& certificate,
|
||||
const std::string& signature) {
|
||||
absl::ReaderMutexLock lock(&validated_signer_cache_mutex_);
|
||||
ValidatedSignerCertificate* validated_signer =
|
||||
gtl::FindOrNull(validated_signer_cache_, serial_number);
|
||||
return (validated_signer != nullptr) &&
|
||||
(validated_signer->certificate == certificate) &&
|
||||
(validated_signer->signature == signature);
|
||||
}
|
||||
|
||||
util::Status ValidatedSignerCache::ValidateSigner(const std::string& serial_number,
|
||||
const std::string& certificate,
|
||||
const std::string& signature) {
|
||||
{
|
||||
absl::ReaderMutexLock key_lock(&rsa_root_public_key_mutex_);
|
||||
if (rsa_root_public_key_ == nullptr) {
|
||||
return util::Status(error_space, ROOT_CERTIFICATE_NOT_SET, "");
|
||||
}
|
||||
if (!rsa_root_public_key_->VerifySignature(certificate, signature)) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signer-certificate-verification-failed");
|
||||
}
|
||||
}
|
||||
absl::WriterMutexLock cache_lock(&validated_signer_cache_mutex_);
|
||||
validated_signer_cache_[serial_number].certificate = certificate;
|
||||
validated_signer_cache_[serial_number].signature = signature;
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
void ValidatedSignerCache::ResetSignerCache() {
|
||||
absl::WriterMutexLock lock(&validated_signer_cache_mutex_);
|
||||
validated_signer_cache_.clear();
|
||||
}
|
||||
|
||||
size_t ValidatedSignerCache::SignerCacheSize() {
|
||||
absl::ReaderMutexLock lock(&validated_signer_cache_mutex_);
|
||||
return validated_signer_cache_.size();
|
||||
}
|
||||
|
||||
ValidatedSignerCache* ValidatedSignerCache::GetSingleton() {
|
||||
static auto* const kInstance = new ValidatedSignerCache();
|
||||
return kInstance;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(user): change to util::StatusOr<std::unique_ptr<ClientCert>>
|
||||
// instead of ClientCert** to explicitly assigning ownership of the created
|
||||
// object to the caller.
|
||||
|
||||
util::Status ClientCert::Create(ClientIdentification::TokenType token_type,
|
||||
const std::string& token, ClientCert** client_cert) {
|
||||
DCHECK(client_cert);
|
||||
if (token_type == ClientIdentification::KEYBOX) {
|
||||
*client_cert = nullptr;
|
||||
if (token.size() < kKeyboxSizeBytes) {
|
||||
return util::Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||
"keybox-token-is-too-short");
|
||||
}
|
||||
return ClientCert::CreateWithToken(token, client_cert);
|
||||
} else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
||||
return CreateCertificateClientCert(token, client_cert);
|
||||
} else {
|
||||
return util::Status(error_space, util::error::UNIMPLEMENTED,
|
||||
"client-type-not-implemented");
|
||||
}
|
||||
}
|
||||
|
||||
util::Status ClientCert::CreateWithToken(const std::string& keybox_token,
|
||||
ClientCert** client_cert) {
|
||||
*client_cert = nullptr;
|
||||
std::unique_ptr<ClientCert> ret(new KeyboxClientCert(keybox_token));
|
||||
if (ret->status() != util::OkStatus()) {
|
||||
return ret->status();
|
||||
}
|
||||
*client_cert = ret.release();
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status ClientCert::CreateCertificateClientCert(
|
||||
const std::string& drm_certificate, ClientCert** client_cert) {
|
||||
std::unique_ptr<ClientCert> ret(new CertificateClientCert(drm_certificate));
|
||||
if (ret->status() != util::OkStatus()) {
|
||||
return ret->status();
|
||||
}
|
||||
*client_cert = ret.release();
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
void ClientCert::CreateSignature(const std::string& message, std::string* signature) {
|
||||
DCHECK(signature);
|
||||
DCHECK(!signing_key().empty());
|
||||
if (signature == nullptr) {
|
||||
return;
|
||||
}
|
||||
using crypto_util::CreateSignatureHmacSha256;
|
||||
*signature =
|
||||
CreateSignatureHmacSha256(GetServerSigningKey(signing_key()), message);
|
||||
}
|
||||
|
||||
void ClientCert::GenerateSigningKey(const std::string& message,
|
||||
ProtocolVersion protocol_version) {
|
||||
if (!signing_key_.empty()) return;
|
||||
DCHECK(!key().empty());
|
||||
using crypto_util::DeriveKey;
|
||||
using crypto_util::kSigningKeyLabel;
|
||||
set_signing_key(DeriveKey(key(), kSigningKeyLabel, message,
|
||||
SigningKeyMaterialSize(protocol_version)));
|
||||
}
|
||||
|
||||
KeyboxClientCert::~KeyboxClientCert() {}
|
||||
|
||||
void KeyboxClientCert::SetPreProvisioningKeys(
|
||||
const std::multimap<uint32_t, std::string>& keymap) {
|
||||
std::vector<WvmTokenHandler::PreprovKey> keyvector;
|
||||
keyvector.reserve(keymap.size());
|
||||
for (std::multimap<uint32_t, std::string>::const_iterator it = keymap.begin();
|
||||
it != keymap.end(); ++it) {
|
||||
std::string key = absl::HexStringToBytes(it->second);
|
||||
DCHECK_EQ(key.size(), 16);
|
||||
keyvector.push_back(WvmTokenHandler::PreprovKey(it->first, key));
|
||||
}
|
||||
WvmTokenHandler::SetPreprovKeys(keyvector);
|
||||
}
|
||||
|
||||
bool KeyboxClientCert::IsSystemIdKnown(const uint32_t system_id) {
|
||||
return WvmTokenHandler::IsSystemIdKnown(system_id);
|
||||
}
|
||||
|
||||
uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
|
||||
return WvmTokenHandler::GetSystemId(keybox_bytes);
|
||||
}
|
||||
|
||||
KeyboxClientCert::KeyboxClientCert(const std::string& keybox_bytes) {
|
||||
if (keybox_bytes.size() < kKeyboxSizeBytes) {
|
||||
set_status(util::Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||
"keybox-token-is-too-short"));
|
||||
return;
|
||||
}
|
||||
|
||||
set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes));
|
||||
set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes));
|
||||
bool insecure_keybox = false;
|
||||
util::Status status = WvmTokenHandler::DecryptDeviceKey(
|
||||
keybox_bytes, &device_key_, nullptr, &insecure_keybox);
|
||||
if (!status.ok()) {
|
||||
Errors new_code = status.error_code() == util::error::NOT_FOUND
|
||||
? MISSING_PRE_PROV_KEY
|
||||
: KEYBOX_DECRYPT_ERROR;
|
||||
set_status(util::Status(error_space, new_code, status.error_message()));
|
||||
}
|
||||
}
|
||||
|
||||
bool KeyboxClientCert::VerifySignature(const std::string& message,
|
||||
const std::string& signature,
|
||||
ProtocolVersion protocol_version) {
|
||||
DCHECK(!signing_key().empty());
|
||||
using crypto_util::VerifySignatureHmacSha256;
|
||||
if (!VerifySignatureHmacSha256(
|
||||
GetClientSigningKey(signing_key(), protocol_version), signature,
|
||||
message)) {
|
||||
set_status(util::Status(error_space, INVALID_SIGNATURE, ""));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
util::Status CertificateClientCert::SetDrmRootCertificatePublicKey(
|
||||
const std::string& root_public_key) {
|
||||
std::unique_ptr<RsaPublicKey> new_root_key(
|
||||
RsaPublicKey::Create(root_public_key));
|
||||
if (new_root_key == nullptr) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"root-certificate-rsa-public-key-failed");
|
||||
}
|
||||
ValidatedSignerCache::GetSingleton()->SetRootPublicKey(
|
||||
std::move(new_root_key));
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
// Checks the device certificate using the following steps.
|
||||
// 1. Load the certificate bytes into a signed device certificate.
|
||||
// 2. Get the signer for the certificate.
|
||||
// 3. Verify the signature of the certificate using the signer.
|
||||
// 4. Load the root certificate.
|
||||
// 5. Verify the signature of the signer certificate.
|
||||
util::Status CertificateClientCert::ValidateCertificate(
|
||||
const SignedDrmCertificate& signed_drm_certificate) {
|
||||
// TODO(user): Cache valid certificates.
|
||||
// TODO(user): Find out why signed_drm_certificate.has_signer() always
|
||||
// returns false. Blindly assuming signer is there for now.
|
||||
const SignedDrmCertificate& signer = signed_drm_certificate.signer();
|
||||
DrmCertificate intermediate_certificate;
|
||||
if (!intermediate_certificate.ParseFromString(signer.drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-invalid-signer");
|
||||
}
|
||||
std::unique_ptr<RsaPublicKey> rsa_public_signer_key(
|
||||
RsaPublicKey::Create(intermediate_certificate.public_key()));
|
||||
if (!rsa_public_signer_key.get()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signer-certificate-public-key-failed");
|
||||
}
|
||||
if (!rsa_public_signer_key->VerifySignature(
|
||||
signed_drm_certificate.drm_certificate(),
|
||||
signed_drm_certificate.signature())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-verification-failed");
|
||||
}
|
||||
if (!intermediate_certificate.has_serial_number()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-signer-serial-number");
|
||||
}
|
||||
// Check to see if this intermediate device certificate is signed by a
|
||||
// provisioner (entity using Widevine Provisioning Server SDK).
|
||||
// TODO(user): refactor this code for clarity with the cert chaining.
|
||||
if (signer.has_signer()) {
|
||||
DrmCertificate provisioner_certificate;
|
||||
if (!provisioner_certificate.ParseFromString(
|
||||
signer.signer().drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"intermediate-certificate-invalid-signer");
|
||||
}
|
||||
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
|
||||
set_signed_by_provisioner(true);
|
||||
} else {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"expected-provisioning-provider-certificate-type");
|
||||
}
|
||||
if (!CheckSignerCache(provisioner_certificate.serial_number(),
|
||||
signer.signer().drm_certificate(),
|
||||
signer.signature())) {
|
||||
util::Status status = ValidateSigner(
|
||||
provisioner_certificate.serial_number(),
|
||||
signer.signer().drm_certificate(), signer.signer().signature());
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
if (!provisioner_certificate.has_provider_id() ||
|
||||
provisioner_certificate.provider_id().empty()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-provisioning-service-id");
|
||||
}
|
||||
set_service_id(provisioner_certificate.provider_id());
|
||||
} else {
|
||||
if (!CheckSignerCache(intermediate_certificate.serial_number(),
|
||||
signer.drm_certificate(), signer.signature())) {
|
||||
util::Status status =
|
||||
ValidateSigner(intermediate_certificate.serial_number(),
|
||||
signer.drm_certificate(), signer.signature());
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
set_signer_serial_number(intermediate_certificate.serial_number());
|
||||
set_signer_creation_time_seconds(
|
||||
intermediate_certificate.creation_time_seconds());
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
CertificateClientCert::CertificateClientCert(
|
||||
const std::string& signed_drm_certificate_bytes) {
|
||||
SignedDrmCertificate signed_drm_certificate;
|
||||
if (!signed_drm_certificate.ParseFromString(signed_drm_certificate_bytes)) {
|
||||
set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-invalid-token"));
|
||||
return;
|
||||
}
|
||||
util::Status status = ValidateCertificate(signed_drm_certificate);
|
||||
if (!status.ok()) {
|
||||
set_status(status);
|
||||
return;
|
||||
}
|
||||
DrmCertificate drm_certificate;
|
||||
if (!drm_certificate.ParseFromString(
|
||||
signed_drm_certificate.drm_certificate())) {
|
||||
set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-invalid"));
|
||||
return;
|
||||
}
|
||||
if (!drm_certificate.has_system_id()) {
|
||||
set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-missing-system-id"));
|
||||
}
|
||||
set_system_id(drm_certificate.system_id());
|
||||
set_serial_number(drm_certificate.serial_number());
|
||||
set_public_key(drm_certificate.public_key());
|
||||
rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
|
||||
if (rsa_public_key_ == nullptr) {
|
||||
set_status(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-public-key-failed"));
|
||||
return;
|
||||
}
|
||||
set_key(Random16Bytes());
|
||||
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
|
||||
set_status(util::Status(error_space, ENCRYPT_ERROR,
|
||||
"device-certificate-failed-encrypt-session-key"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CertificateClientCert::~CertificateClientCert() {}
|
||||
|
||||
bool CertificateClientCert::VerifySignature(const std::string& message,
|
||||
const std::string& signature,
|
||||
ProtocolVersion protocol_version) {
|
||||
if (!rsa_public_key_->VerifySignature(message, signature)) {
|
||||
set_status(util::Status(error_space, INVALID_SIGNATURE, ""));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CertificateClientCert::CheckSignerCache(const std::string& serial_number,
|
||||
const std::string& certificate,
|
||||
const std::string& signature) const {
|
||||
return ValidatedSignerCache::GetSingleton()->Exist(serial_number, certificate,
|
||||
signature);
|
||||
}
|
||||
|
||||
util::Status CertificateClientCert::ValidateSigner(const std::string& serial_number,
|
||||
const std::string& certificate,
|
||||
const std::string& signature) {
|
||||
return ValidatedSignerCache::GetSingleton()->ValidateSigner(
|
||||
serial_number, certificate, signature);
|
||||
}
|
||||
|
||||
void CertificateClientCert::ResetSignerCache() {
|
||||
ValidatedSignerCache::GetSingleton()->ResetSignerCache();
|
||||
}
|
||||
|
||||
size_t CertificateClientCert::SignerCacheSize() {
|
||||
return ValidatedSignerCache::GetSingleton()->SignerCacheSize();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
216
license_server_sdk/internal/client_cert.h
Normal file
216
license_server_sdk/internal/client_cert.h
Normal file
@@ -0,0 +1,216 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__
|
||||
#define LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "util/status.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class SignedDrmCertificate;
|
||||
|
||||
// Handler class for LicenseRequests; validates requests and encrypts licenses.
|
||||
// TODO(user): Remove extra accessors after Keybox parsing is moved
|
||||
// to a separate class in KeyboxClientCert class.
|
||||
class ClientCert {
|
||||
public:
|
||||
virtual ~ClientCert() {}
|
||||
static util::Status Create(
|
||||
widevine::ClientIdentification::TokenType token_type,
|
||||
const std::string& token, ClientCert** client_cert);
|
||||
// Creates a Keybox based ClientCert.
|
||||
static util::Status CreateWithToken(const std::string& keybox_token,
|
||||
ClientCert** client_cert);
|
||||
// Creates a Device Certificate based ClientCert.
|
||||
static util::Status CreateCertificateClientCert(const std::string& drm_certificate,
|
||||
ClientCert** client_cert);
|
||||
// Creates a HMAC SHA256 signature based on the message and the key().
|
||||
// signature is owned by the caller and can not be NULL.
|
||||
virtual void CreateSignature(const std::string& message, std::string* signature);
|
||||
// Checks the passed in signature against a signature created used the
|
||||
// classes information and the passed in message. Returns true if signature
|
||||
// is valid.
|
||||
virtual bool VerifySignature(const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) = 0;
|
||||
// Creates a signing_key that is accessible using signing_key(). Signing_key
|
||||
// is constructed by doing a key derivation using the key() and message.
|
||||
virtual void GenerateSigningKey(const std::string& message,
|
||||
ProtocolVersion protocol_version);
|
||||
// Used to create signing keys. For Keybox token types this is the device key.
|
||||
// For Device Certificate token types this the session key.
|
||||
virtual const std::string& key() const = 0;
|
||||
virtual void set_key(const std::string& key) = 0;
|
||||
virtual const std::string& encrypted_key() const = 0;
|
||||
virtual uint32_t system_id() const { return system_id_; }
|
||||
virtual const std::string& signing_key() const { return signing_key_; }
|
||||
virtual const std::string& public_key() const { return public_key_; }
|
||||
virtual const std::string& serial_number() const { return serial_number_; }
|
||||
virtual void set_serial_number(const std::string& serial_number) {
|
||||
serial_number_ = serial_number;
|
||||
}
|
||||
virtual const std::string& signer_serial_number() const {
|
||||
return signer_serial_number_;
|
||||
}
|
||||
virtual uint32_t signer_creation_time_seconds() const {
|
||||
return signer_creation_time_seconds_;
|
||||
}
|
||||
virtual const util::Status& status() const { return status_; }
|
||||
virtual widevine::ClientIdentification::TokenType type() const = 0;
|
||||
virtual std::string service_id() const { return service_id_; }
|
||||
virtual bool signed_by_provisioner() const { return signed_by_provisioner_; }
|
||||
|
||||
protected:
|
||||
ClientCert() {}
|
||||
|
||||
virtual void set_system_id(uint32_t system_id) { system_id_ = system_id; }
|
||||
virtual void set_signing_key(const std::string& signing_key) {
|
||||
signing_key_ = signing_key;
|
||||
}
|
||||
virtual void set_status(const util::Status& status) { status_ = status; }
|
||||
virtual void set_service_id(const std::string& service_id) {
|
||||
service_id_ = service_id;
|
||||
}
|
||||
virtual void set_signed_by_provisioner(bool provisioner_signed_flag) {
|
||||
signed_by_provisioner_ = provisioner_signed_flag;
|
||||
}
|
||||
|
||||
std::string public_key_;
|
||||
std::string serial_number_;
|
||||
std::string signer_serial_number_;
|
||||
uint32_t signer_creation_time_seconds_ = 0;
|
||||
bool signed_by_provisioner_ = false;
|
||||
|
||||
private:
|
||||
uint32_t system_id_ = 0;
|
||||
std::string signing_key_;
|
||||
util::Status status_;
|
||||
std::string service_id_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ClientCert);
|
||||
};
|
||||
|
||||
// This class implements the crypto operations based on the Widevine keybox.
|
||||
// It will unpack token and perform all the crypto operations for securing
|
||||
// the key material in the license response.
|
||||
class KeyboxClientCert : public ClientCert {
|
||||
public:
|
||||
~KeyboxClientCert() override;
|
||||
|
||||
// Set the system-wide pre-provisioning keys; argument must be human-readable
|
||||
// hex digits.
|
||||
// Must be called before any other method of this class is called, unless
|
||||
// created by ClientCert::CreateWithPreProvisioningKey(...).
|
||||
static void SetPreProvisioningKeys(const std::multimap<uint32_t, std::string>& keys);
|
||||
static bool IsSystemIdKnown(const uint32_t system_id);
|
||||
static uint32_t GetSystemId(const std::string& keybox_bytes);
|
||||
|
||||
bool VerifySignature(const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) override;
|
||||
const std::string& key() const override { return device_key_; }
|
||||
void set_key(const std::string& key) override { device_key_ = key; }
|
||||
const std::string& encrypted_key() const override { return encrypted_device_key_; }
|
||||
widevine::ClientIdentification::TokenType type() const override {
|
||||
return widevine::ClientIdentification::KEYBOX;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ClientCert;
|
||||
friend class MockKeyboxClientCert;
|
||||
explicit KeyboxClientCert(const std::string& keybox_bytes);
|
||||
|
||||
std::string device_key_;
|
||||
std::string encrypted_device_key_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(KeyboxClientCert);
|
||||
};
|
||||
// This class implements the device certificate operations based on RSA keys.
|
||||
// It will unpack token and perform all the crypto operations for securing
|
||||
// the key material in the license response.
|
||||
using widevine::RsaPublicKey;
|
||||
class CertificateClientCert : public ClientCert {
|
||||
public:
|
||||
~CertificateClientCert() override;
|
||||
// Sets the root certificate for certificate validation.
|
||||
static util::Status SetDrmRootCertificatePublicKey(
|
||||
const std::string& root_public_key);
|
||||
bool VerifySignature(const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) override;
|
||||
const std::string& key() const override { return session_key_; }
|
||||
void set_key(const std::string& key) override { session_key_ = key; }
|
||||
const std::string& encrypted_key() const override {
|
||||
return encrypted_session_key_;
|
||||
}
|
||||
widevine::ClientIdentification::TokenType type() const override {
|
||||
return widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class ClientCert;
|
||||
friend class MockCertificateClientCert;
|
||||
explicit CertificateClientCert(const std::string& signed_drm_certificate_bytes);
|
||||
util::Status ValidateCertificate(
|
||||
const SignedDrmCertificate& signed_drm_certificate);
|
||||
virtual void set_public_key(const std::string& public_key) {
|
||||
public_key_ = public_key;
|
||||
}
|
||||
virtual void set_signer_serial_number(const std::string& signer_serial_number) {
|
||||
signer_serial_number_ = signer_serial_number;
|
||||
}
|
||||
virtual void set_signer_creation_time_seconds(uint32_t creation_time_seconds) {
|
||||
signer_creation_time_seconds_ = creation_time_seconds;
|
||||
}
|
||||
// This method checks to see if a cached signature for the signer certificate
|
||||
// exists. The cache is populated by the ValidateSignature method, below.
|
||||
// - serial_number is the signer (intermediate) certificate serial number.
|
||||
// This method does a cached signature lookup using this value as the key.
|
||||
// - certificate is the serialized signer certificate. This method compares
|
||||
// this value to the value cached to determine whether there is a match.
|
||||
// - signature is the signature for the serialized signer certificate. This
|
||||
// method compares this value to the value cached to determine whether
|
||||
// there is a match.
|
||||
// Returns true if there exists a matching cached signature for the signer
|
||||
// certificate with the specified serial number, serialized
|
||||
// DrmCertificate, and serialized DrmCertificate signature.
|
||||
// Returns false otherwise.
|
||||
virtual bool CheckSignerCache(const std::string& serial_number,
|
||||
const std::string& certificate,
|
||||
const std::string& signature) const;
|
||||
// This method verifies the signature of a signer (intermediate) certificate,
|
||||
// caching it in the signer cache if verification succeeds.
|
||||
// - serial_number is the signer (intermediate) certificate serial number.
|
||||
// - certificate is the serialized signer certificate.
|
||||
// - signature is the signature for the serialized signer certificate, signed
|
||||
// with the root certificate private key.
|
||||
// Returns util::Status::OK and caches the validated signer information in
|
||||
// the signer cache if signature validation succeeds. Otherwise, returns
|
||||
virtual util::Status ValidateSigner(const std::string& serial_number,
|
||||
const std::string& certificate,
|
||||
const std::string& signature);
|
||||
|
||||
// The below two functions are only used for testing.
|
||||
static void ResetSignerCache();
|
||||
static size_t SignerCacheSize();
|
||||
|
||||
std::string session_key_;
|
||||
std::string encrypted_session_key_;
|
||||
std::unique_ptr<RsaPublicKey> rsa_public_key_;
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(CertificateClientCert);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
#endif // LICENSE_SERVER_SDK_INTERNAL_CLIENT_CERT_H__
|
||||
607
license_server_sdk/internal/client_cert_test.cc
Normal file
607
license_server_sdk/internal/client_cert_test.cc
Normal file
@@ -0,0 +1,607 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "license_server_sdk/internal/client_cert.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/wvm_test_keys.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
// TODO(user): Change these tests to use on-the-fly generated intermediate
|
||||
// and device certificates based on RsaTestKeys.
|
||||
// TODO(user): Add testcase(s) VerifySignature, CreateSignature,
|
||||
// and GenerateSigningKey.
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
|
||||
class ClientCertTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
if (!setup_preprov_keys_) {
|
||||
KeyboxClientCert::SetPreProvisioningKeys(
|
||||
wvm_test_keys::GetPreprovKeyMultimap());
|
||||
setup_preprov_keys_ = true;
|
||||
}
|
||||
CHECK_OK(CertificateClientCert::SetDrmRootCertificatePublicKey(
|
||||
test_keys_.public_test_key_1_3072_bits()));
|
||||
}
|
||||
|
||||
protected:
|
||||
// Simple container struct for test value and expected keys.
|
||||
class TestTokenAndKeys {
|
||||
public:
|
||||
const std::string token_;
|
||||
uint32_t expected_system_id_;
|
||||
const std::string expected_serial_number_;
|
||||
const std::string expected_device_key_;
|
||||
TestTokenAndKeys(const std::string& token, uint32_t expected_system_id,
|
||||
const std::string& expected_serial_number,
|
||||
const std::string& expected_device_key)
|
||||
: token_(token),
|
||||
expected_system_id_(expected_system_id),
|
||||
expected_serial_number_(expected_serial_number),
|
||||
expected_device_key_(expected_device_key) {}
|
||||
};
|
||||
|
||||
class TestCertificateAndData {
|
||||
public:
|
||||
const std::string certificate_;
|
||||
const std::string expected_serial_number_;
|
||||
uint32_t expected_system_id_;
|
||||
util::Status expected_status_;
|
||||
TestCertificateAndData(const std::string& certificate,
|
||||
const std::string& expected_serial_number,
|
||||
uint32_t expected_system_id,
|
||||
util::Status expected_status)
|
||||
: certificate_(certificate),
|
||||
expected_serial_number_(expected_serial_number),
|
||||
expected_system_id_(expected_system_id),
|
||||
expected_status_(std::move(expected_status)) {}
|
||||
};
|
||||
|
||||
void TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||
const bool expect_success,
|
||||
const bool compare_device_key);
|
||||
void TestBasicValidationDrmCertificate(
|
||||
const TestCertificateAndData& expectation, const bool compare_data);
|
||||
|
||||
void GenerateSignature(const std::string& message, const std::string& private_key,
|
||||
std::string* signature);
|
||||
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate,
|
||||
SignedDrmCertificate* signer,
|
||||
const std::string& private_key);
|
||||
DrmCertificate* GenerateProvisionerCertificate(uint32_t system_id,
|
||||
const std::string& serial_number,
|
||||
const std::string& provider_id);
|
||||
SignedDrmCertificate* GenerateSignedProvisionerCertificate(
|
||||
uint32_t system_id, const std::string& serial_number, const std::string& service_id);
|
||||
DrmCertificate* GenerateIntermediateCertificate(uint32_t system_id,
|
||||
const std::string& serial_number);
|
||||
SignedDrmCertificate* GenerateSignedIntermediateCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number);
|
||||
DrmCertificate* GenerateDrmCertificate(uint32_t system_id,
|
||||
const std::string& serial_number);
|
||||
SignedDrmCertificate* GenerateSignedDrmCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number);
|
||||
|
||||
RsaTestKeys test_keys_;
|
||||
static bool setup_preprov_keys_;
|
||||
};
|
||||
bool ClientCertTest::setup_preprov_keys_(false);
|
||||
|
||||
void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||
const bool expect_success,
|
||||
const bool compare_device_key) {
|
||||
// Test validation of a valid request.
|
||||
util::Status status;
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
|
||||
// Two ways to create a client cert object, test both.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (i == 0) {
|
||||
status = ClientCert::Create(ClientIdentification::KEYBOX,
|
||||
expectation.token_, &client_cert_ptr);
|
||||
} else {
|
||||
status =
|
||||
ClientCert::CreateWithToken(expectation.token_, &client_cert_ptr);
|
||||
}
|
||||
std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
|
||||
if (expect_success) {
|
||||
ASSERT_EQ(util::OkStatus(), status);
|
||||
ASSERT_TRUE(keybox_cert.get());
|
||||
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
|
||||
EXPECT_EQ(expectation.expected_serial_number_,
|
||||
keybox_cert->serial_number());
|
||||
if (compare_device_key) {
|
||||
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
|
||||
}
|
||||
} else {
|
||||
EXPECT_NE(util::OkStatus(), status);
|
||||
EXPECT_FALSE(keybox_cert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientCertTest::TestBasicValidationDrmCertificate(
|
||||
const TestCertificateAndData& expectation, const bool compare_data) {
|
||||
// Test validation of a valid request.
|
||||
util::Status status;
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
status = ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
expectation.certificate_, &client_cert_ptr);
|
||||
std::unique_ptr<ClientCert> drm_certificate_cert(client_cert_ptr);
|
||||
ASSERT_EQ(expectation.expected_status_, status);
|
||||
if (expectation.expected_status_.ok()) {
|
||||
ASSERT_TRUE(drm_certificate_cert.get());
|
||||
if (compare_data) {
|
||||
ASSERT_EQ(expectation.expected_serial_number_,
|
||||
drm_certificate_cert->signer_serial_number());
|
||||
ASSERT_EQ(expectation.expected_system_id_,
|
||||
drm_certificate_cert->system_id());
|
||||
}
|
||||
} else {
|
||||
ASSERT_FALSE(drm_certificate_cert.get());
|
||||
}
|
||||
}
|
||||
|
||||
void ClientCertTest::GenerateSignature(const std::string& message,
|
||||
const std::string& private_key,
|
||||
std::string* signature) {
|
||||
std::unique_ptr<RsaPrivateKey> rsa_private_key(
|
||||
RsaPrivateKey::Create(private_key));
|
||||
ASSERT_TRUE(rsa_private_key != nullptr);
|
||||
rsa_private_key->GenerateSignature(message, signature);
|
||||
}
|
||||
|
||||
// The caller relinquishes ownership of |signer|, which may also be nullptr.
|
||||
SignedDrmCertificate* ClientCertTest::SignCertificate(
|
||||
const DrmCertificate& certificate, SignedDrmCertificate* signer,
|
||||
const std::string& private_key) {
|
||||
std::unique_ptr<SignedDrmCertificate> signed_certificate(
|
||||
new SignedDrmCertificate);
|
||||
signed_certificate->set_drm_certificate(certificate.SerializeAsString());
|
||||
GenerateSignature(signed_certificate->drm_certificate(), private_key,
|
||||
signed_certificate->mutable_signature());
|
||||
if (signer != nullptr) {
|
||||
signed_certificate->set_allocated_signer(signer);
|
||||
}
|
||||
return signed_certificate.release();
|
||||
}
|
||||
|
||||
DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
|
||||
uint32_t system_id, const std::string& serial_number) {
|
||||
std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate);
|
||||
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
|
||||
intermediate_certificate->set_serial_number(serial_number);
|
||||
intermediate_certificate->set_public_key(
|
||||
test_keys_.public_test_key_2_2048_bits());
|
||||
intermediate_certificate->set_system_id(system_id);
|
||||
intermediate_certificate->set_creation_time_seconds(1234);
|
||||
return intermediate_certificate.release();
|
||||
}
|
||||
|
||||
SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number) {
|
||||
std::unique_ptr<DrmCertificate> intermediate_certificate(
|
||||
GenerateIntermediateCertificate(system_id, serial_number));
|
||||
return SignCertificate(*intermediate_certificate, signer,
|
||||
test_keys_.private_test_key_1_3072_bits());
|
||||
}
|
||||
|
||||
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
||||
uint32_t system_id, const std::string& serial_number) {
|
||||
std::unique_ptr<DrmCertificate> drm_certificate(new DrmCertificate);
|
||||
drm_certificate->set_type(DrmCertificate::DEVICE);
|
||||
drm_certificate->set_serial_number(serial_number);
|
||||
drm_certificate->set_system_id(system_id);
|
||||
drm_certificate->set_public_key(test_keys_.public_test_key_3_2048_bits());
|
||||
drm_certificate->set_creation_time_seconds(4321);
|
||||
return drm_certificate.release();
|
||||
}
|
||||
|
||||
SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number) {
|
||||
std::unique_ptr<DrmCertificate> drm_certificate(
|
||||
GenerateDrmCertificate(system_id, serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate(
|
||||
*drm_certificate, signer, test_keys_.private_test_key_2_2048_bits()));
|
||||
return signed_drm_certificate.release();
|
||||
}
|
||||
|
||||
DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
|
||||
uint32_t system_id, const std::string& serial_number, const std::string& provider_id) {
|
||||
std::unique_ptr<DrmCertificate> provisioner_certificate(new DrmCertificate);
|
||||
provisioner_certificate->set_type(DrmCertificate::PROVISIONER);
|
||||
provisioner_certificate->set_serial_number(serial_number);
|
||||
// TODO(user): Need to generate 3072 bit test for provisioner certificates.
|
||||
provisioner_certificate->set_public_key(
|
||||
test_keys_.public_test_key_2_2048_bits());
|
||||
provisioner_certificate->set_system_id(system_id);
|
||||
provisioner_certificate->set_provider_id(provider_id);
|
||||
provisioner_certificate->set_creation_time_seconds(1234);
|
||||
return provisioner_certificate.release();
|
||||
}
|
||||
|
||||
SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
|
||||
uint32_t system_id, const std::string& serial_number, const std::string& service_id) {
|
||||
std::unique_ptr<DrmCertificate> provisioner_certificate(
|
||||
GenerateProvisionerCertificate(system_id, serial_number, service_id));
|
||||
return SignCertificate(*provisioner_certificate, nullptr,
|
||||
test_keys_.private_test_key_1_3072_bits());
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, BasicValidation) {
|
||||
const TestTokenAndKeys kValidTokenAndExpectedKeys[] = {
|
||||
TestTokenAndKeys(
|
||||
absl::HexStringToBytes(
|
||||
"00000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e"),
|
||||
274, absl::HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"),
|
||||
absl::HexStringToBytes("4071197f1f8910d9bf10c6bc4c987638")),
|
||||
TestTokenAndKeys(
|
||||
absl::HexStringToBytes(
|
||||
"0000000200000112d906feebe1750c5886ff77c2dfa31bb40e002f3adbc0fa5b"
|
||||
"eb2486cf5f419549cdaa23230e5165ac2ffab56d53b692b7ba0c1857400c6add"
|
||||
"3af3ff3d5cb24985"),
|
||||
274, absl::HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"),
|
||||
absl::HexStringToBytes("42cfb1765201042302a404d1e0fac8ed"))};
|
||||
|
||||
for (size_t i = 0; i < ABSL_ARRAYSIZE(kValidTokenAndExpectedKeys); ++i) {
|
||||
SCOPED_TRACE("Test data: " + absl::StrCat(i));
|
||||
TestBasicValidation(kValidTokenAndExpectedKeys[i], true, true);
|
||||
}
|
||||
|
||||
EXPECT_EQ(
|
||||
wvm_test_keys::kTestSystemId,
|
||||
KeyboxClientCert::GetSystemId(kValidTokenAndExpectedKeys[0].token_));
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, BasicCertValidation) {
|
||||
const uint32_t system_id = 1234;
|
||||
const std::string serial_number("serial_number");
|
||||
std::unique_ptr<SignedDrmCertificate> signed_cert(
|
||||
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
|
||||
nullptr, system_id, serial_number),
|
||||
system_id, serial_number + "-device"));
|
||||
const TestCertificateAndData kValidCertificateAndExpectedData(
|
||||
signed_cert->SerializeAsString(), serial_number, system_id,
|
||||
util::OkStatus());
|
||||
const bool compare_data = true;
|
||||
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
|
||||
compare_data);
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, InvalidKeybox) {
|
||||
const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = {
|
||||
// This tests a malformed, but appropriately sized keybox.
|
||||
TestTokenAndKeys(
|
||||
absl::HexStringToBytes(
|
||||
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e"),
|
||||
0, absl::HexStringToBytes(""), absl::HexStringToBytes("")),
|
||||
// This has a length and system_id, but nothing else.
|
||||
TestTokenAndKeys(absl::HexStringToBytes("0000000200000112"), 0,
|
||||
absl::HexStringToBytes(""), absl::HexStringToBytes("")),
|
||||
// This has only a byte.
|
||||
TestTokenAndKeys(absl::HexStringToBytes(""), 0,
|
||||
absl::HexStringToBytes(""), absl::HexStringToBytes("")),
|
||||
// This has an emptry std::string for the keybox.
|
||||
TestTokenAndKeys(absl::HexStringToBytes(""), 0,
|
||||
absl::HexStringToBytes(""), absl::HexStringToBytes(""))};
|
||||
|
||||
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidTokenAndExpectedKeys); ++i) {
|
||||
SCOPED_TRACE("Test data: " + absl::StrCat(i));
|
||||
TestBasicValidation(kInvalidTokenAndExpectedKeys[i], false, false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, InvalidCertificate) {
|
||||
const uint32_t system_id(1234);
|
||||
const std::string device_sn("device-serial-number");
|
||||
const std::string signer_sn("signer-serial-number");
|
||||
std::unique_ptr<DrmCertificate> dev_cert;
|
||||
std::unique_ptr<DrmCertificate> signer_cert;
|
||||
std::unique_ptr<SignedDrmCertificate> signed_signer;
|
||||
|
||||
// Invalid serialized device certificate.
|
||||
std::unique_ptr<SignedDrmCertificate> invalid_drm_cert(
|
||||
new SignedDrmCertificate);
|
||||
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
|
||||
GenerateSignature(invalid_drm_cert->drm_certificate(),
|
||||
test_keys_.private_test_key_2_2048_bits(),
|
||||
invalid_drm_cert->mutable_signature());
|
||||
invalid_drm_cert->set_allocated_signer(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
||||
// Missing system ID.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
dev_cert->clear_system_id();
|
||||
std::unique_ptr<SignedDrmCertificate> no_system_id(SignCertificate(
|
||||
*dev_cert.get(),
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
// Invalid device public key.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
dev_cert->set_public_key("bad-device-public-key");
|
||||
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
|
||||
*dev_cert,
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
// Invalid serialized intermediate certificate.
|
||||
signed_signer.reset(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
||||
signed_signer->set_drm_certificate("bad-serialized-cert");
|
||||
GenerateSignature(signed_signer->drm_certificate(),
|
||||
test_keys_.private_test_key_1_3072_bits(),
|
||||
signed_signer->mutable_signature());
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
std::unique_ptr<SignedDrmCertificate> invalid_signer(
|
||||
SignCertificate(*dev_cert, signed_signer.release(),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
// Invalid signer public key.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||
signer_cert->set_public_key("bad-signer-public-key");
|
||||
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_keys_.private_test_key_1_3072_bits()),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
// Invalid device certificate signature.
|
||||
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
|
||||
GenerateSignedDrmCertificate(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
||||
system_id, device_sn));
|
||||
bad_device_signature->set_signature("bad-signature");
|
||||
// Missing signer serial number.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||
signer_cert->clear_serial_number();
|
||||
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_keys_.private_test_key_1_3072_bits()),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
// Invalid serialized intermediate certificate.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signed_signer.reset(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
||||
signed_signer->set_signature("bad-signature");
|
||||
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
|
||||
SignCertificate(*dev_cert, signed_signer.release(),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
|
||||
const TestCertificateAndData kInvalidCertificate[] = {
|
||||
TestCertificateAndData("f", "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-invalid-token")),
|
||||
TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-invalid")),
|
||||
TestCertificateAndData(
|
||||
no_system_id->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-missing-system-id")),
|
||||
TestCertificateAndData(
|
||||
bad_device_public_key->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-public-key-failed")),
|
||||
TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-invalid-signer")),
|
||||
TestCertificateAndData(
|
||||
bad_signer_public_key->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signer-certificate-public-key-failed")),
|
||||
TestCertificateAndData(
|
||||
bad_device_signature->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-verification-failed")),
|
||||
TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-signer-serial-number")),
|
||||
TestCertificateAndData(
|
||||
bad_signer_signature->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signer-certificate-verification-failed")),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) {
|
||||
TestBasicValidationDrmCertificate(kInvalidCertificate[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
class MockCertificateClientCert : public CertificateClientCert {
|
||||
public:
|
||||
using CertificateClientCert::ResetSignerCache;
|
||||
using CertificateClientCert::SignerCacheSize;
|
||||
|
||||
explicit MockCertificateClientCert(const std::string& cert_bytes)
|
||||
: CertificateClientCert(cert_bytes) {}
|
||||
MOCK_METHOD3(ValidateSigner, util::Status(const std::string& serial_number,
|
||||
const std::string& certificate,
|
||||
const std::string& signature));
|
||||
util::Status CallValidateCertificate(
|
||||
const SignedDrmCertificate& signed_drm_certificate) {
|
||||
return ValidateCertificate(signed_drm_certificate);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ClientCertTest, SignerCache) {
|
||||
const uint32_t system_id = 1234;
|
||||
const std::string serial_number("serial-number");
|
||||
std::unique_ptr<SignedDrmCertificate> signed_cert(
|
||||
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
|
||||
nullptr, system_id, serial_number),
|
||||
system_id, serial_number + "-device"));
|
||||
// TODO(user): Remove work from the ClientCert constructors to make it
|
||||
// more testable, and because it's just bad practice.
|
||||
MockCertificateClientCert::ResetSignerCache();
|
||||
MockCertificateClientCert client_cert(signed_cert->SerializeAsString());
|
||||
EXPECT_EQ(1, MockCertificateClientCert::SignerCacheSize());
|
||||
EXPECT_CALL(client_cert, ValidateSigner(_, _, _))
|
||||
.Times(0)
|
||||
.WillRepeatedly(Return(util::OkStatus()));
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
client_cert.CallValidateCertificate(*signed_cert));
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, MissingPreProvKey) {
|
||||
// system ID in token is 0x01234567
|
||||
const std::string token(absl::HexStringToBytes(
|
||||
"00000002012345678e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e"));
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
util::Status status = ClientCert::CreateWithToken(token, &client_cert_ptr);
|
||||
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
|
||||
const uint32_t system_id = 4890;
|
||||
const std::string service_id("widevine_test.com");
|
||||
const std::string device_serial_number("device-serial-number");
|
||||
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||
const std::string provisioner_serial_number("provisioner-serial-number");
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
|
||||
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
|
||||
service_id));
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
|
||||
system_id,
|
||||
intermediate_serial_number));
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, device_serial_number));
|
||||
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
|
||||
EXPECT_OK(ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr));
|
||||
ASSERT_TRUE(client_cert_ptr != nullptr);
|
||||
std::unique_ptr<ClientCert> drm_cert(client_cert_ptr);
|
||||
|
||||
EXPECT_EQ(service_id, drm_cert->service_id());
|
||||
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
|
||||
EXPECT_EQ(intermediate_serial_number, drm_cert->signer_serial_number());
|
||||
EXPECT_EQ(system_id, drm_cert->system_id());
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, InValidProvisionerDeviceCertEmptyServiceId) {
|
||||
const uint32_t system_id = 4890;
|
||||
const std::string service_id("");
|
||||
const std::string device_serial_number("device-serial-number");
|
||||
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||
const std::string provisioner_serial_number("provisioner-serial-number");
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
|
||||
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
|
||||
service_id));
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
|
||||
system_id,
|
||||
intermediate_serial_number));
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, device_serial_number));
|
||||
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
|
||||
EXPECT_EQ("missing-provisioning-service-id",
|
||||
ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr)
|
||||
.error_message());
|
||||
EXPECT_FALSE(client_cert_ptr);
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, InValidProvisionerDeviceCertChain) {
|
||||
const uint32_t system_id = 4890;
|
||||
const uint32_t system_id2 = 4892;
|
||||
const std::string service_id("widevine_test.com");
|
||||
const std::string device_serial_number("device-serial-number");
|
||||
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||
const std::string intermediate_serial_number2("intermediate-serial-number-2");
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id2,
|
||||
intermediate_serial_number2));
|
||||
|
||||
// Instead of using a provisioner certificate to sign this intermediate
|
||||
// certificate, use another intermediate certificate. This is an invalid
|
||||
// chain and should generate an error when trying to create a client
|
||||
// certificate.
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
GenerateSignedIntermediateCertificate(signed_intermediate_cert2.release(),
|
||||
system_id,
|
||||
intermediate_serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, device_serial_number));
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
|
||||
ASSERT_EQ("expected-provisioning-provider-certificate-type",
|
||||
ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr)
|
||||
.error_message());
|
||||
EXPECT_FALSE(client_cert_ptr);
|
||||
|
||||
// Make a normal intermediate certificate.
|
||||
signed_intermediate_cert.reset(GenerateSignedIntermediateCertificate(
|
||||
nullptr, system_id, intermediate_serial_number));
|
||||
signed_device_cert.reset(GenerateSignedDrmCertificate(
|
||||
signed_intermediate_cert.release(), system_id, device_serial_number));
|
||||
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
EXPECT_OK(ClientCert::Create(ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr));
|
||||
|
||||
// The service Id should only get set if a provisioning cert exist.
|
||||
std::unique_ptr<ClientCert> drm_cert(client_cert_ptr);
|
||||
EXPECT_TRUE(drm_cert->service_id().empty());
|
||||
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
|
||||
EXPECT_EQ(intermediate_serial_number, drm_cert->signer_serial_number());
|
||||
EXPECT_EQ(system_id, drm_cert->system_id());
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
241
license_server_sdk/internal/device_status_list.cc
Normal file
241
license_server_sdk/internal/device_status_list.cc
Normal file
@@ -0,0 +1,241 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Implements the DeviceStatusList class.
|
||||
|
||||
#include "license_server_sdk/internal/device_status_list.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "license_server_sdk/internal/client_cert.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
DeviceStatusList* DeviceStatusList::Instance() {
|
||||
// TODO(user): This is "ok" according to Google's Coding for Dummies, but
|
||||
// we should inject the status list into the sessions. This will require
|
||||
// exposing additional objects in the public interface.
|
||||
static DeviceStatusList* device_status_list(nullptr);
|
||||
if (!device_status_list) device_status_list = new DeviceStatusList;
|
||||
return device_status_list;
|
||||
}
|
||||
|
||||
DeviceStatusList::DeviceStatusList()
|
||||
: creation_time_seconds_(0),
|
||||
expiration_period_seconds_(0),
|
||||
allow_unknown_devices_(true),
|
||||
allow_test_only_devices_(false) {}
|
||||
|
||||
DeviceStatusList::~DeviceStatusList() {}
|
||||
|
||||
util::Status DeviceStatusList::UpdateStatusList(
|
||||
const std::string& root_certificate_public_key,
|
||||
const std::string& serialized_certificate_status_list,
|
||||
uint32_t expiration_period_seconds) {
|
||||
SignedDeviceCertificateStatusList signed_certificate_status_list;
|
||||
if (!signed_certificate_status_list.ParseFromString(
|
||||
serialized_certificate_status_list)) {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"signed-certificate-status-list-parse-error");
|
||||
}
|
||||
if (!signed_certificate_status_list.has_certificate_status_list()) {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-status-list");
|
||||
}
|
||||
if (!signed_certificate_status_list.has_signature()) {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-status-list-signature");
|
||||
}
|
||||
std::unique_ptr<RsaPublicKey> root_key(
|
||||
RsaPublicKey::Create(root_certificate_public_key));
|
||||
if (root_key == nullptr) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-public-key");
|
||||
}
|
||||
if (!root_key->VerifySignature(
|
||||
signed_certificate_status_list.certificate_status_list(),
|
||||
signed_certificate_status_list.signature())) {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"invalid-status-list-signature");
|
||||
}
|
||||
DeviceCertificateStatusList certificate_status_list;
|
||||
if (!certificate_status_list.ParseFromString(
|
||||
signed_certificate_status_list.certificate_status_list())) {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-parse-error");
|
||||
}
|
||||
if (expiration_period_seconds &&
|
||||
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
|
||||
expiration_period_seconds))) {
|
||||
return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-expired");
|
||||
}
|
||||
|
||||
absl::WriterMutexLock lock(&status_map_lock_);
|
||||
device_status_map_.clear();
|
||||
for (int i = 0, n = certificate_status_list.certificate_status_size(); i < n;
|
||||
i++) {
|
||||
const DeviceCertificateStatus& cert_status =
|
||||
certificate_status_list.certificate_status(i);
|
||||
if (cert_status.has_device_info()) {
|
||||
const ProvisionedDeviceInfo& device_info = cert_status.device_info();
|
||||
if (device_info.has_system_id()) {
|
||||
device_status_map_[device_info.system_id()] = cert_status;
|
||||
} else {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"device-info-missing-system-id");
|
||||
}
|
||||
}
|
||||
}
|
||||
creation_time_seconds_ = certificate_status_list.creation_time_seconds();
|
||||
expiration_period_seconds_ = expiration_period_seconds;
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status DeviceStatusList::GetCertStatus(
|
||||
const ClientCert& client_cert, ProvisionedDeviceInfo* device_info) {
|
||||
CHECK(device_info);
|
||||
|
||||
// Keybox checks.
|
||||
if (client_cert.type() == ClientIdentification::KEYBOX) {
|
||||
if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) {
|
||||
return util::Status(error_space, UNSUPPORTED_SYSTEM_ID,
|
||||
"keybox-unsupported-system-id");
|
||||
}
|
||||
// Get device information from certificate status list if available.
|
||||
if (!GetDeviceInfo(client_cert, device_info)) {
|
||||
device_info->Clear();
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
// DRM certificate checks.
|
||||
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-unsupported-token-type");
|
||||
}
|
||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||
if (expiration_period_seconds_ &&
|
||||
(GetCurrentTime() >
|
||||
(creation_time_seconds_ + expiration_period_seconds_))) {
|
||||
return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-expired");
|
||||
}
|
||||
DeviceCertificateStatus* device_cert_status =
|
||||
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
||||
if (device_cert_status) {
|
||||
*device_info = device_cert_status->device_info();
|
||||
if (device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_REVOKED) {
|
||||
if (IsRevokedSystemIdAllowed(client_cert.system_id())) {
|
||||
LOG(WARNING) << "Allowing REVOKED device: "
|
||||
<< device_info->ShortDebugString();
|
||||
} else {
|
||||
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||
"device-certificate-revoked");
|
||||
}
|
||||
}
|
||||
if ((device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
||||
!allow_test_only_devices_) {
|
||||
return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
"test-only-drm-certificate-not-allowed");
|
||||
}
|
||||
if (!client_cert.signed_by_provisioner() &&
|
||||
(client_cert.signer_serial_number() !=
|
||||
device_cert_status->drm_serial_number())) {
|
||||
// Widevine-provisioned device, and the intermediate certificate serial
|
||||
// number does not match that in the status list. If the status list is
|
||||
// newer than the certificate, indicate an invalid certificate, so that
|
||||
// the device re-provisions. If, on the other hand, the certificate status
|
||||
// list is older than the certificate, the certificate is for all purposes
|
||||
// unknown.
|
||||
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"intermediate-certificate-serial-number-mismatch");
|
||||
}
|
||||
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
"device-certificate-status-unknown");
|
||||
}
|
||||
} else {
|
||||
if (!allow_unknown_devices_) {
|
||||
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
"device-certificate-status-unknown");
|
||||
}
|
||||
device_info->Clear();
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
|
||||
ProvisionedDeviceInfo* device_info) {
|
||||
CHECK(device_info);
|
||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||
DeviceCertificateStatus* device_cert_status =
|
||||
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
||||
if (device_cert_status) {
|
||||
*device_info = device_cert_status->device_info();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeviceStatusList::IsSystemIdActive(uint32_t system_id) {
|
||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||
DeviceCertificateStatus* device_cert_status =
|
||||
gtl::FindOrNull(device_status_map_, system_id);
|
||||
if (!device_cert_status) {
|
||||
return allow_unknown_devices_ ||
|
||||
KeyboxClientCert::IsSystemIdKnown(system_id);
|
||||
}
|
||||
if (device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_TEST_ONLY) {
|
||||
return allow_test_only_devices_;
|
||||
}
|
||||
if (device_cert_status) {
|
||||
ProvisionedDeviceInfo device_info = device_cert_status->device_info();
|
||||
if (device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_REVOKED) {
|
||||
if (IsRevokedSystemIdAllowed(system_id)) {
|
||||
LOG(WARNING) << "REVOKED system_id: " << system_id
|
||||
<< " is allowed to be active";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return device_cert_status->status() !=
|
||||
DeviceCertificateStatus::STATUS_REVOKED;
|
||||
}
|
||||
|
||||
uint32_t DeviceStatusList::GetCurrentTime() const { return time(nullptr); }
|
||||
|
||||
void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) {
|
||||
for (absl::string_view sp : absl::StrSplit(system_id_list, ',')) {
|
||||
allowed_revoked_devices_.push_back(std::stoi(std::string(sp)));
|
||||
}
|
||||
std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end());
|
||||
}
|
||||
|
||||
bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
|
||||
auto it = std::binary_search(allowed_revoked_devices_.begin(),
|
||||
allowed_revoked_devices_.end(), system_id);
|
||||
return it;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
99
license_server_sdk/internal/device_status_list.h
Normal file
99
license_server_sdk/internal/device_status_list.h
Normal file
@@ -0,0 +1,99 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// DeviceStatusList class header.
|
||||
|
||||
#ifndef LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__
|
||||
#define LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/status.h"
|
||||
#include "protos/public/device_certificate_status.pb.h"
|
||||
#include "protos/public/provisioned_device_info.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class ClientCert;
|
||||
// Manages the certificate status of devices. The list of
|
||||
// DeviceCertificateStatus is provided by the DRM server. Each license
|
||||
// request is checked to ensure the certificate in the request is valid and
|
||||
// not revoked. Also checks to see if the intermediate certificates were
|
||||
// updated where the system Id is the same, but the serial number changes.
|
||||
// This case should cause the clients to re-provision.
|
||||
class DeviceStatusList {
|
||||
public:
|
||||
// Returns a pointer to a singleton DeviceStatusList.
|
||||
static DeviceStatusList* Instance();
|
||||
|
||||
DeviceStatusList();
|
||||
virtual ~DeviceStatusList();
|
||||
// Takes |signed_certificate_status_list| and copies to an internal map of
|
||||
// device certifcate status list. The internal map is used to verify
|
||||
// a device was not revoked. Returns true is the list was successfully parsed.
|
||||
util::Status UpdateStatusList(const std::string& root_certificate_public_key,
|
||||
const std::string& signed_certificate_status_list,
|
||||
uint32_t expiration_period_seconds);
|
||||
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
|
||||
bool allow_unknown_devices() const { return allow_unknown_devices_; }
|
||||
void set_allow_test_only_devices(bool allow) {
|
||||
allow_test_only_devices_ = allow;
|
||||
}
|
||||
bool allow_test_only_devices() const { return allow_test_only_devices_; }
|
||||
|
||||
// Checks the device status list and returns either:
|
||||
// OK
|
||||
// UNSUPPORTED_SYSTEM_ID
|
||||
// INVALID_DRM_CERTIFICATE
|
||||
// DRM_DEVICE_CERTIFICATE_REVOKED
|
||||
// DRM_DEVICE_CERTIFICATE_UNKNOWN
|
||||
// If status is OK, a copy of the provisioned device info is copied
|
||||
// into |device_info|. Caller owns |device_info| and it must not be null.
|
||||
util::Status GetCertStatus(
|
||||
const ClientCert& client_cert,
|
||||
widevine::ProvisionedDeviceInfo* device_info);
|
||||
// Returns true if the pre-provisioning key or certificate for the specified
|
||||
// system ID are active (not disallowed or revoked).
|
||||
bool IsSystemIdActive(uint32_t system_id);
|
||||
|
||||
// Returns true if the system ID
|
||||
// Returns true is a ProvisionedDeviceInfo exist based on <client_cert>.
|
||||
// Caller owns <device_info> and it must not be null.
|
||||
bool GetDeviceInfo(const ClientCert& client_cert,
|
||||
widevine::ProvisionedDeviceInfo* device_info);
|
||||
// Returns the current POSIX time.
|
||||
virtual uint32_t GetCurrentTime() const;
|
||||
|
||||
// Enable delivery of licenses to revoked client devices. |system_id_list| is
|
||||
// a comma separated list of systems Ids to allow even if revoked.
|
||||
virtual void AllowRevokedDevices(const std::string& system_id_list);
|
||||
|
||||
private:
|
||||
// Returns true if the system ID is allowed to be revoked.
|
||||
// Caller owns |system_id|. They must not be null.
|
||||
bool IsRevokedSystemIdAllowed(uint32_t system_id);
|
||||
|
||||
absl::Mutex status_map_lock_;
|
||||
// Key is the system id for the device.
|
||||
std::map<uint32_t, widevine::DeviceCertificateStatus> device_status_map_;
|
||||
uint32_t creation_time_seconds_;
|
||||
uint32_t expiration_period_seconds_;
|
||||
bool allow_unknown_devices_;
|
||||
bool allow_test_only_devices_;
|
||||
// Contains the list of system_id values that are allowed to succeed even if
|
||||
// revoked.
|
||||
std::vector<uint32_t> allowed_revoked_devices_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DeviceStatusList);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
#endif // LICENSE_SERVER_SDK_INTERNAL_DEVICE_STATUS_LIST_H__
|
||||
377
license_server_sdk/internal/device_status_list_test.cc
Normal file
377
license_server_sdk/internal/device_status_list_test.cc
Normal file
@@ -0,0 +1,377 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "license_server_sdk/internal/device_status_list.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "license_server_sdk/internal/client_cert.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/provisioned_device_info.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnRef;
|
||||
using ::testing::ReturnRefOfCopy;
|
||||
|
||||
const uint32_t kValidCertSystemId = 100;
|
||||
const uint32_t kRevokedCertSystemId = 101;
|
||||
const uint32_t kValidPpkSystemId = 102;
|
||||
const uint32_t kTestOnlyCertSystemId = 103;
|
||||
const uint32_t kRevokedAllowedDeviceCertSystemId = 104;
|
||||
const uint32_t kUnknownSystemId = 666;
|
||||
const char kValidSerialNumber[] = "valid-serial-number";
|
||||
const char kRevokedSerialNumber[] = "revoked-serial-number";
|
||||
const char kRevokedAllowDeviceSerialNumber[] =
|
||||
"revoked-allow-device-serial-number";
|
||||
const char kTestOnlySerialNumber[] = "test_only-serial-number";
|
||||
const char kMismatchSerialNumber[] = "mismatch-serial-number";
|
||||
const char kDeviceModel[] = "device-model-x";
|
||||
const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
|
||||
const uint32_t kStatusListCreationTime = 17798001;
|
||||
const uint32_t kDefaultExpirePeriod = 0;
|
||||
|
||||
class MockCertificateClientCert : public CertificateClientCert {
|
||||
public:
|
||||
explicit MockCertificateClientCert() : CertificateClientCert("token-bytes") {}
|
||||
MOCK_CONST_METHOD0(system_id, uint32_t());
|
||||
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
|
||||
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
|
||||
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
|
||||
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
|
||||
};
|
||||
|
||||
class MockKeyboxClientCert : public KeyboxClientCert {
|
||||
public:
|
||||
explicit MockKeyboxClientCert() : KeyboxClientCert("token-bytes") {}
|
||||
MOCK_CONST_METHOD0(system_id, uint32_t());
|
||||
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
|
||||
};
|
||||
|
||||
class DeviceStatusListTest : public ::testing::Test {
|
||||
public:
|
||||
~DeviceStatusListTest() override {}
|
||||
|
||||
void SetUp() override {
|
||||
DeviceCertificateStatus *cert_status;
|
||||
|
||||
// Device cert with status RELEASED.
|
||||
cert_status = cert_status_list_.add_certificate_status();
|
||||
cert_status->mutable_device_info()->set_system_id(kValidCertSystemId);
|
||||
cert_status->set_drm_serial_number(kValidSerialNumber);
|
||||
cert_status->mutable_device_info()->set_model(kDeviceModel);
|
||||
cert_status->set_status(DeviceCertificateStatus::STATUS_RELEASED);
|
||||
|
||||
// Device cert with status REVOKED.
|
||||
cert_status = cert_status_list_.add_certificate_status();
|
||||
cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId);
|
||||
cert_status->set_drm_serial_number(kRevokedSerialNumber);
|
||||
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
|
||||
|
||||
// Device cert with status REVOKED ALLOWED DEVICE.
|
||||
cert_status = cert_status_list_.add_certificate_status();
|
||||
cert_status->mutable_device_info()->set_system_id(
|
||||
kRevokedAllowedDeviceCertSystemId);
|
||||
cert_status->set_drm_serial_number(kRevokedAllowDeviceSerialNumber);
|
||||
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
|
||||
device_status_list_.AllowRevokedDevices(
|
||||
absl::StrCat(kRevokedAllowedDeviceCertSystemId));
|
||||
|
||||
// Device cert with status TEST_ONLY.
|
||||
cert_status = cert_status_list_.add_certificate_status();
|
||||
cert_status->mutable_device_info()->set_system_id(kTestOnlyCertSystemId);
|
||||
cert_status->set_drm_serial_number(kTestOnlySerialNumber);
|
||||
cert_status->set_status(DeviceCertificateStatus::STATUS_TEST_ONLY);
|
||||
|
||||
cert_status_list_.set_creation_time_seconds(kStatusListCreationTime);
|
||||
cert_status_list_.SerializeToString(
|
||||
signed_cert_status_list_.mutable_certificate_status_list());
|
||||
std::unique_ptr<RsaPrivateKey> root_key(
|
||||
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
|
||||
ASSERT_TRUE(root_key);
|
||||
|
||||
ASSERT_TRUE(root_key->GenerateSignature(
|
||||
signed_cert_status_list_.certificate_status_list(),
|
||||
signed_cert_status_list_.mutable_signature()));
|
||||
ASSERT_TRUE(
|
||||
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
||||
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
device_status_list_.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, kDefaultExpirePeriod));
|
||||
}
|
||||
|
||||
DeviceStatusList device_status_list_;
|
||||
RsaTestKeys test_keys_;
|
||||
DeviceCertificateStatusList cert_status_list_;
|
||||
SignedDeviceCertificateStatusList signed_cert_status_list_;
|
||||
std::string serialized_status_list_;
|
||||
};
|
||||
|
||||
// Returns the number of DevcieCertificateStatus messages in the list.
|
||||
|
||||
TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
||||
// Test case where the Certificate status is set to Valid.
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockCertificateClientCert valid_client_cert;
|
||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||
EXPECT_CALL(valid_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(valid_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
|
||||
// Test case where the Certificate status is Revoked.
|
||||
MockCertificateClientCert revoked_client_cert;
|
||||
std::string revoked_drm_serial_number(kRevokedSerialNumber);
|
||||
EXPECT_CALL(revoked_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(revoked_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kRevokedCertSystemId));
|
||||
EXPECT_CALL(revoked_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(revoked_drm_serial_number));
|
||||
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||
device_status_list_.GetCertStatus(revoked_client_cert, &device_info)
|
||||
.error_code());
|
||||
|
||||
// Test case where the revoked cert is allowed.
|
||||
device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId));
|
||||
EXPECT_OK(
|
||||
device_status_list_.GetCertStatus(revoked_client_cert, &device_info));
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockCertificateClientCert test_only_client_cert;
|
||||
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
||||
EXPECT_CALL(test_only_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(test_only_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
||||
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||
EXPECT_EQ(
|
||||
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
device_status_list_.GetCertStatus(test_only_client_cert, &device_info)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockCertificateClientCert test_only_client_cert;
|
||||
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
||||
device_status_list_.set_allow_test_only_devices(true);
|
||||
EXPECT_CALL(test_only_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(test_only_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
||||
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus(
|
||||
test_only_client_cert, &device_info));
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
||||
std::multimap<uint32_t, std::string> preprov_keys;
|
||||
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
|
||||
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
|
||||
|
||||
// Test case where the Certificate status is set to Valid.
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockKeyboxClientCert valid_client_keybox;
|
||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||
EXPECT_CALL(valid_client_keybox, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||
EXPECT_CALL(valid_client_keybox, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus(
|
||||
valid_client_keybox, &device_info));
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
|
||||
MockKeyboxClientCert unknown_client_keybox;
|
||||
EXPECT_CALL(unknown_client_keybox, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||
EXPECT_CALL(unknown_client_keybox, system_id())
|
||||
.WillRepeatedly(Return(kUnknownSystemId));
|
||||
EXPECT_EQ(
|
||||
UNSUPPORTED_SYSTEM_ID,
|
||||
device_status_list_.GetCertStatus(unknown_client_keybox, &device_info)
|
||||
.error_code());
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
||||
device_status_list_.set_allow_unknown_devices(true);
|
||||
|
||||
// Test case where the signer certificate is older than the current status
|
||||
// list.
|
||||
MockCertificateClientCert older_client_cert;
|
||||
ProvisionedDeviceInfo device_info;
|
||||
std::string mismatch_drm_serial_number(kMismatchSerialNumber);
|
||||
EXPECT_CALL(older_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(older_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(older_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||
EXPECT_CALL(older_client_cert, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
||||
EXPECT_EQ(INVALID_DRM_CERTIFICATE,
|
||||
device_status_list_.GetCertStatus(older_client_cert, &device_info)
|
||||
.error_code());
|
||||
|
||||
// We allow this case only for certs signed by a provisioner cert.
|
||||
EXPECT_CALL(older_client_cert, signed_by_provisioner())
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
device_status_list_.GetCertStatus(older_client_cert, &device_info));
|
||||
EXPECT_TRUE(device_info.has_system_id());
|
||||
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
|
||||
|
||||
// Test case where the signer certificate is newer than the current status
|
||||
// list, and unknown devices are allowed.
|
||||
MockCertificateClientCert newer_client_cert1;
|
||||
EXPECT_CALL(newer_client_cert1, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(newer_client_cert1, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(newer_client_cert1, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||
EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime));
|
||||
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
device_status_list_.GetCertStatus(newer_client_cert1, &device_info)
|
||||
.error_code());
|
||||
|
||||
// Test case where the signer certificate is newer than the current status
|
||||
// list, and unknown devices are not allowed.
|
||||
device_status_list_.set_allow_unknown_devices(false);
|
||||
MockCertificateClientCert newer_client_cert2;
|
||||
EXPECT_CALL(newer_client_cert2, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(newer_client_cert2, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(newer_client_cert2, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||
EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime + 1));
|
||||
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
device_status_list_.GetCertStatus(newer_client_cert2, &device_info)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, InvalidStatusList) {
|
||||
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
||||
device_status_list_
|
||||
.UpdateStatusList(test_keys_.public_test_key_2_2048_bits(),
|
||||
serialized_status_list_, 0)
|
||||
.error_code());
|
||||
|
||||
++(*signed_cert_status_list_.mutable_certificate_status_list())[4];
|
||||
ASSERT_TRUE(
|
||||
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
||||
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
||||
device_status_list_
|
||||
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 0)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
class MockDeviceStatusList : public DeviceStatusList {
|
||||
public:
|
||||
MOCK_CONST_METHOD0(GetCurrentTime, uint32_t());
|
||||
};
|
||||
|
||||
TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
|
||||
MockDeviceStatusList mock_device_status_list;
|
||||
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
|
||||
.Times(2)
|
||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 100));
|
||||
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
mock_device_status_list
|
||||
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 100)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
||||
MockDeviceStatusList mock_device_status_list;
|
||||
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
|
||||
.Times(3)
|
||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 100));
|
||||
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockCertificateClientCert valid_client_cert;
|
||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||
EXPECT_CALL(valid_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(valid_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
||||
EXPECT_EQ(util::OkStatus(), mock_device_status_list.GetCertStatus(
|
||||
valid_client_cert, &device_info));
|
||||
|
||||
EXPECT_EQ(
|
||||
EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
mock_device_status_list.GetCertStatus(valid_client_cert, &device_info)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, IsSystemIdActive) {
|
||||
std::multimap<uint32_t, std::string> preprov_keys;
|
||||
preprov_keys.insert(
|
||||
std::make_pair(kValidPpkSystemId, "00112233445566778899aabbccddeeff"));
|
||||
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
|
||||
device_status_list_.set_allow_unknown_devices(false);
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId));
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId));
|
||||
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId));
|
||||
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kUnknownSystemId));
|
||||
device_status_list_.set_allow_unknown_devices(true);
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId));
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId));
|
||||
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId));
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kUnknownSystemId));
|
||||
EXPECT_TRUE(
|
||||
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
90
license_server_sdk/internal/generate_error_response.cc
Normal file
90
license_server_sdk/internal/generate_error_response.cc
Normal file
@@ -0,0 +1,90 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "license_server_sdk/internal/generate_error_response.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/drm_service_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "sdk/external/common/wvpl/wvpl_types.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace util = widevine::util;
|
||||
using widevine::DRM_DEVICE_CERTIFICATE_REVOKED;
|
||||
using widevine::DrmServiceCertificate;
|
||||
using widevine::EXPIRED_CERTIFICATE_STATUS_LIST;
|
||||
using widevine::INVALID_DRM_CERTIFICATE;
|
||||
using widevine::LicenseError;
|
||||
using widevine::SERVICE_CERTIFICATE_REQUEST_MESSAGE;
|
||||
using widevine::SignedMessage;
|
||||
|
||||
namespace widevine {
|
||||
bool GenerateErrorResponse(const util::Status& create_session_status,
|
||||
std::string* license_response) {
|
||||
DCHECK(license_response);
|
||||
|
||||
LicenseError error_proto;
|
||||
if (create_session_status.error_space() == error_space) {
|
||||
switch (create_session_status.error_code()) {
|
||||
case INVALID_DRM_CERTIFICATE:
|
||||
error_proto.set_error_code(
|
||||
LicenseError::INVALID_DRM_DEVICE_CERTIFICATE);
|
||||
break;
|
||||
case DRM_DEVICE_CERTIFICATE_REVOKED:
|
||||
// TODO(user): Do we want to rename this error (INACTIVE) or
|
||||
// generate new ones?
|
||||
error_proto.set_error_code(
|
||||
LicenseError::REVOKED_DRM_DEVICE_CERTIFICATE);
|
||||
break;
|
||||
case EXPIRED_CERTIFICATE_STATUS_LIST:
|
||||
error_proto.set_error_code(LicenseError::SERVICE_UNAVAILABLE);
|
||||
break;
|
||||
case SERVICE_CERTIFICATE_REQUEST_MESSAGE: {
|
||||
SignedMessage signed_message;
|
||||
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE);
|
||||
signed_message.set_msg(
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie()
|
||||
->certificate());
|
||||
if (!signed_message.SerializeToString(license_response)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((create_session_status.error_space() ==
|
||||
util::Status::canonical_space()) &&
|
||||
(create_session_status.error_code() == util::error::UNAVAILABLE)) {
|
||||
error_proto.set_error_code(LicenseError::SERVICE_UNAVAILABLE);
|
||||
}
|
||||
if (!error_proto.has_error_code()) {
|
||||
return false;
|
||||
}
|
||||
SignedMessage signed_message;
|
||||
signed_message.set_type(SignedMessage::ERROR_RESPONSE);
|
||||
if (!error_proto.SerializeToString(signed_message.mutable_msg())) {
|
||||
return false;
|
||||
}
|
||||
if (!signed_message.SerializeToString(license_response)) {
|
||||
return false;
|
||||
}
|
||||
switch (error_proto.error_code()) {
|
||||
case LicenseError::INVALID_DRM_DEVICE_CERTIFICATE:
|
||||
case LicenseError::REVOKED_DRM_DEVICE_CERTIFICATE:
|
||||
case LicenseError::SERVICE_UNAVAILABLE:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace widevine
|
||||
27
license_server_sdk/internal/generate_error_response.h
Normal file
27
license_server_sdk/internal/generate_error_response.h
Normal file
@@ -0,0 +1,27 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef LICENSE_SERVER_SDK_INTERNAL_GENERATE_ERROR_RESPONSE_H_
|
||||
#define LICENSE_SERVER_SDK_INTERNAL_GENERATE_ERROR_RESPONSE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/status.h"
|
||||
|
||||
namespace widevine {
|
||||
// Generates a SignedMessage containing a message generated in response to
|
||||
// an error condition. |status| is a previous error status returned by the
|
||||
// Session or util::Status(util::error::UNAVAILABLE, ...) to indicate that the
|
||||
// backend is unavailable, |signed_message| points to a std::string to contain the
|
||||
// serialized SignedMessage, and may not be NULL. This method returns true if
|
||||
// there is an error license to be sent to the client, or false otherwise.
|
||||
// Example usage in the Session::Create comments above.
|
||||
bool GenerateErrorResponse(const util::Status& status,
|
||||
std::string* license_response);
|
||||
} // namespace widevine
|
||||
#endif // LICENSE_SERVER_SDK_INTERNAL_GENERATE_ERROR_RESPONSE_H_
|
||||
224
license_server_sdk/internal/key_control_block.cc
Normal file
224
license_server_sdk/internal/key_control_block.cc
Normal file
@@ -0,0 +1,224 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Handles population of the Key Control Block (KCB) as defined here:
|
||||
// https://docs.google.com/document/d/1pHSJ2IKL0axmQz2gmDZ7olxPWb_ZcULaJrYwDZAeS7k/edit#heading=h.pmnr7d8jqxea
|
||||
|
||||
#include "license_server_sdk/internal/key_control_block.h"
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/license_server_sdk.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace key_control_block {
|
||||
|
||||
void DevicesCanHandleOEMCryptoVersionInKCB(const std::string& system_ids) {
|
||||
for (absl::string_view sp : absl::StrSplit(system_ids, ',')) {
|
||||
devices_can_handle_oemcrypto_version_.push_back(std::stoi(std::string(sp)));
|
||||
}
|
||||
std::sort(devices_can_handle_oemcrypto_version_.begin(),
|
||||
devices_can_handle_oemcrypto_version_.end());
|
||||
}
|
||||
|
||||
bool Generate(
|
||||
const License::KeyContainer& key_container, uint32_t duration, uint32_t nonce,
|
||||
const ClientIdentification::ClientCapabilities& client_capabilities,
|
||||
bool has_provider_session_token,
|
||||
const SessionInit* session_init,
|
||||
LicenseType license_type,
|
||||
uint32_t system_id, std::string* key_control_block) {
|
||||
DCHECK(key_control_block);
|
||||
|
||||
bool disable_oem_crypto_api_version_reflection = false;
|
||||
std::string override_oem_crypto_api_version;
|
||||
if (session_init != nullptr) {
|
||||
disable_oem_crypto_api_version_reflection =
|
||||
session_init->disable_oem_crypto_api_version_reflection();
|
||||
override_oem_crypto_api_version =
|
||||
session_init->override_oem_crypto_api_version();
|
||||
}
|
||||
uint32_t control_bits = 0;
|
||||
// Indicates the device must be at OEMCrypto v9 or greater.
|
||||
bool min_v9_block = false;
|
||||
// Some devices (such as LSI implementation) will fail if we return kc10 in
|
||||
// the KCB. Until OEMCrypto version 13 is rolled-out, we will whitelist
|
||||
// devices that can support kc10, kc11, kc12, ...
|
||||
bool device_can_support_kcxx = false;
|
||||
auto it = std::binary_search(devices_can_handle_oemcrypto_version_.begin(),
|
||||
devices_can_handle_oemcrypto_version_.end(),
|
||||
system_id);
|
||||
// Set |device_can_support_kcxx| if either
|
||||
// (a) Device System Id is whitelisted.
|
||||
// (b) The device client capabilities has an OEM Crypto API version >=13
|
||||
// AND
|
||||
// |disable_oem_crypto_api_version_reflection| has been enabled.
|
||||
if ((it || client_capabilities.oem_crypto_api_version() >= 13) &&
|
||||
!disable_oem_crypto_api_version_reflection) {
|
||||
device_can_support_kcxx = true;
|
||||
}
|
||||
switch (key_container.type()) {
|
||||
case License::KeyContainer::CONTENT:
|
||||
case License::KeyContainer::ENTITLEMENT: {
|
||||
if (key_container.has_required_protection() &&
|
||||
key_container.required_protection().has_cgms_flags()) {
|
||||
control_bits |= kKeyControlFlagsObserveCgms; // Observe CGMS
|
||||
if (key_container.required_protection().cgms_flags() ==
|
||||
License::KeyContainer::OutputProtection::COPY_ONCE) {
|
||||
control_bits |= kKeyControlFlagsCgmsCopyOnce;
|
||||
} else if (key_container.required_protection().cgms_flags() ==
|
||||
License::KeyContainer::OutputProtection::COPY_NEVER) {
|
||||
control_bits |= kKeyControlFlagsCgmsCopyNever;
|
||||
}
|
||||
}
|
||||
if (key_container.has_required_protection() &&
|
||||
key_container.required_protection().has_hdcp() &&
|
||||
key_container.required_protection().hdcp() !=
|
||||
License::KeyContainer::OutputProtection::HDCP_NONE) {
|
||||
control_bits |= kKeyControlFlagsObserveHdcp; // Observe HDCP
|
||||
control_bits |= kKeyControlFlagsHdcpRequired;
|
||||
switch (key_container.required_protection().hdcp()) {
|
||||
case License::KeyContainer::OutputProtection::HDCP_V1:
|
||||
control_bits |= kKeyControlFlagsHdcp_V1_0;
|
||||
break;
|
||||
case License::KeyContainer::OutputProtection::HDCP_V2:
|
||||
control_bits |= kKeyControlFlagsHdcp_V2_0;
|
||||
break;
|
||||
case License::KeyContainer::OutputProtection::HDCP_V2_1:
|
||||
control_bits |= kKeyControlFlagsHdcp_V2_1;
|
||||
break;
|
||||
case License::KeyContainer::OutputProtection::HDCP_V2_2:
|
||||
control_bits |= kKeyControlFlagsHdcp_V2_2;
|
||||
break;
|
||||
case License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT:
|
||||
control_bits |= kKeyControlFlagsLocalDisplayOnly;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (key_container.has_required_protection() &&
|
||||
key_container.required_protection().has_hdcp_srm_rule() &&
|
||||
key_container.required_protection().hdcp_srm_rule() ==
|
||||
License::KeyContainer::OutputProtection::CURRENT_SRM) {
|
||||
// Observe SRM_Version_Required
|
||||
control_bits |= kKeyControlFlagsSrmVersionRequired;
|
||||
}
|
||||
if (key_container.anti_rollback_usage_table()) {
|
||||
if (client_capabilities.has_oem_crypto_api_version() &&
|
||||
client_capabilities.oem_crypto_api_version() < 10) {
|
||||
// Attempt to send a key requiring anti rollback to a client that is
|
||||
// earlier than version 10 is not allowed.
|
||||
return false;
|
||||
}
|
||||
control_bits |= kKeyControlFlagsAntiRollbackUsageTableRequired;
|
||||
}
|
||||
if (key_container.has_level()) {
|
||||
control_bits |= kKeyControlFlagsObserveDataPath; // Observe DataPath
|
||||
if (key_container.level() == License::KeyContainer::HW_SECURE_ALL) {
|
||||
control_bits |= kKeyControlFlagsDataPathSecure;
|
||||
}
|
||||
}
|
||||
if (key_container.has_required_protection() &&
|
||||
key_container.required_protection().disable_analog_output()) {
|
||||
control_bits |= kKeyControlFlagsDisableAnalogOutput;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case License::KeyContainer::OPERATOR_SESSION: {
|
||||
if (key_container.has_operator_session_key_permissions()) {
|
||||
if (key_container.operator_session_key_permissions().allow_encrypt()) {
|
||||
control_bits |= kKeyControlFlagsAllowEncrypt;
|
||||
}
|
||||
if (key_container.operator_session_key_permissions().allow_decrypt()) {
|
||||
control_bits |= kKeyControlFlagsAllowDecrypt;
|
||||
}
|
||||
if (key_container.operator_session_key_permissions().allow_sign()) {
|
||||
control_bits |= kKeyControlFlagsAllowSign;
|
||||
}
|
||||
if (key_container.operator_session_key_permissions()
|
||||
.allow_signature_verify()) {
|
||||
control_bits |= kKeyControlFlagsAllowVerify;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case License::KeyContainer::KEY_CONTROL:
|
||||
// No control bits for this type of key container used for license
|
||||
// renewals.
|
||||
break;
|
||||
default:
|
||||
// Should not generate key control blocks for any key types other than
|
||||
// the above.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (license_type == STREAMING) {
|
||||
control_bits |= kKeyControlFlagsNonceEnable;
|
||||
}
|
||||
if (client_capabilities.session_token()) {
|
||||
if (has_provider_session_token) {
|
||||
if (license_type == STREAMING) {
|
||||
control_bits |= kKeyControlFlagsReplayControl1;
|
||||
} else {
|
||||
control_bits |= kKeyControlFlagsReplayControl2;
|
||||
}
|
||||
}
|
||||
min_v9_block = true;
|
||||
}
|
||||
if (client_capabilities.has_oem_crypto_api_version() &&
|
||||
client_capabilities.oem_crypto_api_version() >= 9) {
|
||||
min_v9_block = true;
|
||||
}
|
||||
|
||||
const size_t kcb_buffer_len = 4;
|
||||
uint32_t kcb_buffer[kcb_buffer_len];
|
||||
char* kcb_char_ptr = reinterpret_cast<char*>(kcb_buffer);
|
||||
kcb_char_ptr[0] = 'k';
|
||||
kcb_char_ptr[1] = 'c';
|
||||
kcb_char_ptr[2] = 't';
|
||||
kcb_char_ptr[3] = 'l';
|
||||
if (!override_oem_crypto_api_version.empty() && device_can_support_kcxx) {
|
||||
memcpy(kcb_char_ptr, override_oem_crypto_api_version.data(),
|
||||
(override_oem_crypto_api_version.size() > kcb_buffer_len
|
||||
? kcb_buffer_len : override_oem_crypto_api_version.size()));
|
||||
} else if (min_v9_block) {
|
||||
// Limited by only 2 characters in KCB, hence cannot support 3 digits
|
||||
// like 100.
|
||||
const int maxApiVersionSupported = 99;
|
||||
if (client_capabilities.oem_crypto_api_version() <= 9 ||
|
||||
!device_can_support_kcxx) {
|
||||
kcb_char_ptr[2] = '0';
|
||||
kcb_char_ptr[3] = '9';
|
||||
} else if (client_capabilities.oem_crypto_api_version() <=
|
||||
maxApiVersionSupported) {
|
||||
kcb_char_ptr[2] = '0' + client_capabilities.oem_crypto_api_version() / 10;
|
||||
kcb_char_ptr[3] = '0' + client_capabilities.oem_crypto_api_version() % 10;
|
||||
} else {
|
||||
// TODO(user): Update KCB to handle 3 digit version.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
kcb_buffer[1] = htonl(duration);
|
||||
kcb_buffer[2] = htonl(nonce);
|
||||
kcb_buffer[3] = htonl(control_bits);
|
||||
key_control_block->assign(reinterpret_cast<const char*>(&kcb_buffer[0]),
|
||||
sizeof(kcb_buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace key_control_block
|
||||
} // namespace widevine
|
||||
71
license_server_sdk/internal/key_control_block.h
Normal file
71
license_server_sdk/internal/key_control_block.h
Normal file
@@ -0,0 +1,71 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef LICENSE_SERVER_SDK_INTERNAL_KEY_CONTROL_BLOCK_H__
|
||||
#define LICENSE_SERVER_SDK_INTERNAL_KEY_CONTROL_BLOCK_H__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
#include "protos/public/license_server_sdk.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace key_control_block {
|
||||
|
||||
// This class implements the Key Control Block (KCB) embedded in the license.
|
||||
// Documemtation on the KCB is located here:
|
||||
// 1pHSJ2IKL0axmQz2gmDZ7olxPWb_ZcULaJrYwDZAeS7k/edit#
|
||||
const uint32_t kKeyControlFlagsCgmsCopyOnce = 0x00000002;
|
||||
const uint32_t kKeyControlFlagsCgmsCopyNever = 0x00000003;
|
||||
const uint32_t kKeyControlFlagsHdcpRequired = 0x00000004;
|
||||
const uint32_t kKeyControlFlagsNonceEnable = 0x00000008;
|
||||
const uint32_t kKeyControlFlagsDataPathSecure = 0x00000010;
|
||||
const uint32_t kKeyControlFlagsAllowVerify = 0x00000020;
|
||||
const uint32_t kKeyControlFlagsAllowSign = 0x00000040;
|
||||
const uint32_t kKeyControlFlagsAllowDecrypt = 0x00000080;
|
||||
const uint32_t kKeyControlFlagsAllowEncrypt = 0x00000100;
|
||||
const uint32_t kKeyControlFlagsAntiRollbackUsageTableRequired = 0x10000000;
|
||||
const uint32_t kKeyControlFlagsObserveCgms = 0x20000000;
|
||||
const uint32_t kKeyControlFlagsObserveHdcp = 0x40000000;
|
||||
const uint32_t kKeyControlFlagsObserveDataPath = 0x80000000;
|
||||
const uint32_t kKeyControlFlagsReplayControlMask = 0x00006000;
|
||||
const uint32_t kKeyControlFlagsReplayControl1 = 0x00002000;
|
||||
const uint32_t kKeyControlFlagsReplayControl2 = 0x00004000;
|
||||
const uint32_t kKeyControlFlagsHdcpMask = 0x00001e00;
|
||||
const uint32_t kKeyControlFlagsHdcp_V1_0 = 0x00000200;
|
||||
const uint32_t kKeyControlFlagsHdcp_V2_0 = 0x00000400;
|
||||
const uint32_t kKeyControlFlagsHdcp_V2_1 = 0x00000600;
|
||||
const uint32_t kKeyControlFlagsHdcp_V2_2 = 0x00000800;
|
||||
const uint32_t kKeyControlFlagsLocalDisplayOnly = 0x00001E00;
|
||||
const uint32_t kKeyControlFlagsDisableAnalogOutput = 0x00200000;
|
||||
const uint32_t kKeyControlFlagsSrmVersionRequired = 0x00400000;
|
||||
|
||||
// Generate the key control block structuure. |key_control_block| must not be
|
||||
// null and is used to hold the key control data. Returns true if successful,
|
||||
// false otherwise.
|
||||
bool Generate(
|
||||
const License::KeyContainer& key_container, uint32_t duration, uint32_t nonce,
|
||||
const ClientIdentification::ClientCapabilities& client_capabilities,
|
||||
bool has_provider_session_token,
|
||||
const SessionInit* session_init, LicenseType license_type,
|
||||
uint32_t system_id, std::string* key_control_block);
|
||||
|
||||
// Specify a comma separated list of system Ids that can support having
|
||||
// OEMCrypto version, as specified in the license request, refected back in
|
||||
// the Key Control Block. Otherwise, only 'kctl' or 'kc09' is returned in KCB.
|
||||
void DevicesCanHandleOEMCryptoVersionInKCB(const std::string& system_ids);
|
||||
|
||||
// Contains the list of system_id values that can support reflecting OEMCrypto
|
||||
// version in KCB.
|
||||
static std::vector<uint32_t> devices_can_handle_oemcrypto_version_;
|
||||
|
||||
} // namespace key_control_block
|
||||
} // namespace widevine
|
||||
#endif // LICENSE_SERVER_SDK_INTERNAL_KEY_CONTROL_BLOCK_H__
|
||||
802
license_server_sdk/internal/key_control_block_test.cc
Normal file
802
license_server_sdk/internal/key_control_block_test.cc
Normal file
@@ -0,0 +1,802 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "license_server_sdk/internal/key_control_block.h"
|
||||
|
||||
#include "base/googleinit.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
#include "protos/public/license_server_sdk.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace key_control_block {
|
||||
|
||||
const char* kKCBAntiRollbackFalse = "6b633039000000000000000000002008";
|
||||
const char* kKCBAntiRollbackTrue = "6b633039000000000000000010002008";
|
||||
|
||||
TEST(KeyControlBlockTest, test_cmgs_bits) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
SessionInit session_init;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
key_container.mutable_required_protection()->set_cgms_flags(
|
||||
License::KeyContainer::OutputProtection::COPY_FREE);
|
||||
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000020002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_cgms_flags(
|
||||
License::KeyContainer::OutputProtection::COPY_ONCE);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63303900000000000000002000200a",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_cgms_flags(
|
||||
License::KeyContainer::OutputProtection::COPY_NEVER);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63303900000000000000002000200b",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_hdcp_bits_v0) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(false);
|
||||
SessionInit session_init;
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
key_container.mutable_required_protection()->set_hdcp(
|
||||
License::KeyContainer::OutputProtection::HDCP_NONE);
|
||||
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63746c000000000000000000000008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_hdcp(
|
||||
License::KeyContainer::OutputProtection::HDCP_V1);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63746c00000000000000004000020c",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_hdcp_bits_v9) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
|
||||
key_container.mutable_required_protection()->set_hdcp(
|
||||
License::KeyContainer::OutputProtection::HDCP_NONE);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_hdcp(
|
||||
License::KeyContainer::OutputProtection::HDCP_V1);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63303900000000000000004000220c",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_hdcp(
|
||||
License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000040003e0c",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_hdcp_versioning) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
client_capabilities.set_oem_crypto_api_version(0);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
|
||||
key_container.mutable_required_protection()->set_hdcp(
|
||||
License::KeyContainer::OutputProtection::HDCP_V2);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63303900000000000000004000240c",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_hdcp(
|
||||
License::KeyContainer::OutputProtection::HDCP_V2_1);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63303900000000000000004000260c",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_hdcp(
|
||||
License::KeyContainer::OutputProtection::HDCP_V2_2);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63303900000000000000004000280c",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_srm_version_required_bits) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
|
||||
key_container.mutable_requested_protection()->set_hdcp_srm_rule(
|
||||
License::KeyContainer::OutputProtection::CURRENT_SRM);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_hdcp_srm_rule(
|
||||
License::KeyContainer::OutputProtection::HDCP_SRM_RULE_NONE);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_hdcp_srm_rule(
|
||||
License::KeyContainer::OutputProtection::CURRENT_SRM);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000402008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_analog_output) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
|
||||
// Requested is ignored.
|
||||
key_container.mutable_requested_protection()->set_disable_analog_output(
|
||||
false);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_requested_protection()->set_disable_analog_output(true);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_disable_analog_output(false);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_required_protection()->set_disable_analog_output(true);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000202008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_data_path_bits) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
|
||||
key_container.set_level(License::KeyContainer::SW_SECURE_CRYPTO);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000080002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.set_level(License::KeyContainer::SW_SECURE_DECODE);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000080002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.set_level(License::KeyContainer::HW_SECURE_CRYPTO);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000080002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.set_level(License::KeyContainer::HW_SECURE_DECODE);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000080002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.set_level(License::KeyContainer::HW_SECURE_ALL);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000080002018",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
// Test updating the duration for all keys associated with this license.
|
||||
// This is the case for license renewals.
|
||||
TEST(KeyControlBlockTest, test_renewal) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0x12345678;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::KEY_CONTROL);
|
||||
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000001234567800002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
duration = 0x6789;
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000067891234567800002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
// Test for operator session key control blocks.
|
||||
TEST(KeyControlBlockTest, test_operator_session_key) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0x12345678;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::OPERATOR_SESSION);
|
||||
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000001234567800002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_operator_session_key_permissions()->set_allow_encrypt(
|
||||
true);
|
||||
duration = 0x87654321;
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039876543211234567800002108",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_operator_session_key_permissions()->set_allow_decrypt(
|
||||
true);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039876543211234567800002188",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_operator_session_key_permissions()->set_allow_sign(
|
||||
true);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b6330398765432112345678000021c8",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
key_container.mutable_operator_session_key_permissions()
|
||||
->set_allow_signature_verify(true);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b6330398765432112345678000021e8",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_signing_key) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0x12345678;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::SIGNING);
|
||||
|
||||
EXPECT_FALSE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_has_provider_session_token) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0x12345678;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::KEY_CONTROL);
|
||||
|
||||
EXPECT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000001234567800002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
EXPECT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, OFFLINE, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000001234567800004000",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
has_provider_session_token = false;
|
||||
EXPECT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000001234567800000008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
EXPECT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, OFFLINE, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000001234567800000000",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_client_supports_session_token) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0x12345678;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::KEY_CONTROL);
|
||||
|
||||
EXPECT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, OFFLINE, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000001234567800004000",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
client_capabilities.set_session_token(false);
|
||||
EXPECT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, OFFLINE, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63746c000000001234567800000000",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
EXPECT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63746c000000001234567800000008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
client_capabilities.set_session_token(false);
|
||||
EXPECT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b63746c000000001234567800000008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_replay_control) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
client_capabilities.set_oem_crypto_api_version(9);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, OFFLINE, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000004000",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_anti_rollback_bit) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
uint32_t system_id = 0;
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
client_capabilities.set_oem_crypto_api_version(9);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
|
||||
// anti_rollback bit in KCB is false because client does not support anti
|
||||
// rollback.
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
kKCBAntiRollbackFalse,
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
// anti_rollback bit in KCB is false because oem api version < 10.
|
||||
client_capabilities.set_anti_rollback_usage_table(true);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
kKCBAntiRollbackFalse,
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
// anti_rollback bit in KCB is false because provider did not specify it as
|
||||
// required in the key_container.
|
||||
client_capabilities.set_oem_crypto_api_version(10);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
kKCBAntiRollbackFalse,
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
// anti_rollback bit in KCB is true.
|
||||
key_container.set_anti_rollback_usage_table(true);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
kKCBAntiRollbackTrue,
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
// KCB call should fail due to invalid usage. The provider is attempting to
|
||||
// require anti rollback on clients earlier than version 10.
|
||||
client_capabilities.set_oem_crypto_api_version(9);
|
||||
ASSERT_FALSE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
}
|
||||
|
||||
TEST(KeyControlBlockTest, test_OEMCrypto_Version) {
|
||||
uint32_t duration = 0;
|
||||
uint32_t nonce = 0;
|
||||
bool has_provider_session_token = true;
|
||||
// This device (system_id) can only support kctl, kc09 in the KCB.
|
||||
const uint32_t default_system_id = 1000;
|
||||
// This device (system_id) can support kcXX in the KCB, where XX is the
|
||||
// OEMVersion.
|
||||
const uint32_t oem_version_in_kcb_system_id = 1001;
|
||||
const std::string system_id_can_handle_oem_version("2000,1001,3000");
|
||||
SessionInit session_init;
|
||||
ClientIdentification::ClientCapabilities client_capabilities;
|
||||
client_capabilities.set_session_token(true);
|
||||
License::KeyContainer key_container;
|
||||
key_container.set_type(License::KeyContainer::CONTENT);
|
||||
|
||||
key_control_block::DevicesCanHandleOEMCryptoVersionInKCB(
|
||||
system_id_can_handle_oem_version);
|
||||
|
||||
client_capabilities.set_oem_crypto_api_version(8);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, default_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
// Should be 'kc09' because the client_capabilities has session token.
|
||||
// k c 0 9
|
||||
// "6b 63 30 39 000000000000000000002008",
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
client_capabilities.set_oem_crypto_api_version(9);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, default_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
// Should be 'kc09' because the client_capabilities has session token.
|
||||
// k c 0 9
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
client_capabilities.set_oem_crypto_api_version(10);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, default_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
// Should be 'kc09' because the client_capabilities has session token.
|
||||
// k c 0 9
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
client_capabilities.set_oem_crypto_api_version(10);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING,
|
||||
oem_version_in_kcb_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
// Should return 'kc10' because this client can handle the version in KCB.
|
||||
// k c 1 0
|
||||
EXPECT_EQ(
|
||||
"6b633130000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
client_capabilities.set_oem_crypto_api_version(11);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, default_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
// k c 0 9
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
client_capabilities.set_oem_crypto_api_version(11);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING,
|
||||
oem_version_in_kcb_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
// k c 1 1
|
||||
EXPECT_EQ(
|
||||
"6b633131000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
// Cannot support version > 99, Generate() should fail.
|
||||
client_capabilities.set_oem_crypto_api_version(999);
|
||||
ASSERT_FALSE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING,
|
||||
oem_version_in_kcb_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
|
||||
// Return the latest OEM version starting from V13. Make sure V12 still
|
||||
// returns kc09.
|
||||
client_capabilities.set_oem_crypto_api_version(12);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, default_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
// k c 0 9
|
||||
EXPECT_EQ(
|
||||
"6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
// Return the latest OEM version starting from V13. Make sure V13 returns
|
||||
// kc13.
|
||||
client_capabilities.set_oem_crypto_api_version(13);
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, default_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
// k c 1 3
|
||||
EXPECT_EQ(
|
||||
"6b633133000000000000000000002008",
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()));
|
||||
|
||||
for (int i = 0 ; i < 2; i++) {
|
||||
// For v13 OEM version or later, if
|
||||
// 'disable_oem_crypto_api_version_reflection' is set to true, return kc09
|
||||
// (b/38270837).
|
||||
session_init.set_disable_oem_crypto_api_version_reflection(true);
|
||||
client_capabilities.set_oem_crypto_api_version(13);
|
||||
if (i == 1) {
|
||||
// overriding the api version should not change expectation since
|
||||
// disable_oem_crypto_api_version_reflection is true.
|
||||
session_init.set_override_oem_crypto_api_version("kc13");
|
||||
}
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, default_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
// k c 0 9
|
||||
EXPECT_EQ("6b633039000000000000000000002008",
|
||||
absl::BytesToHexString(
|
||||
key_container.key_control().key_control_block()));
|
||||
}
|
||||
|
||||
client_capabilities.set_oem_crypto_api_version(13);
|
||||
session_init.clear_override_oem_crypto_api_version();
|
||||
std::string expected_kcb;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (i == 0) {
|
||||
session_init.set_disable_oem_crypto_api_version_reflection(true);
|
||||
// Refection is diabled, expect kc09;
|
||||
expected_kcb = "6b633039000000000000000000002008"; // kc09;
|
||||
} else if (i == 1) {
|
||||
session_init.set_disable_oem_crypto_api_version_reflection(false);
|
||||
session_init.set_override_oem_crypto_api_version("kc12");
|
||||
expected_kcb = "6b633132000000000000000000002008"; // kc12
|
||||
} else if (i == 2) {
|
||||
session_init.set_override_oem_crypto_api_version("kc13");
|
||||
expected_kcb = "6b633133000000000000000000002008"; // kc13
|
||||
} else if (i == 3) {
|
||||
session_init.set_override_oem_crypto_api_version("kc14");
|
||||
expected_kcb = "6b633134000000000000000000002008"; // kc14
|
||||
} else if (i == 4) {
|
||||
// override value is larger that supported (length of 4)
|
||||
session_init.set_override_oem_crypto_api_version("123456789");
|
||||
expected_kcb = "31323334000000000000000000002008"; // 1234
|
||||
} else if (i == 5) {
|
||||
// override value is smaller than expected (length of 4)
|
||||
session_init.set_override_oem_crypto_api_version("12");
|
||||
expected_kcb = "3132746c000000000000000000002008"; // 12tl
|
||||
}
|
||||
// Return the OEM version specified in session_init.
|
||||
ASSERT_TRUE(key_control_block::Generate(
|
||||
key_container, duration, nonce, client_capabilities,
|
||||
has_provider_session_token, &session_init, STREAMING, default_system_id,
|
||||
key_container.mutable_key_control()->mutable_key_control_block()));
|
||||
EXPECT_EQ(
|
||||
expected_kcb,
|
||||
absl::BytesToHexString(key_container.key_control().key_control_block()))
|
||||
<< ">> session_init test case: " << i;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace key_control_block
|
||||
} // namespace widevine
|
||||
242
license_server_sdk/internal/parse_content_id.cc
Normal file
242
license_server_sdk/internal/parse_content_id.cc
Normal file
@@ -0,0 +1,242 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "license_server_sdk/internal/parse_content_id.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "util/endian/endian.h"
|
||||
#include "common/error_space.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/license_server_sdk.pb.h"
|
||||
#include "protos/public/widevine_pssh.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
||||
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
||||
0xd5, 0x1d, 0x21, 0xed};
|
||||
|
||||
void AddKeyIdIfNotFound(const std::string& key_id,
|
||||
ContentInfo::ContentInfoEntry* entry) {
|
||||
for (int idx = 0; idx < entry->key_ids_size(); ++idx)
|
||||
if (entry->key_ids(idx) == key_id) return;
|
||||
|
||||
entry->add_key_ids(key_id);
|
||||
}
|
||||
|
||||
util::Status AddWidevinePsshInfo(
|
||||
const std::string& pssh_data,
|
||||
ContentInfo::ContentInfoEntry* content_info_entry) {
|
||||
if (pssh_data.empty()) {
|
||||
return util::Status(error_space, INVALID_WIDEVINE_PSSH_DATA,
|
||||
"widevine-pssh-data-is-empty");
|
||||
}
|
||||
|
||||
if (!content_info_entry->mutable_pssh()
|
||||
->mutable_widevine_data()
|
||||
->ParseFromString(pssh_data)) {
|
||||
return util::Status(error_space, INVALID_WIDEVINE_PSSH_DATA,
|
||||
"invalid-widevine-pssh-data");
|
||||
}
|
||||
content_info_entry->mutable_pssh()->set_system_id(
|
||||
std::string(kWidevineSystemId, kWidevineSystemId + sizeof(kWidevineSystemId)));
|
||||
const widevine::WidevinePsshData& wv_pssh =
|
||||
content_info_entry->pssh().widevine_data();
|
||||
for (int idx = 0; idx < wv_pssh.key_ids_size(); ++idx)
|
||||
AddKeyIdIfNotFound(wv_pssh.key_ids(idx), content_info_entry);
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status ParseCencId(
|
||||
const LicenseRequest::ContentIdentification& content_id,
|
||||
ContentInfo* content_info) {
|
||||
content_info->set_init_data_type(
|
||||
LicenseRequest::ContentIdentification::InitData::CENC);
|
||||
for (int idx = 0; idx < content_id.cenc_id_deprecated().pssh_size(); ++idx) {
|
||||
util::Status status =
|
||||
AddWidevinePsshInfo(content_id.cenc_id_deprecated().pssh(idx),
|
||||
content_info->add_content_info_entry());
|
||||
if (!status.ok()) return status;
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status AddWebmKeyId(const std::string& key_id, ContentInfo* content_info) {
|
||||
content_info->set_init_data_type(
|
||||
LicenseRequest::ContentIdentification::InitData::WEBM);
|
||||
content_info->add_content_info_entry()->add_key_ids(key_id);
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status ParseIsoBmffBoxes(const std::string& boxes, ContentInfo* content_info) {
|
||||
const uint32_t kPsshType = 0x70737368;
|
||||
const size_t kPsshSystemIdSize = 16;
|
||||
const size_t kKeyIdSize = 16;
|
||||
const size_t kMinPsshSize = kPsshSystemIdSize + 8;
|
||||
|
||||
if (boxes.empty()) {
|
||||
return util::Status(error_space, INVALID_CENC_INIT_DATA,
|
||||
"init-data-is-empty");
|
||||
}
|
||||
|
||||
const char* r_ptr = boxes.data();
|
||||
const char* end_ptr = r_ptr + boxes.size();
|
||||
|
||||
while (r_ptr < end_ptr) {
|
||||
ContentInfo::ContentInfoEntry content_info_entry;
|
||||
|
||||
if (r_ptr + 8 > end_ptr)
|
||||
return util::Status(error_space, INVALID_CENC_INIT_DATA,
|
||||
"init-data-too-short");
|
||||
|
||||
const char* box_start = r_ptr;
|
||||
uint64_t box_size = BigEndian::Load32(r_ptr);
|
||||
r_ptr += 4;
|
||||
uint32_t box_type = BigEndian::Load32(r_ptr);
|
||||
r_ptr += 4;
|
||||
|
||||
if (box_size == 1) {
|
||||
if (r_ptr + 8 > end_ptr) {
|
||||
return util::Status(error_space, INVALID_CENC_INIT_DATA,
|
||||
"init-data-too-short");
|
||||
}
|
||||
box_size = BigEndian::Load64(r_ptr);
|
||||
r_ptr += 8;
|
||||
}
|
||||
|
||||
const char* box_end = box_start + box_size;
|
||||
if (box_end > end_ptr) {
|
||||
return util::Status(error_space, INVALID_CENC_INIT_DATA,
|
||||
"init-data-too-short");
|
||||
}
|
||||
if (box_end < r_ptr) {
|
||||
return util::Status(error_space, INVALID_CENC_INIT_DATA,
|
||||
"invalid-box-size");
|
||||
}
|
||||
|
||||
if (box_type != kPsshType) {
|
||||
r_ptr = box_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r_ptr + kMinPsshSize > box_end)
|
||||
return util::Status(error_space, INVALID_PSSH, "pssh-contents-too-short");
|
||||
|
||||
const uint32_t version_and_flags = BigEndian::Load32(r_ptr);
|
||||
r_ptr += 4;
|
||||
|
||||
const uint8_t version = static_cast<uint8_t>(version_and_flags >> 24);
|
||||
if (version > 1) {
|
||||
return util::Status(error_space, UNSUPPORTED_PSSH_VERSION,
|
||||
absl::StrCat("unsupported-pssh-version ", version));
|
||||
}
|
||||
|
||||
content_info_entry.mutable_pssh()->set_system_id(
|
||||
std::string(r_ptr, r_ptr + kPsshSystemIdSize));
|
||||
bool is_widevine_pssh =
|
||||
!memcmp(r_ptr, kWidevineSystemId, kPsshSystemIdSize);
|
||||
r_ptr += kPsshSystemIdSize;
|
||||
|
||||
if (version == 1) {
|
||||
if (r_ptr + 4 > box_end) {
|
||||
return util::Status(error_space, INVALID_PSSH,
|
||||
"pssh-contents-too-short");
|
||||
}
|
||||
|
||||
const uint32_t num_key_ids = BigEndian::Load32(r_ptr);
|
||||
r_ptr += 4;
|
||||
|
||||
if (r_ptr + (num_key_ids * kKeyIdSize) > box_end) {
|
||||
return util::Status(error_space, INVALID_PSSH,
|
||||
"pssh-contents-too-short");
|
||||
}
|
||||
|
||||
for (uint32_t idx = 0; idx < num_key_ids; ++idx) {
|
||||
AddKeyIdIfNotFound(std::string(r_ptr, r_ptr + kKeyIdSize),
|
||||
&content_info_entry);
|
||||
r_ptr += kKeyIdSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (r_ptr + 4 > box_end)
|
||||
return util::Status(error_space, INVALID_PSSH, "pssh-contents-too-short");
|
||||
|
||||
uint32_t data_size = BigEndian::Load32(r_ptr);
|
||||
r_ptr += 4;
|
||||
if (r_ptr + data_size > box_end)
|
||||
return util::Status(error_space, INVALID_PSSH, "pssh-contents-too-short");
|
||||
if (r_ptr + data_size < box_end)
|
||||
return util::Status(error_space, INVALID_PSSH, "pssh-contents-too-long");
|
||||
|
||||
if (is_widevine_pssh) {
|
||||
util::Status status = AddWidevinePsshInfo(
|
||||
std::string(r_ptr, r_ptr + data_size), &content_info_entry);
|
||||
if (!status.ok()) return status;
|
||||
} else {
|
||||
content_info_entry.mutable_pssh()->set_raw_data(
|
||||
std::string(r_ptr, r_ptr + data_size));
|
||||
}
|
||||
r_ptr += data_size;
|
||||
|
||||
*content_info->add_content_info_entry() = content_info_entry;
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status ParseInitData(
|
||||
const LicenseRequest::ContentIdentification& content_id,
|
||||
ContentInfo* content_info) {
|
||||
if (!content_id.init_data().has_init_data())
|
||||
return util::Status(error_space, MISSING_INIT_DATA, "missing-init-data");
|
||||
|
||||
if (content_id.init_data().init_data_type() ==
|
||||
LicenseRequest::ContentIdentification::InitData::CENC) {
|
||||
return ParseIsoBmffBoxes(content_id.init_data().init_data(), content_info);
|
||||
}
|
||||
if (content_id.init_data().init_data_type() ==
|
||||
LicenseRequest::ContentIdentification::InitData::WEBM) {
|
||||
return AddWebmKeyId(content_id.init_data().init_data(), content_info);
|
||||
}
|
||||
return util::Status(error_space, UNKNOWN_INIT_DATA_TYPE,
|
||||
"unknown-init-data-type");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
util::Status ParseContentId(
|
||||
const LicenseRequest::ContentIdentification& content_id,
|
||||
ContentInfo* content_info) {
|
||||
DCHECK(content_info);
|
||||
|
||||
content_info->Clear();
|
||||
switch (content_id.content_id_variant_case()) {
|
||||
case LicenseRequest_ContentIdentification::kCencIdDeprecated:
|
||||
return ParseCencId(content_id, content_info);
|
||||
case LicenseRequest_ContentIdentification::kWebmIdDeprecated:
|
||||
return AddWebmKeyId(content_id.webm_id_deprecated().header(),
|
||||
content_info);
|
||||
case LicenseRequest_ContentIdentification::kInitData:
|
||||
return ParseInitData(content_id, content_info);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return util::Status(error_space, INVALID_CONTENT_ID_TYPE,
|
||||
"invalid-content-id-type");
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
29
license_server_sdk/internal/parse_content_id.h
Normal file
29
license_server_sdk/internal/parse_content_id.h
Normal file
@@ -0,0 +1,29 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef LICENSE_SERVER_SDK_INTERNAL_PARSE_CONTENT_ID_H__
|
||||
#define LICENSE_SERVER_SDK_INTERNAL_PARSE_CONTENT_ID_H__
|
||||
|
||||
#include "util/status.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class ContentInfo;
|
||||
|
||||
// Parse the ContentIdentification message passed into |content_id| and populate
|
||||
// the ContentInfo message passed into |content_info|. This function deep parses
|
||||
// PSSH boxes and the Widevine PSSH Data. |content_info| may not be NULL and the
|
||||
// caller retains ownership.
|
||||
util::Status ParseContentId(
|
||||
const LicenseRequest::ContentIdentification& content_id,
|
||||
ContentInfo* content_info);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // LICENSE_SERVER_SDK_INTERNAL_PARSE_CONTENT_ID_H__
|
||||
302
license_server_sdk/internal/parse_content_id_test.cc
Normal file
302
license_server_sdk/internal/parse_content_id_test.cc
Normal file
@@ -0,0 +1,302 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "license_server_sdk/internal/parse_content_id.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "testing/gunit.h"
|
||||
#include "util/endian/endian.h"
|
||||
#include "common/error_space.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/license_server_sdk.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kExpectedKey1[] = "0123456789abcdef";
|
||||
|
||||
const uint8_t kWvSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce,
|
||||
0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed};
|
||||
|
||||
const uint8_t kWvPsshV0[] = {
|
||||
0x00, 0x00, 0x00, 0x34, // Size of this box = 52.
|
||||
0x70, 0x73, 0x73, 0x68, // Type 'pssh'.
|
||||
0x00, 0x00, 0x00, 0x00, // Version and flags should be 0.
|
||||
0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8,
|
||||
0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, // Widevine System ID.
|
||||
0x00, 0x00, 0x00, 0x14, // Size of data = 20.
|
||||
0x08, 0x01, 0x12, 0x10, 0x72, 0x65, 0x5d, 0x53, 0xc9, 0x09,
|
||||
0x57, 0xbf, 0xb0, 0x7b, 0xef, 0xd3, 0xdd, 0x54, 0xa2, 0xbd // Data.
|
||||
};
|
||||
const size_t kWvPsshV0DataOffset = 32;
|
||||
const uint8_t kWvPsshV0KeyId[] = {0x72, 0x65, 0x5d, 0x53, 0xc9, 0x09, 0x57, 0xbf,
|
||||
0xb0, 0x7b, 0xef, 0xd3, 0xdd, 0x54, 0xa2, 0xbd};
|
||||
|
||||
const uint8_t kInvalidWvPsshV0[] = {
|
||||
0x00, 0x00, 0x00, 0x24, // Size of this box = 36.
|
||||
0x70, 0x73, 0x73, 0x68, // Type 'pssh'.
|
||||
0x00, 0x00, 0x00, 0x00, // Version and flags should be 0.
|
||||
0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce,
|
||||
0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, // Widevine System ID.
|
||||
0x00, 0x00, 0x00, 0x04, // Size of data = 4.
|
||||
0x08, 0x01, 0x12, 0x10};
|
||||
|
||||
const uint8_t kWvPsshV1[] = {
|
||||
0x00, 0x00, 0x00, 0x58, // Size of this box = 88.
|
||||
0x70, 0x73, 0x73, 0x68, // Type 0x70737368 == 'pssh'.
|
||||
0x01, 0x00, 0x00, 0x00, // PSSH Version 1.
|
||||
0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, //
|
||||
0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, // Widevine System ID.
|
||||
0x00, 0x00, 0x00, 0x02, // 2 Key IDs.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // KeyID 1.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // KeyID 2.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, //
|
||||
0x00, 0x00, 0x00, 0x14, // Size of data = 20.
|
||||
0x08, 0x01, 0x12, 0x10, 0x72, 0x65, 0x5d, 0x53, 0xc9, 0x09, 0x57, // Data.
|
||||
0xbf, 0xb0, 0x7b, 0xef, 0xd3, 0xdd, 0x54, 0xa2, 0xbd};
|
||||
const size_t kWvPsshV1BoxSizeOffset = 0;
|
||||
const size_t kWvPsshV1BoxVersionOffset = 8;
|
||||
const size_t kWvPsshV1NumKeyIdsOffset = 28;
|
||||
const size_t kWvPsshV1DataSizeOffset = 64;
|
||||
const size_t kWvPsshV1DataOffset = 68;
|
||||
|
||||
const uint8_t kNonWvPsshV1[] = {
|
||||
0x00, 0x00, 0x00, 0x48, // Size of this box = 72.
|
||||
0x70, 0x73, 0x73, 0x68, // Type 0x70737368 == 'pssh'.
|
||||
0x01, 0x00, 0x00, 0x00, // PSSH Version 1.
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // System ID.
|
||||
0x00, 0x00, 0x00, 0x02, // 2 Key IDs.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // KeyID 1.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // KeyID 2.
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||
0x00, 0x00, 0x00, 0x04, // Size of data = 4.
|
||||
0x00, 0x01, 0x1a, 0x10 // Data.
|
||||
};
|
||||
|
||||
const uint8_t kNonPsshBox[] = {
|
||||
0x00, 0x00, 0x00, 0x08, // Size of this box = 8.
|
||||
0x70, 0x73, 0x73, 0x74, // Type 0x70737368 == 'psst'.
|
||||
};
|
||||
|
||||
void MakeDeprecatedWebmContentId(
|
||||
const std::string& key_id, LicenseRequest::ContentIdentification* content_id) {
|
||||
content_id->Clear();
|
||||
content_id->mutable_webm_id_deprecated()->set_header(key_id);
|
||||
}
|
||||
|
||||
void MakeInitDataWebmContentId(
|
||||
const std::string& key_id, LicenseRequest::ContentIdentification* content_id) {
|
||||
content_id->Clear();
|
||||
content_id->mutable_init_data()->set_init_data_type(
|
||||
LicenseRequest::ContentIdentification::InitData::WEBM);
|
||||
content_id->mutable_init_data()->set_init_data(key_id);
|
||||
}
|
||||
|
||||
void VerifyWebmContentId(
|
||||
const LicenseRequest::ContentIdentification& content_id) {
|
||||
ContentInfo content_info;
|
||||
ASSERT_EQ(util::OkStatus(), ParseContentId(content_id, &content_info));
|
||||
ASSERT_EQ(LicenseRequest::ContentIdentification::InitData::WEBM,
|
||||
content_info.init_data_type());
|
||||
ASSERT_EQ(1, content_info.content_info_entry_size());
|
||||
ASSERT_EQ(1, content_info.content_info_entry(0).key_ids_size());
|
||||
ASSERT_EQ(kExpectedKey1, content_info.content_info_entry(0).key_ids(0));
|
||||
ASSERT_FALSE(content_info.content_info_entry(0).has_pssh());
|
||||
}
|
||||
|
||||
void MakeDeprecatedCencContentId(
|
||||
const std::string& pssh_data,
|
||||
LicenseRequest::ContentIdentification* content_id) {
|
||||
content_id->Clear();
|
||||
*content_id->mutable_cenc_id_deprecated()->add_pssh() = pssh_data;
|
||||
}
|
||||
|
||||
void MakeInitDataCencContentId(
|
||||
const std::string& pssh, LicenseRequest::ContentIdentification* content_id) {
|
||||
content_id->Clear();
|
||||
content_id->mutable_init_data()->set_init_data_type(
|
||||
LicenseRequest::ContentIdentification::InitData::CENC);
|
||||
content_id->mutable_init_data()->set_init_data(pssh);
|
||||
}
|
||||
|
||||
void MakeExistingLicenseContentId(
|
||||
LicenseRequest::ContentIdentification* content_id) {
|
||||
content_id->Clear();
|
||||
content_id->mutable_existing_license();
|
||||
}
|
||||
|
||||
void VerifyCencContentId(
|
||||
const LicenseRequest::ContentIdentification& content_id) {
|
||||
ContentInfo content_info;
|
||||
ASSERT_EQ(util::OkStatus(), ParseContentId(content_id, &content_info));
|
||||
ASSERT_EQ(LicenseRequest::ContentIdentification::InitData::CENC,
|
||||
content_info.init_data_type());
|
||||
ASSERT_EQ(1, content_info.content_info_entry_size());
|
||||
ASSERT_EQ(1, content_info.content_info_entry(0).key_ids_size());
|
||||
ASSERT_TRUE(content_info.content_info_entry(0).has_pssh());
|
||||
ASSERT_EQ(std::string(kWvSystemId, kWvSystemId + sizeof(kWvSystemId)),
|
||||
content_info.content_info_entry(0).pssh().system_id());
|
||||
ASSERT_TRUE(content_info.content_info_entry(0).pssh().has_widevine_data());
|
||||
ASSERT_FALSE(content_info.content_info_entry(0).pssh().has_raw_data());
|
||||
}
|
||||
|
||||
void MakeModifiedV1WvCencContentId(
|
||||
size_t modification_offset, uint32_t modification_value,
|
||||
LicenseRequest::ContentIdentification* content_id) {
|
||||
std::string box_data(kWvPsshV1, kWvPsshV1 + sizeof(kWvPsshV1));
|
||||
BigEndian::Store32(&box_data[modification_offset], modification_value);
|
||||
MakeInitDataCencContentId(box_data, content_id);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ParseContentIdTest, DeprecatedWebmContentId) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeDeprecatedWebmContentId(kExpectedKey1, &content_id);
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyWebmContentId(content_id));
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, InitDataWebmContentId) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeInitDataWebmContentId(kExpectedKey1, &content_id);
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyWebmContentId(content_id));
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, DeprecatedCencContentId) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeDeprecatedCencContentId(
|
||||
std::string(kWvPsshV0 + kWvPsshV0DataOffset, kWvPsshV0 + sizeof(kWvPsshV0)),
|
||||
&content_id);
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyCencContentId(content_id));
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, InitDataCencContentId) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeInitDataCencContentId(std::string(kWvPsshV0, kWvPsshV0 + sizeof(kWvPsshV0)),
|
||||
&content_id);
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyCencContentId(content_id));
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, PsshV1) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeInitDataCencContentId(std::string(kWvPsshV1, kWvPsshV1 + sizeof(kWvPsshV1)),
|
||||
&content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ(util::OkStatus(), ParseContentId(content_id, &content_info));
|
||||
EXPECT_EQ(LicenseRequest::ContentIdentification::InitData::CENC,
|
||||
content_info.init_data_type());
|
||||
ASSERT_EQ(1, content_info.content_info_entry_size());
|
||||
ASSERT_EQ(3, content_info.content_info_entry(0).key_ids_size());
|
||||
EXPECT_TRUE(content_info.content_info_entry(0).has_pssh());
|
||||
EXPECT_EQ(std::string(kWvSystemId, kWvSystemId + sizeof(kWvSystemId)),
|
||||
content_info.content_info_entry(0).pssh().system_id());
|
||||
EXPECT_TRUE(content_info.content_info_entry(0).pssh().has_widevine_data());
|
||||
EXPECT_FALSE(content_info.content_info_entry(0).pssh().has_raw_data());
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, ExistingLicense) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
ContentInfo content_info;
|
||||
MakeExistingLicenseContentId(&content_id);
|
||||
EXPECT_EQ(util::Status(error_space, INVALID_CONTENT_ID_TYPE,
|
||||
"invalid-content-id-type"),
|
||||
ParseContentId(content_id, &content_info));
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, MultipleBoxes) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeInitDataCencContentId(
|
||||
std::string(kNonPsshBox, kNonPsshBox + sizeof(kNonPsshBox)) +
|
||||
std::string(kWvPsshV0, kWvPsshV0 + sizeof(kWvPsshV0)) +
|
||||
std::string(kNonPsshBox, kNonPsshBox + sizeof(kNonPsshBox)) +
|
||||
std::string(kWvPsshV1, kWvPsshV1 + sizeof(kWvPsshV1)) +
|
||||
std::string(kNonPsshBox, kNonPsshBox + sizeof(kNonPsshBox)) +
|
||||
std::string(kNonWvPsshV1, kNonWvPsshV1 + sizeof(kNonWvPsshV1)) +
|
||||
std::string(kNonPsshBox, kNonPsshBox + sizeof(kNonPsshBox)),
|
||||
&content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ(util::OkStatus(), ParseContentId(content_id, &content_info));
|
||||
EXPECT_EQ(LicenseRequest::ContentIdentification::InitData::CENC,
|
||||
content_info.init_data_type());
|
||||
EXPECT_EQ(3, content_info.content_info_entry_size());
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, BoxSizeTooLarge) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeModifiedV1WvCencContentId(kWvPsshV1BoxSizeOffset, 89, &content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ("Errors::INVALID_CENC_INIT_DATA: init-data-too-short",
|
||||
ParseContentId(content_id, &content_info).ToString());
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, BoxSizeTooSmall) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeModifiedV1WvCencContentId(kWvPsshV1BoxSizeOffset, 87, &content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-short",
|
||||
ParseContentId(content_id, &content_info).ToString());
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, UnsupportedPsshVersion) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeModifiedV1WvCencContentId(kWvPsshV1BoxVersionOffset, 0x02000000,
|
||||
&content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ("Errors::UNSUPPORTED_PSSH_VERSION: unsupported-pssh-version 2",
|
||||
ParseContentId(content_id, &content_info).ToString());
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, TooManyKeyIds) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeModifiedV1WvCencContentId(kWvPsshV1NumKeyIdsOffset, 3, &content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-short",
|
||||
ParseContentId(content_id, &content_info).ToString());
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, TooFewKeyIds) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeModifiedV1WvCencContentId(kWvPsshV1NumKeyIdsOffset, 1, &content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-long",
|
||||
ParseContentId(content_id, &content_info).ToString());
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, DataSizeTooSmall) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeModifiedV1WvCencContentId(kWvPsshV1DataSizeOffset, 19, &content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-long",
|
||||
ParseContentId(content_id, &content_info).ToString());
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, DataSizeTooLarge) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeModifiedV1WvCencContentId(kWvPsshV1DataSizeOffset, 21, &content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ("Errors::INVALID_PSSH: pssh-contents-too-short",
|
||||
ParseContentId(content_id, &content_info).ToString());
|
||||
}
|
||||
|
||||
TEST(ParseContentIdTest, InvalidWidevinePsshData) {
|
||||
LicenseRequest::ContentIdentification content_id;
|
||||
MakeInitDataCencContentId(
|
||||
std::string(kInvalidWvPsshV0, kInvalidWvPsshV0 + sizeof(kInvalidWvPsshV0)),
|
||||
&content_id);
|
||||
ContentInfo content_info;
|
||||
EXPECT_EQ("Errors::INVALID_WIDEVINE_PSSH_DATA: invalid-widevine-pssh-data",
|
||||
ParseContentId(content_id, &content_info).ToString());
|
||||
}
|
||||
} // namespace widevine
|
||||
1151
license_server_sdk/internal/session_impl.cc
Normal file
1151
license_server_sdk/internal/session_impl.cc
Normal file
File diff suppressed because it is too large
Load Diff
377
license_server_sdk/internal/session_impl.h
Normal file
377
license_server_sdk/internal/session_impl.h
Normal file
@@ -0,0 +1,377 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef LICENSE_SERVER_SDK_INTERNAL_SESSION_IMPL_H_
|
||||
#define LICENSE_SERVER_SDK_INTERNAL_SESSION_IMPL_H_
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/macros.h"
|
||||
#include "util/status.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
#include "protos/public/license_server_sdk.pb.h"
|
||||
#include "protos/public/provisioned_device_info.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class ClientCert;
|
||||
class ClientIdentification;
|
||||
class ContentInfo;
|
||||
class SessionInit;
|
||||
class SessionState;
|
||||
class SessionTest;
|
||||
class SessionUsage;
|
||||
|
||||
const uint32_t kMajorVersion = 4;
|
||||
const uint32_t kMinorVersion = 5;
|
||||
const uint32_t kRelease = 1;
|
||||
|
||||
const uint32_t kMasterSigningKeySizeBytes = 16;
|
||||
const uint32_t kSigningKeySizeBytes = 64;
|
||||
const uint32_t kProviderSessionTokenSizeBytes = 255;
|
||||
|
||||
// Helper function to determine which provider client token to use.
|
||||
std::string GetProviderClientToken(const SessionInit& session_init,
|
||||
const ClientIdentification& client_id);
|
||||
|
||||
// A Session represents a lifetime for the request/response between the DRM
|
||||
// client and the DRM server. During this lifetime, referred to as a DRM
|
||||
// session, some security secrets are established. These secrets are used
|
||||
// to wrap a license from the DRM server. A DRM session is used for each
|
||||
// license request. Example usage:
|
||||
// Session::SetPreProvisioningKeys(<hex formatted std::string>);
|
||||
// Session::SetCertificateStatusList(
|
||||
// widevine::sdk::kCertificateTypeProduction,
|
||||
// cert_status_list,
|
||||
// expiration_period_seconds,
|
||||
// /* allow unknown device */ false);
|
||||
// Session::AddDrmServiceCertificate(
|
||||
// widevine::sdk::kCertificateTypeProduction,
|
||||
// service_certificate,
|
||||
// service_private_key,
|
||||
// service_private_key_passphrase);
|
||||
// std::string signed_license_request;
|
||||
// // assign signed_license_request to incoming license request.
|
||||
// Session* session = NULL;
|
||||
// util::Status status = Session::Create(signed_license_request, &session);
|
||||
// if (!status.ok()) {
|
||||
// std::string error_license;
|
||||
// if (Session::GenerateErrorResponse(status, &error_license)) {
|
||||
// // Send error_license to the client.
|
||||
// } else {
|
||||
// // Handle error
|
||||
// }
|
||||
// return ...
|
||||
// }
|
||||
// License::Policy policies;
|
||||
// License::KeyContainer key_container;
|
||||
// // ... evaluate request().content_id and request().type to determine
|
||||
// // ... what License::Policy and License::KeyContainer objects
|
||||
// // ... are needed to fulfill this client request.
|
||||
// SessionInit init; // Required if |policies| contains |can_renew| true.
|
||||
// SessionState cache; // Required if caching state.
|
||||
// std::string signed_license;
|
||||
// Status status = session->GenerateSignedLicense(&policies,
|
||||
// &key_container,
|
||||
// &init,
|
||||
// &cache,
|
||||
// &signed_license);
|
||||
// // For renewal, either SessionState may be cached and provided to the call
|
||||
// // to GenerateSignedLicense. Or if it is not possible to cache the
|
||||
// // SessionState, then SessionInit must be provided with either
|
||||
// // |signing_key| or |master_signing_key|, in order to allow the sdk to
|
||||
// // validate the signature of the renewal request, and to sign the
|
||||
// // license issued.
|
||||
|
||||
class SessionImpl {
|
||||
public:
|
||||
// Set pre-provisioning keys system-wide. Map key is system_id, value.
|
||||
// Value should be human-readable hex digits suitable for passing to
|
||||
// absl::HexStringToBytes().
|
||||
// Must be called before any other calls to this class. Calls are
|
||||
// thread-safe, so the keys can be updated at any time.
|
||||
static void SetPreProvisioningKeys(const std::map<uint32_t, std::string>& keys);
|
||||
static void SetPreProvisioningKeys(const std::multimap<uint32_t, std::string>& keys);
|
||||
|
||||
// Set the certificate status list system-wide. |cert_type| specifies
|
||||
// whether to use development or production root certificates.
|
||||
// |expiration_period| is the number of seconds until the
|
||||
// certificate_status_list expires after its creation time
|
||||
// (creation_time_seconds). If |allow_unknown_devices| is false, an error is
|
||||
// returned if the device does not appear in the certificate_status_list.
|
||||
static util::Status SetCertificateStatusList(
|
||||
CertificateType cert_type, const std::string& certificate_status_list,
|
||||
uint32_t expiration_period_seconds, bool allow_unknown_devices);
|
||||
|
||||
// Add a service certificate system-wide. |cert_type| indicates the type of
|
||||
// root certificate used to sign the service certificate;
|
||||
// |service_certificate| is a Google-generated certificate used to
|
||||
// authenticate the service provider for purposes of device privacy;
|
||||
// |service_private_key| is the encrypted PKCS#8 private RSA key corresponding
|
||||
// to the service certificate; and |service_private_key_passphrase| is the
|
||||
// password required to decrypt |service_private_key|.
|
||||
static util::Status AddDrmServiceCertificate(
|
||||
CertificateType cert_type, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
|
||||
// Enable delivery of licenses to client devices. This includes devices with
|
||||
// TEST_ONLY status, and development platform verification certificates.
|
||||
// Defaults to false.
|
||||
static void AllowDevelopmentClients(bool enable);
|
||||
|
||||
// Enable delivery of licenses to revoked client devices. |system_id_list| is
|
||||
// a comma separated list of systems Ids to allow even if the device is in the
|
||||
// revoked state.
|
||||
static void AllowRevokedDevices(const std::string& system_id_list);
|
||||
|
||||
// Creates a Session object.
|
||||
// |signed_license_request| is the serialized SignedMessage received from the
|
||||
// client. |session| points to a Session*, which must be initialized to NULL
|
||||
// on entry, but |session| itself may not be NULL. The new Session object will
|
||||
// be owned by the caller. This method returns util::Status::OK if successful,
|
||||
// or an appropriate error status, in which case
|
||||
// Session::GenerateErrorResponse should be invoked.
|
||||
// Example usage:
|
||||
// Session* session = NULL;
|
||||
// util::Status status = Session::Create(request_from_client, &session);
|
||||
// if (!status.ok()) {
|
||||
// std::string error_license;
|
||||
// if (Session::GenerateErrorResponse(status, &error_license)) {
|
||||
// // Send error_license to the client.
|
||||
// } else {
|
||||
// // Handle error
|
||||
// }
|
||||
// return ...
|
||||
// }
|
||||
// // Create license, invoke GenerateSignedLicense, etc.
|
||||
static util::Status Create(const std::string& signed_license_request,
|
||||
SessionImpl** session);
|
||||
|
||||
// Variation of Session::Create which also fills in the parsed LicenseRequest,
|
||||
// for use in logging or debugging.
|
||||
static util::Status Create(const std::string& signed_license_request,
|
||||
SessionImpl** session,
|
||||
LicenseRequest* parsed_request_out);
|
||||
|
||||
// Same as Create(), but caller can specify the ClientIdentification
|
||||
// message and/or PlatformVerificationStatus. If ClientIdentification is
|
||||
// specified, this variation of Create() will use the specified |client_id|
|
||||
// instead of what is specified in |signed_license_request|. If
|
||||
// PlatformVerificationStatus is specified, this method will use the specified
|
||||
// |platform_verification_status| instead of attempting to determine it by
|
||||
// calling SessionImpl::VerifyPlatform(...).
|
||||
// Background for this function is to support cases where the client
|
||||
// identification is encrypted with the provider's service certificate in
|
||||
// which case we won't be able to decrypt OR when the provider determines
|
||||
// platform verification. The provider will specify the
|
||||
// clear client identification in |client_id| and the platform verification
|
||||
// in |platform_verification_status|.
|
||||
static util::Status CreateForProxy(
|
||||
const std::string& signed_license_request,
|
||||
const PlatformVerificationStatus platform_verification_status,
|
||||
const ClientIdentification* client_id, SessionImpl** session,
|
||||
LicenseRequest* parsed_request_out);
|
||||
|
||||
// Generates a SignedMessage containing a message generated in response to
|
||||
// an error condition. |status| is a previous error status returned by the
|
||||
// Session or util::Status(util::error::UNAVAILABLE, ...) to indicate that the
|
||||
// backend is unavailable, |signed_message| points to a std::string to contain the
|
||||
// serialized SignedMessage, and may not be NULL. This method returns true if
|
||||
// there is an error license to be sent to the client, or false otherwise.
|
||||
// Example usage in the Session::Create comments above.
|
||||
static bool GenerateErrorResponse(const util::Status& status,
|
||||
std::string* signed_message_bytes);
|
||||
|
||||
// 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
|
||||
static std::string DeriveKey(const std::string& key, const std::string& label,
|
||||
const std::string& context, const uint32_t size_bits);
|
||||
|
||||
// Returns a std::string containing the hex-encoded SHA-256 digest of the root
|
||||
// certificate specified by |cert_type|. Used for purposes of root certificate
|
||||
// verification.
|
||||
static std::string GetRootCertificateDigest(CertificateType cert_type);
|
||||
|
||||
// Returns a std::string containing the Widevine License Server SDK version in the
|
||||
// form <major_version>.<minor_version>.<release> <build date> <build time> .
|
||||
static std::string GetSdkVersionString();
|
||||
|
||||
// Returns true if service certificate is loaded.
|
||||
static bool is_service_certificate_loaded() {
|
||||
return is_service_certificate_loaded_;
|
||||
}
|
||||
|
||||
static const char* kEncryptionKeyLabel; // NOLINT
|
||||
static const uint32_t kEncryptionKeySizeBits;
|
||||
static const char* kSigningKeyLabel; // NOLINT
|
||||
static const uint32_t kSigningKeySizeBits;
|
||||
|
||||
virtual ~SessionImpl();
|
||||
virtual const LicenseRequest& request() const { return *license_request_; }
|
||||
virtual const std::string& GetSessionId();
|
||||
|
||||
// Returns true if a provisioned device info exists. Caller
|
||||
// owns |provisioned_device_info| and it must not be null.
|
||||
virtual bool GetProvisionedDeviceInfo(
|
||||
widevine::ProvisionedDeviceInfo* device_info);
|
||||
|
||||
// Returns true if client info exists, otherwise returns false. Populate the
|
||||
// specified |client_info| structure.
|
||||
virtual bool GetClientInfo(ClientIdentification* client_info) const;
|
||||
|
||||
// Accessor for request_id field which may be encoded in one of multiple
|
||||
// places in the liciense request protcol buffer. Use this method instead
|
||||
// of accessing directly. |request_id| is a pointer to a std::string to contain
|
||||
// the request ID upon successful return.
|
||||
virtual util::Status GetRequestId(std::string* request_id) const;
|
||||
|
||||
// Accessor for license_type field which may be encoded in one of multiple
|
||||
// places in the license request protcol buffer. Use this method instead
|
||||
// of accessing directly. |license_type| is a pointer to a value to contain
|
||||
// the license type upon successful return.
|
||||
virtual util::Status GetLicenseType(LicenseType* license_type) const;
|
||||
|
||||
// Method used to get ContentIdentification in a consistent message regardless
|
||||
// of the type or version of initialization data contained in the content_id
|
||||
// field of the license request. Use this method instead of accessing the
|
||||
// fields of ContentIdentification directly. |content_info| is a pointer to a
|
||||
// message to contain the parsed values from content_id upon successful
|
||||
// return.
|
||||
virtual util::Status GetContentInfo(ContentInfo* content_info) const;
|
||||
|
||||
// Returns the serial number of certificate associated with this device and
|
||||
// content provider.
|
||||
virtual std::string GetDrmDeviceId() const;
|
||||
|
||||
// Copies the session usage table from license request to |usage_report|.
|
||||
// Returns true if session usage exist in the license request, otherwise
|
||||
// returns false.
|
||||
bool GetSessionUsage(SessionUsage* usage_report) const;
|
||||
|
||||
// Generates a serialized signed License response, emptying |policy| and
|
||||
// |key_container|, encrypting the keys therein. |session_init| and
|
||||
// |session_state| are returned to be cached and provided in subsequent
|
||||
// calls to the function. If no additional PolicyItem or KeyContainer objects
|
||||
// are necessary to fulfill the request (such as the case with license
|
||||
// renewal), |policy| and/or |key_container| may be NULL.
|
||||
// The response is expected to be sent to the Widevine CDM.
|
||||
virtual util::Status GenerateSignedLicense(
|
||||
/*IN*/ const License::Policy* policy,
|
||||
/*IN*/ const std::list<License::KeyContainer>* key_container,
|
||||
/*IN*/ const SessionInit* session_init,
|
||||
/*INOUT*/ SessionState* session_state,
|
||||
/*OUT*/ std::string* signed_message_bytes);
|
||||
|
||||
// Verify the required |output_protection| can be satisified based on the
|
||||
// |device_capabilities| specified by the client.
|
||||
virtual util::Status VerifyDeviceCapabilities(
|
||||
const ClientIdentification::ClientCapabilities& device_capabilities,
|
||||
const License::KeyContainer::OutputProtection& output_protection) const;
|
||||
|
||||
|
||||
virtual PlatformVerificationStatus GetPlatformVerificationStatus() const;
|
||||
|
||||
// Returns the service id of the provider that owns the device certificate.
|
||||
virtual std::string GetDrmDeviceServiceId() const;
|
||||
|
||||
// Returns true if the license request contained a key control nonce, else
|
||||
// false.
|
||||
virtual bool HasKeyControlNonce() const { return has_key_control_nonce_; }
|
||||
|
||||
protected:
|
||||
friend class Session;
|
||||
PlatformVerificationStatus platform_verification_status_ =
|
||||
PLATFORM_NO_VERIFICATION;
|
||||
|
||||
// For testing only. This allows unit tests to define a mock Session class.
|
||||
SessionImpl();
|
||||
|
||||
private:
|
||||
friend class SessionTest;
|
||||
|
||||
// Takes ownership of |message|, |request|, |client_cert| and |device_info|.
|
||||
SessionImpl(SignedMessage* message, LicenseRequest* request, bool has_nonce,
|
||||
uint32_t nonce, widevine::ProvisionedDeviceInfo* device_info);
|
||||
|
||||
// Called by the SessionImpl::Create factory to initialize a new session.
|
||||
util::Status Init();
|
||||
util::Status GenerateNewLicenseInfo(/*IN*/ const SessionInit* session_init,
|
||||
/*OUT*/ LicenseIdentification* new_id,
|
||||
/*OUT*/ std::string* renewal_signing_key,
|
||||
/*OUT*/ std::string* signing_key);
|
||||
util::Status GeneratePriorLicenseInfo(
|
||||
/*IN*/ const SessionInit* session_init,
|
||||
/*INOUT*/ SessionState* session_state,
|
||||
/*OUT*/ LicenseIdentification* new_id,
|
||||
/*OUT*/ std::string* signing_key);
|
||||
void SetSessionKey(const std::string& session_key);
|
||||
|
||||
// Verifies remote attestation in the license request and sets
|
||||
// |platform_verification_status_|.
|
||||
virtual util::Status VerifyRemoteAttestation();
|
||||
// Return true if the provider session token should get included in the
|
||||
// license response.
|
||||
static bool ShouldSetProviderSessionToken(
|
||||
bool pst_provided, bool client_supports_pst,
|
||||
ProvisionedDeviceInfo::WvSecurityLevel level, uint32_t oem_crypto_version);
|
||||
|
||||
// Verifies the platform by platform-specific means such as Remote Attestion
|
||||
// or host code verification. Only meaningful for Widevine Level 2-3, as
|
||||
// Level 1 devices are verified by default.
|
||||
virtual util::Status VerifyPlatform();
|
||||
|
||||
// Extracts the nonce from |request| and populates |key_control_nonce|. Sets
|
||||
// |key_control_nonce| to true if the nonce is found.
|
||||
static util::Status LoadKeyControlNonce(const LicenseRequest& request,
|
||||
bool* has_key_control_nonce,
|
||||
uint32_t* key_control_nonce);
|
||||
|
||||
// Validates required fields are set in the license |request|.
|
||||
static util::Status CheckLicenseRequestFields(const LicenseRequest& request);
|
||||
|
||||
// De-serialize the license request from the |signed_license_request|
|
||||
// into |license_request| and |signed_message|.
|
||||
static util::Status ParseLicenseRequestFromString(
|
||||
const std::string& signed_license_request, SignedMessage* signed_message,
|
||||
LicenseRequest* license_request);
|
||||
|
||||
// Validates the Provider Session Token from |pst_src|.
|
||||
static util::Status CheckProviderSessionToken(const std::string& pst_src);
|
||||
|
||||
std::unique_ptr<SignedMessage> signed_message_;
|
||||
std::unique_ptr<LicenseRequest> license_request_;
|
||||
// Client cert object used for all crypto operations.
|
||||
std::unique_ptr<ClientCert> client_cert_;
|
||||
std::unique_ptr<ClientIdentification> client_identification_;
|
||||
std::string session_key_;
|
||||
std::string session_id_;
|
||||
absl::Mutex session_id_lock_;
|
||||
bool has_key_control_nonce_ = false;
|
||||
static bool is_service_certificate_loaded_;
|
||||
uint32_t key_control_nonce_;
|
||||
std::unique_ptr<ProvisionedDeviceInfo> provisioned_device_info_;
|
||||
std::string remote_attestation_cert_serial_number_;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(SessionImpl);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // LICENSE_SERVER_SDK_INTERNAL_SESSION_IMPL_H_
|
||||
2888
license_server_sdk/internal/session_impl_test.cc
Normal file
2888
license_server_sdk/internal/session_impl_test.cc
Normal file
File diff suppressed because it is too large
Load Diff
53
license_server_sdk/internal/session_usage_report.h
Normal file
53
license_server_sdk/internal/session_usage_report.h
Normal file
@@ -0,0 +1,53 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef LICENSE_SERVER_SDK_INTERNAL_SESSION_USAGE_REPORT_H_
|
||||
#define LICENSE_SERVER_SDK_INTERNAL_SESSION_USAGE_REPORT_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
const uint32_t kMaxProviderSessionTokenSizeBytes = 128;
|
||||
|
||||
enum SessionUsageStatus {
|
||||
SessionUsageStatus_Unused = 0,
|
||||
// License still active on the device, has not been released yet.
|
||||
SessionUsageStatus_Active = 1,
|
||||
// Deprecated in OEMCrypto V13, replaced with Inactive_Used and
|
||||
// Inactive_Unused. All Inactive status values indicate the license was
|
||||
// relased.
|
||||
SessionUsageStatus_Inactive_Deprecated = 2,
|
||||
// Keys released after use.
|
||||
SessionUsageStatus_Inactive_Used = 3,
|
||||
// Keys released before use.
|
||||
SessionUsageStatus_Inactive_Unused = 4,
|
||||
};
|
||||
|
||||
// Data sent in the license release request from the client to indicate the
|
||||
// license usage.
|
||||
struct InternalSessionUsageReport {
|
||||
// HMAC SHA1 of the rest of the report.
|
||||
uint8_t signature[20];
|
||||
// Current status of status report: 0=unused, 1=active, 2=inactive.
|
||||
uint8_t status;
|
||||
// The clock security level are: 0=insecure clock, 1=secure timer,
|
||||
// 2=secure clock.
|
||||
uint8_t clock_security_level;
|
||||
uint8_t pst_length;
|
||||
// Make int64_t's word aligned.
|
||||
uint8_t padding;
|
||||
int64_t seconds_since_license_received;
|
||||
int64_t seconds_since_first_decrypt;
|
||||
int64_t seconds_since_last_decrypt;
|
||||
uint8_t pst[kMaxProviderSessionTokenSizeBytes];
|
||||
} ABSL_ATTRIBUTE_PACKED;
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // LICENSE_SERVER_SDK_INTERNAL_SESSION_USAGE_REPORT_H_
|
||||
75
media_cas_proxy_sdk/external/common/wvpl/BUILD
vendored
Normal file
75
media_cas_proxy_sdk/external/common/wvpl/BUILD
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
# The public interface of Widevine CAS Proxy SDK for content providers.
|
||||
|
||||
package(
|
||||
default_visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "binary_release_files",
|
||||
srcs = glob(["*.h"]),
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "libwvpl_cas_proxy.so",
|
||||
linkshared = 1,
|
||||
deps = [
|
||||
":wvpl_cas_proxy_environment",
|
||||
":wvpl_cas_proxy_session",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "wvpl_cas_proxy_session",
|
||||
srcs = [
|
||||
"wvpl_cas_proxy_session.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"wvpl_cas_proxy_session.h",
|
||||
],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:error_space",
|
||||
"//sdk/external/common/wvpl:wvpl_sdk_environment",
|
||||
"//sdk/external/common/wvpl:wvpl_sdk_session",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "wvpl_cas_proxy_environment",
|
||||
srcs = [
|
||||
"wvpl_cas_proxy_environment.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"wvpl_cas_proxy_environment.h",
|
||||
"wvpl_cas_proxy_session.h",
|
||||
],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:certificate_type",
|
||||
"//common:error_space",
|
||||
"//sdk/external/common/wvpl:wvpl_sdk_environment",
|
||||
"//sdk/external/common/wvpl:wvpl_sdk_session",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "wvpl_cas_proxy_session_test",
|
||||
srcs = ["wvpl_cas_proxy_session_test.cc"],
|
||||
deps = [
|
||||
":wvpl_cas_proxy_session",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/memory",
|
||||
"//sdk/external/common/wvpl:wvpl_types",
|
||||
],
|
||||
)
|
||||
76
media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc
vendored
Normal file
76
media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.h"
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/error_space.h"
|
||||
#include "media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h"
|
||||
|
||||
namespace util = widevine::util;
|
||||
using widevine::error_space;
|
||||
using widevine::kCertificateTypeDevelopment;
|
||||
using widevine::kCertificateTypeTesting;
|
||||
|
||||
namespace widevine_server {
|
||||
namespace wv_pl_sdk {
|
||||
|
||||
WvPLCASProxyEnvironment::WvPLCASProxyEnvironment(
|
||||
const std::map<std::string, std::string>& config_values) {
|
||||
std::map<std::string, std::string>::const_iterator it =
|
||||
config_values.find(kDrmCertificateType);
|
||||
if (it != config_values.end()) {
|
||||
if (it->second == "dev" || it->second == "prod" || it->second == "test") {
|
||||
drm_certificate_type_ = it->second;
|
||||
}
|
||||
if (drm_certificate_type_ == "dev") {
|
||||
certificate_type_ = kCertificateTypeDevelopment;
|
||||
} else if (drm_certificate_type_ == "test") {
|
||||
certificate_type_ = kCertificateTypeTesting;
|
||||
}
|
||||
}
|
||||
it = config_values.find(kProvider);
|
||||
if (it != config_values.end()) {
|
||||
provider_ = (*it).second;
|
||||
}
|
||||
if (it != config_values.end()) {
|
||||
provider_iv_ = new std::string(absl::HexStringToBytes((*it).second));
|
||||
}
|
||||
it = config_values.find(kProviderKey);
|
||||
if (it != config_values.end()) {
|
||||
provider_key_ = new std::string(absl::HexStringToBytes((*it).second));
|
||||
}
|
||||
}
|
||||
|
||||
WvPLStatus WvPLCASProxyEnvironment::CreateSession(
|
||||
const std::string& cas_license_request,
|
||||
WvPLCASProxySession** cas_proxy_session) {
|
||||
WvPLStatus status = util::OkStatus();
|
||||
if (cas_proxy_session == nullptr) {
|
||||
return WvPLStatus(error_space, util::error::INTERNAL,
|
||||
"proxy_session is NULL");
|
||||
}
|
||||
if (*cas_proxy_session != nullptr) {
|
||||
return WvPLStatus(error_space, util::error::INTERNAL,
|
||||
"*proxy_session is not NULL");
|
||||
}
|
||||
std::unique_ptr<WvPLCASProxySession> wvpl_cas_proxy_session(
|
||||
new WvPLCASProxySession(cas_license_request));
|
||||
// TODO(user): Complete the license request parsing in WvPLCASProxySession.
|
||||
// status = wvpl_cas_proxy_session->ParseLicenseRequest();
|
||||
if (status.ok()) {
|
||||
*cas_proxy_session = wvpl_cas_proxy_session.release();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
WvPLCASProxyEnvironment::~WvPLCASProxyEnvironment() {}
|
||||
|
||||
} // namespace wv_pl_sdk
|
||||
} // namespace widevine_server
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user