Replace hardcoded parameters
This commit is contained in:
29
WORKSPACE
29
WORKSPACE
@@ -1,6 +1,6 @@
|
|||||||
workspace(name = "media_cas_packager_sdk")
|
workspace(name = "media_cas_packager_sdk")
|
||||||
|
|
||||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository", "git_repository")
|
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository", "git_repository")
|
||||||
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
|
||||||
# CCTZ (Time-zone framework), needed by abseil.
|
# CCTZ (Time-zone framework), needed by abseil.
|
||||||
git_repository(
|
git_repository(
|
||||||
@@ -11,14 +11,33 @@ git_repository(
|
|||||||
|
|
||||||
git_repository(
|
git_repository(
|
||||||
name = "abseil_repo",
|
name = "abseil_repo",
|
||||||
commit = "475d64f2de7403a01b1b36c487328ed41d29c20c", #2018-04-10
|
commit = "aa844899c937bde5d2b24f276b59997e5b668bde", #2019-08-08
|
||||||
remote = "https://github.com/abseil/abseil-cpp.git",
|
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(
|
git_repository(
|
||||||
name = "protobuf_repo",
|
name = "com_google_protobuf",
|
||||||
remote = "https://github.com/google/protobuf.git",
|
remote = "https://github.com/google/protobuf.git",
|
||||||
tag = "v3.6.1.3",
|
tag = "v3.8.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Bazel custom build rule support.
|
||||||
|
git_repository(
|
||||||
|
name = "bazel_skylib",
|
||||||
|
remote = "https://github.com/bazelbuild/bazel-skylib.git",
|
||||||
|
tag = "0.8.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Protobuf library support. Not included in the recent protobuf release.
|
||||||
|
http_archive(
|
||||||
|
name = "zlib",
|
||||||
|
build_file = "@com_google_protobuf//:third_party/zlib.BUILD",
|
||||||
|
sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1",
|
||||||
|
strip_prefix = "zlib-1.2.11",
|
||||||
|
urls = ["https://zlib.net/zlib-1.2.11.tar.gz"],
|
||||||
)
|
)
|
||||||
|
|
||||||
git_repository(
|
git_repository(
|
||||||
@@ -62,7 +81,7 @@ new_git_repository(
|
|||||||
|
|
||||||
bind(
|
bind(
|
||||||
name = "protobuf",
|
name = "protobuf",
|
||||||
actual = "@protobuf_repo//:protobuf",
|
actual = "@com_google_protobuf//:protobuf",
|
||||||
)
|
)
|
||||||
|
|
||||||
bind(
|
bind(
|
||||||
|
|||||||
514
common/BUILD
514
common/BUILD
@@ -20,6 +20,35 @@ filegroup(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "content_id_util",
|
||||||
|
srcs = ["content_id_util.cc"],
|
||||||
|
hdrs = ["content_id_util.h"],
|
||||||
|
deps = [
|
||||||
|
":error_space",
|
||||||
|
":status",
|
||||||
|
"//license_server_sdk/internal:sdk",
|
||||||
|
"//protos/public:errors_cc_proto",
|
||||||
|
"//protos/public:external_license_cc_proto",
|
||||||
|
"//protos/public:license_protocol_cc_proto",
|
||||||
|
"//protos/public:license_server_sdk_cc_proto",
|
||||||
|
"//protos/public:widevine_pssh_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "content_id_util_test",
|
||||||
|
srcs = ["content_id_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":content_id_util",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"//protos/public:errors_cc_proto",
|
||||||
|
"//protos/public:external_license_cc_proto",
|
||||||
|
"//protos/public:license_protocol_cc_proto",
|
||||||
|
"//protos/public:widevine_pssh_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "widevine_system_id",
|
name = "widevine_system_id",
|
||||||
srcs = ["widevine_system_id.cc"],
|
srcs = ["widevine_system_id.cc"],
|
||||||
@@ -32,12 +61,37 @@ cc_library(
|
|||||||
hdrs = ["certificate_type.h"],
|
hdrs = ["certificate_type.h"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "security_profile_list",
|
||||||
|
srcs = ["security_profile_list.cc"],
|
||||||
|
hdrs = ["security_profile_list.h"],
|
||||||
|
deps = [
|
||||||
|
":client_id_util",
|
||||||
|
"@abseil_repo//absl/synchronization",
|
||||||
|
"//protos/public:client_identification_cc_proto",
|
||||||
|
"//protos/public:provisioned_device_info_cc_proto",
|
||||||
|
"//protos/public:security_profile_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "security_profile_list_test",
|
||||||
|
timeout = "short",
|
||||||
|
srcs = ["security_profile_list_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":security_profile_list",
|
||||||
|
"//base",
|
||||||
|
"//external:protobuf",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"//protos/public:security_profile_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "status",
|
name = "status",
|
||||||
srcs = ["status.cc"],
|
srcs = ["status.cc"],
|
||||||
hdrs = ["status.h"],
|
hdrs = ["status.h"],
|
||||||
deps = [
|
deps = [
|
||||||
"//base",
|
|
||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//util:error_space",
|
"//util:error_space",
|
||||||
@@ -55,12 +109,23 @@ cc_test(
|
|||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "client_cert",
|
name = "client_cert",
|
||||||
srcs = ["client_cert.cc"],
|
srcs = [
|
||||||
hdrs = ["client_cert.h"],
|
"certificate_client_cert.cc",
|
||||||
|
"certificate_client_cert.h",
|
||||||
|
"client_cert.cc",
|
||||||
|
"keybox_client_cert.cc",
|
||||||
|
],
|
||||||
|
hdrs = [
|
||||||
|
"client_cert.h",
|
||||||
|
"keybox_client_cert.h",
|
||||||
|
],
|
||||||
deps = [
|
deps = [
|
||||||
":crypto_util",
|
":crypto_util",
|
||||||
":drm_root_certificate",
|
":drm_root_certificate",
|
||||||
|
":ec_key",
|
||||||
|
":ec_util",
|
||||||
":error_space",
|
":error_space",
|
||||||
|
":openssl_util",
|
||||||
":random_util",
|
":random_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
@@ -68,16 +133,13 @@ cc_library(
|
|||||||
":status",
|
":status",
|
||||||
":wvm_token_handler",
|
":wvm_token_handler",
|
||||||
"//base",
|
"//base",
|
||||||
"//strings",
|
"@abseil_repo//absl/memory",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"//protos/public:client_identification_cc_proto",
|
||||||
"@abseil_repo//absl/time",
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
"//util/gtl:map_util",
|
"//protos/public:errors_cc_proto",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:license_protocol_cc_proto",
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
"//protos/public:errors_proto",
|
|
||||||
"//protos/public:license_protocol_proto",
|
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,22 +148,19 @@ cc_test(
|
|||||||
srcs = ["client_cert_test.cc"],
|
srcs = ["client_cert_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
":client_cert",
|
":client_cert",
|
||||||
":drm_root_certificate",
|
":ec_test_keys",
|
||||||
":error_space",
|
":error_space",
|
||||||
|
":rsa_key",
|
||||||
|
":rsa_test_keys",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
|
":status",
|
||||||
":test_drm_certificates",
|
":test_drm_certificates",
|
||||||
":wvm_test_keys",
|
":wvm_test_keys",
|
||||||
"//base",
|
|
||||||
"//strings",
|
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
"@abseil_repo//absl/time",
|
"//protos/public:errors_cc_proto",
|
||||||
"//common:rsa_key",
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
"//common:rsa_test_keys",
|
|
||||||
"//protos/public:drm_certificate_proto",
|
|
||||||
"//protos/public:errors_proto",
|
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,22 +170,29 @@ cc_library(
|
|||||||
hdrs = ["device_status_list.h"],
|
hdrs = ["device_status_list.h"],
|
||||||
deps = [
|
deps = [
|
||||||
":client_cert",
|
":client_cert",
|
||||||
":crypto_util",
|
|
||||||
":drm_root_certificate",
|
|
||||||
":drm_service_certificate",
|
":drm_service_certificate",
|
||||||
":error_space",
|
":error_space",
|
||||||
":random_util",
|
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":signing_key_util",
|
|
||||||
":status",
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"@abseil_repo//absl/synchronization",
|
||||||
"//util/gtl:map_util",
|
"//util/gtl:map_util",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_cc_proto",
|
||||||
"//protos/public:device_certificate_status_proto",
|
"//protos/public:device_certificate_status_cc_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
"//protos/public:provisioned_device_info_proto",
|
"//protos/public:provisioned_device_info_cc_proto",
|
||||||
|
"//protos/public:signed_device_info_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "device_info_util",
|
||||||
|
srcs = ["device_info_util.cc"],
|
||||||
|
hdrs = ["device_info_util.h"],
|
||||||
|
deps = [
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"//protos/public:provisioned_device_info_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -137,15 +203,19 @@ cc_test(
|
|||||||
deps = [
|
deps = [
|
||||||
":client_cert",
|
":client_cert",
|
||||||
":device_status_list",
|
":device_status_list",
|
||||||
|
":rsa_key",
|
||||||
|
":rsa_test_keys",
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
|
"//external:protobuf",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//common:rsa_key",
|
"//protos/public:client_identification_cc_proto",
|
||||||
"//common:rsa_test_keys",
|
"//protos/public:device_certificate_status_cc_proto",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:provisioned_device_info_cc_proto",
|
||||||
"//protos/public:provisioned_device_info_proto",
|
"//protos/public:signed_device_info_cc_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -155,18 +225,19 @@ cc_library(
|
|||||||
hdrs = ["drm_root_certificate.h"],
|
hdrs = ["drm_root_certificate.h"],
|
||||||
deps = [
|
deps = [
|
||||||
":certificate_type",
|
":certificate_type",
|
||||||
|
":ec_key",
|
||||||
":error_space",
|
":error_space",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
|
":signer_public_key",
|
||||||
":status",
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/memory",
|
"@abseil_repo//absl/memory",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"@abseil_repo//absl/synchronization",
|
||||||
"//external:openssl",
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -176,6 +247,8 @@ cc_test(
|
|||||||
srcs = ["drm_root_certificate_test.cc"],
|
srcs = ["drm_root_certificate_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
":drm_root_certificate",
|
":drm_root_certificate",
|
||||||
|
":ec_key",
|
||||||
|
":ec_test_keys",
|
||||||
":error_space",
|
":error_space",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":rsa_test_keys",
|
":rsa_test_keys",
|
||||||
@@ -183,9 +256,10 @@ cc_test(
|
|||||||
"//base",
|
"//base",
|
||||||
"//external:protobuf",
|
"//external:protobuf",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"//protos/public:drm_certificate_proto",
|
"@abseil_repo//absl/memory",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -195,13 +269,25 @@ cc_library(
|
|||||||
hdrs = ["client_id_util.h"],
|
hdrs = ["client_id_util.h"],
|
||||||
deps = [
|
deps = [
|
||||||
":aes_cbc_util",
|
":aes_cbc_util",
|
||||||
|
":client_cert",
|
||||||
":drm_service_certificate",
|
":drm_service_certificate",
|
||||||
":error_space",
|
":error_space",
|
||||||
":status",
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_cc_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
|
"//protos/public:errors_cc_proto",
|
||||||
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "private_key_util",
|
||||||
|
hdrs = ["private_key_util.h"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"//external:openssl",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -210,6 +296,7 @@ cc_library(
|
|||||||
srcs = ["rsa_util.cc"],
|
srcs = ["rsa_util.cc"],
|
||||||
hdrs = ["rsa_util.h"],
|
hdrs = ["rsa_util.h"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":private_key_util",
|
||||||
"//base",
|
"//base",
|
||||||
"//external:openssl",
|
"//external:openssl",
|
||||||
],
|
],
|
||||||
@@ -269,9 +356,6 @@ cc_library(
|
|||||||
testonly = 1,
|
testonly = 1,
|
||||||
srcs = ["rsa_test_keys.cc"],
|
srcs = ["rsa_test_keys.cc"],
|
||||||
hdrs = ["rsa_test_keys.h"],
|
hdrs = ["rsa_test_keys.h"],
|
||||||
deps = [
|
|
||||||
"//base",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
@@ -284,6 +368,168 @@ cc_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "ec_util",
|
||||||
|
srcs = ["ec_util.cc"],
|
||||||
|
hdrs = [
|
||||||
|
"ec_key.h",
|
||||||
|
"ec_util.h",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":openssl_util",
|
||||||
|
":private_key_util",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "ec_util_test",
|
||||||
|
size = "medium",
|
||||||
|
timeout = "short",
|
||||||
|
srcs = ["ec_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":ec_test_keys",
|
||||||
|
":ec_util",
|
||||||
|
":openssl_util",
|
||||||
|
"//base",
|
||||||
|
"//testing:gunit",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "ec_key",
|
||||||
|
srcs = ["ec_key.cc"],
|
||||||
|
hdrs = ["ec_key.h"],
|
||||||
|
deps = [
|
||||||
|
":aes_cbc_util",
|
||||||
|
":ec_util",
|
||||||
|
":openssl_util",
|
||||||
|
":sha_util",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "ec_key_test",
|
||||||
|
size = "medium",
|
||||||
|
timeout = "short",
|
||||||
|
srcs = ["ec_key_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":ec_key",
|
||||||
|
":ec_test_keys",
|
||||||
|
":ec_util",
|
||||||
|
":random_util",
|
||||||
|
"//testing:gunit",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "ec_key_source",
|
||||||
|
hdrs = ["ec_key_source.h"],
|
||||||
|
deps = [
|
||||||
|
":ec_key",
|
||||||
|
"//base",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "local_ec_key_source",
|
||||||
|
srcs = ["local_ec_key_source.cc"],
|
||||||
|
hdrs = [
|
||||||
|
"local_ec_key_source.h",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":ec_key",
|
||||||
|
":ec_key_source",
|
||||||
|
":ec_util",
|
||||||
|
"//base",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "local_ec_key_source_test",
|
||||||
|
size = "medium",
|
||||||
|
timeout = "short",
|
||||||
|
srcs = ["local_ec_key_source_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":ec_key",
|
||||||
|
":ec_test_keys",
|
||||||
|
":ec_util",
|
||||||
|
":local_ec_key_source",
|
||||||
|
":random_util",
|
||||||
|
"//testing:gunit",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "fake_ec_key_source",
|
||||||
|
testonly = 1,
|
||||||
|
srcs = ["fake_ec_key_source.cc"],
|
||||||
|
hdrs = ["fake_ec_key_source.h"],
|
||||||
|
deps = [
|
||||||
|
":ec_key",
|
||||||
|
":ec_key_source",
|
||||||
|
":ec_test_keys",
|
||||||
|
"//base",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "ecies_crypto",
|
||||||
|
srcs = ["ecies_crypto.cc"],
|
||||||
|
hdrs = ["ecies_crypto.h"],
|
||||||
|
deps = [
|
||||||
|
":aes_cbc_util",
|
||||||
|
":crypto_util",
|
||||||
|
":ec_key",
|
||||||
|
":ec_key_source",
|
||||||
|
":ec_util",
|
||||||
|
":openssl_util",
|
||||||
|
":status",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"//external:openssl",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "ecies_crypto_test",
|
||||||
|
size = "medium",
|
||||||
|
timeout = "short",
|
||||||
|
srcs = ["ecies_crypto_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":ec_key",
|
||||||
|
":ec_key_source",
|
||||||
|
":ec_test_keys",
|
||||||
|
":ec_util",
|
||||||
|
":ecies_crypto",
|
||||||
|
":fake_ec_key_source",
|
||||||
|
"//testing:gunit",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "ec_test_keys",
|
||||||
|
testonly = 1,
|
||||||
|
srcs = ["ec_test_keys.cc"],
|
||||||
|
hdrs = ["ec_test_keys.h"],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "aes_cbc_util",
|
name = "aes_cbc_util",
|
||||||
srcs = ["aes_cbc_util.cc"],
|
srcs = ["aes_cbc_util.cc"],
|
||||||
@@ -313,7 +559,6 @@ cc_library(
|
|||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//external:openssl",
|
"//external:openssl",
|
||||||
"//util/endian",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -326,6 +571,7 @@ cc_test(
|
|||||||
"//testing:gunit",
|
"//testing:gunit",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
|
"//external:openssl",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -420,7 +666,6 @@ cc_library(
|
|||||||
":rsa_key",
|
":rsa_key",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
":status",
|
":status",
|
||||||
"//base",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -431,7 +676,7 @@ cc_library(
|
|||||||
deps = [
|
deps = [
|
||||||
":crypto_util",
|
":crypto_util",
|
||||||
"//base",
|
"//base",
|
||||||
"//protos/public:license_protocol_proto",
|
"//protos/public:license_protocol_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -445,7 +690,7 @@ cc_test(
|
|||||||
"//testing:gunit",
|
"//testing:gunit",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//protos/public:license_protocol_proto",
|
"//protos/public:license_protocol_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -454,10 +699,6 @@ cc_library(
|
|||||||
testonly = 1,
|
testonly = 1,
|
||||||
srcs = ["test_drm_certificates.cc"],
|
srcs = ["test_drm_certificates.cc"],
|
||||||
hdrs = ["test_drm_certificates.h"],
|
hdrs = ["test_drm_certificates.h"],
|
||||||
deps = [
|
|
||||||
"//base",
|
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
@@ -509,7 +750,7 @@ cc_library(
|
|||||||
deps = [
|
deps = [
|
||||||
"//util:error_space",
|
"//util:error_space",
|
||||||
"//util:proto_status",
|
"//util:proto_status",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -527,9 +768,9 @@ cc_library(
|
|||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"@abseil_repo//absl/synchronization",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_cc_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
"//protos/public:remote_attestation_proto",
|
"//protos/public:remote_attestation_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -549,10 +790,10 @@ cc_library(
|
|||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"@abseil_repo//absl/synchronization",
|
||||||
"//util/gtl:map_util",
|
"//util/gtl:map_util",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_cc_proto",
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -572,11 +813,11 @@ cc_test(
|
|||||||
"//external:protobuf",
|
"//external:protobuf",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//protos/public:client_identification_proto",
|
"//protos/public:client_identification_cc_proto",
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
"//protos/public:license_server_sdk_proto",
|
"//protos/public:license_server_sdk_cc_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -587,9 +828,7 @@ cc_library(
|
|||||||
deps = [
|
deps = [
|
||||||
":status",
|
":status",
|
||||||
":vmp_checker",
|
":vmp_checker",
|
||||||
"//base",
|
"//protos/public:license_protocol_cc_proto",
|
||||||
"@abseil_repo//absl/strings",
|
|
||||||
"//protos/public:license_protocol_proto",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -598,7 +837,6 @@ cc_library(
|
|||||||
srcs = ["x509_cert.cc"],
|
srcs = ["x509_cert.cc"],
|
||||||
hdrs = ["x509_cert.h"],
|
hdrs = ["x509_cert.h"],
|
||||||
deps = [
|
deps = [
|
||||||
":error_space",
|
|
||||||
":openssl_util",
|
":openssl_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":status",
|
":status",
|
||||||
@@ -629,7 +867,6 @@ cc_test(
|
|||||||
":rsa_key",
|
":rsa_key",
|
||||||
":test_utils",
|
":test_utils",
|
||||||
":x509_cert",
|
":x509_cert",
|
||||||
"//base",
|
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
],
|
],
|
||||||
@@ -646,8 +883,8 @@ cc_library(
|
|||||||
":status",
|
":status",
|
||||||
":x509_cert",
|
":x509_cert",
|
||||||
"//base",
|
"//base",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
"//protos/public:verified_media_pipeline_proto",
|
"//protos/public:verified_media_pipeline_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -661,8 +898,8 @@ cc_test(
|
|||||||
"//base",
|
"//base",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_cc_proto",
|
||||||
"//protos/public:verified_media_pipeline_proto",
|
"//protos/public:verified_media_pipeline_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -670,10 +907,7 @@ cc_library(
|
|||||||
name = "string_util",
|
name = "string_util",
|
||||||
srcs = ["string_util.cc"],
|
srcs = ["string_util.cc"],
|
||||||
hdrs = ["string_util.h"],
|
hdrs = ["string_util.h"],
|
||||||
deps = [
|
deps = [":status"],
|
||||||
":status",
|
|
||||||
"//base",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
@@ -681,7 +915,121 @@ cc_test(
|
|||||||
srcs = ["string_util_test.cc"],
|
srcs = ["string_util_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
":string_util",
|
":string_util",
|
||||||
"//base",
|
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "output_protection_util",
|
||||||
|
srcs = ["output_protection_util.cc"],
|
||||||
|
hdrs = ["output_protection_util.h"],
|
||||||
|
deps = [
|
||||||
|
":status",
|
||||||
|
"//protos/public:client_identification_cc_proto",
|
||||||
|
"//protos/public:license_protocol_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "output_protection_util_test",
|
||||||
|
srcs = ["output_protection_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":output_protection_util",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "rot_id_util",
|
||||||
|
srcs = ["rot_id_util.cc"],
|
||||||
|
hdrs = ["rot_id_util.h"],
|
||||||
|
deps = [
|
||||||
|
":crypto_util",
|
||||||
|
":ec_key",
|
||||||
|
":local_ec_key_source",
|
||||||
|
":sha_util",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "rot_id_util_test",
|
||||||
|
srcs = ["rot_id_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":rot_id_util",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "rot_id_generator",
|
||||||
|
srcs = ["rot_id_generator.cc"],
|
||||||
|
hdrs = ["rot_id_generator.h"],
|
||||||
|
deps = [
|
||||||
|
":crypto_util",
|
||||||
|
":ec_key",
|
||||||
|
":ecies_crypto",
|
||||||
|
":rot_id_util",
|
||||||
|
":sha_util",
|
||||||
|
":status",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "rot_id_generator_test",
|
||||||
|
srcs = ["rot_id_generator_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":ec_key",
|
||||||
|
":ec_test_keys",
|
||||||
|
":ecies_crypto",
|
||||||
|
":fake_ec_key_source",
|
||||||
|
":rot_id_generator",
|
||||||
|
":rot_id_util",
|
||||||
|
":status",
|
||||||
|
"//external:protobuf",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "signer_public_key",
|
||||||
|
srcs = ["signer_public_key.cc"],
|
||||||
|
hdrs = ["signer_public_key.h"],
|
||||||
|
deps = [
|
||||||
|
":ec_key",
|
||||||
|
":rsa_key",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "signer_public_key_test",
|
||||||
|
srcs = ["signer_public_key_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":ec_key",
|
||||||
|
":ec_test_keys",
|
||||||
|
":rsa_key",
|
||||||
|
":rsa_test_keys",
|
||||||
|
":signer_public_key",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"//protos/public:drm_certificate_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "core_message_util",
|
||||||
|
srcs = ["core_message_util.cc"],
|
||||||
|
hdrs = ["core_message_util.h"],
|
||||||
|
deps = [
|
||||||
|
":sha_util",
|
||||||
|
"//common/oemcrypto_core_message/odk:kdo",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace crypto_util {
|
|||||||
|
|
||||||
// Encrypts the provided plantext std::string using AES-CBC encryption.
|
// Encrypts the provided plantext std::string using AES-CBC encryption.
|
||||||
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
||||||
const std::string& plaintext) {
|
const std::string& plaintext) {
|
||||||
const size_t num_padding_bytes =
|
const size_t num_padding_bytes =
|
||||||
AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE);
|
AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE);
|
||||||
std::string padded_text = plaintext;
|
std::string padded_text = plaintext;
|
||||||
@@ -28,7 +28,7 @@ std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||||
const std::string& plaintext) {
|
const std::string& plaintext) {
|
||||||
if (iv.size() != AES_BLOCK_SIZE) {
|
if (iv.size() != AES_BLOCK_SIZE) {
|
||||||
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
|
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
|
||||||
return std::string();
|
return std::string();
|
||||||
@@ -56,7 +56,7 @@ std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
|||||||
// Decrypts the AES-CBC encrypted text. Returns an empty std::string on error or
|
// Decrypts the AES-CBC encrypted text. Returns an empty std::string on error or
|
||||||
// the plaintext on success.
|
// the plaintext on success.
|
||||||
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
||||||
const std::string& ciphertext) {
|
const std::string& ciphertext) {
|
||||||
if (ciphertext.empty()) {
|
if (ciphertext.empty()) {
|
||||||
LOG(WARNING) << "Empty ciphertext.";
|
LOG(WARNING) << "Empty ciphertext.";
|
||||||
return std::string();
|
return std::string();
|
||||||
@@ -100,7 +100,7 @@ std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||||
const std::string& ciphertext) {
|
const std::string& ciphertext) {
|
||||||
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
|
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
|
||||||
if (local_iv.empty()) {
|
if (local_iv.empty()) {
|
||||||
local_iv.resize(AES_BLOCK_SIZE, '\0');
|
local_iv.resize(AES_BLOCK_SIZE, '\0');
|
||||||
|
|||||||
@@ -16,18 +16,18 @@ namespace crypto_util {
|
|||||||
|
|
||||||
// Helper for wrapping AES CBC encryption. Uses PKCS7 padding.
|
// Helper for wrapping AES CBC encryption. Uses PKCS7 padding.
|
||||||
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
||||||
const std::string& plaintext);
|
const std::string& plaintext);
|
||||||
|
|
||||||
// Helper for wrapping AES CBC encryption. Adds no padding, so the input
|
// 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
|
// must be an multiple of the 16-byte AES block size. Returns empty std::string
|
||||||
// on error.
|
// on error.
|
||||||
std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||||
const std::string& plaintext);
|
const std::string& plaintext);
|
||||||
|
|
||||||
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
|
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
|
||||||
// empty std::string on error or the plaintext on success. Expects PKCS7 padding.
|
// empty std::string on error or the plaintext on success. Expects PKCS7 padding.
|
||||||
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
||||||
const std::string& ciphertext);
|
const std::string& ciphertext);
|
||||||
|
|
||||||
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
|
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
|
||||||
// empty std::string on error or the plaintext on success.
|
// empty std::string on error or the plaintext on success.
|
||||||
@@ -35,7 +35,7 @@ std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
|||||||
// This is used to decrypt the encrypted blob in the WVM keyboxes, with
|
// This is used to decrypt the encrypted blob in the WVM keyboxes, with
|
||||||
// a zero iv.
|
// a zero iv.
|
||||||
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||||
const std::string& ciphertext);
|
const std::string& ciphertext);
|
||||||
|
|
||||||
} // namespace crypto_util
|
} // namespace crypto_util
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -29,16 +29,18 @@ namespace crypto_util {
|
|||||||
|
|
||||||
TEST(CryptoUtilTest, EncryptAndDecryptAesCbc) {
|
TEST(CryptoUtilTest, EncryptAndDecryptAesCbc) {
|
||||||
std::string plain_text("Foo");
|
std::string plain_text("Foo");
|
||||||
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
std::string ciphertext =
|
||||||
std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
||||||
std::string expected_ciphertext(
|
std::string expected_ciphertext(
|
||||||
"\xCF\x1A\x3\x1C\x9C\x8C\xB9Z\xEC\xC0\x17\xDCRxX\xD7");
|
"\xCF\x1A\x3\x1C\x9C\x8C\xB9Z\xEC\xC0\x17\xDCRxX\xD7");
|
||||||
ASSERT_EQ(0, ciphertext.size() % 16);
|
ASSERT_EQ(0, ciphertext.size() % 16);
|
||||||
ASSERT_GT(ciphertext.size(), plain_text.size());
|
ASSERT_GT(ciphertext.size(), plain_text.size());
|
||||||
ASSERT_EQ(expected_ciphertext, ciphertext);
|
ASSERT_EQ(expected_ciphertext, ciphertext);
|
||||||
|
|
||||||
std::string decrypted = DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
std::string decrypted =
|
||||||
std::string(kIv, kIv + sizeof(kIv)), ciphertext);
|
DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)), ciphertext);
|
||||||
ASSERT_EQ(plain_text, decrypted);
|
ASSERT_EQ(plain_text, decrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,28 +67,29 @@ TEST(CryptoUtilTest, DecryptAesCbcNoPad) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::string decrypted = DecryptAesCbcNoPad(
|
std::string decrypted = DecryptAesCbcNoPad(
|
||||||
std::string(kKey, kKey + sizeof(kKey)), std::string(kIv, kIv + sizeof(kIv)),
|
std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)),
|
||||||
std::string(kCiphertext, kCiphertext + sizeof(kCiphertext)));
|
std::string(kCiphertext, kCiphertext + sizeof(kCiphertext)));
|
||||||
ASSERT_EQ(std::string(kExpectedPlaintext,
|
ASSERT_EQ(std::string(kExpectedPlaintext,
|
||||||
kExpectedPlaintext + sizeof(kExpectedPlaintext)),
|
kExpectedPlaintext + sizeof(kExpectedPlaintext)),
|
||||||
decrypted);
|
decrypted);
|
||||||
|
|
||||||
std::string dummy_iv;
|
std::string dummy_iv;
|
||||||
decrypted = DecryptAesCbcNoPad(
|
decrypted = DecryptAesCbcNoPad(
|
||||||
std::string(kKey, kKey + sizeof(kKey)), dummy_iv,
|
std::string(kKey, kKey + sizeof(kKey)), dummy_iv,
|
||||||
std::string(kCiphertext, kCiphertext + sizeof(kCiphertext)));
|
std::string(kCiphertext, kCiphertext + sizeof(kCiphertext)));
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(std::string(
|
||||||
std::string(kExpectedPlaintextEmptyIv,
|
kExpectedPlaintextEmptyIv,
|
||||||
kExpectedPlaintextEmptyIv + sizeof(kExpectedPlaintextEmptyIv)),
|
kExpectedPlaintextEmptyIv + sizeof(kExpectedPlaintextEmptyIv)),
|
||||||
decrypted);
|
decrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, TestFailedEncrypt) {
|
TEST(CryptoUtilTest, TestFailedEncrypt) {
|
||||||
// Test with bogus initialization vector.
|
// Test with bogus initialization vector.
|
||||||
std::string plain_text("Foo");
|
std::string plain_text("Foo");
|
||||||
std::string bogus_iv("bogus");
|
std::string bogus_iv("bogus");
|
||||||
std::string ciphertext =
|
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, plain_text);
|
bogus_iv, plain_text);
|
||||||
ASSERT_EQ(ciphertext.size(), 0);
|
ASSERT_EQ(ciphertext.size(), 0);
|
||||||
|
|
||||||
// Test with bogus key.
|
// Test with bogus key.
|
||||||
@@ -124,14 +127,15 @@ TEST(CryptoUtilTest, TestFailedEncryptNoPad) {
|
|||||||
TEST(CryptoUtilTest, TestFailedDecrypt) {
|
TEST(CryptoUtilTest, TestFailedDecrypt) {
|
||||||
// First, encrypt the data.
|
// First, encrypt the data.
|
||||||
std::string plain_text("Foo");
|
std::string plain_text("Foo");
|
||||||
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
std::string ciphertext =
|
||||||
std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
|
||||||
|
std::string(kIv, kIv + sizeof(kIv)), plain_text);
|
||||||
ASSERT_NE(ciphertext.size(), 0);
|
ASSERT_NE(ciphertext.size(), 0);
|
||||||
|
|
||||||
// Test Decrypt with bogus iv.
|
// Test Decrypt with bogus iv.
|
||||||
std::string bogus_iv("bogus");
|
std::string bogus_iv("bogus");
|
||||||
plain_text =
|
plain_text = DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv,
|
||||||
DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, ciphertext);
|
ciphertext);
|
||||||
ASSERT_EQ(plain_text.size(), 0);
|
ASSERT_EQ(plain_text.size(), 0);
|
||||||
|
|
||||||
// Test Decrypt with bogus key.
|
// Test Decrypt with bogus key.
|
||||||
|
|||||||
268
common/certificate_client_cert.cc
Normal file
268
common/certificate_client_cert.cc
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/certificate_client_cert.h"
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "common/crypto_util.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_util.h"
|
||||||
|
#include "common/error_space.h"
|
||||||
|
#include "common/openssl_util.h"
|
||||||
|
#include "common/random_util.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
#include "common/signing_key_util.h"
|
||||||
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
#include "protos/public/errors.pb.h"
|
||||||
|
#include "protos/public/signed_drm_certificate.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
using EllipticCurve = ECPrivateKey::EllipticCurve;
|
||||||
|
|
||||||
|
ECPrivateKey::EllipticCurve CertificateAlgorithmToCurve(
|
||||||
|
DrmCertificate::Algorithm algorithm) {
|
||||||
|
switch (algorithm) {
|
||||||
|
case DrmCertificate::ECC_SECP256R1:
|
||||||
|
return ECPrivateKey::SECP256R1;
|
||||||
|
case DrmCertificate::ECC_SECP384R1:
|
||||||
|
return ECPrivateKey::SECP384R1;
|
||||||
|
case DrmCertificate::ECC_SECP521R1:
|
||||||
|
return ECPrivateKey::SECP521R1;
|
||||||
|
default:
|
||||||
|
return ECPrivateKey::UNDEFINED_CURVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientCertAlgorithmRSA : public ClientCertAlgorithm {
|
||||||
|
public:
|
||||||
|
ClientCertAlgorithmRSA() {}
|
||||||
|
~ClientCertAlgorithmRSA() override {}
|
||||||
|
ClientCertAlgorithmRSA(const ClientCertAlgorithmRSA&) = delete;
|
||||||
|
ClientCertAlgorithmRSA& operator=(const ClientCertAlgorithmRSA&) = delete;
|
||||||
|
|
||||||
|
Status Initialize(const std::string& public_key,
|
||||||
|
DrmCertificate::Algorithm /*not_used*/) override {
|
||||||
|
rsa_public_key_ =
|
||||||
|
std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key));
|
||||||
|
if (!rsa_public_key_) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"drm-certificate-public-key-failed");
|
||||||
|
}
|
||||||
|
session_key_ = Random16Bytes();
|
||||||
|
if (!rsa_public_key_->Encrypt(session_key_, &wrapped_session_key_)) {
|
||||||
|
return Status(error_space, ENCRYPT_ERROR,
|
||||||
|
"drm-certificate-failed-encrypt-session-key");
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature) const override {
|
||||||
|
CHECK(rsa_public_key_);
|
||||||
|
|
||||||
|
if (!rsa_public_key_->VerifySignature(message, signature)) {
|
||||||
|
return Status(error_space, INVALID_SIGNATURE, "");
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& session_key() const override { return session_key_; }
|
||||||
|
|
||||||
|
const std::string& wrapped_session_key() const override {
|
||||||
|
return wrapped_session_key_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<RsaPublicKey> rsa_public_key_;
|
||||||
|
std::string session_key_;
|
||||||
|
std::string wrapped_session_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ClientCertAlgorithmECC implements the Widevine protocol using ECC. It
|
||||||
|
// verifies an ECC based request and generates keys for use in building a
|
||||||
|
// license. The curve type value is contained in |algorithm|.
|
||||||
|
class ClientCertAlgorithmECC : public ClientCertAlgorithm {
|
||||||
|
public:
|
||||||
|
ClientCertAlgorithmECC() = default;
|
||||||
|
~ClientCertAlgorithmECC() override = default;
|
||||||
|
ClientCertAlgorithmECC(const ClientCertAlgorithmECC&) = delete;
|
||||||
|
ClientCertAlgorithmECC& operator=(const ClientCertAlgorithmECC&) = delete;
|
||||||
|
|
||||||
|
Status Initialize(const std::string& public_key,
|
||||||
|
DrmCertificate::Algorithm algorithm) override {
|
||||||
|
ECPrivateKey::EllipticCurve curve_id =
|
||||||
|
CertificateAlgorithmToCurve(algorithm);
|
||||||
|
if (curve_id == ECPrivateKey::UNDEFINED_CURVE) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"drm-certificate-unknown-curve");
|
||||||
|
}
|
||||||
|
// Parse the certifcate ECC public key.
|
||||||
|
client_ecc_public_key_ = ECPublicKey::Create(public_key);
|
||||||
|
if (client_ecc_public_key_ == nullptr) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"drm-certificate-public-key-failed");
|
||||||
|
}
|
||||||
|
// Generate an ephemeral ecc key pair with the same curve as used by the
|
||||||
|
// certificate public key.
|
||||||
|
ScopedECKEY key = ec_util::GenerateKeyWithCurve(curve_id);
|
||||||
|
auto new_private_key = absl::make_unique<ECPrivateKey>(std::move(key));
|
||||||
|
if (new_private_key == nullptr) {
|
||||||
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_ECC_KEYGEN_FAILED,
|
||||||
|
"drm-certificate-ephemeral-private-key-failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the ephemeral public key for inclusion in a license response.
|
||||||
|
std::unique_ptr<ECPublicKey> new_public_key = new_private_key->PublicKey();
|
||||||
|
if (new_public_key == nullptr ||
|
||||||
|
!new_public_key->SerializedKey(&ephemeral_public_key_)) {
|
||||||
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_ECC_KEYGEN_FAILED,
|
||||||
|
"drm-certificate-ephemeral-public-key-failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the session key from the ephemeral private key and the
|
||||||
|
// certificate public key.
|
||||||
|
if (!new_private_key->DeriveSharedSessionKey(*client_ecc_public_key_,
|
||||||
|
&derived_session_key_)) {
|
||||||
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_ECC_KEYGEN_FAILED,
|
||||||
|
"drm-certificate-shared-key-gen-failed");
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature) const override {
|
||||||
|
CHECK(client_ecc_public_key_);
|
||||||
|
|
||||||
|
if (!client_ecc_public_key_->VerifySignature(message, signature)) {
|
||||||
|
return Status(error_space, INVALID_SIGNATURE, "");
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
// Returns an aes key generated from the sha256 hash of the shared ecc secret.
|
||||||
|
// This key is used for key derivation.
|
||||||
|
const std::string& session_key() const override {
|
||||||
|
return derived_session_key_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an ephemeral serialized ecc public key. This key is added to a
|
||||||
|
// license response in the SignedMessage::session_key field. The client will
|
||||||
|
// use this key to generate the shared secret and derived session key.
|
||||||
|
const std::string& wrapped_session_key() const override {
|
||||||
|
return ephemeral_public_key_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<ECPublicKey> client_ecc_public_key_;
|
||||||
|
std::string ephemeral_public_key_;
|
||||||
|
std::string derived_session_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
Status CertificateClientCert::Initialize(
|
||||||
|
const DrmRootCertificate* root_certificate,
|
||||||
|
const std::string& serialized_certificate) {
|
||||||
|
CHECK(root_certificate);
|
||||||
|
|
||||||
|
if (is_initialized_) {
|
||||||
|
return Status(error_space, INVALID_PARAMETER,
|
||||||
|
"certificate-is-already-initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
SignedDrmCertificate signed_device_cert;
|
||||||
|
Status status = root_certificate->VerifyCertificate(
|
||||||
|
serialized_certificate, &signed_device_cert, &device_cert_);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
if (device_cert_.type() != DrmCertificate::DEVICE ||
|
||||||
|
device_cert_.public_key().empty()) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"expected-device-certificate-type");
|
||||||
|
}
|
||||||
|
|
||||||
|
const SignedDrmCertificate& device_cert_signer = signed_device_cert.signer();
|
||||||
|
|
||||||
|
if (!model_certificate_.ParseFromString(
|
||||||
|
device_cert_signer.drm_certificate())) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"drm-certificate-invalid-signer");
|
||||||
|
}
|
||||||
|
if (model_certificate_.type() != DrmCertificate::DEVICE_MODEL) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"expected-device-model-certificate-type");
|
||||||
|
}
|
||||||
|
if (!model_certificate_.has_serial_number()) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"missing-signer-serial-number");
|
||||||
|
}
|
||||||
|
// Check to see if this model certificate is signed by a
|
||||||
|
// provisioner (entity using Widevine Provisioning Server SDK).
|
||||||
|
if (device_cert_signer.has_signer()) {
|
||||||
|
if (!provisioner_certificate_.ParseFromString(
|
||||||
|
device_cert_signer.signer().drm_certificate())) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"model-certificate-invalid-signer");
|
||||||
|
}
|
||||||
|
if (provisioner_certificate_.type() != DrmCertificate::PROVISIONER) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"expected-provisioning-provider-certificate-type");
|
||||||
|
}
|
||||||
|
if (!provisioner_certificate_.has_provider_id() ||
|
||||||
|
provisioner_certificate_.provider_id().empty()) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"missing-provisioning-service-id");
|
||||||
|
}
|
||||||
|
signed_by_provisioner_ = true;
|
||||||
|
}
|
||||||
|
if (!model_certificate_.has_system_id()) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"model-certificate-missing-system-id");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (device_cert_.algorithm()) {
|
||||||
|
case DrmCertificate::RSA:
|
||||||
|
algorithm_ = absl::make_unique<ClientCertAlgorithmRSA>();
|
||||||
|
break;
|
||||||
|
case DrmCertificate::ECC_SECP256R1:
|
||||||
|
case DrmCertificate::ECC_SECP384R1:
|
||||||
|
case DrmCertificate::ECC_SECP521R1:
|
||||||
|
algorithm_ = absl::make_unique<ClientCertAlgorithmECC>();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"unsupported-certificate-algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
status = algorithm_->Initialize(device_cert_.public_key(),
|
||||||
|
device_cert_.algorithm());
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
is_initialized_ = true;
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status CertificateClientCert::VerifySignature(
|
||||||
|
const std::string& message, const std::string& signature,
|
||||||
|
ProtocolVersion protocol_version) const {
|
||||||
|
return algorithm_->VerifySignature(
|
||||||
|
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||||
|
signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CertificateClientCert::GenerateSigningKey(
|
||||||
|
const std::string& message, ProtocolVersion protocol_version) {
|
||||||
|
signing_key_ = crypto_util::DeriveKey(
|
||||||
|
key(), crypto_util::kSigningKeyLabel,
|
||||||
|
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||||
|
SigningKeyMaterialSizeBits(protocol_version));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
96
common/certificate_client_cert.h
Normal file
96
common/certificate_client_cert.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_CERTIFICATE_CLIENT_CERT_H_
|
||||||
|
#define COMMON_CERTIFICATE_CLIENT_CERT_H_
|
||||||
|
|
||||||
|
#include "common/client_cert.h"
|
||||||
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ClientCertAlgorithm {
|
||||||
|
public:
|
||||||
|
ClientCertAlgorithm() = default;
|
||||||
|
virtual ~ClientCertAlgorithm() = default;
|
||||||
|
ClientCertAlgorithm(const ClientCertAlgorithm&) = delete;
|
||||||
|
ClientCertAlgorithm& operator=(const ClientCertAlgorithm&) = delete;
|
||||||
|
|
||||||
|
// Initialize the algorithm module using the |public_key| from the drm
|
||||||
|
// certificate. The |algorithm| value provides additional information needed
|
||||||
|
// by the ECC implementation of this interface.
|
||||||
|
virtual Status Initialize(const std::string& public_key,
|
||||||
|
DrmCertificate::Algorithm algorithm) = 0;
|
||||||
|
|
||||||
|
// Verify the |signature| of an incoming request |message| using the public
|
||||||
|
// key from the drm certificate.
|
||||||
|
virtual Status VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature) const = 0;
|
||||||
|
|
||||||
|
// Returns the key to be used in key derivation of the license
|
||||||
|
// protocol.
|
||||||
|
virtual const std::string& session_key() const = 0;
|
||||||
|
|
||||||
|
// Returns a byte std::string to be included in the SignedMessage::session_key
|
||||||
|
// field of a license response. This key may be either an encrypted aes key,
|
||||||
|
// or the bytes of an ephemeral public key.
|
||||||
|
virtual const std::string& wrapped_session_key() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CertificateClientCert : public ClientCert {
|
||||||
|
public:
|
||||||
|
CertificateClientCert() {}
|
||||||
|
~CertificateClientCert() override {}
|
||||||
|
CertificateClientCert(const CertificateClientCert&) = delete;
|
||||||
|
CertificateClientCert& operator=(const CertificateClientCert&) = delete;
|
||||||
|
Status Initialize(const DrmRootCertificate* root_certificate,
|
||||||
|
const std::string& serialized_certificate);
|
||||||
|
|
||||||
|
Status VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature,
|
||||||
|
ProtocolVersion protocol_version) const override;
|
||||||
|
|
||||||
|
void GenerateSigningKey(const std::string& message,
|
||||||
|
ProtocolVersion protocol_version) override;
|
||||||
|
|
||||||
|
const std::string& encrypted_key() const override {
|
||||||
|
return algorithm_->wrapped_session_key();
|
||||||
|
}
|
||||||
|
const std::string& key() const override { return algorithm_->session_key(); }
|
||||||
|
const std::string& serial_number() const override {
|
||||||
|
return device_cert_.serial_number();
|
||||||
|
}
|
||||||
|
const std::string& service_id() const override {
|
||||||
|
return provisioner_certificate_.provider_id();
|
||||||
|
}
|
||||||
|
const std::string& signing_key() const override { return signing_key_; }
|
||||||
|
const std::string& signer_serial_number() const override {
|
||||||
|
return model_certificate_.serial_number();
|
||||||
|
}
|
||||||
|
uint32_t signer_creation_time_seconds() const override {
|
||||||
|
return model_certificate_.creation_time_seconds();
|
||||||
|
}
|
||||||
|
bool signed_by_provisioner() const override { return signed_by_provisioner_; }
|
||||||
|
uint32_t system_id() const override { return model_certificate_.system_id(); }
|
||||||
|
widevine::ClientIdentification::TokenType type() const override {
|
||||||
|
return ClientIdentification::DRM_DEVICE_CERTIFICATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<ClientCertAlgorithm> algorithm_;
|
||||||
|
bool signed_by_provisioner_ = false;
|
||||||
|
std::string signing_key_;
|
||||||
|
DrmCertificate model_certificate_;
|
||||||
|
DrmCertificate device_cert_;
|
||||||
|
DrmCertificate provisioner_certificate_;
|
||||||
|
bool is_initialized_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_CERTIFICATE_CLIENT_CERT_H_
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Copyright 2017 Google LLC.
|
// Copyright 2019 Google LLC.
|
||||||
//
|
//
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
@@ -8,19 +8,19 @@
|
|||||||
|
|
||||||
#include "common/client_cert.h"
|
#include "common/client_cert.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "strings/serialize.h"
|
#include "absl/memory/memory.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "common/certificate_client_cert.h"
|
||||||
#include "util/gtl/map_util.h"
|
|
||||||
#include "common/crypto_util.h"
|
#include "common/crypto_util.h"
|
||||||
#include "common/drm_root_certificate.h"
|
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
|
#include "common/keybox_client_cert.h"
|
||||||
#include "common/random_util.h"
|
#include "common/random_util.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
#include "common/sha_util.h"
|
#include "common/sha_util.h"
|
||||||
#include "common/signing_key_util.h"
|
#include "common/signing_key_util.h"
|
||||||
#include "common/status.h"
|
#include "common/status.h"
|
||||||
@@ -30,93 +30,6 @@
|
|||||||
#include "protos/public/signed_drm_certificate.pb.h"
|
#include "protos/public/signed_drm_certificate.pb.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace {
|
|
||||||
|
|
||||||
const int kKeyboxSizeBytes = 72;
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// instead of ClientCert** to explicitly assigning ownership of the created
|
|
||||||
// object to the caller.
|
|
||||||
|
|
||||||
Status ClientCert::Create(const DrmRootCertificate* root_certificate,
|
|
||||||
ClientIdentification::TokenType token_type,
|
|
||||||
const std::string& token, ClientCert** client_cert) {
|
|
||||||
DCHECK(client_cert);
|
|
||||||
if (token_type == ClientIdentification::KEYBOX) {
|
|
||||||
*client_cert = nullptr;
|
|
||||||
if (token.size() < kKeyboxSizeBytes) {
|
|
||||||
return Status(error_space, INVALID_KEYBOX_TOKEN,
|
|
||||||
"keybox-token-is-too-short");
|
|
||||||
}
|
|
||||||
return ClientCert::CreateWithKeybox(token, client_cert);
|
|
||||||
} else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
|
||||||
return CreateWithDrmCertificate(root_certificate, token, client_cert);
|
|
||||||
} else {
|
|
||||||
return Status(error_space, error::UNIMPLEMENTED,
|
|
||||||
"client-type-not-implemented");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
|
|
||||||
ClientCert** client_cert) {
|
|
||||||
CHECK(client_cert);
|
|
||||||
*client_cert = nullptr;
|
|
||||||
|
|
||||||
std::unique_ptr<KeyboxClientCert> new_client_cert(new KeyboxClientCert);
|
|
||||||
Status status = new_client_cert->Initialize(keybox_token);
|
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
*client_cert = new_client_cert.release();
|
|
||||||
return OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
Status ClientCert::CreateWithDrmCertificate(
|
|
||||||
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
|
|
||||||
ClientCert** client_cert) {
|
|
||||||
CHECK(client_cert);
|
|
||||||
*client_cert = nullptr;
|
|
||||||
|
|
||||||
std::unique_ptr<CertificateClientCert> new_client_cert(
|
|
||||||
new CertificateClientCert);
|
|
||||||
Status status =
|
|
||||||
new_client_cert->Initialize(root_certificate, drm_certificate);
|
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
*client_cert = new_client_cert.release();
|
|
||||||
return OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientCert::CreateSignature(const std::string& message, std::string* signature) {
|
|
||||||
DCHECK(signature);
|
|
||||||
DCHECK(!signing_key().empty());
|
|
||||||
if (signature == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
using crypto_util::CreateSignatureHmacSha256;
|
|
||||||
*signature =
|
|
||||||
CreateSignatureHmacSha256(GetServerSigningKey(signing_key()), message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientCert::GenerateSigningKey(const std::string& message,
|
|
||||||
ProtocolVersion protocol_version) {
|
|
||||||
if (!signing_key_.empty()) return;
|
|
||||||
DCHECK(!key().empty());
|
|
||||||
using crypto_util::DeriveKey;
|
|
||||||
using crypto_util::kSigningKeyLabel;
|
|
||||||
set_signing_key(
|
|
||||||
DeriveKey(key(), kSigningKeyLabel,
|
|
||||||
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
|
||||||
SigningKeyMaterialSizeBits(protocol_version)));
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyboxClientCert::KeyboxClientCert() {}
|
|
||||||
|
|
||||||
KeyboxClientCert::~KeyboxClientCert() {}
|
|
||||||
|
|
||||||
void KeyboxClientCert::SetPreProvisioningKeys(
|
void KeyboxClientCert::SetPreProvisioningKeys(
|
||||||
const std::multimap<uint32_t, std::string>& keymap) {
|
const std::multimap<uint32_t, std::string>& keymap) {
|
||||||
@@ -139,124 +52,50 @@ uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
|
|||||||
return WvmTokenHandler::GetSystemId(keybox_bytes);
|
return WvmTokenHandler::GetSystemId(keybox_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) {
|
Status ClientCert::Create(
|
||||||
if (keybox_bytes.size() < kKeyboxSizeBytes) {
|
const DrmRootCertificate* root_certificate,
|
||||||
return Status(error_space, INVALID_KEYBOX_TOKEN,
|
widevine::ClientIdentification::TokenType token_type,
|
||||||
"keybox-token-is-too-short");
|
const std::string& token, std::unique_ptr<ClientCert>* client_cert) {
|
||||||
}
|
CHECK(client_cert);
|
||||||
|
Status status;
|
||||||
|
switch (token_type) {
|
||||||
|
case ClientIdentification::KEYBOX:
|
||||||
|
return CreateWithKeybox(token, client_cert);
|
||||||
|
|
||||||
set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes));
|
case ClientIdentification::DRM_DEVICE_CERTIFICATE:
|
||||||
set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes));
|
return CreateWithDrmCertificate(root_certificate, token, client_cert);
|
||||||
bool insecure_keybox = false;
|
|
||||||
Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &device_key_,
|
default:
|
||||||
nullptr, &insecure_keybox);
|
return Status(error_space, error::UNIMPLEMENTED,
|
||||||
if (!status.ok()) {
|
"client-type-not-implemented");
|
||||||
Errors new_code = status.error_code() == error::NOT_FOUND
|
|
||||||
? MISSING_PRE_PROV_KEY
|
|
||||||
: KEYBOX_DECRYPT_ERROR;
|
|
||||||
return Status(error_space, new_code, status.error_message());
|
|
||||||
}
|
}
|
||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status KeyboxClientCert::VerifySignature(const std::string& message,
|
// Creates a Device Certificate based ClientCert. The |client_cert| is a
|
||||||
const std::string& signature,
|
// caller supplied unique_ptr to receive the new ClientCert.
|
||||||
ProtocolVersion protocol_version) {
|
Status ClientCert::CreateWithDrmCertificate(
|
||||||
DCHECK(!signing_key().empty());
|
const DrmRootCertificate* root_certificate,
|
||||||
using crypto_util::VerifySignatureHmacSha256;
|
const std::string& drm_certificate,
|
||||||
if (!VerifySignatureHmacSha256(
|
std::unique_ptr<ClientCert>* client_cert) {
|
||||||
GetClientSigningKey(signing_key(), protocol_version), signature,
|
CHECK(client_cert);
|
||||||
message)) {
|
auto device_cert = absl::make_unique<CertificateClientCert>();
|
||||||
return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
|
Status status = device_cert->Initialize(root_certificate, drm_certificate);
|
||||||
|
if (status.ok()) {
|
||||||
|
*client_cert = std::move(device_cert);
|
||||||
}
|
}
|
||||||
return OkStatus();
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
CertificateClientCert::CertificateClientCert() {}
|
Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
|
||||||
|
std::unique_ptr<ClientCert>* client_cert) {
|
||||||
CertificateClientCert::~CertificateClientCert() {}
|
CHECK(client_cert);
|
||||||
|
auto kbx_cert = absl::make_unique<KeyboxClientCert>();
|
||||||
Status CertificateClientCert::Initialize(
|
Status status = kbx_cert->Initialize(keybox_token);
|
||||||
const DrmRootCertificate* drm_root_certificate,
|
if (status.ok()) {
|
||||||
const std::string& serialized_certificate) {
|
*client_cert = std::move(kbx_cert);
|
||||||
CHECK(drm_root_certificate);
|
|
||||||
|
|
||||||
SignedDrmCertificate signed_device_cert;
|
|
||||||
DrmCertificate device_cert;
|
|
||||||
Status status = drm_root_certificate->VerifyCertificate(
|
|
||||||
serialized_certificate, &signed_device_cert, &device_cert);
|
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
return status;
|
||||||
const SignedDrmCertificate& signer = signed_device_cert.signer();
|
|
||||||
DrmCertificate model_certificate;
|
|
||||||
if (!model_certificate.ParseFromString(signer.drm_certificate())) {
|
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
||||||
"drm-certificate-invalid-signer");
|
|
||||||
}
|
|
||||||
if (!model_certificate.has_serial_number()) {
|
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
||||||
"missing-signer-serial-number");
|
|
||||||
}
|
|
||||||
// Check to see if this model certificate is signed by a
|
|
||||||
// provisioner (entity using Widevine Provisioning Server SDK).
|
|
||||||
if (signer.has_signer()) {
|
|
||||||
DrmCertificate provisioner_certificate;
|
|
||||||
if (!provisioner_certificate.ParseFromString(
|
|
||||||
signer.signer().drm_certificate())) {
|
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
||||||
"model-certificate-invalid-signer");
|
|
||||||
}
|
|
||||||
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
|
|
||||||
set_signed_by_provisioner(true);
|
|
||||||
} else {
|
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
||||||
"expected-provisioning-provider-certificate-type");
|
|
||||||
}
|
|
||||||
if (!provisioner_certificate.has_provider_id() ||
|
|
||||||
provisioner_certificate.provider_id().empty()) {
|
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
||||||
"missing-provisioning-service-id");
|
|
||||||
}
|
|
||||||
set_service_id(provisioner_certificate.provider_id());
|
|
||||||
}
|
|
||||||
set_signer_serial_number(model_certificate.serial_number());
|
|
||||||
set_signer_creation_time_seconds(model_certificate.creation_time_seconds());
|
|
||||||
if (!model_certificate.has_system_id()) {
|
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
||||||
"model-certificate-missing-system-id");
|
|
||||||
}
|
|
||||||
set_system_id(model_certificate.system_id());
|
|
||||||
set_serial_number(device_cert.serial_number());
|
|
||||||
set_public_key(device_cert.public_key());
|
|
||||||
rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
|
|
||||||
if (rsa_public_key_ == nullptr) {
|
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
||||||
"drm-certificate-public-key-failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(user): Move this somewhere else. It is license protocol.
|
|
||||||
set_key(Random16Bytes());
|
|
||||||
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
|
|
||||||
return Status(error_space, ENCRYPT_ERROR,
|
|
||||||
"drm-certificate-failed-encrypt-session-key");
|
|
||||||
}
|
|
||||||
|
|
||||||
return OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
Status CertificateClientCert::VerifySignature(
|
|
||||||
const std::string& message, const std::string& signature,
|
|
||||||
ProtocolVersion protocol_version) {
|
|
||||||
CHECK(rsa_public_key_);
|
|
||||||
|
|
||||||
if (!rsa_public_key_->VerifySignature(
|
|
||||||
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
|
||||||
signature)) {
|
|
||||||
return Status(error_space, INVALID_SIGNATURE, "");
|
|
||||||
}
|
|
||||||
return OkStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Copyright 2017 Google LLC.
|
// Copyright 2019 Google LLC.
|
||||||
//
|
//
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
@@ -9,179 +9,67 @@
|
|||||||
#ifndef COMMON_CLIENT_CERT_H__
|
#ifndef COMMON_CLIENT_CERT_H__
|
||||||
#define COMMON_CLIENT_CERT_H__
|
#define COMMON_CLIENT_CERT_H__
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "common/rsa_key.h"
|
#include "common/drm_root_certificate.h"
|
||||||
#include "common/status.h"
|
#include "common/status.h"
|
||||||
#include "protos/public/client_identification.pb.h"
|
#include "protos/public/client_identification.pb.h"
|
||||||
#include "protos/public/license_protocol.pb.h"
|
#include "protos/public/license_protocol.pb.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
class DrmRootCertificate;
|
|
||||||
class SignedDrmCertificate;
|
|
||||||
|
|
||||||
// Handler class for LicenseRequests; validates requests and encrypts licenses.
|
// 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 {
|
class ClientCert {
|
||||||
|
protected:
|
||||||
|
ClientCert() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~ClientCert() {}
|
// Creates a ClientCert from the |token|. The type of ClientCert created is
|
||||||
|
// determined by the |token_type|.
|
||||||
static Status Create(
|
static Status Create(
|
||||||
const DrmRootCertificate* root_certificate,
|
const DrmRootCertificate* root_certificate,
|
||||||
widevine::ClientIdentification::TokenType token_type,
|
widevine::ClientIdentification::TokenType token_type,
|
||||||
const std::string& token, ClientCert** client_cert);
|
const std::string& token, std::unique_ptr<ClientCert>* client_cert);
|
||||||
// Creates a Keybox based ClientCert.
|
|
||||||
|
// Creates a Keybox based ClientCert. The |client_cert| is a caller supplied
|
||||||
|
// unique_ptr to receive the new ClientCert.
|
||||||
static Status CreateWithKeybox(const std::string& keybox_token,
|
static Status CreateWithKeybox(const std::string& keybox_token,
|
||||||
ClientCert** client_cert);
|
std::unique_ptr<ClientCert>* client_cert);
|
||||||
|
|
||||||
// Creates a Device Certificate based ClientCert.
|
// Creates a Device Certificate based ClientCert.
|
||||||
static Status CreateWithDrmCertificate(
|
static Status CreateWithDrmCertificate(
|
||||||
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
|
const DrmRootCertificate* root_certificate,
|
||||||
ClientCert** client_cert);
|
const std::string& drm_certificate,
|
||||||
// Creates a HMAC SHA256 signature based on the message and the key().
|
std::unique_ptr<ClientCert>* client_cert);
|
||||||
// signature is owned by the caller and can not be NULL.
|
|
||||||
virtual void CreateSignature(const std::string& message, std::string* signature);
|
virtual ~ClientCert() = default;
|
||||||
|
ClientCert(const ClientCert&) = delete;
|
||||||
|
ClientCert& operator=(const ClientCert&) = delete;
|
||||||
|
|
||||||
// Checks the passed in signature against a signature created used the
|
// Checks the passed in signature against a signature created used the
|
||||||
// classes information and the passed in message. Returns OK if signature
|
// classes information and the passed in message. Returns OK if signature
|
||||||
// is valid.
|
// is valid.
|
||||||
virtual Status VerifySignature(const std::string& message, const std::string& signature,
|
virtual Status VerifySignature(const std::string& message,
|
||||||
ProtocolVersion protocol_version) = 0;
|
const std::string& signature,
|
||||||
|
ProtocolVersion protocol_version) const = 0;
|
||||||
|
|
||||||
// Creates a signing_key that is accessible using signing_key(). Signing_key
|
// Creates a signing_key that is accessible using signing_key(). Signing_key
|
||||||
// is constructed by doing a key derivation using the key() and message.
|
// is constructed by doing a key derivation using the key() and message.
|
||||||
virtual void GenerateSigningKey(const std::string& message,
|
virtual void GenerateSigningKey(const std::string& message,
|
||||||
ProtocolVersion protocol_version);
|
ProtocolVersion protocol_version) = 0;
|
||||||
// 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 const std::string& encrypted_key() const = 0;
|
||||||
virtual uint32_t system_id() const { return system_id_; }
|
virtual const std::string& key() const = 0;
|
||||||
virtual const std::string& signing_key() const { return signing_key_; }
|
virtual const std::string& serial_number() const = 0;
|
||||||
virtual const std::string& public_key() const { return public_key_; }
|
virtual const std::string& service_id() const = 0;
|
||||||
virtual const std::string& serial_number() const { return serial_number_; }
|
virtual const std::string& signing_key() const = 0;
|
||||||
virtual void set_serial_number(const std::string& serial_number) {
|
virtual const std::string& signer_serial_number() const = 0;
|
||||||
serial_number_ = serial_number;
|
virtual uint32_t signer_creation_time_seconds() const = 0;
|
||||||
}
|
virtual bool signed_by_provisioner() const = 0;
|
||||||
virtual const std::string& signer_serial_number() const {
|
virtual uint32_t system_id() const = 0;
|
||||||
return signer_serial_number_;
|
|
||||||
}
|
|
||||||
virtual uint32_t signer_creation_time_seconds() const {
|
|
||||||
return signer_creation_time_seconds_;
|
|
||||||
}
|
|
||||||
virtual widevine::ClientIdentification::TokenType type() const = 0;
|
virtual widevine::ClientIdentification::TokenType type() const = 0;
|
||||||
virtual std::string service_id() const { return service_id_; }
|
|
||||||
virtual bool signed_by_provisioner() const { return signed_by_provisioner_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ClientCert() {}
|
|
||||||
|
|
||||||
virtual void set_system_id(uint32_t system_id) { system_id_ = system_id; }
|
|
||||||
virtual void set_signing_key(const std::string& signing_key) {
|
|
||||||
signing_key_ = signing_key;
|
|
||||||
}
|
|
||||||
virtual void set_service_id(const std::string& service_id) {
|
|
||||||
service_id_ = service_id;
|
|
||||||
}
|
|
||||||
virtual void set_signed_by_provisioner(bool provisioner_signed_flag) {
|
|
||||||
signed_by_provisioner_ = provisioner_signed_flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string public_key_;
|
|
||||||
std::string serial_number_;
|
|
||||||
std::string signer_serial_number_;
|
|
||||||
uint32_t signer_creation_time_seconds_ = 0;
|
|
||||||
bool signed_by_provisioner_ = false;
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t system_id_ = 0;
|
|
||||||
std::string signing_key_;
|
|
||||||
std::string service_id_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ClientCert);
|
|
||||||
};
|
|
||||||
|
|
||||||
// This class implements the crypto operations based on the Widevine keybox.
|
|
||||||
// It will unpack token and perform all the crypto operations for securing
|
|
||||||
// the key material in the license response.
|
|
||||||
class KeyboxClientCert : public ClientCert {
|
|
||||||
public:
|
|
||||||
~KeyboxClientCert() override;
|
|
||||||
|
|
||||||
// Set the system-wide pre-provisioning keys; argument must be human-readable
|
|
||||||
// hex digits.
|
|
||||||
// Must be called before any other method of this class is called, unless
|
|
||||||
// created by ClientCert::CreateWithPreProvisioningKey(...).
|
|
||||||
static void SetPreProvisioningKeys(const std::multimap<uint32_t, std::string>& keys);
|
|
||||||
static bool IsSystemIdKnown(const uint32_t system_id);
|
|
||||||
static uint32_t GetSystemId(const std::string& keybox_bytes);
|
|
||||||
|
|
||||||
Status Initialize(const std::string& keybox_bytes);
|
|
||||||
|
|
||||||
Status VerifySignature(const std::string& message, const std::string& signature,
|
|
||||||
ProtocolVersion protocol_version) override;
|
|
||||||
const std::string& key() const override { return device_key_; }
|
|
||||||
void set_key(const std::string& key) override { device_key_ = key; }
|
|
||||||
const std::string& encrypted_key() const override { return encrypted_device_key_; }
|
|
||||||
widevine::ClientIdentification::TokenType type() const override {
|
|
||||||
return widevine::ClientIdentification::KEYBOX;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
KeyboxClientCert();
|
|
||||||
|
|
||||||
friend class ClientCert;
|
|
||||||
friend class MockKeyboxClientCert;
|
|
||||||
|
|
||||||
std::string device_key_;
|
|
||||||
std::string encrypted_device_key_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(KeyboxClientCert);
|
|
||||||
};
|
|
||||||
// This class implements the device certificate operations based on RSA keys.
|
|
||||||
// It will unpack token and perform all the crypto operations for securing
|
|
||||||
// the key material in the license response.
|
|
||||||
using widevine::RsaPublicKey;
|
|
||||||
class CertificateClientCert : public ClientCert {
|
|
||||||
public:
|
|
||||||
~CertificateClientCert() override;
|
|
||||||
|
|
||||||
Status VerifySignature(const std::string& message, const std::string& signature,
|
|
||||||
ProtocolVersion protocol_version) override;
|
|
||||||
const std::string& key() const override { return session_key_; }
|
|
||||||
void set_key(const std::string& key) override { session_key_ = key; }
|
|
||||||
const std::string& encrypted_key() const override {
|
|
||||||
return encrypted_session_key_;
|
|
||||||
}
|
|
||||||
widevine::ClientIdentification::TokenType type() const override {
|
|
||||||
return widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
friend class ClientCert;
|
|
||||||
friend class MockCertificateClientCert;
|
|
||||||
Status Initialize(const DrmRootCertificate* drm_root_certificate,
|
|
||||||
const std::string& serialized_certificate);
|
|
||||||
virtual void set_public_key(const std::string& public_key) {
|
|
||||||
public_key_ = public_key;
|
|
||||||
}
|
|
||||||
virtual void set_signer_serial_number(const std::string& signer_serial_number) {
|
|
||||||
signer_serial_number_ = signer_serial_number;
|
|
||||||
}
|
|
||||||
virtual void set_signer_creation_time_seconds(uint32_t creation_time_seconds) {
|
|
||||||
signer_creation_time_seconds_ = creation_time_seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string session_key_;
|
|
||||||
std::string encrypted_session_key_;
|
|
||||||
std::unique_ptr<RsaPublicKey> rsa_public_key_;
|
|
||||||
|
|
||||||
private:
|
|
||||||
CertificateClientCert();
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(CertificateClientCert);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // COMMON_CLIENT_CERT_H__
|
#endif // COMMON_CLIENT_CERT_H__
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Copyright 2017 Google LLC.
|
// Copyright 2019 Google LLC.
|
||||||
//
|
//
|
||||||
// This software is licensed under the terms defined in the Widevine Master
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
// License Agreement. For a copy of this agreement, please contact
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
@@ -8,41 +8,42 @@
|
|||||||
|
|
||||||
#include "common/client_cert.h"
|
#include "common/client_cert.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "glog/logging.h"
|
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "common/ec_test_keys.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/error_space.h"
|
||||||
|
#include "common/keybox_client_cert.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
#include "common/rsa_test_keys.h"
|
#include "common/rsa_test_keys.h"
|
||||||
#include "common/sha_util.h"
|
#include "common/sha_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
#include "common/test_drm_certificates.h"
|
#include "common/test_drm_certificates.h"
|
||||||
#include "common/wvm_test_keys.h"
|
#include "common/wvm_test_keys.h"
|
||||||
#include "protos/public/drm_certificate.pb.h"
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
#include "protos/public/errors.pb.h"
|
#include "protos/public/errors.pb.h"
|
||||||
#include "protos/public/signed_drm_certificate.pb.h"
|
#include "protos/public/signed_drm_certificate.pb.h"
|
||||||
|
|
||||||
|
using widevine::ClientCert;
|
||||||
|
using widevine::ClientIdentification;
|
||||||
|
using widevine::Status;
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
const DrmCertificate::Type kNoSigner = DrmCertificate::ROOT;
|
||||||
|
const DrmCertificate::Type kDeviceModelSigner = DrmCertificate::DEVICE_MODEL;
|
||||||
|
const DrmCertificate::Type kProvisionerSigner = DrmCertificate::PROVISIONER;
|
||||||
|
|
||||||
// TODO(user): Change these tests to use on-the-fly generated intermediate
|
// TODO(user): Change these tests to use on-the-fly generated intermediate
|
||||||
// and device certificates based on RsaTestKeys.
|
// and device certificates based on RsaTestKeys.
|
||||||
// TODO(user): Add testcase(s) CreateSignature,
|
// TODO(user): Add testcase(s) CreateSignature,
|
||||||
// and GenerateSigningKey.
|
// and GenerateSigningKey.
|
||||||
|
|
||||||
namespace widevine {
|
class ClientCertTest
|
||||||
|
: public ::testing::TestWithParam<DrmCertificate::Algorithm> {
|
||||||
using ::testing::_;
|
|
||||||
using ::testing::Return;
|
|
||||||
|
|
||||||
class ClientCertTest : public ::testing::Test {
|
|
||||||
public:
|
public:
|
||||||
|
~ClientCertTest() override = default;
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
if (!setup_preprov_keys_) {
|
if (!setup_preprov_keys_) {
|
||||||
KeyboxClientCert::SetPreProvisioningKeys(
|
KeyboxClientCert::SetPreProvisioningKeys(
|
||||||
@@ -82,7 +83,7 @@ class ClientCertTest : public ::testing::Test {
|
|||||||
: certificate_(certificate),
|
: certificate_(certificate),
|
||||||
expected_serial_number_(expected_serial_number),
|
expected_serial_number_(expected_serial_number),
|
||||||
expected_system_id_(expected_system_id),
|
expected_system_id_(expected_system_id),
|
||||||
expected_status_(std::move(expected_status)) {}
|
expected_status_(expected_status) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
void TestBasicValidation(const TestTokenAndKeys& expectation,
|
void TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||||
@@ -91,32 +92,41 @@ class ClientCertTest : public ::testing::Test {
|
|||||||
void TestBasicValidationDrmCertificate(
|
void TestBasicValidationDrmCertificate(
|
||||||
const TestCertificateAndData& expectation, const bool compare_data);
|
const TestCertificateAndData& expectation, const bool compare_data);
|
||||||
|
|
||||||
void GenerateSignature(const std::string& message, const std::string& private_key,
|
void GenerateSignature(const std::string& message,
|
||||||
|
const std::string& private_key,
|
||||||
std::string* signature);
|
std::string* signature);
|
||||||
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate,
|
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate,
|
||||||
SignedDrmCertificate* signer,
|
SignedDrmCertificate* signer,
|
||||||
const std::string& private_key);
|
const std::string& private_key);
|
||||||
DrmCertificate* GenerateProvisionerCertificate(uint32_t system_id,
|
DrmCertificate* GenerateProvisionerCertificate(
|
||||||
const std::string& serial_number,
|
uint32_t system_id, const std::string& serial_number,
|
||||||
const std::string& provider_id);
|
const std::string& provider_id);
|
||||||
SignedDrmCertificate* GenerateSignedProvisionerCertificate(
|
SignedDrmCertificate* GenerateSignedProvisionerCertificate(
|
||||||
uint32_t system_id, const std::string& serial_number, const std::string& service_id);
|
uint32_t system_id, const std::string& serial_number,
|
||||||
DrmCertificate* GenerateIntermediateCertificate(uint32_t system_id,
|
const std::string& service_id);
|
||||||
const std::string& serial_number);
|
DrmCertificate* GenerateIntermediateCertificate(
|
||||||
|
uint32_t system_id, const std::string& serial_number);
|
||||||
SignedDrmCertificate* GenerateSignedIntermediateCertificate(
|
SignedDrmCertificate* GenerateSignedIntermediateCertificate(
|
||||||
SignedDrmCertificate* signer, uint32_t system_id,
|
SignedDrmCertificate* signer, uint32_t system_id,
|
||||||
const std::string& serial_number);
|
const std::string& serial_number, DrmCertificate::Type signer_cert_type);
|
||||||
DrmCertificate* GenerateDrmCertificate(uint32_t system_id,
|
DrmCertificate* GenerateDrmCertificate(
|
||||||
const std::string& serial_number);
|
uint32_t system_id, const std::string& serial_number,
|
||||||
|
DrmCertificate::Algorithm = DrmCertificate::RSA);
|
||||||
SignedDrmCertificate* GenerateSignedDrmCertificate(
|
SignedDrmCertificate* GenerateSignedDrmCertificate(
|
||||||
SignedDrmCertificate* signer, uint32_t system_id,
|
SignedDrmCertificate* signer, uint32_t system_id,
|
||||||
const std::string& serial_number);
|
const std::string& serial_number,
|
||||||
|
DrmCertificate::Algorithm = DrmCertificate::RSA);
|
||||||
|
|
||||||
|
std::string GetPublicKeyByCertType(DrmCertificate::Type cert_type);
|
||||||
|
std::string GetPrivateKeyByCertType(DrmCertificate::Type cert_type);
|
||||||
|
std::string GetECCPublicKey(DrmCertificate::Algorithm algorithm);
|
||||||
|
|
||||||
RsaTestKeys test_rsa_keys_;
|
RsaTestKeys test_rsa_keys_;
|
||||||
TestDrmCertificates test_drm_certs_;
|
TestDrmCertificates test_drm_certs_;
|
||||||
std::unique_ptr<DrmRootCertificate> root_cert_;
|
std::unique_ptr<DrmRootCertificate> root_cert_;
|
||||||
static bool setup_preprov_keys_;
|
static bool setup_preprov_keys_;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ClientCertTest::setup_preprov_keys_(false);
|
bool ClientCertTest::setup_preprov_keys_(false);
|
||||||
|
|
||||||
void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||||
@@ -124,32 +134,22 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
|||||||
const bool compare_device_key) {
|
const bool compare_device_key) {
|
||||||
// Test validation of a valid request.
|
// Test validation of a valid request.
|
||||||
Status status;
|
Status status;
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
std::unique_ptr<ClientCert> keybox_cert;
|
||||||
|
|
||||||
// Two ways to create a client cert object, test both.
|
status = ClientCert::Create(root_cert_.get(), ClientIdentification::KEYBOX,
|
||||||
for (int i = 0; i < 2; i++) {
|
expectation.token_, &keybox_cert);
|
||||||
if (i == 0) {
|
if (expect_success) {
|
||||||
status =
|
ASSERT_EQ(OkStatus(), status);
|
||||||
ClientCert::Create(root_cert_.get(), ClientIdentification::KEYBOX,
|
ASSERT_TRUE(keybox_cert.get());
|
||||||
expectation.token_, &client_cert_ptr);
|
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
|
||||||
} else {
|
EXPECT_EQ(expectation.expected_serial_number_,
|
||||||
status =
|
keybox_cert->serial_number());
|
||||||
ClientCert::CreateWithKeybox(expectation.token_, &client_cert_ptr);
|
if (compare_device_key) {
|
||||||
}
|
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
|
||||||
std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
|
|
||||||
if (expect_success) {
|
|
||||||
ASSERT_EQ(OkStatus(), status);
|
|
||||||
ASSERT_TRUE(keybox_cert.get());
|
|
||||||
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
|
|
||||||
EXPECT_EQ(expectation.expected_serial_number_,
|
|
||||||
keybox_cert->serial_number());
|
|
||||||
if (compare_device_key) {
|
|
||||||
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EXPECT_NE(OkStatus(), status);
|
|
||||||
EXPECT_FALSE(keybox_cert);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
EXPECT_NE(OkStatus(), status);
|
||||||
|
EXPECT_FALSE(keybox_cert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,11 +162,10 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
|
|||||||
|
|
||||||
// Test validation of a valid request.
|
// Test validation of a valid request.
|
||||||
Status status;
|
Status status;
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
std::unique_ptr<ClientCert> drm_certificate_cert;
|
||||||
status = ClientCert::Create(root_cert_.get(),
|
status = ClientCert::Create(root_cert_.get(),
|
||||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
expectation.certificate_, &client_cert_ptr);
|
expectation.certificate_, &drm_certificate_cert);
|
||||||
std::unique_ptr<ClientCert> drm_certificate_cert(client_cert_ptr);
|
|
||||||
ASSERT_EQ(expectation.expected_status_, status);
|
ASSERT_EQ(expectation.expected_status_, status);
|
||||||
if (expectation.expected_status_.ok()) {
|
if (expectation.expected_status_.ok()) {
|
||||||
ASSERT_TRUE(drm_certificate_cert.get());
|
ASSERT_TRUE(drm_certificate_cert.get());
|
||||||
@@ -205,13 +204,48 @@ SignedDrmCertificate* ClientCertTest::SignCertificate(
|
|||||||
return signed_certificate.release();
|
return signed_certificate.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ClientCertTest::GetPublicKeyByCertType(
|
||||||
|
DrmCertificate::Type cert_type) {
|
||||||
|
if (cert_type == DrmCertificate::DEVICE) {
|
||||||
|
return test_rsa_keys_.public_test_key_3_2048_bits();
|
||||||
|
} else if (cert_type == DrmCertificate::DEVICE_MODEL) {
|
||||||
|
return test_rsa_keys_.public_test_key_2_2048_bits();
|
||||||
|
}
|
||||||
|
return test_rsa_keys_.public_test_key_1_3072_bits();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ClientCertTest::GetPrivateKeyByCertType(
|
||||||
|
DrmCertificate::Type cert_type) {
|
||||||
|
if (cert_type == DrmCertificate::DEVICE) {
|
||||||
|
return test_rsa_keys_.private_test_key_3_2048_bits();
|
||||||
|
} else if (cert_type == DrmCertificate::DEVICE_MODEL) {
|
||||||
|
return test_rsa_keys_.private_test_key_2_2048_bits();
|
||||||
|
}
|
||||||
|
return test_rsa_keys_.private_test_key_1_3072_bits();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ClientCertTest::GetECCPublicKey(
|
||||||
|
DrmCertificate::Algorithm algorithm) {
|
||||||
|
ECTestKeys keys;
|
||||||
|
switch (algorithm) {
|
||||||
|
case DrmCertificate::ECC_SECP256R1:
|
||||||
|
return keys.public_test_key_1_secp256r1();
|
||||||
|
case DrmCertificate::ECC_SECP384R1:
|
||||||
|
return keys.public_test_key_1_secp384r1();
|
||||||
|
case DrmCertificate::ECC_SECP521R1:
|
||||||
|
return keys.public_test_key_1_secp521r1();
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
|
DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
|
||||||
uint32_t system_id, const std::string& serial_number) {
|
uint32_t system_id, const std::string& serial_number) {
|
||||||
std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate);
|
std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate);
|
||||||
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
|
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
|
||||||
intermediate_certificate->set_serial_number(serial_number);
|
intermediate_certificate->set_serial_number(serial_number);
|
||||||
intermediate_certificate->set_public_key(
|
intermediate_certificate->set_public_key(
|
||||||
test_rsa_keys_.public_test_key_2_2048_bits());
|
GetPublicKeyByCertType(DrmCertificate::DEVICE_MODEL));
|
||||||
intermediate_certificate->set_system_id(system_id);
|
intermediate_certificate->set_system_id(system_id);
|
||||||
intermediate_certificate->set_creation_time_seconds(1234);
|
intermediate_certificate->set_creation_time_seconds(1234);
|
||||||
return intermediate_certificate.release();
|
return intermediate_certificate.release();
|
||||||
@@ -219,42 +253,49 @@ DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
|
|||||||
|
|
||||||
SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
|
SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
|
||||||
SignedDrmCertificate* signer, uint32_t system_id,
|
SignedDrmCertificate* signer, uint32_t system_id,
|
||||||
const std::string& serial_number) {
|
const std::string& serial_number, DrmCertificate::Type signer_cert_type) {
|
||||||
std::unique_ptr<DrmCertificate> intermediate_certificate(
|
std::unique_ptr<DrmCertificate> intermediate_certificate(
|
||||||
GenerateIntermediateCertificate(system_id, serial_number));
|
GenerateIntermediateCertificate(system_id, serial_number));
|
||||||
|
// Must use the same key pair used by GenerateIntermediateCertificate().
|
||||||
return SignCertificate(*intermediate_certificate, signer,
|
return SignCertificate(*intermediate_certificate, signer,
|
||||||
test_rsa_keys_.private_test_key_1_3072_bits());
|
GetPrivateKeyByCertType(signer_cert_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
||||||
uint32_t system_id, const std::string& serial_number) {
|
uint32_t system_id, const std::string& serial_number,
|
||||||
|
DrmCertificate::Algorithm algorithm) {
|
||||||
std::unique_ptr<DrmCertificate> drm_certificate(new DrmCertificate);
|
std::unique_ptr<DrmCertificate> drm_certificate(new DrmCertificate);
|
||||||
drm_certificate->set_type(DrmCertificate::DEVICE);
|
drm_certificate->set_type(DrmCertificate::DEVICE);
|
||||||
drm_certificate->set_serial_number(serial_number);
|
drm_certificate->set_serial_number(serial_number);
|
||||||
drm_certificate->set_system_id(system_id);
|
drm_certificate->set_system_id(system_id);
|
||||||
drm_certificate->set_public_key(test_rsa_keys_.public_test_key_3_2048_bits());
|
drm_certificate->set_public_key(
|
||||||
|
algorithm == DrmCertificate::RSA
|
||||||
|
? GetPublicKeyByCertType(DrmCertificate::DEVICE)
|
||||||
|
: GetECCPublicKey(algorithm));
|
||||||
drm_certificate->set_creation_time_seconds(4321);
|
drm_certificate->set_creation_time_seconds(4321);
|
||||||
|
drm_certificate->set_algorithm(algorithm);
|
||||||
return drm_certificate.release();
|
return drm_certificate.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
|
SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
|
||||||
SignedDrmCertificate* signer, uint32_t system_id,
|
SignedDrmCertificate* signer, uint32_t system_id,
|
||||||
const std::string& serial_number) {
|
const std::string& serial_number, DrmCertificate::Algorithm algorithm) {
|
||||||
std::unique_ptr<DrmCertificate> drm_certificate(
|
std::unique_ptr<DrmCertificate> drm_certificate(
|
||||||
GenerateDrmCertificate(system_id, serial_number));
|
GenerateDrmCertificate(system_id, serial_number, algorithm));
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(
|
||||||
*drm_certificate, signer, test_rsa_keys_.private_test_key_2_2048_bits()));
|
SignCertificate(*drm_certificate, signer,
|
||||||
|
GetPrivateKeyByCertType(DrmCertificate::DEVICE_MODEL)));
|
||||||
return signed_drm_certificate.release();
|
return signed_drm_certificate.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
|
DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
|
||||||
uint32_t system_id, const std::string& serial_number, const std::string& provider_id) {
|
uint32_t system_id, const std::string& serial_number,
|
||||||
|
const std::string& provider_id) {
|
||||||
std::unique_ptr<DrmCertificate> provisioner_certificate(new DrmCertificate);
|
std::unique_ptr<DrmCertificate> provisioner_certificate(new DrmCertificate);
|
||||||
provisioner_certificate->set_type(DrmCertificate::PROVISIONER);
|
provisioner_certificate->set_type(DrmCertificate::PROVISIONER);
|
||||||
provisioner_certificate->set_serial_number(serial_number);
|
provisioner_certificate->set_serial_number(serial_number);
|
||||||
// TODO(user): Need to generate 3072 bit test for provisioner certificates.
|
|
||||||
provisioner_certificate->set_public_key(
|
provisioner_certificate->set_public_key(
|
||||||
test_rsa_keys_.public_test_key_1_3072_bits());
|
GetPublicKeyByCertType(DrmCertificate::DrmCertificate::PROVISIONER));
|
||||||
provisioner_certificate->set_system_id(system_id);
|
provisioner_certificate->set_system_id(system_id);
|
||||||
provisioner_certificate->set_provider_id(provider_id);
|
provisioner_certificate->set_provider_id(provider_id);
|
||||||
provisioner_certificate->set_creation_time_seconds(1234);
|
provisioner_certificate->set_creation_time_seconds(1234);
|
||||||
@@ -262,11 +303,12 @@ DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
|
SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
|
||||||
uint32_t system_id, const std::string& serial_number, const std::string& service_id) {
|
uint32_t system_id, const std::string& serial_number,
|
||||||
|
const std::string& service_id) {
|
||||||
std::unique_ptr<DrmCertificate> provisioner_certificate(
|
std::unique_ptr<DrmCertificate> provisioner_certificate(
|
||||||
GenerateProvisionerCertificate(system_id, serial_number, service_id));
|
GenerateProvisionerCertificate(system_id, serial_number, service_id));
|
||||||
return SignCertificate(*provisioner_certificate, nullptr,
|
return SignCertificate(*provisioner_certificate, nullptr,
|
||||||
test_rsa_keys_.private_test_key_1_3072_bits());
|
GetPrivateKeyByCertType(DrmCertificate::ROOT));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ClientCertTest, BasicValidation) {
|
TEST_F(ClientCertTest, BasicValidation) {
|
||||||
@@ -296,19 +338,25 @@ TEST_F(ClientCertTest, BasicValidation) {
|
|||||||
KeyboxClientCert::GetSystemId(kValidTokenAndExpectedKeys[0].token_));
|
KeyboxClientCert::GetSystemId(kValidTokenAndExpectedKeys[0].token_));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ClientCertTest, BasicCertValidation) {
|
TEST_P(ClientCertTest, BasicCertValidation) {
|
||||||
const uint32_t system_id = 1234;
|
const uint32_t system_id = 1234;
|
||||||
const std::string serial_number("serial_number");
|
const std::string serial_number("serial_number");
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_cert(
|
std::unique_ptr<SignedDrmCertificate> signed_cert(
|
||||||
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
|
GenerateSignedDrmCertificate(
|
||||||
nullptr, system_id, serial_number),
|
GenerateSignedIntermediateCertificate(nullptr, system_id,
|
||||||
system_id, serial_number + "-device"));
|
serial_number, kNoSigner),
|
||||||
|
system_id, serial_number + "-device", GetParam()));
|
||||||
const TestCertificateAndData kValidCertificateAndExpectedData(
|
const TestCertificateAndData kValidCertificateAndExpectedData(
|
||||||
signed_cert->SerializeAsString(), serial_number, system_id, OkStatus());
|
signed_cert->SerializeAsString(), serial_number, system_id, OkStatus());
|
||||||
const bool compare_data = true;
|
const bool compare_data = true;
|
||||||
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
|
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
|
||||||
compare_data);
|
compare_data);
|
||||||
}
|
}
|
||||||
|
INSTANTIATE_TEST_SUITE_P(BasicCertValidation, ClientCertTest,
|
||||||
|
testing::Values(DrmCertificate::RSA,
|
||||||
|
DrmCertificate::ECC_SECP256R1,
|
||||||
|
DrmCertificate::ECC_SECP384R1,
|
||||||
|
DrmCertificate::ECC_SECP521R1));
|
||||||
|
|
||||||
TEST_F(ClientCertTest, InvalidKeybox) {
|
TEST_F(ClientCertTest, InvalidKeybox) {
|
||||||
const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = {
|
const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = {
|
||||||
@@ -350,18 +398,19 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
GenerateSignature(invalid_drm_cert->drm_certificate(),
|
GenerateSignature(invalid_drm_cert->drm_certificate(),
|
||||||
test_rsa_keys_.private_test_key_2_2048_bits(),
|
test_rsa_keys_.private_test_key_2_2048_bits(),
|
||||||
invalid_drm_cert->mutable_signature());
|
invalid_drm_cert->mutable_signature());
|
||||||
invalid_drm_cert->set_allocated_signer(
|
invalid_drm_cert->set_allocated_signer(GenerateSignedIntermediateCertificate(
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
nullptr, system_id, signer_sn, kNoSigner));
|
||||||
// Invalid device public key.
|
// Invalid device public key.
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||||
dev_cert->set_public_key("bad-device-public-key");
|
dev_cert->set_public_key("bad-device-public-key");
|
||||||
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(
|
||||||
*dev_cert,
|
SignCertificate(*dev_cert,
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
GenerateSignedIntermediateCertificate(
|
||||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
nullptr, system_id, signer_sn, kNoSigner),
|
||||||
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// Invalid serialized intermediate certificate.
|
// Invalid serialized intermediate certificate.
|
||||||
signed_signer.reset(
|
signed_signer.reset(GenerateSignedIntermediateCertificate(
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
nullptr, system_id, signer_sn, kNoSigner));
|
||||||
signed_signer->set_drm_certificate("bad-serialized-cert");
|
signed_signer->set_drm_certificate("bad-serialized-cert");
|
||||||
GenerateSignature(signed_signer->drm_certificate(),
|
GenerateSignature(signed_signer->drm_certificate(),
|
||||||
test_rsa_keys_.private_test_key_1_3072_bits(),
|
test_rsa_keys_.private_test_key_1_3072_bits(),
|
||||||
@@ -382,7 +431,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
// Invalid device certificate signature.
|
// Invalid device certificate signature.
|
||||||
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
|
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
|
||||||
GenerateSignedDrmCertificate(
|
GenerateSignedDrmCertificate(
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn,
|
||||||
|
kNoSigner),
|
||||||
system_id, device_sn));
|
system_id, device_sn));
|
||||||
bad_device_signature->set_signature("bad-signature");
|
bad_device_signature->set_signature("bad-signature");
|
||||||
// Missing model system ID.
|
// Missing model system ID.
|
||||||
@@ -405,8 +455,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// Invalid serialized intermediate certificate.
|
// Invalid serialized intermediate certificate.
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||||
signed_signer.reset(
|
signed_signer.reset(GenerateSignedIntermediateCertificate(
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
nullptr, system_id, signer_sn, kNoSigner));
|
||||||
signed_signer->set_signature("bad-signature");
|
signed_signer->set_signature("bad-signature");
|
||||||
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
|
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
|
||||||
SignCertificate(*dev_cert, signed_signer.release(),
|
SignCertificate(*dev_cert, signed_signer.release(),
|
||||||
@@ -453,8 +503,9 @@ TEST_F(ClientCertTest, MissingPreProvKey) {
|
|||||||
"00000002012345678e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
"00000002012345678e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||||
"2517a12f4922953e"));
|
"2517a12f4922953e"));
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
std::unique_ptr<ClientCert> client_cert_ptr;
|
||||||
Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr);
|
Status status = ClientCert::Create(
|
||||||
|
root_cert_.get(), ClientIdentification::KEYBOX, token, &client_cert_ptr);
|
||||||
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
|
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,9 +521,9 @@ TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
|
|||||||
service_id));
|
service_id));
|
||||||
|
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||||
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
|
GenerateSignedIntermediateCertificate(
|
||||||
system_id,
|
signed_provisioner_cert.release(), system_id,
|
||||||
intermediate_serial_number));
|
intermediate_serial_number, kProvisionerSigner));
|
||||||
|
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||||
@@ -480,13 +531,12 @@ TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
|
|||||||
|
|
||||||
std::string serialized_cert;
|
std::string serialized_cert;
|
||||||
signed_device_cert->SerializeToString(&serialized_cert);
|
signed_device_cert->SerializeToString(&serialized_cert);
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
std::unique_ptr<ClientCert> drm_cert;
|
||||||
|
|
||||||
EXPECT_OK(ClientCert::Create(root_cert_.get(),
|
EXPECT_OK(ClientCert::Create(root_cert_.get(),
|
||||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
serialized_cert, &client_cert_ptr));
|
serialized_cert, &drm_cert));
|
||||||
ASSERT_TRUE(client_cert_ptr != nullptr);
|
ASSERT_TRUE(drm_cert);
|
||||||
std::unique_ptr<ClientCert> drm_cert(client_cert_ptr);
|
|
||||||
|
|
||||||
EXPECT_EQ(service_id, drm_cert->service_id());
|
EXPECT_EQ(service_id, drm_cert->service_id());
|
||||||
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
|
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
|
||||||
@@ -506,9 +556,9 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertEmptyServiceId) {
|
|||||||
service_id));
|
service_id));
|
||||||
|
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||||
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
|
GenerateSignedIntermediateCertificate(
|
||||||
system_id,
|
signed_provisioner_cert.release(), system_id,
|
||||||
intermediate_serial_number));
|
intermediate_serial_number, kProvisionerSigner));
|
||||||
|
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||||
@@ -516,7 +566,7 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertEmptyServiceId) {
|
|||||||
|
|
||||||
std::string serialized_cert;
|
std::string serialized_cert;
|
||||||
signed_device_cert->SerializeToString(&serialized_cert);
|
signed_device_cert->SerializeToString(&serialized_cert);
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
std::unique_ptr<ClientCert> client_cert_ptr;
|
||||||
|
|
||||||
EXPECT_EQ("missing-provisioning-service-id",
|
EXPECT_EQ("missing-provisioning-service-id",
|
||||||
ClientCert::Create(root_cert_.get(),
|
ClientCert::Create(root_cert_.get(),
|
||||||
@@ -535,27 +585,119 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
|
|||||||
const std::string intermediate_serial_number2("intermediate-serial-number-2");
|
const std::string intermediate_serial_number2("intermediate-serial-number-2");
|
||||||
|
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
|
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id2,
|
GenerateSignedIntermediateCertificate(
|
||||||
intermediate_serial_number2));
|
nullptr, system_id2, intermediate_serial_number2, kNoSigner));
|
||||||
|
|
||||||
// Instead of using a provisioner certificate to sign this intermediate
|
// Instead of using a provisioner certificate to sign this intermediate
|
||||||
// certificate, use another intermediate certificate. This is an invalid
|
// certificate, use another intermediate certificate. This is an invalid
|
||||||
// chain and should generate an error when trying to create a client
|
// chain and should generate an error when trying to create a client
|
||||||
// certificate.
|
// certificate.
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||||
GenerateSignedIntermediateCertificate(signed_intermediate_cert2.release(),
|
GenerateSignedIntermediateCertificate(
|
||||||
system_id,
|
signed_intermediate_cert2.release(), system_id,
|
||||||
intermediate_serial_number));
|
intermediate_serial_number, kDeviceModelSigner));
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||||
system_id, device_serial_number));
|
system_id, device_serial_number));
|
||||||
std::string serialized_cert;
|
std::string serialized_cert;
|
||||||
signed_device_cert->SerializeToString(&serialized_cert);
|
signed_device_cert->SerializeToString(&serialized_cert);
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
std::unique_ptr<ClientCert> client_cert_ptr;
|
||||||
|
|
||||||
// TODO(user): Fix this test. It is failing for the right reasons, but the
|
ASSERT_EQ("expected-provisioning-provider-certificate-type",
|
||||||
// certificate chain is broken (intermediate signature does not match signer).
|
ClientCert::Create(root_cert_.get(),
|
||||||
ASSERT_EQ("cache-miss-invalid-signature",
|
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
|
serialized_cert, &client_cert_ptr)
|
||||||
|
.error_message());
|
||||||
|
EXPECT_FALSE(client_cert_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ClientCertTest, InvalidDeviceCertChainSize_TooLong) {
|
||||||
|
const uint32_t system_id = 5000;
|
||||||
|
const std::string service_id("widevine_test.com");
|
||||||
|
const std::string device_serial_number("device-serial-number");
|
||||||
|
const std::string intermediate_serial_number1("intermediate-serial-number-1");
|
||||||
|
const std::string intermediate_serial_number2("intermediate-serial-number-2");
|
||||||
|
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_cert1(
|
||||||
|
GenerateSignedIntermediateCertificate(
|
||||||
|
signed_provisioner_cert.release(), system_id,
|
||||||
|
intermediate_serial_number1, kProvisionerSigner));
|
||||||
|
|
||||||
|
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
|
||||||
|
GenerateSignedIntermediateCertificate(
|
||||||
|
signed_intermediate_cert1.release(), system_id,
|
||||||
|
intermediate_serial_number2, kDeviceModelSigner));
|
||||||
|
|
||||||
|
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||||
|
GenerateSignedDrmCertificate(signed_intermediate_cert2.release(),
|
||||||
|
system_id, device_serial_number));
|
||||||
|
|
||||||
|
std::string serialized_cert;
|
||||||
|
signed_device_cert->SerializeToString(&serialized_cert);
|
||||||
|
std::unique_ptr<ClientCert> client_cert_ptr = nullptr;
|
||||||
|
|
||||||
|
ASSERT_EQ("certificate-chain-size-exceeded",
|
||||||
|
ClientCert::Create(root_cert_.get(),
|
||||||
|
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
|
serialized_cert, &client_cert_ptr)
|
||||||
|
.error_message());
|
||||||
|
EXPECT_FALSE(client_cert_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ClientCertTest, DeviceCertTypeNotLeaf) {
|
||||||
|
const uint32_t system_id = 5000;
|
||||||
|
const std::string service_id("widevine_test.com");
|
||||||
|
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||||
|
const std::string provisioner_serial_number("provisioner-serial-number");
|
||||||
|
const std::string drm_serial_number("drm-serial-number");
|
||||||
|
|
||||||
|
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
|
||||||
|
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
|
||||||
|
service_id));
|
||||||
|
|
||||||
|
// Use a DEVICE certificate as the intermediate certificate.
|
||||||
|
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||||
|
GenerateSignedDrmCertificate(signed_provisioner_cert.release(), system_id,
|
||||||
|
intermediate_serial_number));
|
||||||
|
|
||||||
|
std::unique_ptr<SignedDrmCertificate> signed_drm_cert(
|
||||||
|
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||||
|
system_id, drm_serial_number));
|
||||||
|
std::string serialized_cert;
|
||||||
|
signed_drm_cert->SerializeToString(&serialized_cert);
|
||||||
|
std::unique_ptr<ClientCert> client_cert_ptr;
|
||||||
|
|
||||||
|
EXPECT_EQ("device-cert-must-be-leaf",
|
||||||
|
ClientCert::Create(root_cert_.get(),
|
||||||
|
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
|
serialized_cert, &client_cert_ptr)
|
||||||
|
.error_message());
|
||||||
|
EXPECT_FALSE(client_cert_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ClientCertTest, InvalidLeafCertificateType) {
|
||||||
|
const uint32_t system_id = 5000;
|
||||||
|
const std::string service_id("widevine_test.com");
|
||||||
|
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, kProvisionerSigner));
|
||||||
|
std::string serialized_cert;
|
||||||
|
signed_intermediate_cert->SerializeToString(&serialized_cert);
|
||||||
|
std::unique_ptr<ClientCert> client_cert_ptr;
|
||||||
|
// Leaf certificate must be a device certificate.
|
||||||
|
EXPECT_EQ("expected-device-certificate-type",
|
||||||
ClientCert::Create(root_cert_.get(),
|
ClientCert::Create(root_cert_.get(),
|
||||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
serialized_cert, &client_cert_ptr)
|
serialized_cert, &client_cert_ptr)
|
||||||
@@ -566,11 +708,10 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
|
|||||||
TEST_F(ClientCertTest, Protocol21WithDrmCert) {
|
TEST_F(ClientCertTest, Protocol21WithDrmCert) {
|
||||||
const char message[] = "A weekend wasted is a weekend well spent.";
|
const char message[] = "A weekend wasted is a weekend well spent.";
|
||||||
|
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
std::unique_ptr<ClientCert> client_cert;
|
||||||
ASSERT_OK(ClientCert::Create(
|
ASSERT_OK(ClientCert::Create(
|
||||||
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
|
test_drm_certs_.test_user_device_certificate(), &client_cert));
|
||||||
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
|
|
||||||
|
|
||||||
std::unique_ptr<RsaPrivateKey> private_key(
|
std::unique_ptr<RsaPrivateKey> private_key(
|
||||||
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
||||||
@@ -592,11 +733,10 @@ TEST_F(ClientCertTest, Protocol22WithDrmCert) {
|
|||||||
const char message[] = "There is nothing permanent except change.";
|
const char message[] = "There is nothing permanent except change.";
|
||||||
const std::string message_hash(Sha512_Hash(message));
|
const std::string message_hash(Sha512_Hash(message));
|
||||||
|
|
||||||
ClientCert* client_cert_ptr = nullptr;
|
std::unique_ptr<ClientCert> client_cert;
|
||||||
ASSERT_OK(ClientCert::Create(
|
ASSERT_OK(ClientCert::Create(
|
||||||
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||||
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
|
test_drm_certs_.test_user_device_certificate(), &client_cert));
|
||||||
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
|
|
||||||
|
|
||||||
std::unique_ptr<RsaPrivateKey> private_key(
|
std::unique_ptr<RsaPrivateKey> private_key(
|
||||||
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
||||||
|
|||||||
@@ -10,9 +10,13 @@
|
|||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "common/aes_cbc_util.h"
|
#include "common/aes_cbc_util.h"
|
||||||
|
#include "common/client_cert.h"
|
||||||
#include "common/drm_service_certificate.h"
|
#include "common/drm_service_certificate.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
|
#include "common/keybox_client_cert.h"
|
||||||
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
#include "protos/public/errors.pb.h"
|
#include "protos/public/errors.pb.h"
|
||||||
|
#include "protos/public/signed_drm_certificate.pb.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
@@ -37,12 +41,13 @@ bool SetClientInfo(ClientIdentification* client_id, absl::string_view name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||||
absl::string_view name) {
|
absl::string_view name) {
|
||||||
return GetClientInfo(client_id, name, std::string());
|
return GetClientInfo(client_id, name, std::string());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||||
absl::string_view name, const std::string& default_value) {
|
absl::string_view name,
|
||||||
|
const std::string& default_value) {
|
||||||
for (const auto& nv : client_id.client_info()) {
|
for (const auto& nv : client_id.client_info()) {
|
||||||
if (nv.name() == name) {
|
if (nv.name() == name) {
|
||||||
return nv.value();
|
return nv.value();
|
||||||
@@ -86,4 +91,28 @@ Status DecryptEncryptedClientIdentification(
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t GetSystemId(const ClientIdentification& client_id) {
|
||||||
|
uint32_t system_id = 0;
|
||||||
|
if (client_id.has_token()) {
|
||||||
|
switch (client_id.type()) {
|
||||||
|
case ClientIdentification::KEYBOX:
|
||||||
|
system_id = KeyboxClientCert::GetSystemId(client_id.token());
|
||||||
|
break;
|
||||||
|
case ClientIdentification::DRM_DEVICE_CERTIFICATE: {
|
||||||
|
SignedDrmCertificate signed_drm_certificate;
|
||||||
|
if (signed_drm_certificate.ParseFromString(client_id.token())) {
|
||||||
|
DrmCertificate drm_certificate;
|
||||||
|
if (drm_certificate.ParseFromString(
|
||||||
|
signed_drm_certificate.drm_certificate())) {
|
||||||
|
system_id = drm_certificate.system_id();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return system_id;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -32,12 +32,13 @@ bool SetClientInfo(ClientIdentification* client_id, absl::string_view name,
|
|||||||
// Return the value from client_id.client_info() matching the given name,
|
// Return the value from client_id.client_info() matching the given name,
|
||||||
// or the empty std::string if not found.
|
// or the empty std::string if not found.
|
||||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||||
absl::string_view name);
|
absl::string_view name);
|
||||||
|
|
||||||
// Return the value from client_id.client_info() matching the given name,
|
// Return the value from client_id.client_info() matching the given name,
|
||||||
// or the given default value if not found.
|
// or the given default value if not found.
|
||||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||||
absl::string_view name, const std::string& default_value);
|
absl::string_view name,
|
||||||
|
const std::string& default_value);
|
||||||
|
|
||||||
// Decrypts the encrypted client identification in |encrypted_client_id| into
|
// Decrypts the encrypted client identification in |encrypted_client_id| into
|
||||||
// |client_id| using the private key for the service certificate which was
|
// |client_id| using the private key for the service certificate which was
|
||||||
@@ -56,6 +57,8 @@ Status DecryptEncryptedClientIdentification(
|
|||||||
const EncryptedClientIdentification& encrypted_client_id,
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
const std::string& privacy_key, ClientIdentification* client_id);
|
const std::string& privacy_key, ClientIdentification* client_id);
|
||||||
|
|
||||||
|
uint32_t GetSystemId(const ClientIdentification& client_id);
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // COMMON_CLIENT_ID_UTIL_H_
|
#endif // COMMON_CLIENT_ID_UTIL_H_
|
||||||
|
|||||||
64
common/content_id_util.cc
Normal file
64
common/content_id_util.cc
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#include "common/content_id_util.h"
|
||||||
|
|
||||||
|
#include "common/error_space.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
#include "license_server_sdk/internal/parse_content_id.h"
|
||||||
|
#include "protos/public/errors.pb.h"
|
||||||
|
#include "protos/public/external_license.pb.h"
|
||||||
|
#include "protos/public/license_protocol.pb.h"
|
||||||
|
#include "protos/public/license_server_sdk.pb.h"
|
||||||
|
#include "protos/public/widevine_pssh.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// TODO(user): Move the util methods from
|
||||||
|
// //license_server_sdk/internal/parse_content_id.h
|
||||||
|
// into this file.
|
||||||
|
|
||||||
|
Status GetContentIdFromExternalLicenseRequest(
|
||||||
|
const ExternalLicenseRequest& external_license_request,
|
||||||
|
std::string* content_id) {
|
||||||
|
LicenseRequest::ContentIdentification content_identification =
|
||||||
|
external_license_request.content_id();
|
||||||
|
WidevinePsshData widevine_pssh_data;
|
||||||
|
if (content_identification.has_widevine_pssh_data()) {
|
||||||
|
widevine_pssh_data.ParseFromString(
|
||||||
|
content_identification.widevine_pssh_data().pssh_data(0));
|
||||||
|
} else if (content_identification.has_webm_key_id()) {
|
||||||
|
widevine_pssh_data.ParseFromString(
|
||||||
|
content_identification.webm_key_id().header());
|
||||||
|
} else if (content_identification.has_init_data()) {
|
||||||
|
ContentInfo content_info;
|
||||||
|
if (ParseContentId(content_identification, &content_info).ok()) {
|
||||||
|
widevine_pssh_data =
|
||||||
|
content_info.content_info_entry(0).pssh().widevine_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*content_id = widevine_pssh_data.content_id();
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status GetContentIdFromSignedExternalLicenseRequest(
|
||||||
|
const SignedMessage& signed_message, std::string* content_id) {
|
||||||
|
if (signed_message.type() != SignedMessage::EXTERNAL_LICENSE_REQUEST) {
|
||||||
|
return Status(
|
||||||
|
error_space, error::INVALID_ARGUMENT,
|
||||||
|
"Unexpected SignedMessage Type. EXTERNAL_LICENSE_REQUEST expected");
|
||||||
|
}
|
||||||
|
ExternalLicenseRequest external_license_request;
|
||||||
|
if (!external_license_request.ParseFromString(signed_message.msg())) {
|
||||||
|
return Status(error_space, EXTERNAL_LICENSE_REQUEST_PARSE_ERROR,
|
||||||
|
"Unable to parse into External License Request");
|
||||||
|
}
|
||||||
|
return GetContentIdFromExternalLicenseRequest(external_license_request,
|
||||||
|
content_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
30
common/content_id_util.h
Normal file
30
common/content_id_util.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_CONTENT_ID_UTIL_H_
|
||||||
|
#define COMMON_CONTENT_ID_UTIL_H_
|
||||||
|
|
||||||
|
#include "common/status.h"
|
||||||
|
#include "protos/public/external_license.pb.h"
|
||||||
|
#include "protos/public/license_protocol.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Get content identifier as a std::string from a SignedMessage that includes a
|
||||||
|
// serialized ExternalLicenseRequest.
|
||||||
|
Status GetContentIdFromSignedExternalLicenseRequest(
|
||||||
|
const SignedMessage& signed_message, std::string* content_id);
|
||||||
|
|
||||||
|
// Get content identifier as a std::string from an ExternalLicenseRequest.
|
||||||
|
Status GetContentIdFromExternalLicenseRequest(
|
||||||
|
const ExternalLicenseRequest& external_license_request,
|
||||||
|
std::string* content_id);
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_CONTENT_ID_UTIL_H_
|
||||||
81
common/content_id_util_test.cc
Normal file
81
common/content_id_util_test.cc
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/content_id_util.h"
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "protos/public/errors.pb.h"
|
||||||
|
#include "protos/public/external_license.pb.h"
|
||||||
|
#include "protos/public/license_protocol.pb.h"
|
||||||
|
#include "protos/public/widevine_pssh.pb.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const char kContentId[] = "TestContentId";
|
||||||
|
const char kPlayReadyChallenge[] = "<TestPRChallenge></TestPRChallenge>";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Builds a SignedMessage that includes an ExternalLicenseRequest.
|
||||||
|
SignedMessage BuildSignedExternalLicenseRequest(
|
||||||
|
const ExternalLicenseRequest::RequestType type, const std::string& request,
|
||||||
|
const std::string& content_id) {
|
||||||
|
ExternalLicenseRequest external_license_request;
|
||||||
|
external_license_request.set_request_type(type);
|
||||||
|
external_license_request.set_request(request);
|
||||||
|
LicenseRequest::ContentIdentification::WidevinePsshData* cenc_id =
|
||||||
|
external_license_request.mutable_content_id()
|
||||||
|
->mutable_widevine_pssh_data();
|
||||||
|
WidevinePsshData widevine_pssh_data;
|
||||||
|
widevine_pssh_data.set_content_id(content_id);
|
||||||
|
std::string widevine_pssh_string;
|
||||||
|
widevine_pssh_data.SerializeToString(&widevine_pssh_string);
|
||||||
|
cenc_id->add_pssh_data(widevine_pssh_string);
|
||||||
|
SignedMessage signed_message;
|
||||||
|
signed_message.set_type(SignedMessage::EXTERNAL_LICENSE_REQUEST);
|
||||||
|
EXPECT_TRUE(
|
||||||
|
external_license_request.SerializeToString(signed_message.mutable_msg()));
|
||||||
|
return signed_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ContentIdUtil, GetContentId) {
|
||||||
|
std::string content_id;
|
||||||
|
EXPECT_OK(GetContentIdFromSignedExternalLicenseRequest(
|
||||||
|
BuildSignedExternalLicenseRequest(
|
||||||
|
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST,
|
||||||
|
kPlayReadyChallenge, kContentId),
|
||||||
|
&content_id));
|
||||||
|
EXPECT_EQ(kContentId, content_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ContentIdUtil, GetContentIdFailureWithIncorrectType) {
|
||||||
|
std::string content_id;
|
||||||
|
SignedMessage signed_message = BuildSignedExternalLicenseRequest(
|
||||||
|
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST, kPlayReadyChallenge,
|
||||||
|
kContentId);
|
||||||
|
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
|
||||||
|
Status status =
|
||||||
|
GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id);
|
||||||
|
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
|
||||||
|
EXPECT_TRUE(content_id.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ContentIdUtil, GetContentIdFailureWithInvalidExternalLicenseRequest) {
|
||||||
|
std::string content_id;
|
||||||
|
SignedMessage signed_message = BuildSignedExternalLicenseRequest(
|
||||||
|
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST, kPlayReadyChallenge,
|
||||||
|
kContentId);
|
||||||
|
signed_message.set_msg("Invalid payload");
|
||||||
|
Status status =
|
||||||
|
GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id);
|
||||||
|
EXPECT_EQ(EXTERNAL_LICENSE_REQUEST_PARSE_ERROR, status.error_code());
|
||||||
|
EXPECT_TRUE(content_id.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
51
common/core_message_util.cc
Normal file
51
common/core_message_util.cc
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/core_message_util.h"
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_deserialize.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_serialize.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_serialize_proto.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
|
||||||
|
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
|
||||||
|
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
|
||||||
|
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
|
||||||
|
using oemcrypto_core_message::serialize::CreateCoreLicenseResponseFromProto;
|
||||||
|
using oemcrypto_core_message::serialize::
|
||||||
|
CreateCoreProvisioningResponseFromProto;
|
||||||
|
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
|
||||||
|
using widevine::Sha256_Hash;
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace core_message_util {
|
||||||
|
void GetCoreProvisioningResponse(const std::string& provisioning_response,
|
||||||
|
std::string* core_message) {
|
||||||
|
oemcrypto_core_message::ODK_ProvisioningRequest odk_provisioning_request;
|
||||||
|
CoreProvisioningRequestFromMessage(*core_message, &odk_provisioning_request);
|
||||||
|
CreateCoreProvisioningResponseFromProto(
|
||||||
|
provisioning_response, odk_provisioning_request, core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetCoreRenewalOrReleaseLicenseResponse(std::string* core_message) {
|
||||||
|
oemcrypto_core_message::ODK_RenewalRequest odk_renewal_request;
|
||||||
|
CoreRenewalRequestFromMessage(*core_message, &odk_renewal_request);
|
||||||
|
CreateCoreRenewalResponse(odk_renewal_request, core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetCoreNewLicenseResponse(const std::string& license,
|
||||||
|
std::string* core_message) {
|
||||||
|
oemcrypto_core_message::ODK_LicenseRequest odk_license_request;
|
||||||
|
CoreLicenseRequestFromMessage(*core_message, &odk_license_request);
|
||||||
|
std::string core_request_sha256 = Sha256_Hash(*core_message);
|
||||||
|
CreateCoreLicenseResponseFromProto(license, odk_license_request,
|
||||||
|
core_request_sha256, core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace core_message_util
|
||||||
|
} // namespace widevine
|
||||||
33
common/core_message_util.h
Normal file
33
common/core_message_util.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_CORE_MESSAGE_UTIL_H_
|
||||||
|
#define COMMON_CORE_MESSAGE_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace core_message_util {
|
||||||
|
// Gets the core message from |provisioning_response|. The output is held in
|
||||||
|
// |core_message|. The provisioning response to be sent will be updated with
|
||||||
|
// this |core_message|.
|
||||||
|
void GetCoreProvisioningResponse(const std::string& provisioning_response,
|
||||||
|
std::string* core_message);
|
||||||
|
|
||||||
|
// Gets the core message for renewal or release license response. The output
|
||||||
|
// is held in |core_message|.
|
||||||
|
void GetCoreRenewalOrReleaseLicenseResponse(std::string* core_message);
|
||||||
|
|
||||||
|
// Gets the core message from |license|. The output is held in |core_message|.
|
||||||
|
// The license to be sent will be updated with this |core_message|.
|
||||||
|
void GetCoreNewLicenseResponse(const std::string& license,
|
||||||
|
std::string* core_message);
|
||||||
|
|
||||||
|
} // namespace core_message_util
|
||||||
|
} // namespace widevine
|
||||||
|
#endif // COMMON_CORE_MESSAGE_UTIL_H_
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
#include "openssl/evp.h"
|
#include "openssl/evp.h"
|
||||||
#include "openssl/hmac.h"
|
#include "openssl/hmac.h"
|
||||||
#include "openssl/sha.h"
|
#include "openssl/sha.h"
|
||||||
#include "util/endian/endian.h"
|
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace crypto_util {
|
namespace crypto_util {
|
||||||
@@ -41,6 +40,11 @@ const char kGroupKeyLabel[] = "GROUP_ENCRYPTION";
|
|||||||
const char kPhonyGroupMasterKey[] = "fedcba9876543210";
|
const char kPhonyGroupMasterKey[] = "fedcba9876543210";
|
||||||
const int kAes128KeySizeBits = 128;
|
const int kAes128KeySizeBits = 128;
|
||||||
const int kAes128KeySizeBytes = 16;
|
const int kAes128KeySizeBytes = 16;
|
||||||
|
const int kAes256KeySizeBytes = 32;
|
||||||
|
const char kKeyboxV3Label[] = "Keyboxv3";
|
||||||
|
|
||||||
|
const int kAesBlockSizeBits = AES_BLOCK_SIZE * 8;
|
||||||
|
const int kAesMaxDerivedBlocks = 255;
|
||||||
|
|
||||||
const uint32_t kCENCSchemeID = 0x63656E63; // 'cenc' (AES-CTR): 0x63656E63
|
const uint32_t kCENCSchemeID = 0x63656E63; // 'cenc' (AES-CTR): 0x63656E63
|
||||||
const uint32_t kCBC1SchemeID = 0x63626331; // 'cbc1' (AES-CBC): 0x63626331
|
const uint32_t kCBC1SchemeID = 0x63626331; // 'cbc1' (AES-CBC): 0x63626331
|
||||||
@@ -51,7 +55,7 @@ const uint32_t kCBCSSchemeID =
|
|||||||
|
|
||||||
// Creates a SHA-256 HMAC signature for the given message.
|
// Creates a SHA-256 HMAC signature for the given message.
|
||||||
std::string CreateSignatureHmacSha256(absl::string_view key,
|
std::string CreateSignatureHmacSha256(absl::string_view key,
|
||||||
absl::string_view message) {
|
absl::string_view message) {
|
||||||
HMAC_CTX ctx;
|
HMAC_CTX ctx;
|
||||||
HMAC_CTX_init(&ctx);
|
HMAC_CTX_init(&ctx);
|
||||||
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha256());
|
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha256());
|
||||||
@@ -74,7 +78,7 @@ bool VerifySignatureHmacSha256(absl::string_view key,
|
|||||||
|
|
||||||
// Creates a SHA-1 HMAC signature for the given message.
|
// Creates a SHA-1 HMAC signature for the given message.
|
||||||
std::string CreateSignatureHmacSha1(absl::string_view key,
|
std::string CreateSignatureHmacSha1(absl::string_view key,
|
||||||
absl::string_view message) {
|
absl::string_view message) {
|
||||||
HMAC_CTX ctx;
|
HMAC_CTX ctx;
|
||||||
HMAC_CTX_init(&ctx);
|
HMAC_CTX_init(&ctx);
|
||||||
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha1());
|
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha1());
|
||||||
@@ -94,31 +98,45 @@ bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature,
|
|||||||
return CreateSignatureHmacSha1(key, message) == signature;
|
return CreateSignatureHmacSha1(key, message) == signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derives an AES 128 key from the provided key and additional info.
|
// Derives a key from the provided AES 128 or 256 key and additional info.
|
||||||
std::string DeriveKey(absl::string_view key, absl::string_view label,
|
std::string DeriveKey(absl::string_view key, absl::string_view label,
|
||||||
absl::string_view context, const uint32_t size_bits) {
|
absl::string_view context, const uint32_t size_bits) {
|
||||||
if (key.size() != kAes128KeySizeBytes) return "";
|
// We only handle multiples of AES blocks (16 bytes) with a maximum of 255
|
||||||
|
// blocks.
|
||||||
// We only handle even multiples of 16 bytes (128 bits) right now.
|
const uint32_t output_block_count = size_bits / kAesBlockSizeBits;
|
||||||
if ((size_bits % 128) || (size_bits > (128 * 255))) {
|
if (size_bits % kAesBlockSizeBits ||
|
||||||
|
output_block_count > kAesMaxDerivedBlocks) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string result;
|
const EVP_CIPHER* cipher = nullptr;
|
||||||
|
const size_t key_size_bytes = key.size();
|
||||||
|
|
||||||
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
|
switch (key_size_bytes) {
|
||||||
|
case kAes128KeySizeBytes:
|
||||||
|
cipher = EVP_aes_128_cbc();
|
||||||
|
break;
|
||||||
|
case kAes256KeySizeBytes:
|
||||||
|
cipher = EVP_aes_256_cbc();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string result;
|
||||||
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
||||||
|
|
||||||
for (unsigned char counter = 1; counter <= (size_bits / 128); counter++) {
|
for (unsigned char counter = 0; counter < output_block_count; counter++) {
|
||||||
if (CMAC_Init(cmac_ctx, key.data(), key.size(), cipher, 0)) {
|
if (CMAC_Init(cmac_ctx, key.data(), key_size_bytes, cipher, nullptr)) {
|
||||||
std::string message;
|
std::string message;
|
||||||
message.append(1, counter);
|
message.append(1, counter + 1);
|
||||||
message.append(label.data(), label.size());
|
message.append(label.data(), label.size());
|
||||||
message.append(1, '\0');
|
message.append(1, '\0');
|
||||||
message.append(context.data(), context.size());
|
message.append(context.data(), context.size());
|
||||||
char size_string[4];
|
message.append(1, (size_bits >> 24) & 0xFF);
|
||||||
BigEndian::Store32(&size_string, size_bits);
|
message.append(1, (size_bits >> 16) & 0xFF);
|
||||||
message.append(&size_string[0], &size_string[0] + 4);
|
message.append(1, (size_bits >> 8) & 0xFF);
|
||||||
|
message.append(1, size_bits & 0xFF);
|
||||||
if (CMAC_Update(cmac_ctx, reinterpret_cast<const uint8_t*>(message.data()),
|
if (CMAC_Update(cmac_ctx, reinterpret_cast<const uint8_t*>(message.data()),
|
||||||
message.size())) {
|
message.size())) {
|
||||||
size_t reslen;
|
size_t reslen;
|
||||||
@@ -146,12 +164,12 @@ std::string DeriveKeyId(absl::string_view context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string DeriveGroupSessionKey(absl::string_view context,
|
std::string DeriveGroupSessionKey(absl::string_view context,
|
||||||
const uint32_t size_bits) {
|
const uint32_t size_bits) {
|
||||||
return DeriveKey(kPhonyGroupMasterKey, kGroupKeyLabel, context, size_bits);
|
return DeriveKey(kPhonyGroupMasterKey, kGroupKeyLabel, context, size_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
|
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
|
||||||
const uint32_t size_bits) {
|
const uint32_t size_bits) {
|
||||||
return DeriveKey(key, kSigningKeyLabel, context, size_bits);
|
return DeriveKey(key, kSigningKeyLabel, context, size_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "base/macros.h"
|
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
@@ -32,6 +31,7 @@ extern const char kIvLabel[];
|
|||||||
extern const int kIvSizeBits;
|
extern const int kIvSizeBits;
|
||||||
extern const int kAes128KeySizeBits;
|
extern const int kAes128KeySizeBits;
|
||||||
extern const int kAes128KeySizeBytes;
|
extern const int kAes128KeySizeBytes;
|
||||||
|
extern const char kKeyboxV3Label[];
|
||||||
|
|
||||||
extern const uint32_t kCENCSchemeID; // 'cenc' (AES-CTR): 0x63656E63
|
extern const uint32_t kCENCSchemeID; // 'cenc' (AES-CTR): 0x63656E63
|
||||||
extern const uint32_t kCBC1SchemeID; // 'cbc1' (AES-CBC): 0x63626331
|
extern const uint32_t kCBC1SchemeID; // 'cbc1' (AES-CBC): 0x63626331
|
||||||
@@ -44,7 +44,7 @@ extern const uint32_t kCBCSSchemeID; // 'cbcs' (AES-CBC subsample): 0x63626373
|
|||||||
// AES-CMAC:
|
// AES-CMAC:
|
||||||
// http://tools.ietf.org/html/rfc4493
|
// http://tools.ietf.org/html/rfc4493
|
||||||
std::string DeriveKey(absl::string_view key, absl::string_view label,
|
std::string DeriveKey(absl::string_view key, absl::string_view label,
|
||||||
absl::string_view context, const uint32_t size_bits);
|
absl::string_view context, const uint32_t size_bits);
|
||||||
|
|
||||||
// Derives an IV from the provided |context|.
|
// Derives an IV from the provided |context|.
|
||||||
std::string DeriveIv(absl::string_view context);
|
std::string DeriveIv(absl::string_view context);
|
||||||
@@ -53,15 +53,16 @@ std::string DeriveIv(absl::string_view context);
|
|||||||
std::string DeriveKeyId(absl::string_view context);
|
std::string DeriveKeyId(absl::string_view context);
|
||||||
|
|
||||||
// Helper function to derive a key using the group master key and 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);
|
std::string DeriveGroupSessionKey(absl::string_view context,
|
||||||
|
const uint32_t size_bits);
|
||||||
|
|
||||||
// Helper function to derive a signing key for from the signing context.
|
// Helper function to derive a signing key for from the signing context.
|
||||||
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
|
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
|
||||||
const uint32_t size_bits);
|
const uint32_t size_bits);
|
||||||
|
|
||||||
// Helper function to create a SHA-256 HMAC signature for the given message.
|
// Helper function to create a SHA-256 HMAC signature for the given message.
|
||||||
std::string CreateSignatureHmacSha256(absl::string_view key,
|
std::string CreateSignatureHmacSha256(absl::string_view key,
|
||||||
absl::string_view message);
|
absl::string_view message);
|
||||||
|
|
||||||
// Helper function which compares the SHA-256 HMAC against the provided
|
// Helper function which compares the SHA-256 HMAC against the provided
|
||||||
// signature.
|
// signature.
|
||||||
@@ -71,7 +72,7 @@ bool VerifySignatureHmacSha256(absl::string_view key,
|
|||||||
|
|
||||||
// Helper function to create a SHA-1 HMAC signature for the given message.
|
// Helper function to create a SHA-1 HMAC signature for the given message.
|
||||||
std::string CreateSignatureHmacSha1(absl::string_view key,
|
std::string CreateSignatureHmacSha1(absl::string_view key,
|
||||||
absl::string_view message);
|
absl::string_view message);
|
||||||
|
|
||||||
// Helper function which compares the SHA-1 HMAC against the provided
|
// Helper function which compares the SHA-1 HMAC against the provided
|
||||||
// signature.
|
// signature.
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
// Unit tests for the crypto_util helper functions.
|
// Unit tests for the crypto_util helper functions.
|
||||||
|
|
||||||
|
#include "common/crypto_util.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
@@ -15,7 +17,7 @@
|
|||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "common/crypto_util.h"
|
#include "openssl/aes.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace crypto_util {
|
namespace crypto_util {
|
||||||
@@ -25,19 +27,33 @@ const char kCBC1Str[] = "cbc1";
|
|||||||
const char kCENSStr[] = "cens";
|
const char kCENSStr[] = "cens";
|
||||||
const char kCBCSStr[] = "cbcs";
|
const char kCBCSStr[] = "cbcs";
|
||||||
|
|
||||||
static unsigned char key_data[] =
|
static unsigned char kAes128KeyData[] = {0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82,
|
||||||
{ 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e,
|
0x32, 0x9e, 0x6b, 0x3b, 0x4e, 0x29,
|
||||||
0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b };
|
0xfa, 0x3b, 0x00, 0x4b};
|
||||||
|
|
||||||
static std::string key_str(key_data, key_data + sizeof(key_data));
|
static unsigned char kAes256KeyData[] = {
|
||||||
|
0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e, 0x6b, 0x3b, 0x4e,
|
||||||
|
0x29, 0xfa, 0x3b, 0x00, 0x4b, 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82,
|
||||||
|
0x32, 0x9e, 0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b};
|
||||||
|
|
||||||
static unsigned char iv_data[] =
|
static unsigned char kAes128IvData[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
|
||||||
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
|
||||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
|
0x0c, 0x0d, 0x0e, 0x0f};
|
||||||
|
|
||||||
static std::string iv_str(iv_data, iv_data + sizeof(iv_data));
|
class CryptoUtilTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
CryptoUtilTest()
|
||||||
|
: aes_128_key_(kAes128KeyData, kAes128KeyData + sizeof(kAes128KeyData)),
|
||||||
|
aes_256_key_(kAes256KeyData, kAes256KeyData + sizeof(kAes256KeyData)),
|
||||||
|
iv_128_(kAes128IvData, kAes128IvData + sizeof(kAes128IvData)) {}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, DeriveAes128KeyTest) {
|
protected:
|
||||||
|
std::string aes_128_key_;
|
||||||
|
std::string aes_256_key_;
|
||||||
|
std::string iv_128_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(CryptoUtilTest, DeriveAes128MasterKeyTest) {
|
||||||
unsigned char label[] = { 0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1,
|
unsigned char label[] = { 0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1,
|
||||||
0x92, 0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08,
|
0x92, 0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08,
|
||||||
0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60,
|
0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60,
|
||||||
@@ -60,22 +76,88 @@ TEST(CryptoUtilTest, DeriveAes128KeyTest) {
|
|||||||
0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22 };
|
0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22 };
|
||||||
|
|
||||||
std::string label_str(label, label + sizeof(label));
|
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 context_str(context, context + sizeof(context));
|
||||||
std::string result = DeriveKey(key_str, label_str, context_str, 128);
|
std::string result = DeriveKey(aes_128_key_, label_str, context_str, 128);
|
||||||
|
|
||||||
std::string output_128(output0, output0 + sizeof(output0));
|
std::string output_128(output0, output0 + sizeof(output0));
|
||||||
|
|
||||||
ASSERT_EQ(result, output_128);
|
ASSERT_EQ(result, output_128);
|
||||||
|
|
||||||
result = DeriveKey(key_str, label_str, context_str, 384);
|
result = DeriveKey(aes_128_key_, label_str, context_str, 384);
|
||||||
|
|
||||||
std::string output_384(output1, output1 + sizeof(output1));
|
std::string output_384(output1, output1 + sizeof(output1));
|
||||||
|
|
||||||
ASSERT_EQ(result, output_384);
|
ASSERT_EQ(result, output_384);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, DeriveGroupSesionKey) {
|
TEST_F(CryptoUtilTest, DeriveAes256MasterKeyTest) {
|
||||||
|
const unsigned char label[] = {
|
||||||
|
0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1, 0x92, 0xa0, 0x34,
|
||||||
|
0x8a, 0x8b, 0x6b, 0x77, 0x08, 0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda,
|
||||||
|
0xfb, 0x60, 0xd1, 0xcf, 0x6a, 0x4d, 0x40, 0xa1, 0xe3, 0xfe, 0xd3,
|
||||||
|
0xe9, 0xa6, 0x58, 0x4c, 0xd4, 0xad, 0xa4, 0xa2};
|
||||||
|
|
||||||
|
const unsigned char context[] = {0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d,
|
||||||
|
0x6d, 0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5,
|
||||||
|
0x5b, 0x80, 0x81, 0x91, 0xff};
|
||||||
|
|
||||||
|
const unsigned char expected_128[] = {0x76, 0x36, 0x33, 0x0e, 0x0b, 0x2c,
|
||||||
|
0x38, 0xc2, 0x9e, 0x53, 0x23, 0x8d,
|
||||||
|
0x2e, 0xc6, 0x3a, 0x46};
|
||||||
|
|
||||||
|
const std::string label_str(label, label + sizeof(label));
|
||||||
|
const std::string context_str(context, context + sizeof(context));
|
||||||
|
std::string result = DeriveKey(aes_256_key_, label_str, context_str, 128);
|
||||||
|
EXPECT_EQ(std::string(expected_128, expected_128 + sizeof(expected_128)),
|
||||||
|
result)
|
||||||
|
<< absl::BytesToHexString(result);
|
||||||
|
|
||||||
|
const unsigned char expected_256[] = {
|
||||||
|
0xfb, 0x8f, 0xdf, 0x0e, 0x22, 0xfe, 0xf7, 0x2b, 0xd1, 0x9a, 0x1d,
|
||||||
|
0xd2, 0xcb, 0xb0, 0x11, 0x5c, 0x6c, 0xa7, 0xe1, 0x7f, 0x72, 0xce,
|
||||||
|
0x3a, 0x60, 0x34, 0x89, 0x6d, 0x08, 0xef, 0xde, 0x19, 0x45};
|
||||||
|
result = DeriveKey(aes_256_key_, label_str, context_str, 256);
|
||||||
|
EXPECT_EQ(std::string(expected_256, expected_256 + sizeof(expected_256)),
|
||||||
|
result)
|
||||||
|
<< absl::BytesToHexString(result);
|
||||||
|
|
||||||
|
const unsigned char expected_384[] = {
|
||||||
|
0x65, 0xbc, 0xe3, 0xf3, 0xfb, 0xfa, 0xce, 0x1d, 0x24, 0x63, 0x9c, 0x8f,
|
||||||
|
0x48, 0x0e, 0xbd, 0x76, 0xd1, 0x14, 0x0b, 0xb1, 0x3a, 0x3d, 0x6e, 0x30,
|
||||||
|
0xa9, 0xf4, 0x40, 0x35, 0x0d, 0x6b, 0xc5, 0x1e, 0x9c, 0xa9, 0x5f, 0xf9,
|
||||||
|
0xde, 0x96, 0xa0, 0xa4, 0x22, 0x62, 0x21, 0xc5, 0xd6, 0xd4, 0xf4, 0x6f};
|
||||||
|
result = DeriveKey(aes_256_key_, label_str, context_str, 384);
|
||||||
|
EXPECT_EQ(std::string(expected_384, expected_384 + sizeof(expected_384)),
|
||||||
|
result)
|
||||||
|
<< absl::BytesToHexString(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CryptoUtilTest, DeriveAesInvalidSizeModulus) {
|
||||||
|
// This is the control case that we correctly derive 128 bits.
|
||||||
|
EXPECT_NE("", DeriveKey(aes_128_key_, "foo", "bar", 128));
|
||||||
|
EXPECT_EQ("", DeriveKey(aes_128_key_, "foo", "bar", 127));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CryptoUtilTest, DeriveAesMaxBlocks) {
|
||||||
|
EXPECT_EQ(
|
||||||
|
255 * AES_BLOCK_SIZE,
|
||||||
|
DeriveKey(aes_128_key_, "foo", "bar", AES_BLOCK_SIZE * 8 * 255).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CryptoUtilTest, DeriveAesTooManyBlocks) {
|
||||||
|
EXPECT_EQ("",
|
||||||
|
DeriveKey(aes_128_key_, "foo", "bar", AES_BLOCK_SIZE * 8 * 256));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CryptoUtilTest, DeriveAes128InvalidKeySize) {
|
||||||
|
EXPECT_EQ("", DeriveKey(aes_128_key_.substr(0, 15), "foo", "bar", 128));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CryptoUtilTest, DeriveAes256InvalidKeySize) {
|
||||||
|
EXPECT_EQ("", DeriveKey(aes_256_key_.substr(0, 31), "foo", "bar", 128));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CryptoUtilTest, DeriveGroupSesionKey) {
|
||||||
unsigned char output[] = { 0x92, 0x6c, 0x2f, 0x5, 0xa6, 0x4f, 0xff, 0xb1,
|
unsigned char output[] = { 0x92, 0x6c, 0x2f, 0x5, 0xa6, 0x4f, 0xff, 0xb1,
|
||||||
0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1 };
|
0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1 };
|
||||||
std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128);
|
std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128);
|
||||||
@@ -84,7 +166,7 @@ TEST(CryptoUtilTest, DeriveGroupSesionKey) {
|
|||||||
ASSERT_EQ(output_128, group_session_key);
|
ASSERT_EQ(output_128, group_session_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
|
TEST_F(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
|
||||||
unsigned char message_data[] = {
|
unsigned char message_data[] = {
|
||||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||||
@@ -96,14 +178,14 @@ TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
|
|||||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||||
|
|
||||||
std::string message(message_data, message_data + sizeof(message_data));
|
std::string message(message_data, message_data + sizeof(message_data));
|
||||||
std::string signature(CreateSignatureHmacSha256(key_str, message));
|
std::string signature(CreateSignatureHmacSha256(aes_128_key_, message));
|
||||||
|
|
||||||
ASSERT_EQ(signature.size(), 32);
|
ASSERT_EQ(signature.size(), 32);
|
||||||
|
|
||||||
ASSERT_TRUE(VerifySignatureHmacSha256(key_str, signature, message));
|
ASSERT_TRUE(VerifySignatureHmacSha256(aes_128_key_, signature, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) {
|
TEST_F(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) {
|
||||||
unsigned char message_data[] = {
|
unsigned char message_data[] = {
|
||||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||||
@@ -123,20 +205,20 @@ TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) {
|
|||||||
ASSERT_EQ(signature.size(), 32);
|
ASSERT_EQ(signature.size(), 32);
|
||||||
|
|
||||||
// Create valid signature to compare.
|
// Create valid signature to compare.
|
||||||
signature = CreateSignatureHmacSha256(key_str, message);
|
signature = CreateSignatureHmacSha256(aes_128_key_, message);
|
||||||
|
|
||||||
// Test with bogus key.
|
// Test with bogus key.
|
||||||
ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message));
|
ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message));
|
||||||
|
|
||||||
// Test with munged signature.
|
// Test with munged signature.
|
||||||
signature[0] = 0xFF;
|
signature[0] = 0xFF;
|
||||||
ASSERT_FALSE(VerifySignatureHmacSha256(key_str, signature, message));
|
ASSERT_FALSE(VerifySignatureHmacSha256(aes_128_key_, signature, message));
|
||||||
|
|
||||||
// Test with bogus signature.
|
// Test with bogus signature.
|
||||||
ASSERT_FALSE(VerifySignatureHmacSha256(key_str, "bogus", message));
|
ASSERT_FALSE(VerifySignatureHmacSha256(aes_128_key_, "bogus", message));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) {
|
TEST_F(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) {
|
||||||
unsigned char message_data[] = {
|
unsigned char message_data[] = {
|
||||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||||
@@ -148,13 +230,13 @@ TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) {
|
|||||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||||
|
|
||||||
std::string message(message_data, message_data + sizeof(message_data));
|
std::string message(message_data, message_data + sizeof(message_data));
|
||||||
std::string signature(CreateSignatureHmacSha1(key_str, message));
|
std::string signature(CreateSignatureHmacSha1(aes_128_key_, message));
|
||||||
|
|
||||||
ASSERT_EQ(20, signature.size());
|
ASSERT_EQ(20, signature.size());
|
||||||
ASSERT_TRUE(VerifySignatureHmacSha1(key_str, signature, message));
|
ASSERT_TRUE(VerifySignatureHmacSha1(aes_128_key_, signature, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) {
|
TEST_F(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) {
|
||||||
unsigned char message_data[] = {
|
unsigned char message_data[] = {
|
||||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||||
@@ -173,17 +255,17 @@ TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) {
|
|||||||
// This should still produce an hmac signature.
|
// This should still produce an hmac signature.
|
||||||
ASSERT_EQ(20, signature.size());
|
ASSERT_EQ(20, signature.size());
|
||||||
// Create valid signature to compare.
|
// Create valid signature to compare.
|
||||||
signature = CreateSignatureHmacSha1(key_str, message);
|
signature = CreateSignatureHmacSha1(aes_128_key_, message);
|
||||||
// Test with bogus key.
|
// Test with bogus key.
|
||||||
ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message));
|
ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message));
|
||||||
// Test with munged signature.
|
// Test with munged signature.
|
||||||
signature[0] = 0xFF;
|
signature[0] = 0xFF;
|
||||||
ASSERT_FALSE(VerifySignatureHmacSha1(key_str, signature, message));
|
ASSERT_FALSE(VerifySignatureHmacSha1(aes_128_key_, signature, message));
|
||||||
// Test with bogus signature.
|
// Test with bogus signature.
|
||||||
ASSERT_FALSE(VerifySignatureHmacSha1(key_str, "bogus", message));
|
ASSERT_FALSE(VerifySignatureHmacSha1(aes_128_key_, "bogus", message));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, DeriveIv) {
|
TEST_F(CryptoUtilTest, DeriveIv) {
|
||||||
// First value in the pair is the key_id, second value is the expected IV.
|
// First value in the pair is the key_id, second value is the expected IV.
|
||||||
std::pair<std::string, std::string> id_iv_pairs[] = {
|
std::pair<std::string, std::string> id_iv_pairs[] = {
|
||||||
{"1234567890123456", "3278234c7682d1a2e153af4912975f5f"},
|
{"1234567890123456", "3278234c7682d1a2e153af4912975f5f"},
|
||||||
@@ -198,7 +280,7 @@ TEST(CryptoUtilTest, DeriveIv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, DeriveKeyId) {
|
TEST_F(CryptoUtilTest, DeriveKeyId) {
|
||||||
// First value in the pair is the context, second value is the expected id.
|
// First value in the pair is the context, second value is the expected id.
|
||||||
std::pair<std::string, std::string> context_id_pairs[] = {
|
std::pair<std::string, std::string> context_id_pairs[] = {
|
||||||
{"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"},
|
{"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"},
|
||||||
@@ -213,14 +295,14 @@ TEST(CryptoUtilTest, DeriveKeyId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) {
|
TEST_F(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) {
|
||||||
uint32_t cc_code;
|
uint32_t cc_code;
|
||||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code));
|
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code));
|
||||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code));
|
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code));
|
||||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code));
|
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromString) {
|
TEST_F(CryptoUtilTest, Verify4CCEncryptionIDFromString) {
|
||||||
uint32_t cc_code = 0;
|
uint32_t cc_code = 0;
|
||||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code));
|
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code));
|
||||||
ASSERT_EQ(kCENCSchemeID, cc_code);
|
ASSERT_EQ(kCENCSchemeID, cc_code);
|
||||||
|
|||||||
41
common/device_info_util.cc
Normal file
41
common/device_info_util.cc
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Implements the device info helper function.
|
||||||
|
#include "common/device_info_util.h"
|
||||||
|
|
||||||
|
#include "absl/strings/ascii.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
bool IsMatchedMakeModel(const std::string& expected_make,
|
||||||
|
const std::string& expected_model,
|
||||||
|
const std::string& make_from_client,
|
||||||
|
const std::string& model_from_client) {
|
||||||
|
return absl::AsciiStrToLower(expected_make) ==
|
||||||
|
absl::AsciiStrToLower(make_from_client) &&
|
||||||
|
absl::AsciiStrToLower(expected_model) ==
|
||||||
|
absl::AsciiStrToLower(model_from_client);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerifyMakeModel(const ProvisionedDeviceInfo& device_info,
|
||||||
|
const std::string& make_from_client,
|
||||||
|
const std::string& model_from_client) {
|
||||||
|
if (IsMatchedMakeModel(device_info.manufacturer(), device_info.model(),
|
||||||
|
make_from_client, model_from_client)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (ProvisionedDeviceInfo::ModelInfo product_info :
|
||||||
|
device_info.model_info()) {
|
||||||
|
if (IsMatchedMakeModel(product_info.manufacturer(), product_info.model(),
|
||||||
|
make_from_client, model_from_client)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace widevine
|
||||||
29
common/device_info_util.h
Normal file
29
common/device_info_util.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#ifndef COMMON_DEVICE_INFO_UTIL_H_
|
||||||
|
#define COMMON_DEVICE_INFO_UTIL_H_
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "protos/public/provisioned_device_info.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Helpers function to compare the expected and actual make model field.
|
||||||
|
bool IsMatchedMakeModel(const std::string& expected_make,
|
||||||
|
const std::string& expected_model,
|
||||||
|
const std::string& make_from_client,
|
||||||
|
const std::string& model_from_client);
|
||||||
|
/**
|
||||||
|
* Return true if make/model from client in device_info matches any of the
|
||||||
|
* registered makes/models.
|
||||||
|
*/
|
||||||
|
bool VerifyMakeModel(const ProvisionedDeviceInfo& device_info,
|
||||||
|
const std::string& make_from_client,
|
||||||
|
const std::string& model_from_client);
|
||||||
|
} // namespace widevine
|
||||||
|
#endif // COMMON_DEVICE_INFO_UTIL_H_
|
||||||
@@ -11,9 +11,13 @@
|
|||||||
#include "common/device_status_list.h"
|
#include "common/device_status_list.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
|
#include "absl/strings/ascii.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/strings/numbers.h"
|
#include "absl/strings/numbers.h"
|
||||||
#include "absl/strings/str_split.h"
|
#include "absl/strings/str_split.h"
|
||||||
@@ -23,9 +27,17 @@
|
|||||||
#include "common/client_cert.h"
|
#include "common/client_cert.h"
|
||||||
#include "common/drm_service_certificate.h"
|
#include "common/drm_service_certificate.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
|
#include "common/keybox_client_cert.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
|
#include "common/status.h"
|
||||||
#include "protos/public/client_identification.pb.h"
|
#include "protos/public/client_identification.pb.h"
|
||||||
|
#include "protos/public/device_certificate_status.pb.h"
|
||||||
#include "protos/public/errors.pb.h"
|
#include "protos/public/errors.pb.h"
|
||||||
|
#include "protos/public/signed_device_info.pb.h"
|
||||||
|
|
||||||
|
using ::widevine::DeviceCertificateStatusListRequest;
|
||||||
|
using ::widevine::SignedDeviceInfo;
|
||||||
|
using ::widevine::SignedDeviceInfoRequest;
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
@@ -44,29 +56,19 @@ DeviceStatusList* DeviceStatusList::Instance() {
|
|||||||
return device_status_list;
|
return device_status_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceStatusList::DeviceStatusList()
|
DeviceStatusList::DeviceStatusList() {}
|
||||||
: creation_time_seconds_(0),
|
|
||||||
expiration_period_seconds_(0),
|
|
||||||
allow_unknown_devices_(true),
|
|
||||||
allow_test_only_devices_(false) {}
|
|
||||||
|
|
||||||
DeviceStatusList::~DeviceStatusList() {}
|
DeviceStatusList::~DeviceStatusList() {}
|
||||||
|
|
||||||
Status DeviceStatusList::UpdateStatusList(
|
Status DeviceStatusList::UpdateStatusList(
|
||||||
const std::string& root_certificate_public_key,
|
const std::string& root_certificate_public_key,
|
||||||
const std::string& serialized_certificate_status_list,
|
const std::string& serialized_device_certificate_status_list,
|
||||||
uint32_t expiration_period_seconds) {
|
const std::string& signature, uint32_t expiration_period_seconds) {
|
||||||
SignedDeviceCertificateStatusList signed_certificate_status_list;
|
if (serialized_device_certificate_status_list.empty()) {
|
||||||
if (!signed_certificate_status_list.ParseFromString(
|
|
||||||
serialized_certificate_status_list)) {
|
|
||||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
||||||
"signed-certificate-status-list-parse-error");
|
|
||||||
}
|
|
||||||
if (!signed_certificate_status_list.has_certificate_status_list()) {
|
|
||||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"missing-status-list");
|
"missing-status-list");
|
||||||
}
|
}
|
||||||
if (!signed_certificate_status_list.has_signature()) {
|
if (signature.empty()) {
|
||||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"missing-status-list-signature");
|
"missing-status-list-signature");
|
||||||
}
|
}
|
||||||
@@ -76,17 +78,16 @@ Status DeviceStatusList::UpdateStatusList(
|
|||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-root-public-key");
|
"invalid-root-public-key");
|
||||||
}
|
}
|
||||||
if (!root_key->VerifySignature(
|
if (!root_key->VerifySignature(serialized_device_certificate_status_list,
|
||||||
signed_certificate_status_list.certificate_status_list(),
|
signature)) {
|
||||||
signed_certificate_status_list.signature())) {
|
|
||||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"invalid-status-list-signature");
|
"invalid-status-list-signature");
|
||||||
}
|
}
|
||||||
DeviceCertificateStatusList certificate_status_list;
|
DeviceCertificateStatusList certificate_status_list;
|
||||||
if (!certificate_status_list.ParseFromString(
|
if (!certificate_status_list.ParseFromString(
|
||||||
signed_certificate_status_list.certificate_status_list())) {
|
serialized_device_certificate_status_list)) {
|
||||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"certificate-status-list-parse-error");
|
"signed-certificate-status-list-parse-error");
|
||||||
}
|
}
|
||||||
if (expiration_period_seconds &&
|
if (expiration_period_seconds &&
|
||||||
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
|
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
|
||||||
@@ -117,6 +118,7 @@ Status DeviceStatusList::UpdateStatusList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
|
Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
|
||||||
|
const std::string& device_manufacturer,
|
||||||
ProvisionedDeviceInfo* device_info) {
|
ProvisionedDeviceInfo* device_info) {
|
||||||
CHECK(device_info);
|
CHECK(device_info);
|
||||||
|
|
||||||
@@ -162,8 +164,19 @@ Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
|
|||||||
if ((device_cert_status->status() ==
|
if ((device_cert_status->status() ==
|
||||||
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
||||||
!allow_test_only_devices_) {
|
!allow_test_only_devices_) {
|
||||||
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
if (IsTestOnlyDeviceAllowed(client_cert.system_id(),
|
||||||
"test-only-drm-certificate-not-allowed");
|
device_manufacturer)) {
|
||||||
|
LOG(WARNING) << "Allowing TEST_ONLY device with systemId = "
|
||||||
|
<< client_cert.system_id()
|
||||||
|
<< "make = " << device_manufacturer
|
||||||
|
<< ", device info = " << device_info->ShortDebugString();
|
||||||
|
} else {
|
||||||
|
VLOG(2) << "Not allowing TEST ONLY device with systemId = "
|
||||||
|
<< client_cert.system_id() << "make = " << device_manufacturer
|
||||||
|
<< ", device info = " << device_info->ShortDebugString();
|
||||||
|
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||||
|
"test-only-drm-certificate-not-allowed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!client_cert.signed_by_provisioner() &&
|
if (!client_cert.signed_by_provisioner() &&
|
||||||
(client_cert.signer_serial_number() !=
|
(client_cert.signer_serial_number() !=
|
||||||
@@ -198,13 +211,27 @@ bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
|
|||||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||||
DeviceCertificateStatus* device_cert_status =
|
DeviceCertificateStatus* device_cert_status =
|
||||||
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
||||||
if (device_cert_status) {
|
if (device_cert_status != nullptr) {
|
||||||
*device_info = device_cert_status->device_info();
|
*device_info = device_cert_status->device_info();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeviceStatusList::GetRevokedIdentifiers(
|
||||||
|
uint32_t system_id,
|
||||||
|
DeviceCertificateStatus::RevokedIdentifiers* revoked_identifiers) {
|
||||||
|
CHECK(revoked_identifiers);
|
||||||
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||||
|
DeviceCertificateStatus* device_cert_status =
|
||||||
|
gtl::FindOrNull(device_status_map_, system_id);
|
||||||
|
if (device_cert_status) {
|
||||||
|
*revoked_identifiers = device_cert_status->revoked_identifiers();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool DeviceStatusList::IsSystemIdActive(uint32_t system_id) {
|
bool DeviceStatusList::IsSystemIdActive(uint32_t system_id) {
|
||||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||||
DeviceCertificateStatus* device_cert_status =
|
DeviceCertificateStatus* device_cert_status =
|
||||||
@@ -241,31 +268,121 @@ void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) {
|
|||||||
std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end());
|
std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeviceStatusList::AllowTestOnlyDevices(const std::string& device_list) {
|
||||||
|
absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_);
|
||||||
|
if (device_list.empty()) {
|
||||||
|
allowed_test_only_devices_.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (absl::string_view device : absl::StrSplit(device_list, ',')) {
|
||||||
|
const std::pair<absl::string_view, absl::string_view> device_split =
|
||||||
|
absl::StrSplit(device, ':');
|
||||||
|
if (device_split.second.empty() || device_split.second == "*") {
|
||||||
|
allowed_test_only_devices_.emplace(
|
||||||
|
std::stoi(std::string(device_split.first)), "*");
|
||||||
|
VLOG(2) << "Whitelisting TEST_ONLY device: systemId = "
|
||||||
|
<< std::stoi(std::string(device_split.first))
|
||||||
|
<< ", manufacturer = *";
|
||||||
|
} else {
|
||||||
|
allowed_test_only_devices_.emplace(
|
||||||
|
std::stoi(std::string(device_split.first)),
|
||||||
|
absl::AsciiStrToUpper(device_split.second));
|
||||||
|
VLOG(2) << "Whitelisting TEST_ONLY device: systemId = "
|
||||||
|
<< std::stoi(std::string(device_split.first))
|
||||||
|
<< ", manufacturer = "
|
||||||
|
<< absl::AsciiStrToUpper(device_split.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
|
bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
|
||||||
auto it = std::binary_search(allowed_revoked_devices_.begin(),
|
auto it = std::binary_search(allowed_revoked_devices_.begin(),
|
||||||
allowed_revoked_devices_.end(), system_id);
|
allowed_revoked_devices_.end(), system_id);
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
bool DeviceStatusList::IsTestOnlyDeviceAllowed(uint32_t system_id,
|
||||||
const std::string& certificate_provisioning_service_response,
|
const std::string manufacturer) {
|
||||||
std::string* signed_certificate_status_list, std::string* certificate_status_list) {
|
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
|
||||||
Status status = OkStatus();
|
std::pair<std::multimap<uint32_t, std::string>::iterator,
|
||||||
size_t signed_list_start =
|
std::multimap<uint32_t, std::string>::iterator>
|
||||||
certificate_provisioning_service_response.find(kSignedList);
|
allowed_manufacturers = allowed_test_only_devices_.equal_range(system_id);
|
||||||
if (signed_list_start != std::string::npos) {
|
for (auto it = allowed_manufacturers.first;
|
||||||
size_t signed_list_end = certificate_provisioning_service_response.find(
|
it != allowed_manufacturers.second; ++it) {
|
||||||
kSignedListTerminator, signed_list_start);
|
std::string allowed_manufacturer = (*it).second;
|
||||||
if (signed_list_end == std::string::npos) {
|
if (allowed_manufacturer == "*" ||
|
||||||
|
allowed_manufacturer == absl::AsciiStrToUpper(manufacturer)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||||
|
const std::string& service_response,
|
||||||
|
DeviceCertificateStatusList* certificate_status_list,
|
||||||
|
std::string* serialized_certificate_status_list, std::string* signature) {
|
||||||
|
if (certificate_status_list == nullptr) {
|
||||||
|
return Status(error_space, error::INVALID_ARGUMENT,
|
||||||
|
"certificate_status_list is empty");
|
||||||
|
} else if (serialized_certificate_status_list == nullptr) {
|
||||||
|
return Status(error_space, error::INVALID_ARGUMENT,
|
||||||
|
"serialized_certificate_status_list is empty");
|
||||||
|
} else if (signature == nullptr) {
|
||||||
|
return Status(error_space, error::INVALID_ARGUMENT, "signature is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We support three types of payload parsing. The legacy path checks for a
|
||||||
|
// JSON encoded payload as well as just a plain base64 (web safe or normal)
|
||||||
|
// payload. If that doesn't match, then the method will try to parse the
|
||||||
|
// serialized PublishedDeviceInfo proto.
|
||||||
|
Status status = ExtractPublishedDevicesInfo(
|
||||||
|
service_response, serialized_certificate_status_list, signature);
|
||||||
|
|
||||||
|
// If the payload was not correctly parsed as a PublishedDevices proto.
|
||||||
|
// then attempt to parse it as a legacy payload.
|
||||||
|
if (!status.ok()) {
|
||||||
|
status = ExtractLegacyDeviceList(
|
||||||
|
service_response, serialized_certificate_status_list, signature);
|
||||||
|
// The payload could not be parsed in either format, return the failure
|
||||||
|
// information.
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!certificate_status_list->ParseFromString(
|
||||||
|
*serialized_certificate_status_list)) {
|
||||||
|
return Status(error_space, widevine::INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
|
"certificate-status-list-parse-error");
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DeviceStatusList::ExtractLegacyDeviceList(
|
||||||
|
const std::string& raw_certificate_provisioning_service_response,
|
||||||
|
std::string* serialized_certificate_status_list, std::string* signature) {
|
||||||
|
// First, attempt to extract the legacy JSON response. Example legacy format.
|
||||||
|
// "signedList":"<b64 encoded data>"
|
||||||
|
// where the b64 encoded data is a DeviceCertificateStatusListResponse.
|
||||||
|
size_t b64_list_response_start =
|
||||||
|
raw_certificate_provisioning_service_response.find(kSignedList);
|
||||||
|
std::string serialized_signed_certificate_status_list;
|
||||||
|
if (b64_list_response_start != std::string::npos) {
|
||||||
|
size_t b64_list_response_end =
|
||||||
|
raw_certificate_provisioning_service_response.find(
|
||||||
|
kSignedListTerminator, b64_list_response_start);
|
||||||
|
if (b64_list_response_end == std::string::npos) {
|
||||||
return Status(
|
return Status(
|
||||||
error_space, error::INVALID_ARGUMENT,
|
error_space, error::INVALID_ARGUMENT,
|
||||||
"Unable to parse the certificate_provisioning_service_response. "
|
"Unable to parse the certificate_provisioning_service_response. "
|
||||||
"SignedList not terminated.");
|
"SignedList not terminated.");
|
||||||
}
|
}
|
||||||
std::string signed_list(
|
std::string signed_list(
|
||||||
certificate_provisioning_service_response.begin() + signed_list_start +
|
raw_certificate_provisioning_service_response.begin() +
|
||||||
kSignedListLen,
|
b64_list_response_start + kSignedListLen,
|
||||||
certificate_provisioning_service_response.begin() + signed_list_end);
|
raw_certificate_provisioning_service_response.begin() +
|
||||||
|
b64_list_response_end);
|
||||||
|
|
||||||
// Strip off quotes.
|
// Strip off quotes.
|
||||||
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\"'),
|
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\"'),
|
||||||
@@ -281,46 +398,55 @@ Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
|||||||
// Strip off carriage return (the control-M character)
|
// Strip off carriage return (the control-M character)
|
||||||
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\r'),
|
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\r'),
|
||||||
signed_list.end());
|
signed_list.end());
|
||||||
if (!absl::WebSafeBase64Unescape(signed_list,
|
if (!absl::WebSafeBase64Unescape(
|
||||||
signed_certificate_status_list)) {
|
signed_list, &serialized_signed_certificate_status_list)) {
|
||||||
if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) {
|
if (!absl::Base64Unescape(signed_list,
|
||||||
|
&serialized_signed_certificate_status_list)) {
|
||||||
return Status(error_space, error::INVALID_ARGUMENT,
|
return Status(error_space, error::INVALID_ARGUMENT,
|
||||||
"Base64 decode of signedlist failed.");
|
"Base64 decode of signedlist failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// certificate_provisioning_service_response is the signed list and not a
|
// If this was not a legacy JSON response, attempt to deserialize the base64
|
||||||
// JSON message.
|
// response.
|
||||||
if (!absl::WebSafeBase64Unescape(certificate_provisioning_service_response,
|
if (!absl::WebSafeBase64Unescape(
|
||||||
signed_certificate_status_list)) {
|
raw_certificate_provisioning_service_response,
|
||||||
if (!absl::Base64Unescape(certificate_provisioning_service_response,
|
&serialized_signed_certificate_status_list)) {
|
||||||
signed_certificate_status_list)) {
|
if (!absl::Base64Unescape(raw_certificate_provisioning_service_response,
|
||||||
|
&serialized_signed_certificate_status_list)) {
|
||||||
return Status(error_space, error::INVALID_ARGUMENT,
|
return Status(error_space, error::INVALID_ARGUMENT,
|
||||||
"Base64 decode of certList failed.");
|
"Base64 decode of certList failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SignedDeviceCertificateStatusList signed_status_list;
|
|
||||||
if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) {
|
// Attempt to parse the legacy serialized signed status list into the proto
|
||||||
|
// and extract the serialized status list and signature.
|
||||||
|
return ParseLegacySignedDeviceCertificateStatusList(
|
||||||
|
serialized_signed_certificate_status_list,
|
||||||
|
serialized_certificate_status_list, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DeviceStatusList::ExtractPublishedDevicesInfo(
|
||||||
|
const std::string& serialized_published_devices,
|
||||||
|
std::string* serialized_certificate_status_list, std::string* signature) {
|
||||||
|
// TODO(b/139067045): Change from using the SignedDeviceInfo proto
|
||||||
|
// to using the correct proto from the API. This duplicate, wire-compatible
|
||||||
|
// proto was a temporary way to workaround Proto2/Proto3 compatibility issues.
|
||||||
|
SignedDeviceInfo devices_info;
|
||||||
|
if (!devices_info.ParseFromString(serialized_published_devices)) {
|
||||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
"signed-certificate-status-list-parse-error");
|
"published-devices-info-parse-error");
|
||||||
}
|
}
|
||||||
if (!signed_status_list.has_certificate_status_list()) {
|
*serialized_certificate_status_list =
|
||||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
devices_info.device_certificate_status_list();
|
||||||
"missing-status-list");
|
*signature = devices_info.signature();
|
||||||
}
|
|
||||||
DeviceCertificateStatusList device_certificate_status_list;
|
|
||||||
if (!device_certificate_status_list.ParseFromString(
|
|
||||||
signed_status_list.certificate_status_list())) {
|
|
||||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
|
||||||
"certificate-status-list-parse-error");
|
|
||||||
}
|
|
||||||
*certificate_status_list = signed_status_list.certificate_status_list();
|
|
||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
||||||
const std::string& version,
|
const std::string& version,
|
||||||
|
const std::string& serialized_service_certificate,
|
||||||
std::string* signed_device_certificate_status_list_request) {
|
std::string* signed_device_certificate_status_list_request) {
|
||||||
if (version.empty()) {
|
if (version.empty()) {
|
||||||
return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty");
|
return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty");
|
||||||
@@ -334,9 +460,10 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
|||||||
DeviceCertificateStatusListRequest request;
|
DeviceCertificateStatusListRequest request;
|
||||||
request.set_sdk_version(version);
|
request.set_sdk_version(version);
|
||||||
request.set_sdk_time_seconds(DeviceStatusList::Instance()->GetCurrentTime());
|
request.set_sdk_time_seconds(DeviceStatusList::Instance()->GetCurrentTime());
|
||||||
|
request.set_service_certificate(serialized_service_certificate);
|
||||||
std::string device_certificate_status_list_request;
|
std::string device_certificate_status_list_request;
|
||||||
request.SerializeToString(&device_certificate_status_list_request);
|
request.SerializeToString(&device_certificate_status_list_request);
|
||||||
SignedDeviceCertificateStatusListRequest signed_request;
|
SignedDeviceInfoRequest signed_request;
|
||||||
signed_request.set_device_certificate_status_list_request(
|
signed_request.set_device_certificate_status_list_request(
|
||||||
device_certificate_status_list_request);
|
device_certificate_status_list_request);
|
||||||
const DrmServiceCertificate* sc =
|
const DrmServiceCertificate* sc =
|
||||||
@@ -359,4 +486,50 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
|||||||
signed_device_certificate_status_list_request);
|
signed_device_certificate_status_list_request);
|
||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status DeviceStatusList::ParseLegacySignedDeviceCertificateStatusList(
|
||||||
|
const std::string& serialized_signed_device_certificate_status_list,
|
||||||
|
std::string* serialized_device_certificate_status_list,
|
||||||
|
std::string* signature) {
|
||||||
|
// Parse the serialized_signed_device_certificate_status_list to extract the
|
||||||
|
// serialized_device_certificate_status_list
|
||||||
|
SignedDeviceCertificateStatusList signed_device_list;
|
||||||
|
if (!signed_device_list.ParseFromString(
|
||||||
|
serialized_signed_device_certificate_status_list)) {
|
||||||
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
|
"signed-certificate-status-list-parse-error");
|
||||||
|
}
|
||||||
|
if (signed_device_list.certificate_status_list().empty()) {
|
||||||
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
|
"missing-status-list");
|
||||||
|
}
|
||||||
|
if (signed_device_list.signature().empty()) {
|
||||||
|
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
|
"missing-status-list-signature");
|
||||||
|
}
|
||||||
|
*serialized_device_certificate_status_list =
|
||||||
|
signed_device_list.certificate_status_list();
|
||||||
|
*signature = signed_device_list.signature();
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceStatusList::RevokedDrmCertificateSerialNumbers(
|
||||||
|
const std::string& drm_certificate_serial_numbers) {
|
||||||
|
for (absl::string_view drm_certificate_serial_number :
|
||||||
|
absl::StrSplit(drm_certificate_serial_numbers, ',')) {
|
||||||
|
revoked_drm_certificate_serial_numbers_.insert(
|
||||||
|
std::string(drm_certificate_serial_number));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceStatusList::IsDrmCertificateRevoked(
|
||||||
|
const std::string& device_certificate_serial_number) {
|
||||||
|
if (revoked_drm_certificate_serial_numbers_.find(
|
||||||
|
device_certificate_serial_number) !=
|
||||||
|
revoked_drm_certificate_serial_numbers_.end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
#define COMMON_DEVICE_STATUS_LIST_H__
|
#define COMMON_DEVICE_STATUS_LIST_H__
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "base/macros.h"
|
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "common/status.h"
|
#include "common/status.h"
|
||||||
#include "protos/public/device_certificate_status.pb.h"
|
#include "protos/public/device_certificate_status.pb.h"
|
||||||
@@ -35,14 +35,20 @@ class DeviceStatusList {
|
|||||||
static DeviceStatusList* Instance();
|
static DeviceStatusList* Instance();
|
||||||
|
|
||||||
DeviceStatusList();
|
DeviceStatusList();
|
||||||
|
|
||||||
|
DeviceStatusList(const DeviceStatusList&) = delete;
|
||||||
|
DeviceStatusList& operator=(const DeviceStatusList&) = delete;
|
||||||
|
|
||||||
virtual ~DeviceStatusList();
|
virtual ~DeviceStatusList();
|
||||||
|
|
||||||
// Takes |serialized_certificate_status_list| and copies to an internal map of
|
// Takes |serialized_device_certificate_status_list| and copies to an
|
||||||
// device certifcate status list. The internal map is used to verify
|
// internal map of device certificate status list. The internal map is used
|
||||||
// a device was not revoked. Returns true is the list was successfully parsed.
|
// to verify a device was not revoked. Returns true is the list was
|
||||||
Status UpdateStatusList(const std::string& root_certificate_public_key,
|
// successfully parsed.
|
||||||
const std::string& serialized_certificate_status_list,
|
Status UpdateStatusList(
|
||||||
uint32_t expiration_period_seconds);
|
const std::string& root_certificate_public_key,
|
||||||
|
const std::string& serialized_device_certificate_status_list,
|
||||||
|
const std::string& signature, uint32_t expiration_period_seconds);
|
||||||
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
|
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
|
||||||
bool allow_unknown_devices() const { return allow_unknown_devices_; }
|
bool allow_unknown_devices() const { return allow_unknown_devices_; }
|
||||||
void set_allow_test_only_devices(bool allow) {
|
void set_allow_test_only_devices(bool allow) {
|
||||||
@@ -50,15 +56,20 @@ class DeviceStatusList {
|
|||||||
}
|
}
|
||||||
bool allow_test_only_devices() const { return allow_test_only_devices_; }
|
bool allow_test_only_devices() const { return allow_test_only_devices_; }
|
||||||
|
|
||||||
// Checks the device status list and returns either:
|
// Checks the device status list and handles the case when a TEST_ONLY device
|
||||||
|
// made the request. Returns one of
|
||||||
// OK
|
// OK
|
||||||
// UNSUPPORTED_SYSTEM_ID
|
// UNSUPPORTED_SYSTEM_ID
|
||||||
// INVALID_DRM_CERTIFICATE
|
// INVALID_DRM_CERTIFICATE
|
||||||
// DRM_DEVICE_CERTIFICATE_REVOKED
|
// DRM_DEVICE_CERTIFICATE_REVOKED
|
||||||
// DRM_DEVICE_CERTIFICATE_UNKNOWN
|
// DRM_DEVICE_CERTIFICATE_UNKNOWN
|
||||||
|
// If a TEST_ONLY device using "make" as identified by |device_manufacturer|,
|
||||||
|
// was not whitelisted, then will return
|
||||||
|
// DEVELOPMENT_CERTIFICATE_NOT_ALLOWED
|
||||||
// If status is OK, a copy of the provisioned device info is copied
|
// 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.
|
// into |device_info|. Caller owns |device_info| and it must not be null.
|
||||||
Status GetCertStatus(const ClientCert& client_cert,
|
Status GetCertStatus(const ClientCert& client_cert,
|
||||||
|
const std::string& device_manufacturer,
|
||||||
widevine::ProvisionedDeviceInfo* device_info);
|
widevine::ProvisionedDeviceInfo* device_info);
|
||||||
// Returns true if the pre-provisioning key or certificate for the specified
|
// Returns true if the pre-provisioning key or certificate for the specified
|
||||||
// system ID are active (not disallowed or revoked).
|
// system ID are active (not disallowed or revoked).
|
||||||
@@ -69,6 +80,14 @@ class DeviceStatusList {
|
|||||||
// Caller owns <device_info> and it must not be null.
|
// Caller owns <device_info> and it must not be null.
|
||||||
bool GetDeviceInfo(const ClientCert& client_cert,
|
bool GetDeviceInfo(const ClientCert& client_cert,
|
||||||
widevine::ProvisionedDeviceInfo* device_info);
|
widevine::ProvisionedDeviceInfo* device_info);
|
||||||
|
|
||||||
|
// Returns true if device certificate status list contains revoked_identifiers
|
||||||
|
// with specific |system_id|.
|
||||||
|
// Caller owns <revoked_identifiers> and it must not be null.
|
||||||
|
bool GetRevokedIdentifiers(
|
||||||
|
uint32_t system_id,
|
||||||
|
DeviceCertificateStatus::RevokedIdentifiers* revoked_identifiers);
|
||||||
|
|
||||||
// Returns the current POSIX time.
|
// Returns the current POSIX time.
|
||||||
virtual uint32_t GetCurrentTime() const;
|
virtual uint32_t GetCurrentTime() const;
|
||||||
|
|
||||||
@@ -76,18 +95,56 @@ class DeviceStatusList {
|
|||||||
// a comma separated list of systems Ids to allow even if revoked.
|
// a comma separated list of systems Ids to allow even if revoked.
|
||||||
virtual void AllowRevokedDevices(const std::string& system_id_list);
|
virtual void AllowRevokedDevices(const std::string& system_id_list);
|
||||||
|
|
||||||
/**
|
// Enable delivery of licenses to TEST_ONLY client devices. |device_list| is
|
||||||
* Parses signed device certificate status list and certificate status list
|
// a comma separated list of devices to allow even if the device state is
|
||||||
* from certificateProvisoningServer response.
|
// TEST_ONLY. Each device is specified by a colon separated system_id and
|
||||||
*
|
// manufacturer. If the manufacturer is not specified, all manufacturers for
|
||||||
* @param certificate_provisioning_service_response
|
// that system_id are allowed.
|
||||||
* @param signed_certificate_status_list
|
// 'device_list' is expected to be of the format <device>,<device>..., and
|
||||||
* @param certificate_status_list
|
// each 'device' will contain a 'system_id' and 'manufacturer' OR will contain
|
||||||
* @return WvPLStatus - Status::OK if success, else error.
|
// only a 'system_id'.
|
||||||
*/
|
// 'device' is expected to be of the format <system_id>:<manufacturer> OR
|
||||||
static Status ExtractFromProvisioningServiceResponse(
|
// of the format <system_id>:
|
||||||
const std::string& certificate_provisioning_service_response,
|
// Example usage:
|
||||||
std::string* signed_certificate_status_list, std::string* certificate_status_list);
|
// const std::string device_list = "4121:LG,7912:*"
|
||||||
|
// AllowTestOnlyDevices(device_list);
|
||||||
|
virtual void AllowTestOnlyDevices(const std::string& device_list);
|
||||||
|
|
||||||
|
// A comma separated list of DRM Certificate Serial Numbers that are revoked.
|
||||||
|
virtual void RevokedDrmCertificateSerialNumbers(
|
||||||
|
const std::string& drm_certificate_serial_numbers);
|
||||||
|
|
||||||
|
// Return true, if the specified |device_certificate_serial_number| was
|
||||||
|
// revoked ... else, false.
|
||||||
|
bool IsDrmCertificateRevoked(
|
||||||
|
const std::string& device_certificate_serial_number);
|
||||||
|
|
||||||
|
// Parses the serialized certificate status list and the signature from the
|
||||||
|
// service_response. The service_response is the JSON payload that comes
|
||||||
|
// in the response to a certificate status list request. Both the legacy
|
||||||
|
// format and the newer SignedDeviceInfo format are supported.
|
||||||
|
//
|
||||||
|
// |service_response| is the response provided from the Widevine API that
|
||||||
|
// produces the certificate list. The response can be in one of a few
|
||||||
|
// formats:
|
||||||
|
// 1) The JSON response from the legacy API.
|
||||||
|
// 2) The Base 64 encoded payload within the JSON response that contains
|
||||||
|
// the serialized certificate list (Web safe or regular base64).
|
||||||
|
// 3) The raw bytes of the serialized PublishedDevices proto returned from
|
||||||
|
// the new Widevine API that generates the serialized certificate list.
|
||||||
|
// The |certificate_status_list| is the deserialized list from the
|
||||||
|
// service_response.
|
||||||
|
// The |serialized_certificate_status_list| is the binary serialized status
|
||||||
|
// list. This is an out parameter which allows the caller to verify the
|
||||||
|
// serialized proto against the |signature|.
|
||||||
|
// The |signature| is the signature of the serialized_certificate_status_list
|
||||||
|
// using RSASSA-PSS signed with the root certificate private key.
|
||||||
|
// Returns WvPLStatus - Status::OK if success, else error.
|
||||||
|
static Status DetermineAndDeserializeServiceResponse(
|
||||||
|
const std::string& service_response,
|
||||||
|
DeviceCertificateStatusList* certificate_status_list,
|
||||||
|
std::string* serialized_certificate_status_list, std::string* signature);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs signed device certificate status list request string.
|
* Constructs signed device certificate status list request string.
|
||||||
*
|
*
|
||||||
@@ -97,25 +154,80 @@ class DeviceStatusList {
|
|||||||
*/
|
*/
|
||||||
static Status GenerateSignedDeviceCertificateStatusListRequest(
|
static Status GenerateSignedDeviceCertificateStatusListRequest(
|
||||||
const std::string& version,
|
const std::string& version,
|
||||||
|
const std::string& serialized_service_certificate,
|
||||||
std::string* signed_device_certificate_status_list_request);
|
std::string* signed_device_certificate_status_list_request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class DeviceStatusListTest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the serialized legacy device certificate status list and signature.
|
||||||
|
* The certificate_provisioning_service_response is the JSON payload that
|
||||||
|
* comes in the response to a certificate status list request.
|
||||||
|
*
|
||||||
|
* @param legacy_certificate_provisioning_service_response
|
||||||
|
* @param serialized_certificate_status_list
|
||||||
|
* @param signature
|
||||||
|
* @return WvPLStatus - Status::OK if success, else error.
|
||||||
|
*/
|
||||||
|
static Status ExtractLegacyDeviceList(
|
||||||
|
const std::string& raw_certificate_provisioning_service_response,
|
||||||
|
std::string* serialized_certificate_status_list, std::string* signature);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the serialized published devices response.
|
||||||
|
* The published_devices_info_response is the JSON payload that comes in the
|
||||||
|
* response to a PublishedDevices request.
|
||||||
|
*
|
||||||
|
* @param published_devices_response the serialized PublishedDevices proto
|
||||||
|
* containing the certificate status list.
|
||||||
|
* @param serialized_certificate_status_list
|
||||||
|
* @param signature
|
||||||
|
* @return WvPLStatus - Status::OK if success, else error.
|
||||||
|
*/
|
||||||
|
static Status ExtractPublishedDevicesInfo(
|
||||||
|
const std::string& serialized_published_devices,
|
||||||
|
std::string* serialized_certificate_status_list, std::string* signature);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a |serialized_device_certificate_status_list| in its output
|
||||||
|
* parameter by parsing |serialized_signed_device_certificate_status_list|
|
||||||
|
* returned from Widevine Certificate Provisioning Server.
|
||||||
|
*
|
||||||
|
* @param serialized_signed_device_certificate_status_list
|
||||||
|
* @param serialized_device_certificate_status_list
|
||||||
|
*
|
||||||
|
* @return Status - Status::OK if success, else error.
|
||||||
|
*/
|
||||||
|
static Status ParseLegacySignedDeviceCertificateStatusList(
|
||||||
|
const std::string& serialized_signed_device_certificate_status_list,
|
||||||
|
std::string* serialized_device_certificate_status_list,
|
||||||
|
std::string* signature);
|
||||||
|
|
||||||
// Returns true if the system ID is allowed to be revoked.
|
// Returns true if the system ID is allowed to be revoked.
|
||||||
// Caller owns |system_id|. They must not be null.
|
// Caller owns |system_id|. They must not be null.
|
||||||
bool IsRevokedSystemIdAllowed(uint32_t system_id);
|
bool IsRevokedSystemIdAllowed(uint32_t system_id);
|
||||||
|
// Returns true if the device, which is identified by system_id and
|
||||||
|
// device_manufacturer, is present in |allowed_test_only_devices_|.
|
||||||
|
bool IsTestOnlyDeviceAllowed(uint32_t system_id,
|
||||||
|
const std::string device_manufacturer);
|
||||||
|
|
||||||
absl::Mutex status_map_lock_;
|
absl::Mutex status_map_lock_;
|
||||||
// Key is the system id for the device.
|
// Key is the system id for the device.
|
||||||
std::map<uint32_t, widevine::DeviceCertificateStatus> device_status_map_;
|
std::map<uint32_t, widevine::DeviceCertificateStatus> device_status_map_;
|
||||||
uint32_t creation_time_seconds_;
|
uint32_t creation_time_seconds_ = 0;
|
||||||
uint32_t expiration_period_seconds_;
|
uint32_t expiration_period_seconds_ = 0;
|
||||||
bool allow_unknown_devices_;
|
bool allow_unknown_devices_ = false;
|
||||||
bool allow_test_only_devices_;
|
bool allow_test_only_devices_ = false;
|
||||||
// Contains the list of system_id values that are allowed to succeed even if
|
// Contains the list of system_id values that are allowed to succeed even if
|
||||||
// revoked.
|
// revoked.
|
||||||
std::vector<uint32_t> allowed_revoked_devices_;
|
std::vector<uint32_t> allowed_revoked_devices_;
|
||||||
|
absl::Mutex allowed_test_only_devices_mutex_;
|
||||||
DISALLOW_COPY_AND_ASSIGN(DeviceStatusList);
|
// Contains a map of 'system_id' to 'make'. If 'make' value is "*", any
|
||||||
|
// 'make' for that 'system_id' is allowed.
|
||||||
|
std::multimap<uint32_t, std::string> allowed_test_only_devices_;
|
||||||
|
// Revoked DRM certificate serial numbers.
|
||||||
|
std::set<std::string> revoked_drm_certificate_serial_numbers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -9,22 +9,39 @@
|
|||||||
#include "common/device_status_list.h"
|
#include "common/device_status_list.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
|
#include "google/protobuf/util/message_differencer.h"
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "common/client_cert.h"
|
#include "common/client_cert.h"
|
||||||
|
#include "common/keybox_client_cert.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "common/rsa_test_keys.h"
|
#include "common/rsa_test_keys.h"
|
||||||
|
#include "common/status.h"
|
||||||
#include "protos/public/client_identification.pb.h"
|
#include "protos/public/client_identification.pb.h"
|
||||||
|
#include "protos/public/device_certificate_status.pb.h"
|
||||||
#include "protos/public/errors.pb.h"
|
#include "protos/public/errors.pb.h"
|
||||||
#include "protos/public/provisioned_device_info.pb.h"
|
#include "protos/public/provisioned_device_info.pb.h"
|
||||||
|
#include "protos/public/signed_device_info.pb.h"
|
||||||
#include "protos/public/signed_drm_certificate.pb.h"
|
#include "protos/public/signed_drm_certificate.pb.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const char kTestSystemId_1[] = "4121";
|
||||||
|
const char kTestManufacturer_LG[] = "LG";
|
||||||
|
const char kTestManufacturer_LGE[] = "LGE";
|
||||||
|
const char kTestSystemId_2[] = "8242";
|
||||||
|
const char kTestManufacturer_Samsung[] = "Samsung";
|
||||||
|
const char kTestSystemId_3[] = "6556";
|
||||||
|
const char kTestManufacturer[] = "TestManufacturer";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
@@ -42,6 +59,10 @@ const char kValidSerialNumber[] = "valid-serial-number";
|
|||||||
const char kRevokedSerialNumber[] = "revoked-serial-number";
|
const char kRevokedSerialNumber[] = "revoked-serial-number";
|
||||||
const char kRevokedAllowDeviceSerialNumber[] =
|
const char kRevokedAllowDeviceSerialNumber[] =
|
||||||
"revoked-allow-device-serial-number";
|
"revoked-allow-device-serial-number";
|
||||||
|
const char kRevokedDeviceCertificateSerialNumber1[] = "revoked-serial-number_1";
|
||||||
|
const char kRevokedDeviceCertificateSerialNumber2[] = "revoked-serial-number_2";
|
||||||
|
const char kRevokedDeviceCertificateSerialNumber3[] = "revoked-serial-number_3";
|
||||||
|
const char kRevokedUniqueIdentifiers[] = "revoked-unique-identifiers";
|
||||||
const char kTestOnlySerialNumber[] = "test_only-serial-number";
|
const char kTestOnlySerialNumber[] = "test_only-serial-number";
|
||||||
const char kMismatchSerialNumber[] = "mismatch-serial-number";
|
const char kMismatchSerialNumber[] = "mismatch-serial-number";
|
||||||
const char kDeviceModel[] = "device-model-x";
|
const char kDeviceModel[] = "device-model-x";
|
||||||
@@ -49,21 +70,25 @@ const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
|
|||||||
const uint32_t kStatusListCreationTime = 17798001;
|
const uint32_t kStatusListCreationTime = 17798001;
|
||||||
const uint32_t kDefaultExpirePeriod = 0;
|
const uint32_t kDefaultExpirePeriod = 0;
|
||||||
|
|
||||||
class MockCertificateClientCert : public CertificateClientCert {
|
class MockClientCert : public ClientCert {
|
||||||
public:
|
public:
|
||||||
MockCertificateClientCert() {}
|
MockClientCert() {}
|
||||||
|
~MockClientCert() override {}
|
||||||
MOCK_CONST_METHOD0(system_id, uint32_t());
|
MOCK_CONST_METHOD0(system_id, uint32_t());
|
||||||
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
|
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
|
||||||
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
|
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
|
||||||
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
|
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
|
||||||
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
|
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
|
||||||
};
|
MOCK_CONST_METHOD3(VerifySignature, Status(const std::string &message,
|
||||||
|
const std::string &signature,
|
||||||
class MockKeyboxClientCert : public KeyboxClientCert {
|
ProtocolVersion protocol_version));
|
||||||
public:
|
MOCK_METHOD2(GenerateSigningKey, void(const std::string &message,
|
||||||
MockKeyboxClientCert() {}
|
ProtocolVersion protocol_version));
|
||||||
MOCK_CONST_METHOD0(system_id, uint32_t());
|
MOCK_CONST_METHOD0(serial_number, const std::string &());
|
||||||
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
|
MOCK_CONST_METHOD0(key, const std::string &());
|
||||||
|
MOCK_CONST_METHOD0(service_id, const std::string &());
|
||||||
|
MOCK_CONST_METHOD0(encrypted_key, const std::string &());
|
||||||
|
MOCK_CONST_METHOD0(signing_key, const std::string &());
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeviceStatusListTest : public ::testing::Test {
|
class DeviceStatusListTest : public ::testing::Test {
|
||||||
@@ -79,6 +104,10 @@ class DeviceStatusListTest : public ::testing::Test {
|
|||||||
cert_status->set_drm_serial_number(kValidSerialNumber);
|
cert_status->set_drm_serial_number(kValidSerialNumber);
|
||||||
cert_status->mutable_device_info()->set_model(kDeviceModel);
|
cert_status->mutable_device_info()->set_model(kDeviceModel);
|
||||||
cert_status->set_status(DeviceCertificateStatus::STATUS_RELEASED);
|
cert_status->set_status(DeviceCertificateStatus::STATUS_RELEASED);
|
||||||
|
cert_status->mutable_revoked_identifiers()->add_revoked_unique_id_hashes(
|
||||||
|
kRevokedUniqueIdentifiers);
|
||||||
|
cert_status->mutable_revoked_identifiers()
|
||||||
|
->add_revoked_certificate_serial_numbers(kRevokedSerialNumber);
|
||||||
|
|
||||||
// Device cert with status REVOKED.
|
// Device cert with status REVOKED.
|
||||||
cert_status = cert_status_list_.add_certificate_status();
|
cert_status = cert_status_list_.add_certificate_status();
|
||||||
@@ -102,36 +131,77 @@ class DeviceStatusListTest : public ::testing::Test {
|
|||||||
cert_status->set_status(DeviceCertificateStatus::STATUS_TEST_ONLY);
|
cert_status->set_status(DeviceCertificateStatus::STATUS_TEST_ONLY);
|
||||||
|
|
||||||
cert_status_list_.set_creation_time_seconds(kStatusListCreationTime);
|
cert_status_list_.set_creation_time_seconds(kStatusListCreationTime);
|
||||||
cert_status_list_.SerializeToString(
|
|
||||||
signed_cert_status_list_.mutable_certificate_status_list());
|
// Generate the serialized list and signature.
|
||||||
std::unique_ptr<RsaPrivateKey> root_key(
|
std::unique_ptr<RsaPrivateKey> root_key(
|
||||||
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
|
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
|
||||||
ASSERT_TRUE(root_key);
|
ASSERT_TRUE(root_key);
|
||||||
|
cert_status_list_.SerializeToString(&serialized_cert_status_list_);
|
||||||
|
ASSERT_TRUE(root_key->GenerateSignature(serialized_cert_status_list_,
|
||||||
|
&cert_status_list_signature_));
|
||||||
|
|
||||||
ASSERT_TRUE(root_key->GenerateSignature(
|
// Update the device_status_list_ with the serialized status list
|
||||||
signed_cert_status_list_.certificate_status_list(),
|
// and signature.
|
||||||
signed_cert_status_list_.mutable_signature()));
|
ASSERT_EQ(OkStatus(),
|
||||||
|
device_status_list_.UpdateStatusList(
|
||||||
|
test_keys_.public_test_key_1_3072_bits(),
|
||||||
|
serialized_cert_status_list_, cert_status_list_signature_,
|
||||||
|
kDefaultExpirePeriod));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateTrivialValidStatusList(std::string *serialized_cert_status_list,
|
||||||
|
std::string *signature) {
|
||||||
|
DeviceCertificateStatusList cert_status_list;
|
||||||
|
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);
|
||||||
|
|
||||||
|
cert_status_list.set_creation_time_seconds(kStatusListCreationTime);
|
||||||
|
|
||||||
|
// Generate the serialized list and signature.
|
||||||
|
std::unique_ptr<RsaPrivateKey> root_key(
|
||||||
|
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
|
||||||
|
ASSERT_TRUE(root_key);
|
||||||
|
cert_status_list.SerializeToString(serialized_cert_status_list);
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(
|
||||||
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
root_key->GenerateSignature(*serialized_cert_status_list, signature));
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT_EQ(OkStatus(), device_status_list_.UpdateStatusList(
|
int VerifyAllowedTestOnlyDevicesAdded() {
|
||||||
test_keys_.public_test_key_1_3072_bits(),
|
return device_status_list_.allowed_test_only_devices_.size();
|
||||||
serialized_status_list_, kDefaultExpirePeriod));
|
}
|
||||||
|
|
||||||
|
bool VerifyIsTestOnlyDeviceAllowed(uint32_t system_id,
|
||||||
|
std::string manufacturer) {
|
||||||
|
return device_status_list_.IsTestOnlyDeviceAllowed(system_id, manufacturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int VerifyRevokedDeviceCertificatesCount() {
|
||||||
|
return device_status_list_.revoked_drm_certificate_serial_numbers_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerifyIsDeviceCertificateRevoked(
|
||||||
|
std::string device_certificate_serial_number) {
|
||||||
|
return device_status_list_.IsDrmCertificateRevoked(
|
||||||
|
device_certificate_serial_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceStatusList device_status_list_;
|
DeviceStatusList device_status_list_;
|
||||||
RsaTestKeys test_keys_;
|
RsaTestKeys test_keys_;
|
||||||
DeviceCertificateStatusList cert_status_list_;
|
DeviceCertificateStatusList cert_status_list_;
|
||||||
SignedDeviceCertificateStatusList signed_cert_status_list_;
|
std::string serialized_cert_status_list_;
|
||||||
std::string serialized_status_list_;
|
std::string cert_status_list_signature_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the number of DevcieCertificateStatus messages in the list.
|
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
||||||
// Test case where the Certificate status is set to Valid.
|
// Test case where the Certificate status is set to Valid.
|
||||||
ProvisionedDeviceInfo device_info;
|
ProvisionedDeviceInfo device_info;
|
||||||
MockCertificateClientCert valid_client_cert;
|
MockClientCert valid_client_cert;
|
||||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||||
EXPECT_CALL(valid_client_cert, type())
|
EXPECT_CALL(valid_client_cert, type())
|
||||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||||
@@ -140,12 +210,13 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
|||||||
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
||||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||||
EXPECT_EQ(OkStatus(),
|
EXPECT_EQ(OkStatus(),
|
||||||
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
|
device_status_list_.GetCertStatus(valid_client_cert,
|
||||||
|
kTestManufacturer, &device_info));
|
||||||
EXPECT_TRUE(device_info.has_model());
|
EXPECT_TRUE(device_info.has_model());
|
||||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||||
|
|
||||||
// Test case where the Certificate status is Revoked.
|
// Test case where the Certificate status is Revoked.
|
||||||
MockCertificateClientCert revoked_client_cert;
|
MockClientCert revoked_client_cert;
|
||||||
std::string revoked_drm_serial_number(kRevokedSerialNumber);
|
std::string revoked_drm_serial_number(kRevokedSerialNumber);
|
||||||
EXPECT_CALL(revoked_client_cert, type())
|
EXPECT_CALL(revoked_client_cert, type())
|
||||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||||
@@ -153,19 +224,21 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
|||||||
.WillRepeatedly(Return(kRevokedCertSystemId));
|
.WillRepeatedly(Return(kRevokedCertSystemId));
|
||||||
EXPECT_CALL(revoked_client_cert, signer_serial_number())
|
EXPECT_CALL(revoked_client_cert, signer_serial_number())
|
||||||
.WillRepeatedly(ReturnRef(revoked_drm_serial_number));
|
.WillRepeatedly(ReturnRef(revoked_drm_serial_number));
|
||||||
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED,
|
EXPECT_EQ(
|
||||||
device_status_list_.GetCertStatus(revoked_client_cert, &device_info)
|
DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||||
.error_code());
|
device_status_list_
|
||||||
|
.GetCertStatus(revoked_client_cert, kTestManufacturer, &device_info)
|
||||||
|
.error_code());
|
||||||
|
|
||||||
// Test case where the revoked cert is allowed.
|
// Test case where the revoked cert is allowed.
|
||||||
device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId));
|
device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId));
|
||||||
EXPECT_OK(
|
EXPECT_OK(device_status_list_.GetCertStatus(revoked_client_cert,
|
||||||
device_status_list_.GetCertStatus(revoked_client_cert, &device_info));
|
kTestManufacturer, &device_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
|
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
||||||
ProvisionedDeviceInfo device_info;
|
ProvisionedDeviceInfo device_info;
|
||||||
MockCertificateClientCert test_only_client_cert;
|
MockClientCert test_only_client_cert;
|
||||||
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
||||||
EXPECT_CALL(test_only_client_cert, type())
|
EXPECT_CALL(test_only_client_cert, type())
|
||||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||||
@@ -175,13 +248,26 @@ TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
|
|||||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||||
device_status_list_.GetCertStatus(test_only_client_cert, &device_info)
|
device_status_list_
|
||||||
|
.GetCertStatus(test_only_client_cert, kTestManufacturer, &device_info)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
TEST_F(DeviceStatusListTest, GetRevokedIfentifiers) {
|
||||||
|
DeviceCertificateStatus::RevokedIdentifiers revoked_identifiers;
|
||||||
|
ASSERT_TRUE(device_status_list_.GetRevokedIdentifiers(kValidCertSystemId,
|
||||||
|
&revoked_identifiers));
|
||||||
|
EXPECT_EQ(kRevokedSerialNumber,
|
||||||
|
revoked_identifiers.revoked_certificate_serial_numbers(0));
|
||||||
|
EXPECT_EQ(kRevokedUniqueIdentifiers,
|
||||||
|
revoked_identifiers.revoked_unique_id_hashes(0));
|
||||||
|
ASSERT_FALSE(device_status_list_.GetRevokedIdentifiers(kUnknownSystemId,
|
||||||
|
&revoked_identifiers));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
|
||||||
ProvisionedDeviceInfo device_info;
|
ProvisionedDeviceInfo device_info;
|
||||||
MockCertificateClientCert test_only_client_cert;
|
MockClientCert test_only_client_cert;
|
||||||
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
||||||
device_status_list_.set_allow_test_only_devices(true);
|
device_status_list_.set_allow_test_only_devices(true);
|
||||||
EXPECT_CALL(test_only_client_cert, type())
|
EXPECT_CALL(test_only_client_cert, type())
|
||||||
@@ -190,8 +276,9 @@ TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
|||||||
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
||||||
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
||||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||||
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(test_only_client_cert,
|
EXPECT_EQ(OkStatus(),
|
||||||
&device_info));
|
device_status_list_.GetCertStatus(test_only_client_cert,
|
||||||
|
kTestManufacturer, &device_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
||||||
@@ -201,25 +288,27 @@ TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
|||||||
|
|
||||||
// Test case where the Certificate status is set to Valid.
|
// Test case where the Certificate status is set to Valid.
|
||||||
ProvisionedDeviceInfo device_info;
|
ProvisionedDeviceInfo device_info;
|
||||||
MockKeyboxClientCert valid_client_keybox;
|
MockClientCert valid_client_keybox;
|
||||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||||
EXPECT_CALL(valid_client_keybox, type())
|
EXPECT_CALL(valid_client_keybox, type())
|
||||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||||
EXPECT_CALL(valid_client_keybox, system_id())
|
EXPECT_CALL(valid_client_keybox, system_id())
|
||||||
.WillRepeatedly(Return(kValidCertSystemId));
|
.WillRepeatedly(Return(kValidCertSystemId));
|
||||||
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(valid_client_keybox,
|
EXPECT_EQ(OkStatus(),
|
||||||
&device_info));
|
device_status_list_.GetCertStatus(valid_client_keybox,
|
||||||
|
kTestManufacturer, &device_info));
|
||||||
EXPECT_TRUE(device_info.has_model());
|
EXPECT_TRUE(device_info.has_model());
|
||||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||||
|
|
||||||
MockKeyboxClientCert unknown_client_keybox;
|
MockClientCert unknown_client_keybox;
|
||||||
EXPECT_CALL(unknown_client_keybox, type())
|
EXPECT_CALL(unknown_client_keybox, type())
|
||||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||||
EXPECT_CALL(unknown_client_keybox, system_id())
|
EXPECT_CALL(unknown_client_keybox, system_id())
|
||||||
.WillRepeatedly(Return(kUnknownSystemId));
|
.WillRepeatedly(Return(kUnknownSystemId));
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
UNSUPPORTED_SYSTEM_ID,
|
UNSUPPORTED_SYSTEM_ID,
|
||||||
device_status_list_.GetCertStatus(unknown_client_keybox, &device_info)
|
device_status_list_
|
||||||
|
.GetCertStatus(unknown_client_keybox, kTestManufacturer, &device_info)
|
||||||
.error_code());
|
.error_code());
|
||||||
EXPECT_TRUE(device_info.has_model());
|
EXPECT_TRUE(device_info.has_model());
|
||||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||||
@@ -230,7 +319,7 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
|||||||
|
|
||||||
// Test case where the signer certificate is older than the current status
|
// Test case where the signer certificate is older than the current status
|
||||||
// list.
|
// list.
|
||||||
MockCertificateClientCert older_client_cert;
|
MockClientCert older_client_cert;
|
||||||
ProvisionedDeviceInfo device_info;
|
ProvisionedDeviceInfo device_info;
|
||||||
std::string mismatch_drm_serial_number(kMismatchSerialNumber);
|
std::string mismatch_drm_serial_number(kMismatchSerialNumber);
|
||||||
EXPECT_CALL(older_client_cert, type())
|
EXPECT_CALL(older_client_cert, type())
|
||||||
@@ -241,21 +330,24 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
|||||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||||
EXPECT_CALL(older_client_cert, signer_creation_time_seconds())
|
EXPECT_CALL(older_client_cert, signer_creation_time_seconds())
|
||||||
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
||||||
EXPECT_EQ(INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(
|
||||||
device_status_list_.GetCertStatus(older_client_cert, &device_info)
|
INVALID_DRM_CERTIFICATE,
|
||||||
.error_code());
|
device_status_list_
|
||||||
|
.GetCertStatus(older_client_cert, kTestManufacturer, &device_info)
|
||||||
|
.error_code());
|
||||||
|
|
||||||
// We allow this case only for certs signed by a provisioner cert.
|
// We allow this case only for certs signed by a provisioner cert.
|
||||||
EXPECT_CALL(older_client_cert, signed_by_provisioner())
|
EXPECT_CALL(older_client_cert, signed_by_provisioner())
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_EQ(OkStatus(),
|
EXPECT_EQ(OkStatus(),
|
||||||
device_status_list_.GetCertStatus(older_client_cert, &device_info));
|
device_status_list_.GetCertStatus(older_client_cert,
|
||||||
|
kTestManufacturer, &device_info));
|
||||||
EXPECT_TRUE(device_info.has_system_id());
|
EXPECT_TRUE(device_info.has_system_id());
|
||||||
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
|
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
|
||||||
|
|
||||||
// Test case where the signer certificate is newer than the current status
|
// Test case where the signer certificate is newer than the current status
|
||||||
// list, and unknown devices are allowed.
|
// list, and unknown devices are allowed.
|
||||||
MockCertificateClientCert newer_client_cert1;
|
MockClientCert newer_client_cert1;
|
||||||
EXPECT_CALL(newer_client_cert1, type())
|
EXPECT_CALL(newer_client_cert1, type())
|
||||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||||
EXPECT_CALL(newer_client_cert1, system_id())
|
EXPECT_CALL(newer_client_cert1, system_id())
|
||||||
@@ -264,14 +356,16 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
|||||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||||
EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds())
|
EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds())
|
||||||
.WillRepeatedly(Return(kStatusListCreationTime));
|
.WillRepeatedly(Return(kStatusListCreationTime));
|
||||||
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
EXPECT_EQ(
|
||||||
device_status_list_.GetCertStatus(newer_client_cert1, &device_info)
|
DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||||
.error_code());
|
device_status_list_
|
||||||
|
.GetCertStatus(newer_client_cert1, kTestManufacturer, &device_info)
|
||||||
|
.error_code());
|
||||||
|
|
||||||
// Test case where the signer certificate is newer than the current status
|
// Test case where the signer certificate is newer than the current status
|
||||||
// list, and unknown devices are not allowed.
|
// list, and unknown devices are not allowed.
|
||||||
device_status_list_.set_allow_unknown_devices(false);
|
device_status_list_.set_allow_unknown_devices(false);
|
||||||
MockCertificateClientCert newer_client_cert2;
|
MockClientCert newer_client_cert2;
|
||||||
EXPECT_CALL(newer_client_cert2, type())
|
EXPECT_CALL(newer_client_cert2, type())
|
||||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||||
EXPECT_CALL(newer_client_cert2, system_id())
|
EXPECT_CALL(newer_client_cert2, system_id())
|
||||||
@@ -280,25 +374,27 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
|||||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||||
EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds())
|
EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds())
|
||||||
.WillRepeatedly(Return(kStatusListCreationTime + 1));
|
.WillRepeatedly(Return(kStatusListCreationTime + 1));
|
||||||
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
EXPECT_EQ(
|
||||||
device_status_list_.GetCertStatus(newer_client_cert2, &device_info)
|
DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||||
.error_code());
|
device_status_list_
|
||||||
|
.GetCertStatus(newer_client_cert2, kTestManufacturer, &device_info)
|
||||||
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, InvalidStatusList) {
|
TEST_F(DeviceStatusListTest, InvalidStatusList) {
|
||||||
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
device_status_list_
|
device_status_list_
|
||||||
.UpdateStatusList(test_keys_.public_test_key_2_2048_bits(),
|
.UpdateStatusList(test_keys_.public_test_key_2_2048_bits(),
|
||||||
serialized_status_list_, 0)
|
serialized_cert_status_list_,
|
||||||
|
cert_status_list_signature_, 0)
|
||||||
.error_code());
|
.error_code());
|
||||||
|
|
||||||
++(*signed_cert_status_list_.mutable_certificate_status_list())[4];
|
++(serialized_cert_status_list_)[4];
|
||||||
ASSERT_TRUE(
|
|
||||||
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
|
||||||
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
||||||
device_status_list_
|
device_status_list_
|
||||||
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
||||||
serialized_status_list_, 0)
|
serialized_cert_status_list_,
|
||||||
|
cert_status_list_signature_, 0)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,11 +411,13 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
|
|||||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||||
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||||
test_keys_.public_test_key_1_3072_bits(),
|
test_keys_.public_test_key_1_3072_bits(),
|
||||||
serialized_status_list_, 100));
|
serialized_cert_status_list_,
|
||||||
|
cert_status_list_signature_, 100));
|
||||||
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
|
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||||
mock_device_status_list
|
mock_device_status_list
|
||||||
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
||||||
serialized_status_list_, 100)
|
serialized_cert_status_list_,
|
||||||
|
cert_status_list_signature_, 100)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,10 +430,11 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
|||||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||||
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||||
test_keys_.public_test_key_1_3072_bits(),
|
test_keys_.public_test_key_1_3072_bits(),
|
||||||
serialized_status_list_, 100));
|
serialized_cert_status_list_,
|
||||||
|
cert_status_list_signature_, 100));
|
||||||
|
|
||||||
ProvisionedDeviceInfo device_info;
|
ProvisionedDeviceInfo device_info;
|
||||||
MockCertificateClientCert valid_client_cert;
|
MockClientCert valid_client_cert;
|
||||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||||
EXPECT_CALL(valid_client_cert, type())
|
EXPECT_CALL(valid_client_cert, type())
|
||||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||||
@@ -345,12 +444,14 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
|||||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||||
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
|
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
|
||||||
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
||||||
EXPECT_EQ(OkStatus(), mock_device_status_list.GetCertStatus(valid_client_cert,
|
EXPECT_EQ(OkStatus(),
|
||||||
&device_info));
|
mock_device_status_list.GetCertStatus(
|
||||||
|
valid_client_cert, kTestManufacturer, &device_info));
|
||||||
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
EXPIRED_CERTIFICATE_STATUS_LIST,
|
EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||||
mock_device_status_list.GetCertStatus(valid_client_cert, &device_info)
|
mock_device_status_list
|
||||||
|
.GetCertStatus(valid_client_cert, kTestManufacturer, &device_info)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,4 +474,219 @@ TEST_F(DeviceStatusListTest, IsSystemIdActive) {
|
|||||||
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
|
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowed) {
|
||||||
|
std::string whitelisted_device_list =
|
||||||
|
std::string(kTestSystemId_1) + ":" + std::string(kTestManufacturer_LG);
|
||||||
|
whitelisted_device_list += "," + std::string(kTestSystemId_2) + ":" +
|
||||||
|
std::string(kTestManufacturer_Samsung);
|
||||||
|
whitelisted_device_list += "," + std::string(kTestSystemId_3) + ":";
|
||||||
|
whitelisted_device_list += ", " + std::string(kTestSystemId_1) + ":" +
|
||||||
|
std::string(kTestManufacturer_LGE);
|
||||||
|
device_status_list_.AllowTestOnlyDevices(whitelisted_device_list);
|
||||||
|
EXPECT_EQ(4, VerifyAllowedTestOnlyDevicesAdded());
|
||||||
|
// Verify that device with system_id = kTestSystemId_1 and
|
||||||
|
// manufacturer = kTestManufacturer_LG is allowed.
|
||||||
|
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_1),
|
||||||
|
kTestManufacturer_LG));
|
||||||
|
// Verify that device with system_id = kTestSystemId_1 and
|
||||||
|
// manufacturer = kTestManufacturer_LGE is allowed.
|
||||||
|
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_1),
|
||||||
|
kTestManufacturer_LGE));
|
||||||
|
// Verify that device with system_id = kTestSystemId_2 and
|
||||||
|
// manufacturer = kTestManufacturer_LGE is not allowed.
|
||||||
|
// This is because this combination is not 'whitelisted'.
|
||||||
|
EXPECT_FALSE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2),
|
||||||
|
kTestManufacturer_LGE));
|
||||||
|
// Verify that device with system_id = kTestSystemId_2 and
|
||||||
|
// manufacturer = kTestManufacturer_Samsung is allowed.
|
||||||
|
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2),
|
||||||
|
kTestManufacturer_Samsung));
|
||||||
|
// Verifes that device with mixed case succeeds.
|
||||||
|
EXPECT_TRUE(
|
||||||
|
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), "samSung"));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), "SAMsung"));
|
||||||
|
// Verify that device with system_id = kTestSystemId_3 and
|
||||||
|
// any manufacturer is allowed. This checks that any manufacturer is
|
||||||
|
// allowed for this system_id.
|
||||||
|
EXPECT_TRUE(
|
||||||
|
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3), "Cisco"));
|
||||||
|
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3),
|
||||||
|
"ScientificAtlanta"));
|
||||||
|
uint32_t unknown_system_id = 7890;
|
||||||
|
// Verify that device with system_id = unknown_system_id and
|
||||||
|
// manufacturer = "Cisco" is not allowed.
|
||||||
|
EXPECT_FALSE(VerifyIsTestOnlyDeviceAllowed(unknown_system_id, "Cisco"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest, IsDrmDeviceCertificateRevoked) {
|
||||||
|
std::string revoked_device_certificate_serial_numbers =
|
||||||
|
std::string(kRevokedDeviceCertificateSerialNumber1);
|
||||||
|
revoked_device_certificate_serial_numbers +=
|
||||||
|
"," + std::string(kRevokedDeviceCertificateSerialNumber2);
|
||||||
|
revoked_device_certificate_serial_numbers +=
|
||||||
|
"," + std::string(kRevokedDeviceCertificateSerialNumber3);
|
||||||
|
device_status_list_.RevokedDrmCertificateSerialNumbers(
|
||||||
|
revoked_device_certificate_serial_numbers);
|
||||||
|
EXPECT_EQ(3, VerifyRevokedDeviceCertificatesCount());
|
||||||
|
// Verify that device certificates with serial number,
|
||||||
|
// 'kRevokedDeviceCertificateSerialNumber1',
|
||||||
|
// 'kRevokedDeviceCertificateSerialNumber2',
|
||||||
|
// 'kRevokedDeviceCertificateSerialNumber3' are revoked.
|
||||||
|
EXPECT_TRUE(
|
||||||
|
VerifyIsDeviceCertificateRevoked(kRevokedDeviceCertificateSerialNumber1));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
VerifyIsDeviceCertificateRevoked(kRevokedDeviceCertificateSerialNumber2));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
VerifyIsDeviceCertificateRevoked(kRevokedDeviceCertificateSerialNumber3));
|
||||||
|
const std::string unrevoked_device_certificate_serial_number =
|
||||||
|
"unrevoked_device_certificate_serial_number";
|
||||||
|
EXPECT_FALSE(VerifyIsDeviceCertificateRevoked(
|
||||||
|
unrevoked_device_certificate_serial_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest, DetermineAndDeserializeServiceResponseSuccess) {
|
||||||
|
SignedDeviceInfo published_devices;
|
||||||
|
GenerateTrivialValidStatusList(
|
||||||
|
published_devices.mutable_device_certificate_status_list(),
|
||||||
|
published_devices.mutable_signature());
|
||||||
|
|
||||||
|
std::string serialized_published_devices;
|
||||||
|
ASSERT_TRUE(
|
||||||
|
published_devices.SerializeToString(&serialized_published_devices));
|
||||||
|
|
||||||
|
DeviceCertificateStatusList actual_cert_status_list;
|
||||||
|
std::string actual_serialized_cert_status_list;
|
||||||
|
std::string actual_signature;
|
||||||
|
ASSERT_EQ(OkStatus(),
|
||||||
|
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||||
|
serialized_published_devices, &actual_cert_status_list,
|
||||||
|
&actual_serialized_cert_status_list, &actual_signature));
|
||||||
|
EXPECT_EQ(published_devices.device_certificate_status_list(),
|
||||||
|
actual_serialized_cert_status_list);
|
||||||
|
EXPECT_EQ(published_devices.signature(), actual_signature);
|
||||||
|
|
||||||
|
DeviceCertificateStatusList expected_cert_status_list;
|
||||||
|
ASSERT_TRUE(expected_cert_status_list.ParseFromString(
|
||||||
|
published_devices.device_certificate_status_list()));
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
expected_cert_status_list, actual_cert_status_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest,
|
||||||
|
DetermineAndDeserializeServiceResponseLegacySuccess) {
|
||||||
|
std::string serialized_cert_status_list;
|
||||||
|
std::string signature;
|
||||||
|
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
|
||||||
|
|
||||||
|
SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
|
||||||
|
*(legacy_signed_cert_status_list.mutable_certificate_status_list()) =
|
||||||
|
serialized_cert_status_list;
|
||||||
|
*(legacy_signed_cert_status_list.mutable_signature()) = signature;
|
||||||
|
|
||||||
|
std::string serialized_signed_cert_status_list;
|
||||||
|
ASSERT_TRUE(legacy_signed_cert_status_list.SerializeToString(
|
||||||
|
&serialized_signed_cert_status_list));
|
||||||
|
|
||||||
|
std::string websafe_b64_serialized_signed_cert_status_list;
|
||||||
|
absl::WebSafeBase64Escape(serialized_signed_cert_status_list,
|
||||||
|
&websafe_b64_serialized_signed_cert_status_list);
|
||||||
|
|
||||||
|
std::string server_response = absl::StrCat(
|
||||||
|
"{\n"
|
||||||
|
" \"kind\": \"certificateprovisioning#certificateStatusListResponse\"\n"
|
||||||
|
" \"signedList\": \"",
|
||||||
|
websafe_b64_serialized_signed_cert_status_list, "\"\n}\n");
|
||||||
|
|
||||||
|
std::string actual_serialized_cert_status_list;
|
||||||
|
std::string actual_signature;
|
||||||
|
DeviceCertificateStatusList actual_cert_status_list;
|
||||||
|
ASSERT_EQ(OkStatus(),
|
||||||
|
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||||
|
server_response, &actual_cert_status_list,
|
||||||
|
&actual_serialized_cert_status_list, &actual_signature));
|
||||||
|
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
|
||||||
|
EXPECT_EQ(signature, actual_signature);
|
||||||
|
|
||||||
|
DeviceCertificateStatusList expected_cert_status_list;
|
||||||
|
ASSERT_TRUE(
|
||||||
|
expected_cert_status_list.ParseFromString(serialized_cert_status_list));
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
expected_cert_status_list, actual_cert_status_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest,
|
||||||
|
DetermineAndDeserializeServiceResponseLegacyWebSafeBase64Success) {
|
||||||
|
std::string serialized_cert_status_list;
|
||||||
|
std::string signature;
|
||||||
|
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
|
||||||
|
|
||||||
|
SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
|
||||||
|
*(legacy_signed_cert_status_list.mutable_certificate_status_list()) =
|
||||||
|
serialized_cert_status_list;
|
||||||
|
*(legacy_signed_cert_status_list.mutable_signature()) = signature;
|
||||||
|
|
||||||
|
std::string serialized_signed_cert_status_list;
|
||||||
|
ASSERT_TRUE(legacy_signed_cert_status_list.SerializeToString(
|
||||||
|
&serialized_signed_cert_status_list));
|
||||||
|
|
||||||
|
std::string websafe_b64_serialized_signed_cert_status_list;
|
||||||
|
absl::WebSafeBase64Escape(serialized_signed_cert_status_list,
|
||||||
|
&websafe_b64_serialized_signed_cert_status_list);
|
||||||
|
|
||||||
|
std::string actual_serialized_cert_status_list;
|
||||||
|
std::string actual_signature;
|
||||||
|
DeviceCertificateStatusList actual_cert_status_list;
|
||||||
|
ASSERT_EQ(OkStatus(),
|
||||||
|
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||||
|
websafe_b64_serialized_signed_cert_status_list,
|
||||||
|
&actual_cert_status_list, &actual_serialized_cert_status_list,
|
||||||
|
&actual_signature));
|
||||||
|
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
|
||||||
|
EXPECT_EQ(signature, actual_signature);
|
||||||
|
|
||||||
|
DeviceCertificateStatusList expected_cert_status_list;
|
||||||
|
ASSERT_TRUE(
|
||||||
|
expected_cert_status_list.ParseFromString(serialized_cert_status_list));
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
expected_cert_status_list, actual_cert_status_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest,
|
||||||
|
DetermineAndDeserializeServiceResponseLegacyBase64Success) {
|
||||||
|
std::string serialized_cert_status_list;
|
||||||
|
std::string signature;
|
||||||
|
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
|
||||||
|
|
||||||
|
SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
|
||||||
|
*(legacy_signed_cert_status_list.mutable_certificate_status_list()) =
|
||||||
|
serialized_cert_status_list;
|
||||||
|
*(legacy_signed_cert_status_list.mutable_signature()) = signature;
|
||||||
|
|
||||||
|
std::string serialized_signed_cert_status_list;
|
||||||
|
ASSERT_TRUE(legacy_signed_cert_status_list.SerializeToString(
|
||||||
|
&serialized_signed_cert_status_list));
|
||||||
|
|
||||||
|
std::string websafe_b64_serialized_signed_cert_status_list;
|
||||||
|
absl::WebSafeBase64Escape(serialized_signed_cert_status_list,
|
||||||
|
&websafe_b64_serialized_signed_cert_status_list);
|
||||||
|
|
||||||
|
std::string actual_serialized_cert_status_list;
|
||||||
|
std::string actual_signature;
|
||||||
|
DeviceCertificateStatusList actual_cert_status_list;
|
||||||
|
ASSERT_EQ(OkStatus(),
|
||||||
|
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||||
|
websafe_b64_serialized_signed_cert_status_list,
|
||||||
|
&actual_cert_status_list, &actual_serialized_cert_status_list,
|
||||||
|
&actual_signature));
|
||||||
|
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
|
||||||
|
EXPECT_EQ(signature, actual_signature);
|
||||||
|
|
||||||
|
DeviceCertificateStatusList expected_cert_status_list;
|
||||||
|
ASSERT_TRUE(
|
||||||
|
expected_cert_status_list.ParseFromString(serialized_cert_status_list));
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
expected_cert_status_list, actual_cert_status_list));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -16,9 +16,11 @@
|
|||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "common/sha_util.h"
|
#include "common/sha_util.h"
|
||||||
|
#include "common/signer_public_key.h"
|
||||||
#include "protos/public/drm_certificate.pb.h"
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
#include "protos/public/errors.pb.h"
|
#include "protos/public/errors.pb.h"
|
||||||
#include "protos/public/signed_drm_certificate.pb.h"
|
#include "protos/public/signed_drm_certificate.pb.h"
|
||||||
@@ -33,6 +35,13 @@ const char kTestingString[] = "test"; // Code development / unit tests.
|
|||||||
|
|
||||||
const bool kUseCache = true;
|
const bool kUseCache = true;
|
||||||
|
|
||||||
|
// Restrict the certificate chain size. All leaf DRM certificates must be a
|
||||||
|
// DEVICE certificate. The DEVICE certificate must be signed by a MODEL
|
||||||
|
// certificate. The MODEL certificate can be signed by Widevine or by a
|
||||||
|
// PROVISIONER certificate which is signed by Widevine. Do not allow any
|
||||||
|
// additional links.
|
||||||
|
const uint32_t kMaxCertificateChainSize = 3;
|
||||||
|
|
||||||
// From common::TestDrmCertificates.
|
// From common::TestDrmCertificates.
|
||||||
// TODO(user): common::test_certificates is a testonly target, consider
|
// TODO(user): common::test_certificates is a testonly target, consider
|
||||||
// how to use instead of dupliciating the test cert here.
|
// how to use instead of dupliciating the test cert here.
|
||||||
@@ -248,12 +257,17 @@ static const unsigned char kProdRootCertificate[] = {
|
|||||||
// number (signer).
|
// number (signer).
|
||||||
struct VerifiedCertSignature {
|
struct VerifiedCertSignature {
|
||||||
VerifiedCertSignature(const std::string& cert, const std::string& sig,
|
VerifiedCertSignature(const std::string& cert, const std::string& sig,
|
||||||
const std::string& signer_sn)
|
const std::string& signer_sn,
|
||||||
: signed_cert(cert), signature(sig), signer_serial(signer_sn) {}
|
const std::string& signer_pub_key)
|
||||||
|
: signed_cert(cert),
|
||||||
|
signature(sig),
|
||||||
|
signer_serial(signer_sn),
|
||||||
|
signer_public_key(signer_pub_key) {}
|
||||||
|
|
||||||
std::string signed_cert;
|
std::string signed_cert;
|
||||||
std::string signature;
|
std::string signature;
|
||||||
std::string signer_serial;
|
std::string signer_serial;
|
||||||
|
std::string signer_public_key;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map of certificate serial number to its signature.
|
// Map of certificate serial number to its signature.
|
||||||
@@ -265,50 +279,57 @@ class VerifiedCertSignatureCache {
|
|||||||
|
|
||||||
// Checks cache, on miss, uses public key. If successful, adds to
|
// Checks cache, on miss, uses public key. If successful, adds to
|
||||||
// cache.
|
// cache.
|
||||||
Status VerifySignature(const std::string& cert, const std::string& serial_number,
|
Status VerifySignature(const std::string& cert,
|
||||||
|
const std::string& serial_number,
|
||||||
const std::string& signature,
|
const std::string& signature,
|
||||||
const std::string& signer_public_key,
|
const DrmCertificate& signer) {
|
||||||
const std::string& signer_serial_number) {
|
|
||||||
{
|
{
|
||||||
VerifiedCertSignatures::iterator cached_signature;
|
VerifiedCertSignatures::iterator cached_signature;
|
||||||
absl::ReaderMutexLock read_lock(&signature_cache_mutex_);
|
absl::ReaderMutexLock read_lock(&signature_cache_mutex_);
|
||||||
cached_signature = signature_cache_.find(serial_number);
|
cached_signature = signature_cache_.find(serial_number);
|
||||||
if (cached_signature != signature_cache_.end()) {
|
if (cached_signature != signature_cache_.end()) {
|
||||||
// TODO(user): Log which of the following three conditions occurs.
|
if (cert != cached_signature->second.signed_cert) {
|
||||||
if ((cert != cached_signature->second.signed_cert) ||
|
return Status(error_space, INVALID_SIGNATURE, "cached-cert-mismatch");
|
||||||
(signature != cached_signature->second.signature) ||
|
}
|
||||||
(signer_serial_number != cached_signature->second.signer_serial)) {
|
if (signature != cached_signature->second.signature) {
|
||||||
// Cached signature mismatch.
|
|
||||||
return Status(error_space, INVALID_SIGNATURE,
|
return Status(error_space, INVALID_SIGNATURE,
|
||||||
"cached-signature-mismatch");
|
"cached-signature-mismatch");
|
||||||
}
|
}
|
||||||
// Cached signature match.
|
if (signer.serial_number() != cached_signature->second.signer_serial) {
|
||||||
|
return Status(error_space, INVALID_SIGNATURE,
|
||||||
|
"cached-serial-number-mismatch");
|
||||||
|
}
|
||||||
|
if (signer.public_key() != cached_signature->second.signer_public_key) {
|
||||||
|
return Status(error_space, INVALID_SIGNATURE,
|
||||||
|
"cached-signer-public-key-mismatch");
|
||||||
|
}
|
||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache miss. Verify signature.
|
// Cache miss. Verify signature.
|
||||||
std::unique_ptr<RsaPublicKey> signer_key(
|
std::unique_ptr<SignerPublicKey> signer_public_key =
|
||||||
key_factory_->CreateFromPkcs1PublicKey(signer_public_key));
|
SignerPublicKey::Create(signer.public_key(), signer.algorithm());
|
||||||
if (!signer_key) {
|
if (signer_public_key == nullptr) {
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signer-public-key");
|
"invalid-signer-public-key");
|
||||||
}
|
}
|
||||||
if (!signer_key->VerifySignature(cert, signature)) {
|
if (!signer_public_key->VerifySignature(cert, signature)) {
|
||||||
return Status(error_space, INVALID_SIGNATURE,
|
return Status(error_space, INVALID_SIGNATURE,
|
||||||
"cache-miss-invalid-signature");
|
"cache-miss-invalid-signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add signature to cache.
|
// Add signature to cache.
|
||||||
absl::WriterMutexLock write_lock(&signature_cache_mutex_);
|
absl::WriterMutexLock write_lock(&signature_cache_mutex_);
|
||||||
signature_cache_.emplace(
|
signature_cache_.emplace(
|
||||||
serial_number,
|
serial_number,
|
||||||
VerifiedCertSignature(cert, signature, signer_serial_number));
|
VerifiedCertSignature(cert, signature, signer.serial_number(),
|
||||||
|
signer.public_key()));
|
||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VerifiedCertSignatures signature_cache_ GUARDED_BY(&signature_cache_mutex_);
|
VerifiedCertSignatures signature_cache_
|
||||||
|
ABSL_GUARDED_BY(&signature_cache_mutex_);
|
||||||
absl::Mutex signature_cache_mutex_;
|
absl::Mutex signature_cache_mutex_;
|
||||||
const RsaKeyFactory* key_factory_;
|
const RsaKeyFactory* key_factory_;
|
||||||
};
|
};
|
||||||
@@ -330,7 +351,8 @@ std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Status DrmRootCertificate::CreateByTypeString(
|
Status DrmRootCertificate::CreateByTypeString(
|
||||||
const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert) {
|
const std::string& cert_type_string,
|
||||||
|
std::unique_ptr<DrmRootCertificate>* cert) {
|
||||||
CHECK(cert);
|
CHECK(cert);
|
||||||
|
|
||||||
CertificateType cert_type;
|
CertificateType cert_type;
|
||||||
@@ -400,9 +422,9 @@ Status DrmRootCertificate::Create(CertificateType cert_type,
|
|||||||
"missing-root-certificate-signature");
|
"missing-root-certificate-signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<RsaPublicKey> public_key(
|
std::unique_ptr<SignerPublicKey> public_key =
|
||||||
key_factory->CreateFromPkcs1PublicKey(root_cert.public_key()));
|
SignerPublicKey::Create(root_cert.public_key(), root_cert.algorithm());
|
||||||
if (!public_key) {
|
if (public_key == nullptr) {
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-root-public-key");
|
"invalid-root-public-key");
|
||||||
}
|
}
|
||||||
@@ -424,10 +446,11 @@ DrmRootCertificate::DrmRootCertificate(
|
|||||||
std::unique_ptr<RsaKeyFactory> key_factory)
|
std::unique_ptr<RsaKeyFactory> key_factory)
|
||||||
: type_(type),
|
: type_(type),
|
||||||
serialized_certificate_(serialized_certificate),
|
serialized_certificate_(serialized_certificate),
|
||||||
serial_number_(serial_number),
|
|
||||||
public_key_(public_key),
|
|
||||||
key_factory_(std::move(key_factory)),
|
key_factory_(std::move(key_factory)),
|
||||||
signature_cache_(new VerifiedCertSignatureCache(key_factory_.get())) {}
|
signature_cache_(new VerifiedCertSignatureCache(key_factory_.get())) {
|
||||||
|
root_cert_.set_public_key(public_key);
|
||||||
|
root_cert_.set_serial_number(serial_number);
|
||||||
|
}
|
||||||
|
|
||||||
DrmRootCertificate::~DrmRootCertificate() {}
|
DrmRootCertificate::~DrmRootCertificate() {}
|
||||||
|
|
||||||
@@ -472,8 +495,9 @@ Status DrmRootCertificate::VerifyCertificate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature chain, but do not use cache for leaf certificates.
|
// Verify signature chain, but do not use cache for leaf certificates.
|
||||||
|
uint32_t certs_in_chain = 0;
|
||||||
return VerifySignatures(*signed_certificate, certificate->serial_number(),
|
return VerifySignatures(*signed_certificate, certificate->serial_number(),
|
||||||
!kUseCache);
|
!kUseCache, &certs_in_chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively verifies certificates with their signing certs or the root.
|
// Recursively verifies certificates with their signing certs or the root.
|
||||||
@@ -483,39 +507,48 @@ Status DrmRootCertificate::VerifyCertificate(
|
|||||||
// Signatures for root-signed certificates are always cached, even if they are
|
// Signatures for root-signed certificates are always cached, even if they are
|
||||||
// leaf certificates. For example service, and provisioner certificates.
|
// leaf certificates. For example service, and provisioner certificates.
|
||||||
Status DrmRootCertificate::VerifySignatures(
|
Status DrmRootCertificate::VerifySignatures(
|
||||||
const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number,
|
const SignedDrmCertificate& signed_cert,
|
||||||
bool use_cache) const {
|
const std::string& cert_serial_number, bool use_cache,
|
||||||
|
uint32_t* certs_in_chain) const {
|
||||||
|
CHECK(certs_in_chain);
|
||||||
|
if (++(*certs_in_chain) > kMaxCertificateChainSize) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"certificate-chain-size-exceeded");
|
||||||
|
}
|
||||||
if (!signed_cert.has_signer()) {
|
if (!signed_cert.has_signer()) {
|
||||||
// Always use cache for root-signed certificates.
|
// Always use cache for root-signed certificates.
|
||||||
return signature_cache_->VerifySignature(
|
return signature_cache_->VerifySignature(
|
||||||
signed_cert.drm_certificate(), cert_serial_number,
|
signed_cert.drm_certificate(), cert_serial_number,
|
||||||
signed_cert.signature(), public_key(), serial_number_);
|
signed_cert.signature(), root_cert_);
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmCertificate signer;
|
DrmCertificate signer;
|
||||||
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
|
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signer-certificate");
|
"invalid-signer-certificate");
|
||||||
}
|
}
|
||||||
|
// Signer cannot be a device certificate.
|
||||||
|
if (signer.type() == DrmCertificate::DEVICE) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"device-cert-must-be-leaf");
|
||||||
|
}
|
||||||
// Verify the signer before verifying signed_cert.
|
// Verify the signer before verifying signed_cert.
|
||||||
Status status =
|
Status status = VerifySignatures(signed_cert.signer(), signer.serial_number(),
|
||||||
VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache);
|
kUseCache, certs_in_chain);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_cache) {
|
if (use_cache) {
|
||||||
status = signature_cache_->VerifySignature(
|
status = signature_cache_->VerifySignature(signed_cert.drm_certificate(),
|
||||||
signed_cert.drm_certificate(), cert_serial_number,
|
cert_serial_number,
|
||||||
signed_cert.signature(), signer.public_key(), signer.serial_number());
|
signed_cert.signature(), signer);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::unique_ptr<RsaPublicKey> signer_public_key(
|
std::unique_ptr<SignerPublicKey> signer_public_key =
|
||||||
key_factory_->CreateFromPkcs1PublicKey(signer.public_key()));
|
SignerPublicKey::Create(signer.public_key(), signer.algorithm());
|
||||||
if (!signer_public_key) {
|
if (signer_public_key == nullptr) {
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-leaf-signer-public-key");
|
"invalid-leaf-signer-public-key");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "base/macros.h"
|
|
||||||
#include "common/status.h"
|
|
||||||
|
|
||||||
#include "common/certificate_type.h"
|
#include "common/certificate_type.h"
|
||||||
|
#include "common/signer_public_key.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
@@ -35,6 +35,9 @@ class VerifiedCertSignatureCache;
|
|||||||
// This object is thread-safe.
|
// This object is thread-safe.
|
||||||
class DrmRootCertificate {
|
class DrmRootCertificate {
|
||||||
public:
|
public:
|
||||||
|
DrmRootCertificate(const DrmRootCertificate&) = delete;
|
||||||
|
DrmRootCertificate& operator=(const DrmRootCertificate&) = delete;
|
||||||
|
|
||||||
virtual ~DrmRootCertificate();
|
virtual ~DrmRootCertificate();
|
||||||
|
|
||||||
// Creates a DrmRootCertificate object given a certificate type.
|
// Creates a DrmRootCertificate object given a certificate type.
|
||||||
@@ -72,12 +75,15 @@ class DrmRootCertificate {
|
|||||||
|
|
||||||
const CertificateType type() const { return type_; }
|
const CertificateType type() const { return type_; }
|
||||||
|
|
||||||
const std::string& public_key() const { return public_key_; }
|
virtual const std::string& public_key() const {
|
||||||
|
return root_cert_.public_key();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DrmRootCertificate(CertificateType cert_type,
|
DrmRootCertificate(CertificateType cert_type,
|
||||||
const std::string& serialized_certificate,
|
const std::string& serialized_certificate,
|
||||||
const std::string& serial_number, const std::string& public_key,
|
const std::string& serial_number,
|
||||||
|
const std::string& public_key,
|
||||||
std::unique_ptr<RsaKeyFactory> key_factory);
|
std::unique_ptr<RsaKeyFactory> key_factory);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -88,17 +94,16 @@ class DrmRootCertificate {
|
|||||||
std::unique_ptr<DrmRootCertificate>* cert);
|
std::unique_ptr<DrmRootCertificate>* cert);
|
||||||
|
|
||||||
Status VerifySignatures(const SignedDrmCertificate& signed_cert,
|
Status VerifySignatures(const SignedDrmCertificate& signed_cert,
|
||||||
const std::string& cert_serial_number,
|
const std::string& cert_serial_number, bool use_cache,
|
||||||
bool use_cache) const;
|
uint32_t* certs_in_chain) const;
|
||||||
|
|
||||||
CertificateType type_;
|
CertificateType type_;
|
||||||
std::string serialized_certificate_;
|
std::string serialized_certificate_;
|
||||||
std::string serial_number_;
|
DrmCertificate root_cert_;
|
||||||
std::string public_key_;
|
// TODO(b/143309971): Either add an ec key_factory object, or drop the rsa
|
||||||
|
// |key_factory_|.
|
||||||
std::unique_ptr<RsaKeyFactory> key_factory_;
|
std::unique_ptr<RsaKeyFactory> key_factory_;
|
||||||
mutable std::unique_ptr<VerifiedCertSignatureCache> signature_cache_;
|
mutable std::unique_ptr<VerifiedCertSignatureCache> signature_cache_;
|
||||||
|
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmRootCertificate);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -13,9 +13,13 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
#include "google/protobuf/util/message_differencer.h"
|
#include "google/protobuf/util/message_differencer.h"
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "common/rsa_test_keys.h"
|
#include "common/rsa_test_keys.h"
|
||||||
@@ -25,6 +29,7 @@
|
|||||||
#include "protos/public/signed_drm_certificate.pb.h"
|
#include "protos/public/signed_drm_certificate.pb.h"
|
||||||
|
|
||||||
using google::protobuf::util::MessageDifferencer;
|
using google::protobuf::util::MessageDifferencer;
|
||||||
|
using ::testing::Values;
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
@@ -80,76 +85,214 @@ TEST(DrmRootCertificateTestCertificatesTest, Success) {
|
|||||||
->VerifyCertificate(test_certs.test_user_device_certificate(),
|
->VerifyCertificate(test_certs.test_user_device_certificate(),
|
||||||
nullptr, nullptr)
|
nullptr, nullptr)
|
||||||
.ok());
|
.ok());
|
||||||
EXPECT_TRUE(root_cert
|
EXPECT_TRUE(
|
||||||
->VerifyCertificate(test_certs.test_service_certificate(),
|
root_cert
|
||||||
nullptr, nullptr)
|
->VerifyCertificate(test_certs.test_service_certificate_no_type(),
|
||||||
.ok());
|
nullptr, nullptr)
|
||||||
|
.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
class DrmRootCertificateTest : public testing::Test {
|
TEST(DrmRootCertificateTestCertificatesTest, NonWidevineRootSigner) {
|
||||||
|
// TODO(b/138929855): Add test to verify certificate chain is signed by
|
||||||
|
// Widevine.
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignerPrivateKey {
|
||||||
|
public:
|
||||||
|
virtual ~SignerPrivateKey() {}
|
||||||
|
virtual bool GenerateSignature(const std::string& message,
|
||||||
|
std::string* signature) const = 0;
|
||||||
|
virtual DrmCertificate::Algorithm algorithm() const = 0;
|
||||||
|
static std::unique_ptr<SignerPrivateKey> Create(
|
||||||
|
const std::string& private_key,
|
||||||
|
widevine::DrmCertificate::Algorithm algorithm);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DrmRootCertificateTest() {
|
SignerPrivateKey() {}
|
||||||
private_keys_.emplace_back(
|
};
|
||||||
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
|
|
||||||
private_keys_.emplace_back(
|
template <typename T>
|
||||||
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
|
class SignerPrivateKeyImpl : public SignerPrivateKey {
|
||||||
private_keys_.emplace_back(
|
public:
|
||||||
RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits()));
|
SignerPrivateKeyImpl(std::unique_ptr<T> private_key,
|
||||||
|
DrmCertificate::Algorithm algorithm)
|
||||||
|
: private_key_(std::move(private_key)), algorithm_(algorithm) {}
|
||||||
|
~SignerPrivateKeyImpl() override {}
|
||||||
|
bool GenerateSignature(const std::string& message,
|
||||||
|
std::string* signature) const override {
|
||||||
|
return private_key_->GenerateSignature(message, signature);
|
||||||
}
|
}
|
||||||
|
DrmCertificate::Algorithm algorithm() const override { return algorithm_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<T> private_key_;
|
||||||
|
DrmCertificate::Algorithm algorithm_;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<SignerPrivateKey> SignerPrivateKey::Create(
|
||||||
|
const std::string& private_key,
|
||||||
|
widevine::DrmCertificate::Algorithm algorithm) {
|
||||||
|
DCHECK(algorithm != DrmCertificate::UNKNOWN_ALGORITHM);
|
||||||
|
switch (algorithm) {
|
||||||
|
case DrmCertificate::RSA: {
|
||||||
|
auto rsa_key =
|
||||||
|
std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key));
|
||||||
|
CHECK(rsa_key);
|
||||||
|
std::unique_ptr<SignerPrivateKey> new_rsa_signer_private_key =
|
||||||
|
absl::make_unique<SignerPrivateKeyImpl<RsaPrivateKey>>(
|
||||||
|
std::move(rsa_key), algorithm);
|
||||||
|
CHECK(new_rsa_signer_private_key);
|
||||||
|
return new_rsa_signer_private_key;
|
||||||
|
}
|
||||||
|
case DrmCertificate::ECC_SECP256R1:
|
||||||
|
case DrmCertificate::ECC_SECP384R1:
|
||||||
|
case DrmCertificate::ECC_SECP521R1: {
|
||||||
|
auto ec_key = ECPrivateKey::Create(private_key);
|
||||||
|
CHECK(ec_key);
|
||||||
|
std::unique_ptr<SignerPrivateKey> new_ec_signer_private_key =
|
||||||
|
absl::make_unique<SignerPrivateKeyImpl<ECPrivateKey>>(
|
||||||
|
std::move(ec_key), algorithm);
|
||||||
|
CHECK(new_ec_signer_private_key);
|
||||||
|
return new_ec_signer_private_key;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int kDrmRootKey = 0;
|
||||||
|
static const int kInterMediateKey = 1;
|
||||||
|
static const int kClientKey = 2;
|
||||||
|
|
||||||
|
class DrmRootCertificateTest : public testing::TestWithParam<const char*> {
|
||||||
|
protected:
|
||||||
|
DrmRootCertificateTest() {}
|
||||||
|
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
drm_certificates_[0].set_serial_number("level 0");
|
bool algorithm_status = false;
|
||||||
drm_certificates_[0].set_creation_time_seconds(0);
|
std::string algorithm(GetParam());
|
||||||
drm_certificates_[0].set_public_key(
|
if (algorithm == "RSA") {
|
||||||
test_keys_.public_test_key_1_3072_bits());
|
RsaTestSetup();
|
||||||
drm_certificates_[1].set_serial_number("level 1");
|
algorithm_status = true;
|
||||||
drm_certificates_[1].set_creation_time_seconds(1);
|
}
|
||||||
drm_certificates_[1].set_public_key(
|
if (algorithm == "ECC") {
|
||||||
test_keys_.public_test_key_2_2048_bits());
|
EcTestSetup();
|
||||||
drm_certificates_[2].set_serial_number("level 2");
|
algorithm_status = true;
|
||||||
drm_certificates_[2].set_creation_time_seconds(2);
|
}
|
||||||
drm_certificates_[2].set_public_key(
|
|
||||||
test_keys_.public_test_key_3_2048_bits());
|
CHECK(algorithm_status);
|
||||||
|
|
||||||
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||||
kCertificateTypeTesting, &root_cert_));
|
kCertificateTypeTesting, &root_cert_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RsaTestSetup() {
|
||||||
|
private_keys_.resize(3);
|
||||||
|
private_keys_[kDrmRootKey] =
|
||||||
|
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits(),
|
||||||
|
widevine::DrmCertificate::RSA);
|
||||||
|
drm_certificates_[kDrmRootKey].set_serial_number("level 0");
|
||||||
|
drm_certificates_[kDrmRootKey].set_creation_time_seconds(0);
|
||||||
|
drm_certificates_[kDrmRootKey].set_public_key(
|
||||||
|
rsa_test_keys_.public_test_key_1_3072_bits());
|
||||||
|
|
||||||
|
private_keys_[kInterMediateKey] =
|
||||||
|
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_2_2048_bits(),
|
||||||
|
widevine::DrmCertificate::RSA);
|
||||||
|
drm_certificates_[kInterMediateKey].set_serial_number("level 1");
|
||||||
|
drm_certificates_[kInterMediateKey].set_creation_time_seconds(1);
|
||||||
|
drm_certificates_[kInterMediateKey].set_public_key(
|
||||||
|
rsa_test_keys_.public_test_key_2_2048_bits());
|
||||||
|
|
||||||
|
private_keys_[kClientKey] =
|
||||||
|
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits(),
|
||||||
|
widevine::DrmCertificate::RSA);
|
||||||
|
drm_certificates_[kClientKey].set_serial_number("level 2");
|
||||||
|
drm_certificates_[kClientKey].set_creation_time_seconds(2);
|
||||||
|
drm_certificates_[kClientKey].set_public_key(
|
||||||
|
rsa_test_keys_.public_test_key_3_2048_bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EcTestSetup() {
|
||||||
|
private_keys_.resize(3);
|
||||||
|
private_keys_[kDrmRootKey] =
|
||||||
|
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits(),
|
||||||
|
widevine::DrmCertificate::RSA);
|
||||||
|
drm_certificates_[kDrmRootKey].set_serial_number("level 0");
|
||||||
|
drm_certificates_[kDrmRootKey].set_creation_time_seconds(0);
|
||||||
|
drm_certificates_[kDrmRootKey].set_public_key(
|
||||||
|
rsa_test_keys_.public_test_key_1_3072_bits());
|
||||||
|
|
||||||
|
private_keys_[kInterMediateKey] =
|
||||||
|
SignerPrivateKey::Create(ec_test_keys_.private_test_key_1_secp521r1(),
|
||||||
|
DrmCertificate::ECC_SECP521R1);
|
||||||
|
drm_certificates_[kInterMediateKey].set_serial_number("level 1");
|
||||||
|
drm_certificates_[kInterMediateKey].set_creation_time_seconds(1);
|
||||||
|
drm_certificates_[kInterMediateKey].set_public_key(
|
||||||
|
ec_test_keys_.public_test_key_1_secp521r1());
|
||||||
|
drm_certificates_[kInterMediateKey].set_algorithm(
|
||||||
|
DrmCertificate::ECC_SECP521R1);
|
||||||
|
|
||||||
|
private_keys_[kClientKey] =
|
||||||
|
SignerPrivateKey::Create(ec_test_keys_.private_test_key_1_secp256r1(),
|
||||||
|
DrmCertificate::ECC_SECP256R1);
|
||||||
|
drm_certificates_[kClientKey].set_serial_number("level 2");
|
||||||
|
drm_certificates_[kClientKey].set_creation_time_seconds(2);
|
||||||
|
drm_certificates_[kClientKey].set_public_key(
|
||||||
|
ec_test_keys_.public_test_key_1_secp256r1());
|
||||||
|
drm_certificates_[kClientKey].set_algorithm(DrmCertificate::ECC_SECP256R1);
|
||||||
|
|
||||||
|
// Client certificate.
|
||||||
|
|
||||||
|
// Intermediate certificate.
|
||||||
|
}
|
||||||
|
|
||||||
void GenerateSignedDrmCertificate() {
|
void GenerateSignedDrmCertificate() {
|
||||||
SignedDrmCertificate* current_sc(&signed_drm_certificate_);
|
SignedDrmCertificate* current_sc(&signed_drm_certificate_);
|
||||||
ASSERT_TRUE(drm_certificates_[2].SerializeToString(
|
drm_certificates_[kClientKey].set_algorithm(
|
||||||
|
private_keys_[kClientKey]->algorithm());
|
||||||
|
ASSERT_TRUE(drm_certificates_[kClientKey].SerializeToString(
|
||||||
current_sc->mutable_drm_certificate()));
|
current_sc->mutable_drm_certificate()));
|
||||||
ASSERT_TRUE(private_keys_[1]->GenerateSignature(
|
ASSERT_TRUE(private_keys_[kInterMediateKey]->GenerateSignature(
|
||||||
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
||||||
|
|
||||||
current_sc = current_sc->mutable_signer();
|
current_sc = current_sc->mutable_signer();
|
||||||
ASSERT_TRUE(drm_certificates_[1].SerializeToString(
|
drm_certificates_[kInterMediateKey].set_algorithm(
|
||||||
|
private_keys_[kInterMediateKey]->algorithm());
|
||||||
|
ASSERT_TRUE(drm_certificates_[kInterMediateKey].SerializeToString(
|
||||||
current_sc->mutable_drm_certificate()));
|
current_sc->mutable_drm_certificate()));
|
||||||
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
|
ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature(
|
||||||
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
||||||
|
|
||||||
current_sc = current_sc->mutable_signer();
|
current_sc = current_sc->mutable_signer();
|
||||||
ASSERT_TRUE(drm_certificates_[0].SerializeToString(
|
drm_certificates_[kDrmRootKey].set_algorithm(
|
||||||
|
private_keys_[kDrmRootKey]->algorithm());
|
||||||
|
ASSERT_TRUE(drm_certificates_[kDrmRootKey].SerializeToString(
|
||||||
current_sc->mutable_drm_certificate()));
|
current_sc->mutable_drm_certificate()));
|
||||||
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
|
ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature(
|
||||||
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
||||||
}
|
}
|
||||||
|
|
||||||
RsaTestKeys test_keys_;
|
RsaTestKeys rsa_test_keys_;
|
||||||
std::vector<std::unique_ptr<RsaPrivateKey>> private_keys_;
|
ECTestKeys ec_test_keys_;
|
||||||
|
std::vector<std::unique_ptr<SignerPrivateKey>> private_keys_;
|
||||||
SignedDrmCertificate signed_drm_certificate_;
|
SignedDrmCertificate signed_drm_certificate_;
|
||||||
DrmCertificate drm_certificates_[3];
|
DrmCertificate drm_certificates_[3];
|
||||||
std::unique_ptr<DrmRootCertificate> root_cert_;
|
std::unique_ptr<DrmRootCertificate> root_cert_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, SuccessNoOutput) {
|
INSTANTIATE_TEST_SUITE_P(SuccessNoOutput, DrmRootCertificateTest,
|
||||||
|
Values("RSA", "ECC"));
|
||||||
|
|
||||||
|
TEST_P(DrmRootCertificateTest, SuccessNoOutput) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
ASSERT_EQ(OkStatus(),
|
ASSERT_EQ(OkStatus(),
|
||||||
root_cert_->VerifyCertificate(
|
root_cert_->VerifyCertificate(
|
||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, SuccessWithOutput) {
|
TEST_P(DrmRootCertificateTest, SuccessWithOutput) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
SignedDrmCertificate out_signed_cert;
|
SignedDrmCertificate out_signed_cert;
|
||||||
DrmCertificate out_cert;
|
DrmCertificate out_cert;
|
||||||
@@ -161,13 +304,13 @@ TEST_F(DrmRootCertificateTest, SuccessWithOutput) {
|
|||||||
EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2]));
|
EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidSignedDrmCertificate) {
|
TEST_P(DrmRootCertificateTest, InvalidSignedDrmCertificate) {
|
||||||
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-signed-drm-certificate"),
|
"invalid-signed-drm-certificate"),
|
||||||
root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr));
|
root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
|
TEST_P(DrmRootCertificateTest, InvalidSignerCertificate) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage");
|
signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage");
|
||||||
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
|
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
@@ -176,7 +319,7 @@ TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
|
|||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
|
TEST_P(DrmRootCertificateTest, MissingDrmCertificate) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
signed_drm_certificate_.clear_drm_certificate();
|
signed_drm_certificate_.clear_drm_certificate();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -185,7 +328,7 @@ TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
|
|||||||
nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) {
|
TEST_P(DrmRootCertificateTest, InvalidDrmCertificate) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
signed_drm_certificate_.set_drm_certificate("junk");
|
signed_drm_certificate_.set_drm_certificate("junk");
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -194,7 +337,7 @@ TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) {
|
|||||||
nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidPublicKey) {
|
TEST_P(DrmRootCertificateTest, InvalidPublicKey) {
|
||||||
drm_certificates_[0].set_public_key("rubbish");
|
drm_certificates_[0].set_public_key("rubbish");
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -203,7 +346,7 @@ TEST_F(DrmRootCertificateTest, InvalidPublicKey) {
|
|||||||
nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, MissingPublicKey) {
|
TEST_P(DrmRootCertificateTest, MissingPublicKey) {
|
||||||
drm_certificates_[2].clear_public_key();
|
drm_certificates_[2].clear_public_key();
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"),
|
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"),
|
||||||
@@ -211,7 +354,7 @@ TEST_F(DrmRootCertificateTest, MissingPublicKey) {
|
|||||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, MissingCreationTime) {
|
TEST_P(DrmRootCertificateTest, MissingCreationTime) {
|
||||||
drm_certificates_[2].clear_creation_time_seconds();
|
drm_certificates_[2].clear_creation_time_seconds();
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -220,7 +363,7 @@ TEST_F(DrmRootCertificateTest, MissingCreationTime) {
|
|||||||
nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, MissingSerialNumber) {
|
TEST_P(DrmRootCertificateTest, MissingSerialNumber) {
|
||||||
drm_certificates_[2].set_serial_number("");
|
drm_certificates_[2].set_serial_number("");
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -229,7 +372,7 @@ TEST_F(DrmRootCertificateTest, MissingSerialNumber) {
|
|||||||
nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
|
TEST_P(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
signed_drm_certificate_.mutable_signer()->set_signature(
|
signed_drm_certificate_.mutable_signer()->set_signature(
|
||||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
||||||
@@ -239,7 +382,7 @@ TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
|
|||||||
nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) {
|
TEST_P(DrmRootCertificateTest, InvalidSignatureWithCache) {
|
||||||
GenerateSignedDrmCertificate();
|
GenerateSignedDrmCertificate();
|
||||||
// Verify and cache.
|
// Verify and cache.
|
||||||
ASSERT_EQ(OkStatus(),
|
ASSERT_EQ(OkStatus(),
|
||||||
|
|||||||
@@ -44,16 +44,18 @@ class DrmServiceCertificateMap {
|
|||||||
void AddCert(std::unique_ptr<DrmServiceCertificate> new_cert);
|
void AddCert(std::unique_ptr<DrmServiceCertificate> new_cert);
|
||||||
void ClearDefaultDrmServiceCertificate();
|
void ClearDefaultDrmServiceCertificate();
|
||||||
const DrmServiceCertificate* GetDefaultCert();
|
const DrmServiceCertificate* GetDefaultCert();
|
||||||
const DrmServiceCertificate* GetCert(const std::string& serial_number);
|
const DrmServiceCertificate* GetCertBySerialNumber(
|
||||||
|
const std::string& serial_number);
|
||||||
|
const DrmServiceCertificate* GetCertByProvider(
|
||||||
|
const std::string& provider_id);
|
||||||
static DrmServiceCertificateMap* GetInstance();
|
static DrmServiceCertificateMap* GetInstance();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
absl::Mutex mutex_;
|
absl::Mutex mutex_;
|
||||||
// Certificate serial number to certificate map.
|
// Certificate serial number to certificate map.
|
||||||
std::map<std::string, std::unique_ptr<DrmServiceCertificate>> map_
|
std::map<std::string, std::unique_ptr<DrmServiceCertificate>> map_
|
||||||
GUARDED_BY(mutex_);
|
ABSL_GUARDED_BY(mutex_);
|
||||||
DrmServiceCertificate* default_cert_ GUARDED_BY(mutex_);
|
DrmServiceCertificate* default_cert_ ABSL_GUARDED_BY(mutex_);
|
||||||
};
|
};
|
||||||
|
|
||||||
DrmServiceCertificateMap::DrmServiceCertificateMap() : default_cert_(nullptr) {}
|
DrmServiceCertificateMap::DrmServiceCertificateMap() : default_cert_(nullptr) {}
|
||||||
@@ -94,12 +96,30 @@ const DrmServiceCertificate* DrmServiceCertificateMap::GetDefaultCert() {
|
|||||||
return default_cert_;
|
return default_cert_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DrmServiceCertificate* DrmServiceCertificateMap::GetCert(
|
const DrmServiceCertificate* DrmServiceCertificateMap::GetCertBySerialNumber(
|
||||||
const std::string& serial_number) {
|
const std::string& serial_number) {
|
||||||
absl::ReaderMutexLock lock(&mutex_);
|
absl::ReaderMutexLock lock(&mutex_);
|
||||||
return map_[serial_number].get();
|
return map_[serial_number].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DrmServiceCertificate* DrmServiceCertificateMap::GetCertByProvider(
|
||||||
|
const std::string& provider_id) {
|
||||||
|
absl::ReaderMutexLock lock(&mutex_);
|
||||||
|
DrmServiceCertificate* provider_drm_cert = nullptr;
|
||||||
|
for (const auto& drm_cert : map_) {
|
||||||
|
if (drm_cert.second->provider_id() == provider_id) {
|
||||||
|
if (provider_drm_cert == nullptr) {
|
||||||
|
provider_drm_cert = drm_cert.second.get();
|
||||||
|
} else if (drm_cert.second->creation_time_seconds() >
|
||||||
|
provider_drm_cert->creation_time_seconds()) {
|
||||||
|
// Use the newest cert.
|
||||||
|
provider_drm_cert = drm_cert.second.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return provider_drm_cert;
|
||||||
|
}
|
||||||
|
|
||||||
DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
|
DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
|
||||||
static auto* const kInstance = new DrmServiceCertificateMap();
|
static auto* const kInstance = new DrmServiceCertificateMap();
|
||||||
return kInstance;
|
return kInstance;
|
||||||
@@ -108,7 +128,8 @@ DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Status DrmServiceCertificate::AddDrmServiceCertificate(
|
Status DrmServiceCertificate::AddDrmServiceCertificate(
|
||||||
const DrmRootCertificate* root_drm_cert, const std::string& service_certificate,
|
const DrmRootCertificate* root_drm_cert,
|
||||||
|
const std::string& service_certificate,
|
||||||
const std::string& service_private_key,
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase) {
|
const std::string& service_private_key_passphrase) {
|
||||||
DrmCertificate drm_cert;
|
DrmCertificate drm_cert;
|
||||||
@@ -166,13 +187,22 @@ DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() {
|
|||||||
return default_cert;
|
return default_cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate(
|
const DrmServiceCertificate*
|
||||||
|
DrmServiceCertificate::GetDrmServiceCertificateBySerialNumber(
|
||||||
const std::string& serial_number) {
|
const std::string& serial_number) {
|
||||||
return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number);
|
return DrmServiceCertificateMap::GetInstance()->GetCertBySerialNumber(
|
||||||
|
serial_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DrmServiceCertificate*
|
||||||
|
DrmServiceCertificate::GetDrmServiceCertificateByProvider(
|
||||||
|
const std::string& provider) {
|
||||||
|
return DrmServiceCertificateMap::GetInstance()->GetCertByProvider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||||
const DrmRootCertificate* root_drm_cert, const std::string& service_certificate,
|
const DrmRootCertificate* root_drm_cert,
|
||||||
|
const std::string& service_certificate,
|
||||||
const std::string& service_private_key,
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase) {
|
const std::string& service_private_key_passphrase) {
|
||||||
DrmServiceCertificateMap::GetInstance()->ClearDefaultDrmServiceCertificate();
|
DrmServiceCertificateMap::GetInstance()->ClearDefaultDrmServiceCertificate();
|
||||||
@@ -207,7 +237,7 @@ Status DrmServiceCertificate::DecryptClientIdentification(
|
|||||||
}
|
}
|
||||||
std::string privacy_key;
|
std::string privacy_key;
|
||||||
std::string provider_id;
|
std::string provider_id;
|
||||||
const DrmServiceCertificate* cert = GetDrmServiceCertificate(
|
const DrmServiceCertificate* cert = GetDrmServiceCertificateBySerialNumber(
|
||||||
encrypted_client_id.service_certificate_serial_number());
|
encrypted_client_id.service_certificate_serial_number());
|
||||||
if (!cert) {
|
if (!cert) {
|
||||||
return Status(
|
return Status(
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "base/macros.h"
|
|
||||||
#include "common/certificate_type.h"
|
#include "common/certificate_type.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "common/status.h"
|
#include "common/status.h"
|
||||||
@@ -36,6 +35,9 @@ class EncryptedClientIdentification;
|
|||||||
// functionality.
|
// functionality.
|
||||||
class DrmServiceCertificate {
|
class DrmServiceCertificate {
|
||||||
public:
|
public:
|
||||||
|
DrmServiceCertificate(const DrmServiceCertificate&) = delete;
|
||||||
|
DrmServiceCertificate& operator=(const DrmServiceCertificate&) = delete;
|
||||||
|
|
||||||
// Create a new DrmServiceCertificate object and add it to the list of valid
|
// Create a new DrmServiceCertificate object and add it to the list of valid
|
||||||
// service certificates. |drm_root_cert| is the root certificate for the type
|
// service certificates. |drm_root_cert| is the root certificate for the type
|
||||||
// of certifiate being added. |service_certificate| is a
|
// of certifiate being added. |service_certificate| is a
|
||||||
@@ -50,7 +52,8 @@ class DrmServiceCertificate {
|
|||||||
// This method is thread-safe.
|
// This method is thread-safe.
|
||||||
static Status AddDrmServiceCertificate(
|
static Status AddDrmServiceCertificate(
|
||||||
const DrmRootCertificate* root_drm_cert,
|
const DrmRootCertificate* root_drm_cert,
|
||||||
const std::string& service_certificate, const std::string& service_private_key,
|
const std::string& service_certificate,
|
||||||
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase);
|
const std::string& service_private_key_passphrase);
|
||||||
|
|
||||||
// Same as AddDrmServiceCertificate(), but will clear the default service
|
// Same as AddDrmServiceCertificate(), but will clear the default service
|
||||||
@@ -58,7 +61,8 @@ class DrmServiceCertificate {
|
|||||||
// being set as the default service certificate.
|
// being set as the default service certificate.
|
||||||
static Status SetDefaultDrmServiceCertificate(
|
static Status SetDefaultDrmServiceCertificate(
|
||||||
const DrmRootCertificate* root_drm_cert,
|
const DrmRootCertificate* root_drm_cert,
|
||||||
const std::string& service_certificate, const std::string& service_private_key,
|
const std::string& service_certificate,
|
||||||
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase);
|
const std::string& service_private_key_passphrase);
|
||||||
|
|
||||||
// Returns the default service certificate. Will return null if no default
|
// Returns the default service certificate. Will return null if no default
|
||||||
@@ -69,11 +73,17 @@ class DrmServiceCertificate {
|
|||||||
// Certificate is set. This method is thread-safe.
|
// Certificate is set. This method is thread-safe.
|
||||||
static const DrmServiceCertificate* GetDefaultDrmServiceCertificateOrDie();
|
static const DrmServiceCertificate* GetDefaultDrmServiceCertificateOrDie();
|
||||||
|
|
||||||
// Returns the service certificate with the given serial number if found, or
|
// Returns the service certificate with the given |cert_serial_number|, or
|
||||||
// null otherwise.
|
// null otherwise.
|
||||||
static const DrmServiceCertificate* GetDrmServiceCertificate(
|
static const DrmServiceCertificate* GetDrmServiceCertificateBySerialNumber(
|
||||||
const std::string& cert_serial_number);
|
const std::string& cert_serial_number);
|
||||||
|
|
||||||
|
// Returns the service certificate with the given |provider_id|, or
|
||||||
|
// null otherwise. If multple certificates exist for the provider, the
|
||||||
|
// newest certificate is returned.
|
||||||
|
static const DrmServiceCertificate* GetDrmServiceCertificateByProvider(
|
||||||
|
const std::string& provider_id);
|
||||||
|
|
||||||
// Decrypts the EncryptedClientIdentification message passed in
|
// Decrypts the EncryptedClientIdentification message passed in
|
||||||
// |encrypted_client_id| into |client_id| using the private key for the
|
// |encrypted_client_id| into |client_id| using the private key for the
|
||||||
// certificate which was used to encrypt the information. |client_id| must
|
// certificate which was used to encrypt the information. |client_id| must
|
||||||
@@ -86,6 +96,7 @@ class DrmServiceCertificate {
|
|||||||
const std::string& certificate() const { return certificate_; }
|
const std::string& certificate() const { return certificate_; }
|
||||||
const std::string& provider_id() const { return provider_id_; }
|
const std::string& provider_id() const { return provider_id_; }
|
||||||
const std::string& serial_number() const { return serial_number_; }
|
const std::string& serial_number() const { return serial_number_; }
|
||||||
|
uint32_t creation_time_seconds() const { return creation_time_seconds_; }
|
||||||
const RsaPrivateKey* const private_key() const { return private_key_.get(); }
|
const RsaPrivateKey* const private_key() const { return private_key_.get(); }
|
||||||
const RsaPublicKey* const public_key() const { return public_key_.get(); }
|
const RsaPublicKey* const public_key() const { return public_key_.get(); }
|
||||||
|
|
||||||
@@ -100,17 +111,20 @@ class DrmServiceCertificate {
|
|||||||
friend class widevine::RequestInspectorTest;
|
friend class widevine::RequestInspectorTest;
|
||||||
|
|
||||||
static Status AddDrmServiceCertificate(
|
static Status AddDrmServiceCertificate(
|
||||||
const std::string& root_public_key, const std::string& service_certificate,
|
const std::string& root_public_key,
|
||||||
|
const std::string& service_certificate,
|
||||||
const std::string& service_private_key,
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase);
|
const std::string& service_private_key_passphrase);
|
||||||
|
|
||||||
static Status SetDefaultDrmServiceCertificate(
|
static Status SetDefaultDrmServiceCertificate(
|
||||||
const std::string& root_public_key, const std::string& service_certificate,
|
const std::string& root_public_key,
|
||||||
|
const std::string& service_certificate,
|
||||||
const std::string& service_private_key,
|
const std::string& service_private_key,
|
||||||
const std::string& service_private_key_passphrase);
|
const std::string& service_private_key_passphrase);
|
||||||
|
|
||||||
DrmServiceCertificate(const std::string& service_certificate,
|
DrmServiceCertificate(const std::string& service_certificate,
|
||||||
const std::string& provider_id, const std::string& serial_number,
|
const std::string& provider_id,
|
||||||
|
const std::string& serial_number,
|
||||||
const uint32_t creation_time_seconds,
|
const uint32_t creation_time_seconds,
|
||||||
std::unique_ptr<RsaPublicKey> public_key,
|
std::unique_ptr<RsaPublicKey> public_key,
|
||||||
std::unique_ptr<RsaPrivateKey> private_key);
|
std::unique_ptr<RsaPrivateKey> private_key);
|
||||||
@@ -123,8 +137,6 @@ class DrmServiceCertificate {
|
|||||||
uint32_t creation_time_seconds_;
|
uint32_t creation_time_seconds_;
|
||||||
std::unique_ptr<RsaPublicKey> public_key_;
|
std::unique_ptr<RsaPublicKey> public_key_;
|
||||||
std::unique_ptr<RsaPrivateKey> private_key_;
|
std::unique_ptr<RsaPrivateKey> private_key_;
|
||||||
|
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmServiceCertificate);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string GenerateDrmServiceCertificate(const std::string& serial_number,
|
std::string GenerateDrmServiceCertificate(const std::string& serial_number,
|
||||||
const std::string& provider_id,
|
const std::string& provider_id,
|
||||||
uint32_t creation_time_seconds,
|
uint32_t creation_time_seconds,
|
||||||
const std::string& public_key) {
|
const std::string& public_key) {
|
||||||
DrmCertificate cert;
|
DrmCertificate cert;
|
||||||
cert.set_type(DrmCertificate::SERVICE);
|
cert.set_type(DrmCertificate::SERVICE);
|
||||||
cert.set_serial_number(serial_number);
|
cert.set_serial_number(serial_number);
|
||||||
@@ -160,6 +160,9 @@ TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) {
|
|||||||
uint32_t creation_time_seconds1(1234);
|
uint32_t creation_time_seconds1(1234);
|
||||||
std::string serial_number2("serial_number2");
|
std::string serial_number2("serial_number2");
|
||||||
uint32_t creation_time_seconds2(1234);
|
uint32_t creation_time_seconds2(1234);
|
||||||
|
std::string serial_number3("serial_number3");
|
||||||
|
std::string provider_id3("service3.com");
|
||||||
|
uint32_t creation_time_seconds3(1235);
|
||||||
std::string bogus_serial_number("bogus-serial-number2");
|
std::string bogus_serial_number("bogus-serial-number2");
|
||||||
|
|
||||||
EXPECT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate());
|
EXPECT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate());
|
||||||
@@ -172,6 +175,9 @@ TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) {
|
|||||||
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id1,
|
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id1,
|
||||||
creation_time_seconds2));
|
creation_time_seconds2));
|
||||||
|
|
||||||
|
EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id3,
|
||||||
|
creation_time_seconds3));
|
||||||
|
|
||||||
EncryptedClientIdentification encrypted_client_id;
|
EncryptedClientIdentification encrypted_client_id;
|
||||||
EncryptClientIdentification(serial_number1, provider_id1,
|
EncryptClientIdentification(serial_number1, provider_id1,
|
||||||
test_keys_.public_test_key_2_2048_bits(),
|
test_keys_.public_test_key_2_2048_bits(),
|
||||||
@@ -199,6 +205,61 @@ TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) {
|
|||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificatesLookup) {
|
||||||
|
std::string serial_number1("serial_number1");
|
||||||
|
std::string provider_id1("service1.com");
|
||||||
|
uint32_t creation_time_seconds1(1231);
|
||||||
|
std::string serial_number2("serial_number2");
|
||||||
|
std::string provider_id2("service2.com");
|
||||||
|
uint32_t creation_time_seconds2(1232);
|
||||||
|
std::string serial_number3("serial_number3");
|
||||||
|
std::string provider_id3("service3.com");
|
||||||
|
uint32_t creation_time_seconds3(1234);
|
||||||
|
std::string serial_number4("serial_number4");
|
||||||
|
std::string bogus_serial_number("bogus-serial-number");
|
||||||
|
|
||||||
|
EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id1,
|
||||||
|
creation_time_seconds1));
|
||||||
|
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id2,
|
||||||
|
creation_time_seconds2));
|
||||||
|
EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id3,
|
||||||
|
creation_time_seconds3));
|
||||||
|
|
||||||
|
EXPECT_EQ(provider_id1,
|
||||||
|
DrmServiceCertificate::GetDrmServiceCertificateBySerialNumber(
|
||||||
|
serial_number1)
|
||||||
|
->provider_id());
|
||||||
|
EXPECT_EQ(provider_id2,
|
||||||
|
DrmServiceCertificate::GetDrmServiceCertificateBySerialNumber(
|
||||||
|
serial_number2)
|
||||||
|
->provider_id());
|
||||||
|
EXPECT_EQ(provider_id3,
|
||||||
|
DrmServiceCertificate::GetDrmServiceCertificateBySerialNumber(
|
||||||
|
serial_number3)
|
||||||
|
->provider_id());
|
||||||
|
|
||||||
|
EXPECT_EQ(
|
||||||
|
serial_number1,
|
||||||
|
DrmServiceCertificate::GetDrmServiceCertificateByProvider(provider_id1)
|
||||||
|
->serial_number());
|
||||||
|
EXPECT_EQ(
|
||||||
|
serial_number2,
|
||||||
|
DrmServiceCertificate::GetDrmServiceCertificateByProvider(provider_id2)
|
||||||
|
->serial_number());
|
||||||
|
EXPECT_EQ(
|
||||||
|
serial_number3,
|
||||||
|
DrmServiceCertificate::GetDrmServiceCertificateByProvider(provider_id3)
|
||||||
|
->serial_number());
|
||||||
|
|
||||||
|
// Add a second cert for provider 3.
|
||||||
|
EXPECT_OK(AddDrmServiceCertificate(serial_number4, provider_id3,
|
||||||
|
creation_time_seconds3 + 60));
|
||||||
|
EXPECT_EQ(
|
||||||
|
serial_number4,
|
||||||
|
DrmServiceCertificate::GetDrmServiceCertificateByProvider(provider_id3)
|
||||||
|
->serial_number());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) {
|
TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) {
|
||||||
std::string serial_number1("serial_number1");
|
std::string serial_number1("serial_number1");
|
||||||
std::string serial_number2("serial_number2");
|
std::string serial_number2("serial_number2");
|
||||||
|
|||||||
280
common/ec_key.cc
Normal file
280
common/ec_key.cc
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Definition of elliptic curve public and private key classes.
|
||||||
|
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "openssl/base.h"
|
||||||
|
#include "openssl/bn.h"
|
||||||
|
#include "openssl/ec.h"
|
||||||
|
#include "openssl/ecdh.h"
|
||||||
|
#include "openssl/ecdsa.h"
|
||||||
|
#include "openssl/err.h"
|
||||||
|
#include "openssl/evp.h"
|
||||||
|
#include "openssl/sha.h"
|
||||||
|
#include "common/aes_cbc_util.h"
|
||||||
|
#include "common/ec_util.h"
|
||||||
|
#include "common/openssl_util.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void* SHA256KDF(const void* in, size_t inlen, void* out, size_t* outlen) {
|
||||||
|
std::string shared_session_key(static_cast<const char*>(in), inlen);
|
||||||
|
std::string derived_shared_session_key = Sha256_Hash(shared_session_key);
|
||||||
|
if (*outlen != SHA256_DIGEST_LENGTH) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
memcpy(out, derived_shared_session_key.data(), *outlen);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECPrivateKey::EllipticCurve ECKeyToCurve(const EC_KEY* key) {
|
||||||
|
if (key == nullptr) {
|
||||||
|
return ECPrivateKey::UNDEFINED_CURVE;
|
||||||
|
}
|
||||||
|
return ec_util::NidToCurve(EC_GROUP_get_curve_name(EC_KEY_get0_group(key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OpenSSLErrorString(uint32_t error) {
|
||||||
|
char buf[ERR_ERROR_STRING_BUF_LEN];
|
||||||
|
ERR_error_string_n(error, buf, sizeof(buf));
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ECPrivateKey::ECPrivateKey(EC_KEY* ec_key) : key_(ec_key) {
|
||||||
|
CHECK(key() != nullptr);
|
||||||
|
CHECK(EC_KEY_get0_private_key(key()) != nullptr);
|
||||||
|
CHECK_NE(ECKeyToCurve(key()), ECPrivateKey::UNDEFINED_CURVE);
|
||||||
|
CHECK_EQ(EC_KEY_check_key(key()), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ECPrivateKey::ECPrivateKey(ScopedECKEY ec_key) : key_(std::move(ec_key)) {}
|
||||||
|
|
||||||
|
ECPrivateKey::ECPrivateKey(const ECPrivateKey& ec_key)
|
||||||
|
: ECPrivateKey(EC_KEY_dup(ec_key.key())) {}
|
||||||
|
|
||||||
|
std::unique_ptr<ECPrivateKey> ECPrivateKey::Create(
|
||||||
|
const std::string& serialized_key) {
|
||||||
|
EC_KEY* key;
|
||||||
|
if (!ec_util::DeserializeECPrivateKey(serialized_key, &key)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ScopedECKEY scoped_ec_key(key);
|
||||||
|
if (EC_KEY_check_key(scoped_ec_key.get()) != 1) {
|
||||||
|
LOG(ERROR) << "Invalid private EC key: "
|
||||||
|
<< OpenSSLErrorString(ERR_get_error());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (ECKeyToCurve(scoped_ec_key.get()) == ECPrivateKey::UNDEFINED_CURVE) {
|
||||||
|
LOG(ERROR) << "Key has unsupported curve";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return absl::make_unique<ECPrivateKey>(scoped_ec_key.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ECPublicKey> ECPrivateKey::PublicKey() const {
|
||||||
|
if (key_ == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return absl::make_unique<ECPublicKey>(ScopedECKEY(EC_KEY_dup(key_.get())));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPrivateKey::DeriveSharedSessionKey(
|
||||||
|
const ECPublicKey& public_key,
|
||||||
|
std::string* derived_shared_session_key) const {
|
||||||
|
if (derived_shared_session_key == nullptr) {
|
||||||
|
LOG(ERROR) << "|derived_shared_session_key| cannot be nullptr";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const EC_GROUP* group1 = EC_KEY_get0_group(key());
|
||||||
|
const EC_GROUP* group2 = EC_KEY_get0_group(public_key.key());
|
||||||
|
if (EC_GROUP_cmp(group1, group2, nullptr) != 0) {
|
||||||
|
LOG(ERROR) << "EC_GROUPs do not match";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const EC_POINT* public_key_point = EC_KEY_get0_public_key(public_key.key());
|
||||||
|
derived_shared_session_key->resize(SHA256_DIGEST_LENGTH, 0);
|
||||||
|
int result = ECDH_compute_key(
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
const_cast<char*>(derived_shared_session_key->data())),
|
||||||
|
derived_shared_session_key->size(), public_key_point, key(), SHA256KDF);
|
||||||
|
if (result == -1) {
|
||||||
|
LOG(ERROR) << "Could not write shared session key: "
|
||||||
|
<< OpenSSLErrorString(ERR_get_error());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (result != SHA256_DIGEST_LENGTH) {
|
||||||
|
LOG(ERROR) << "Wrote " << result
|
||||||
|
<< " bytes to derived shared session key instead of "
|
||||||
|
<< SHA256_DIGEST_LENGTH << " bytes";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPrivateKey::GenerateSignature(const std::string& message,
|
||||||
|
std::string* signature) const {
|
||||||
|
if (message.empty()) {
|
||||||
|
LOG(ERROR) << "|message| cannot be empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (signature == nullptr) {
|
||||||
|
LOG(ERROR) << "|signature| cannot be nullptr";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string message_digest = Sha256_Hash(message);
|
||||||
|
size_t max_signature_size = ECDSA_size(key());
|
||||||
|
if (max_signature_size == 0) {
|
||||||
|
LOG(ERROR) << "key_ does not have a group set";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
signature->resize(max_signature_size);
|
||||||
|
unsigned int bytes_written = 0;
|
||||||
|
int result = ECDSA_sign(
|
||||||
|
0 /* unused type */,
|
||||||
|
reinterpret_cast<const uint8_t*>(message_digest.data()),
|
||||||
|
message_digest.size(),
|
||||||
|
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
|
||||||
|
&bytes_written, key());
|
||||||
|
if (result != 1) {
|
||||||
|
LOG(ERROR) << "Could not calculate signature: "
|
||||||
|
<< OpenSSLErrorString(ERR_get_error());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
signature->resize(bytes_written);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPrivateKey::MatchesPrivateKey(const ECPrivateKey& private_key) const {
|
||||||
|
return BN_cmp(EC_KEY_get0_private_key(key()),
|
||||||
|
EC_KEY_get0_private_key(private_key.key())) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPrivateKey::MatchesPublicKey(const ECPublicKey& public_key) const {
|
||||||
|
return EC_POINT_cmp(EC_KEY_get0_group(key()), EC_KEY_get0_public_key(key()),
|
||||||
|
EC_KEY_get0_public_key(public_key.key()), nullptr) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECPrivateKey::EllipticCurve ECPrivateKey::Curve() const {
|
||||||
|
return ECKeyToCurve(key());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPrivateKey::SerializedKey(std::string* serialized_key) const {
|
||||||
|
CHECK(serialized_key);
|
||||||
|
return ec_util::SerializeECPrivateKey(key_.get(), serialized_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
ECPublicKey::ECPublicKey(EC_KEY* ec_key) : key_(ec_key) {
|
||||||
|
CHECK(key() != nullptr);
|
||||||
|
CHECK(EC_KEY_get0_private_key(key()) == nullptr);
|
||||||
|
CHECK_NE(ECKeyToCurve(key()), ECPrivateKey::UNDEFINED_CURVE);
|
||||||
|
CHECK_EQ(EC_KEY_check_key(key()), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ECPublicKey::ECPublicKey(ScopedECKEY ec_key) : key_(std::move(ec_key)) {}
|
||||||
|
|
||||||
|
ECPublicKey::ECPublicKey(const ECPublicKey& ec_key)
|
||||||
|
: ECPublicKey(EC_KEY_dup(ec_key.key())) {}
|
||||||
|
|
||||||
|
std::unique_ptr<ECPublicKey> ECPublicKey::Create(
|
||||||
|
const std::string& serialized_key) {
|
||||||
|
EC_KEY* key;
|
||||||
|
if (!ec_util::DeserializeECPublicKey(serialized_key, &key)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ScopedECKEY scoped_ec_key(key);
|
||||||
|
if (EC_KEY_check_key(scoped_ec_key.get()) != 1) {
|
||||||
|
LOG(ERROR) << "Invalid public EC key: "
|
||||||
|
<< OpenSSLErrorString(ERR_get_error());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (ECKeyToCurve(scoped_ec_key.get()) == ECPrivateKey::UNDEFINED_CURVE) {
|
||||||
|
LOG(ERROR) << "Key has unsupported curve";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return absl::make_unique<ECPublicKey>(scoped_ec_key.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ECPublicKey> ECPublicKey::CreateFromKeyPoint(
|
||||||
|
ECPrivateKey::EllipticCurve curve, const std::string& key_point) {
|
||||||
|
EC_KEY* key;
|
||||||
|
if (!ec_util::GetPublicKeyFromKeyPoint(curve, key_point, &key)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedECKEY scoped_ec_key(key);
|
||||||
|
if (EC_KEY_check_key(scoped_ec_key.get()) != 1) {
|
||||||
|
LOG(ERROR) << "Invalid public EC key: "
|
||||||
|
<< OpenSSLErrorString(ERR_get_error());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (ECKeyToCurve(scoped_ec_key.get()) == ECPrivateKey::UNDEFINED_CURVE) {
|
||||||
|
LOG(ERROR) << "Key has unsupported curve";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return absl::make_unique<ECPublicKey>(scoped_ec_key.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPublicKey::VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature) const {
|
||||||
|
if (message.empty()) {
|
||||||
|
LOG(ERROR) << "|message| cannot be empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (signature.empty()) {
|
||||||
|
LOG(ERROR) << "|signature| cannot be empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string message_digest = Sha256_Hash(message);
|
||||||
|
int result = ECDSA_verify(
|
||||||
|
0 /* unused type */,
|
||||||
|
reinterpret_cast<const uint8_t*>(message_digest.data()),
|
||||||
|
message_digest.size(),
|
||||||
|
reinterpret_cast<uint8_t*>(const_cast<char*>(signature.data())),
|
||||||
|
signature.size(), key());
|
||||||
|
if (result != 1) {
|
||||||
|
LOG(ERROR) << "Could not verify signature: "
|
||||||
|
<< OpenSSLErrorString(ERR_get_error());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPublicKey::MatchesPrivateKey(const ECPrivateKey& private_key) const {
|
||||||
|
return private_key.MatchesPublicKey(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPublicKey::MatchesPublicKey(const ECPublicKey& public_key) const {
|
||||||
|
return EC_POINT_cmp(EC_KEY_get0_group(key()), EC_KEY_get0_public_key(key()),
|
||||||
|
EC_KEY_get0_public_key(public_key.key()), nullptr) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECPrivateKey::EllipticCurve ECPublicKey::Curve() const {
|
||||||
|
return ECKeyToCurve(key());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPublicKey::SerializedKey(std::string* serialized_key) const {
|
||||||
|
CHECK(serialized_key);
|
||||||
|
return ec_util::SerializeECPublicKey(key_.get(), serialized_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ECPublicKey::GetPointEncodedKey(std::string* encoded_key) const {
|
||||||
|
CHECK(encoded_key);
|
||||||
|
return ec_util::GetPublicKeyPoint(key_.get(), encoded_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
144
common/ec_key.h
Normal file
144
common/ec_key.h
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Generic elliptic curve classes for private and public keys. Declares
|
||||||
|
// methods for deriving shared session keys and signatures.
|
||||||
|
|
||||||
|
#ifndef COMMON_EC_KEY_H_
|
||||||
|
#define COMMON_EC_KEY_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "openssl/ec.h"
|
||||||
|
#include "common/openssl_util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ECPublicKey;
|
||||||
|
|
||||||
|
class ECPrivateKey {
|
||||||
|
public:
|
||||||
|
explicit ECPrivateKey(EC_KEY* ec_key);
|
||||||
|
explicit ECPrivateKey(ScopedECKEY ec_key);
|
||||||
|
ECPrivateKey(const ECPrivateKey&);
|
||||||
|
ECPrivateKey() = delete;
|
||||||
|
virtual ~ECPrivateKey() = default;
|
||||||
|
|
||||||
|
// Accepted standard curves for key generation. Names are derived from SECG.
|
||||||
|
enum EllipticCurve {
|
||||||
|
UNDEFINED_CURVE = 0,
|
||||||
|
SECP256R1,
|
||||||
|
SECP384R1,
|
||||||
|
SECP521R1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Creates an ECPrivateKey using a DER encoded RFC5915 std::string
|
||||||
|
// representing a private key. Returns an ECPrivateKey on success or nullptr
|
||||||
|
// on failure.
|
||||||
|
static std::unique_ptr<ECPrivateKey> Create(
|
||||||
|
const std::string& serialized_key);
|
||||||
|
|
||||||
|
// Exports the matching public key for this private key.
|
||||||
|
virtual std::unique_ptr<ECPublicKey> PublicKey() const;
|
||||||
|
|
||||||
|
// Calculates a shared session key using ECDH and then uses a key derivation
|
||||||
|
// function, SHA256, to derive a key.
|
||||||
|
// |public_key| is an EC public key with the same curve parameters as key_. It
|
||||||
|
// is used as the public key component of ECDH.
|
||||||
|
// |derived_shared_session_key| will be the result of the derivation.
|
||||||
|
// Caller retains ownership of all pointers.
|
||||||
|
// Returns true on success and false on error.
|
||||||
|
virtual bool DeriveSharedSessionKey(
|
||||||
|
const ECPublicKey& public_key,
|
||||||
|
std::string* derived_shared_session_key) const;
|
||||||
|
|
||||||
|
// Given a message, calculates a signature using ECDSA with the key_.
|
||||||
|
// |message| is the message to be signed.
|
||||||
|
// |signature| will contain the resulting signature. This will be an ASN.1
|
||||||
|
// DER-encoded signature.
|
||||||
|
// Caller retains ownership of all pointers.
|
||||||
|
// Returns true on success and false on error.
|
||||||
|
virtual bool GenerateSignature(const std::string& message,
|
||||||
|
std::string* signature) const;
|
||||||
|
|
||||||
|
// Returns whether the given private key is the same as key_.
|
||||||
|
virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const;
|
||||||
|
|
||||||
|
// Returns whether the given public key is part of the same key pair as key_.
|
||||||
|
virtual bool MatchesPublicKey(const ECPublicKey& public_key) const;
|
||||||
|
|
||||||
|
// Returns the EllipticCurve associated with key_.
|
||||||
|
virtual EllipticCurve Curve() const;
|
||||||
|
|
||||||
|
virtual bool SerializedKey(std::string* serialized_key) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ECPublicKey;
|
||||||
|
|
||||||
|
ECPrivateKey& operator=(const ECPrivateKey&) = delete;
|
||||||
|
|
||||||
|
const EC_KEY* key() const { return key_.get(); }
|
||||||
|
|
||||||
|
ScopedECKEY key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ECPublicKey {
|
||||||
|
public:
|
||||||
|
explicit ECPublicKey(EC_KEY* ec_key);
|
||||||
|
explicit ECPublicKey(ScopedECKEY ec_key);
|
||||||
|
ECPublicKey(const ECPublicKey&);
|
||||||
|
virtual ~ECPublicKey() = default;
|
||||||
|
|
||||||
|
// Creates an ECPublicKey using a DER encoded RFC5208 std::string representing
|
||||||
|
// a public key. Returns an ECPublicKey on success or nullptr on failure.
|
||||||
|
static std::unique_ptr<ECPublicKey> Create(const std::string& serialized_key);
|
||||||
|
|
||||||
|
// Creates an ECPublicKey from an uncompressed point compatible with X9.62.
|
||||||
|
// Returns an ECPublicKey on success or nullptr on failure.
|
||||||
|
static std::unique_ptr<ECPublicKey> CreateFromKeyPoint(
|
||||||
|
ECPrivateKey::EllipticCurve curve, const std::string& key_point);
|
||||||
|
|
||||||
|
// Given a message and a signature, verifies that the signature was created
|
||||||
|
// using the private key associated with key_.
|
||||||
|
// |message| is the message that was signed.
|
||||||
|
// |signature| is an ASN.1 DER-encoded signature.
|
||||||
|
// Returns true on success and false on error.
|
||||||
|
virtual bool VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature) const;
|
||||||
|
|
||||||
|
// Returns whether the given private key is part of the same key pair as key_.
|
||||||
|
virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const;
|
||||||
|
|
||||||
|
// Returns whether the given public key is the same as key_.
|
||||||
|
virtual bool MatchesPublicKey(const ECPublicKey& public_key) const;
|
||||||
|
|
||||||
|
// Returns the EllipticCurve associated with key_.
|
||||||
|
virtual ECPrivateKey::EllipticCurve Curve() const;
|
||||||
|
|
||||||
|
virtual bool SerializedKey(std::string* serialized_key) const;
|
||||||
|
|
||||||
|
// Returns the key in the uncompressed point format. Compatible with X9.62
|
||||||
|
// The lead byte indicates uncompressed format (0x4). This is followed by the
|
||||||
|
// octets for the X and Y coordinates.
|
||||||
|
virtual bool GetPointEncodedKey(std::string* encoded_key) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ECPrivateKey;
|
||||||
|
|
||||||
|
ECPublicKey& operator=(const ECPublicKey&) = delete;
|
||||||
|
|
||||||
|
const EC_KEY* key() const { return key_.get(); }
|
||||||
|
|
||||||
|
ScopedECKEY key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_EC_KEY_H_
|
||||||
43
common/ec_key_source.h
Normal file
43
common/ec_key_source.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Declaration for abstract EC key generation class.
|
||||||
|
|
||||||
|
#ifndef COMMON_EC_KEY_SOURCE_H_
|
||||||
|
#define COMMON_EC_KEY_SOURCE_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ECKeySource {
|
||||||
|
public:
|
||||||
|
ECKeySource() = default;
|
||||||
|
virtual ~ECKeySource() = default;
|
||||||
|
|
||||||
|
// Get an EC key pair given a specific curve defined by EllipticCurve.
|
||||||
|
// Parameter |curve| is a standard curve which defines the parameters of the
|
||||||
|
// key generation.
|
||||||
|
// Parameter |private_key| is the serialized EC private key.
|
||||||
|
// Parameter |public_key| is the serialized EC public key.
|
||||||
|
// Caller retains ownership of all pointers and they cannot be nullptr.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
virtual bool GetECKey(ECPrivateKey::EllipticCurve curve,
|
||||||
|
std::string* private_key, std::string* public_key) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ECKeySource(const ECKeySource&) = delete;
|
||||||
|
ECKeySource& operator=(const ECKeySource&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_EC_KEY_SOURCE_H_
|
||||||
274
common/ec_key_test.cc
Normal file
274
common/ec_key_test.cc
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Unit tests for the ECPrivateKey and ECPublicKey classes.
|
||||||
|
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "openssl/sha.h"
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
|
#include "common/ec_util.h"
|
||||||
|
#include "common/random_util.h"
|
||||||
|
|
||||||
|
using ::testing::IsNull;
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ECKeyTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
static std::vector<std::tuple<std::string, std::string> > GetTestKeyList() {
|
||||||
|
std::vector<std::tuple<std::string, std::string> > test_key_list;
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
test_key_list.push_back(
|
||||||
|
std::make_tuple(test_keys.private_test_key_1_secp521r1(),
|
||||||
|
test_keys.public_test_key_1_secp521r1()));
|
||||||
|
test_key_list.push_back(
|
||||||
|
std::make_tuple(test_keys.private_test_key_1_secp384r1(),
|
||||||
|
test_keys.public_test_key_1_secp384r1()));
|
||||||
|
test_key_list.push_back(
|
||||||
|
std::make_tuple(test_keys.private_test_key_1_secp256r1(),
|
||||||
|
test_keys.public_test_key_1_secp256r1()));
|
||||||
|
return test_key_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ECKeyTest() { plaintext_message_ = "This is a plaintext message."; }
|
||||||
|
|
||||||
|
void GenerateEphemeralKeyPair(ECPrivateKey::EllipticCurve curve) {
|
||||||
|
ASSERT_NE(curve, ECPrivateKey::UNDEFINED_CURVE);
|
||||||
|
bssl::UniquePtr<EC_KEY> ephemeral_key_pair(
|
||||||
|
EC_KEY_new_by_curve_name(ec_util::CurveToNid(curve)));
|
||||||
|
ASSERT_TRUE(ephemeral_key_pair != nullptr);
|
||||||
|
ASSERT_TRUE(EC_KEY_generate_key(ephemeral_key_pair.get()));
|
||||||
|
// Separate them out into their private and public parts.
|
||||||
|
std::string private_key;
|
||||||
|
std::string public_key;
|
||||||
|
ASSERT_TRUE(
|
||||||
|
ec_util::SerializeECPrivateKey(ephemeral_key_pair.get(), &private_key));
|
||||||
|
ASSERT_TRUE(
|
||||||
|
ec_util::SerializeECPublicKey(ephemeral_key_pair.get(), &public_key));
|
||||||
|
ephemeral_private_key_ = ECPrivateKey::Create(private_key);
|
||||||
|
ephemeral_public_key_ = ECPublicKey::Create(public_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ECPrivateKey> ephemeral_private_key_;
|
||||||
|
std::unique_ptr<ECPublicKey> ephemeral_public_key_;
|
||||||
|
std::string plaintext_message_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ECKeyTest, KeyCurve) {
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
EXPECT_EQ(
|
||||||
|
ECPrivateKey::Create(test_keys.private_test_key_1_secp521r1())->Curve(),
|
||||||
|
ECPrivateKey::SECP521R1);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ECPrivateKey::Create(test_keys.private_test_key_1_secp384r1())->Curve(),
|
||||||
|
ECPrivateKey::SECP384R1);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ECPrivateKey::Create(test_keys.private_test_key_1_secp256r1())->Curve(),
|
||||||
|
ECPrivateKey::SECP256R1);
|
||||||
|
|
||||||
|
EXPECT_EQ(
|
||||||
|
ECPublicKey::Create(test_keys.public_test_key_1_secp521r1())->Curve(),
|
||||||
|
ECPrivateKey::SECP521R1);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ECPublicKey::Create(test_keys.public_test_key_1_secp384r1())->Curve(),
|
||||||
|
ECPrivateKey::SECP384R1);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ECPublicKey::Create(test_keys.public_test_key_1_secp256r1())->Curve(),
|
||||||
|
ECPrivateKey::SECP256R1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ECKeyTest, KeyPointEncodingNullDeath) {
|
||||||
|
EXPECT_DEATH(ephemeral_public_key_->GetPointEncodedKey(nullptr), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
class ECKeyTestKeyPairs : public ECKeyTest,
|
||||||
|
public ::testing::WithParamInterface<
|
||||||
|
std::tuple<std::string, std::string> > {
|
||||||
|
protected:
|
||||||
|
ECKeyTestKeyPairs() {
|
||||||
|
test_private_key_ = std::get<0>(GetParam());
|
||||||
|
test_public_key_ = std::get<1>(GetParam());
|
||||||
|
private_key_ = ECPrivateKey::Create(test_private_key_);
|
||||||
|
public_key_ = ECPublicKey::Create(test_public_key_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string test_private_key_;
|
||||||
|
std::string test_public_key_;
|
||||||
|
std::unique_ptr<ECPrivateKey> private_key_;
|
||||||
|
std::unique_ptr<ECPublicKey> public_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, CreateWrongKey) {
|
||||||
|
EXPECT_EQ(ECPrivateKey::Create(test_public_key_), nullptr);
|
||||||
|
EXPECT_EQ(ECPublicKey::Create(test_private_key_), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, InvalidKeyConstructorParameters) {
|
||||||
|
EXPECT_DEATH(ECPrivateKey(nullptr), "");
|
||||||
|
EXPECT_DEATH(ECPublicKey(nullptr), "");
|
||||||
|
|
||||||
|
EC_KEY* key;
|
||||||
|
ASSERT_TRUE(ec_util::DeserializeECPrivateKey(test_private_key_, &key));
|
||||||
|
// Brace initialization to avoid most vexing parse.
|
||||||
|
EXPECT_DEATH(ECPublicKey{key}, "");
|
||||||
|
EC_KEY_free(key);
|
||||||
|
|
||||||
|
ASSERT_TRUE(ec_util::DeserializeECPublicKey(test_public_key_, &key));
|
||||||
|
EXPECT_DEATH(ECPrivateKey{key}, "");
|
||||||
|
EC_KEY_free(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, KeyMatch) {
|
||||||
|
EXPECT_TRUE(private_key_->MatchesPrivateKey(*private_key_));
|
||||||
|
EXPECT_TRUE(private_key_->MatchesPublicKey(*public_key_));
|
||||||
|
EXPECT_TRUE(public_key_->MatchesPrivateKey(*private_key_));
|
||||||
|
EXPECT_TRUE(public_key_->MatchesPublicKey(*public_key_));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, DeriveSharedSessionKey) {
|
||||||
|
std::string derived_shared_session_key_1;
|
||||||
|
// Generate a temporary key pair as part of ECDH.
|
||||||
|
GenerateEphemeralKeyPair(private_key_->Curve());
|
||||||
|
EXPECT_TRUE(private_key_->DeriveSharedSessionKey(
|
||||||
|
*ephemeral_public_key_, &derived_shared_session_key_1));
|
||||||
|
EXPECT_EQ(derived_shared_session_key_1.size(), SHA256_DIGEST_LENGTH);
|
||||||
|
std::string derived_shared_session_key_2;
|
||||||
|
EXPECT_TRUE(ephemeral_private_key_->DeriveSharedSessionKey(
|
||||||
|
*public_key_, &derived_shared_session_key_2));
|
||||||
|
EXPECT_EQ(derived_shared_session_key_1, derived_shared_session_key_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, InvalidDeriveSharedSessionKeyParameters) {
|
||||||
|
GenerateEphemeralKeyPair(private_key_->Curve());
|
||||||
|
EXPECT_FALSE(
|
||||||
|
private_key_->DeriveSharedSessionKey(*ephemeral_public_key_, nullptr));
|
||||||
|
EXPECT_FALSE(
|
||||||
|
ephemeral_private_key_->DeriveSharedSessionKey(*public_key_, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, SignVerify) {
|
||||||
|
std::string signature;
|
||||||
|
EXPECT_TRUE(private_key_->GenerateSignature(plaintext_message_, &signature));
|
||||||
|
EXPECT_TRUE(public_key_->VerifySignature(plaintext_message_, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, InvalidSignVerifyParameters) {
|
||||||
|
std::string signature;
|
||||||
|
EXPECT_FALSE(private_key_->GenerateSignature("", &signature));
|
||||||
|
EXPECT_FALSE(public_key_->VerifySignature("", "signature"));
|
||||||
|
EXPECT_FALSE(private_key_->GenerateSignature(plaintext_message_, nullptr));
|
||||||
|
EXPECT_FALSE(public_key_->VerifySignature(plaintext_message_, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, KeyMismatch) {
|
||||||
|
GenerateEphemeralKeyPair(private_key_->Curve());
|
||||||
|
EXPECT_FALSE(private_key_->MatchesPrivateKey(*ephemeral_private_key_));
|
||||||
|
EXPECT_FALSE(private_key_->MatchesPublicKey(*ephemeral_public_key_));
|
||||||
|
EXPECT_FALSE(public_key_->MatchesPrivateKey(*ephemeral_private_key_));
|
||||||
|
EXPECT_FALSE(public_key_->MatchesPublicKey(*ephemeral_public_key_));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, SignVerifyKeyMismatch) {
|
||||||
|
std::string signature;
|
||||||
|
GenerateEphemeralKeyPair(private_key_->Curve());
|
||||||
|
EXPECT_TRUE(private_key_->GenerateSignature(plaintext_message_, &signature));
|
||||||
|
EXPECT_FALSE(
|
||||||
|
ephemeral_public_key_->VerifySignature(plaintext_message_, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, KeyPointEncodingCreateBadKeyPoint) {
|
||||||
|
ASSERT_THAT(ECPublicKey::CreateFromKeyPoint(public_key_->Curve(), ""),
|
||||||
|
IsNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, KeyPointEncodingCreateBadCurve) {
|
||||||
|
std::string serialized_key_point;
|
||||||
|
ASSERT_TRUE(public_key_->GetPointEncodedKey(&serialized_key_point));
|
||||||
|
ASSERT_THAT(ECPublicKey::CreateFromKeyPoint(ECPrivateKey::UNDEFINED_CURVE,
|
||||||
|
serialized_key_point),
|
||||||
|
IsNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, KeyPointEncodingSuccess) {
|
||||||
|
std::string serialized_key_point;
|
||||||
|
ASSERT_TRUE(public_key_->GetPointEncodedKey(&serialized_key_point));
|
||||||
|
ASSERT_EQ(ec_util::GetPublicKeyPointSize(public_key_->Curve()),
|
||||||
|
serialized_key_point.size());
|
||||||
|
// Check that the leading byte indicates uncompressed.
|
||||||
|
ASSERT_EQ(4, serialized_key_point[0]);
|
||||||
|
|
||||||
|
std::unique_ptr<ECPublicKey> new_public_key = ECPublicKey::CreateFromKeyPoint(
|
||||||
|
public_key_->Curve(), serialized_key_point);
|
||||||
|
ASSERT_THAT(new_public_key, testing::NotNull());
|
||||||
|
EXPECT_TRUE(new_public_key->MatchesPublicKey(*public_key_));
|
||||||
|
EXPECT_TRUE(new_public_key->MatchesPrivateKey(*private_key_));
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(ECKeyTestKeyPairs, ECKeyTestKeyPairs,
|
||||||
|
::testing::ValuesIn(ECKeyTest::GetTestKeyList()));
|
||||||
|
|
||||||
|
class ECKeyTestCurveMismatch
|
||||||
|
: public ECKeyTest,
|
||||||
|
public ::testing::WithParamInterface<
|
||||||
|
std::tuple<std::tuple<std::string, std::string>,
|
||||||
|
std::tuple<std::string, std::string> > > {
|
||||||
|
protected:
|
||||||
|
ECKeyTestCurveMismatch() {
|
||||||
|
private_key_ = ECPrivateKey::Create(std::get<0>(std::get<0>(GetParam())));
|
||||||
|
public_key_ = ECPublicKey::Create(std::get<1>(std::get<0>(GetParam())));
|
||||||
|
wrong_curve_private_key_ =
|
||||||
|
ECPrivateKey::Create(std::get<0>(std::get<1>(GetParam())));
|
||||||
|
wrong_curve_public_key_ =
|
||||||
|
ECPublicKey::Create(std::get<1>(std::get<1>(GetParam())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
// Only run combinations where the two curves differ.
|
||||||
|
if (std::get<0>(GetParam()) == std::get<1>(GetParam())) GTEST_SKIP();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ECPrivateKey> private_key_;
|
||||||
|
std::unique_ptr<ECPublicKey> public_key_;
|
||||||
|
std::unique_ptr<ECPrivateKey> wrong_curve_private_key_;
|
||||||
|
std::unique_ptr<ECPublicKey> wrong_curve_public_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestCurveMismatch, CurveMismatch) {
|
||||||
|
EXPECT_FALSE(private_key_->MatchesPrivateKey(*wrong_curve_private_key_));
|
||||||
|
EXPECT_FALSE(private_key_->MatchesPublicKey(*wrong_curve_public_key_));
|
||||||
|
EXPECT_FALSE(public_key_->MatchesPrivateKey(*wrong_curve_private_key_));
|
||||||
|
EXPECT_FALSE(public_key_->MatchesPublicKey(*wrong_curve_public_key_));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestCurveMismatch, DeriveSharedSessionKeyCurveMismatch) {
|
||||||
|
std::string derived_shared_session_key;
|
||||||
|
EXPECT_FALSE(private_key_->DeriveSharedSessionKey(
|
||||||
|
*wrong_curve_public_key_, &derived_shared_session_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestCurveMismatch, SignVerifyCurveMismatch) {
|
||||||
|
std::string signature;
|
||||||
|
EXPECT_TRUE(private_key_->GenerateSignature(plaintext_message_, &signature));
|
||||||
|
EXPECT_FALSE(
|
||||||
|
wrong_curve_public_key_->VerifySignature(plaintext_message_, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
ECKeyTestCurveMismatch, ECKeyTestCurveMismatch,
|
||||||
|
::testing::Combine(::testing::ValuesIn(ECKeyTest::GetTestKeyList()),
|
||||||
|
::testing::ValuesIn(ECKeyTest::GetTestKeyList())));
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
223
common/ec_test_keys.cc
Normal file
223
common/ec_test_keys.cc
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Test EC keys generated using openssl.
|
||||||
|
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
static const unsigned char kTestECPrivateKey1Secp521r1[] = {
|
||||||
|
0x30, 0x81, 0xdc, 0x02, 0x01, 0x01, 0x04, 0x42, 0x01, 0x4a, 0xf8, 0x9d,
|
||||||
|
0xd5, 0x54, 0x6b, 0x11, 0xf7, 0xee, 0x52, 0xf3, 0xe7, 0xe1, 0xd0, 0x9a,
|
||||||
|
0x31, 0xab, 0x35, 0xfb, 0xcc, 0x4a, 0x01, 0x48, 0xc4, 0x9c, 0xf1, 0xb5,
|
||||||
|
0x20, 0x26, 0x7a, 0x08, 0x28, 0x07, 0xb2, 0xb5, 0xd1, 0x5b, 0xe5, 0x3c,
|
||||||
|
0xec, 0xee, 0x32, 0xdf, 0xe5, 0x04, 0x6c, 0x1a, 0xb5, 0x13, 0x5b, 0xda,
|
||||||
|
0xe1, 0xd6, 0xfa, 0x2e, 0x1b, 0x15, 0xde, 0xc2, 0x8c, 0x11, 0x75, 0xc6,
|
||||||
|
0x51, 0x9c, 0xa0, 0x07, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, 0xa1,
|
||||||
|
0x81, 0x89, 0x03, 0x81, 0x86, 0x00, 0x04, 0x00, 0x91, 0x11, 0x34, 0xfd,
|
||||||
|
0x37, 0x4d, 0x3f, 0xb9, 0x87, 0x87, 0xf4, 0x60, 0xaa, 0xfc, 0xe7, 0xe5,
|
||||||
|
0x8e, 0x54, 0x9a, 0x92, 0xef, 0xd1, 0x87, 0xe6, 0x83, 0x03, 0x81, 0x70,
|
||||||
|
0x2c, 0xdf, 0x1e, 0x81, 0x14, 0x4e, 0x76, 0xbc, 0xb8, 0x61, 0x52, 0xd7,
|
||||||
|
0xb4, 0x1f, 0xd2, 0x78, 0x87, 0x40, 0x65, 0xc3, 0x37, 0x87, 0xab, 0x12,
|
||||||
|
0x49, 0xd6, 0x64, 0x59, 0x8b, 0x2c, 0x4f, 0x78, 0x41, 0x13, 0xa2, 0x25,
|
||||||
|
0x87, 0x01, 0x22, 0xe9, 0x09, 0x8c, 0x63, 0x61, 0x1e, 0xee, 0x44, 0x0f,
|
||||||
|
0xe7, 0x89, 0xc0, 0x0b, 0xf5, 0xc8, 0x68, 0xbb, 0xd0, 0x25, 0xda, 0x7f,
|
||||||
|
0x9e, 0x30, 0xf7, 0x02, 0x74, 0x93, 0xb1, 0xc7, 0x00, 0xb4, 0x10, 0x6c,
|
||||||
|
0xa6, 0x5c, 0x45, 0xe7, 0x6d, 0xdc, 0x94, 0xc0, 0x14, 0xe8, 0xcc, 0x5a,
|
||||||
|
0xfb, 0x55, 0xbd, 0x3e, 0x85, 0x54, 0x06, 0xae, 0x31, 0xf0, 0xda, 0x55,
|
||||||
|
0x91, 0x3e, 0xc3, 0x41, 0x60, 0x70, 0xd9,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPublicKey1Secp521r1[] = {
|
||||||
|
0x30, 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
|
||||||
|
0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86,
|
||||||
|
0x00, 0x04, 0x00, 0x91, 0x11, 0x34, 0xfd, 0x37, 0x4d, 0x3f, 0xb9, 0x87,
|
||||||
|
0x87, 0xf4, 0x60, 0xaa, 0xfc, 0xe7, 0xe5, 0x8e, 0x54, 0x9a, 0x92, 0xef,
|
||||||
|
0xd1, 0x87, 0xe6, 0x83, 0x03, 0x81, 0x70, 0x2c, 0xdf, 0x1e, 0x81, 0x14,
|
||||||
|
0x4e, 0x76, 0xbc, 0xb8, 0x61, 0x52, 0xd7, 0xb4, 0x1f, 0xd2, 0x78, 0x87,
|
||||||
|
0x40, 0x65, 0xc3, 0x37, 0x87, 0xab, 0x12, 0x49, 0xd6, 0x64, 0x59, 0x8b,
|
||||||
|
0x2c, 0x4f, 0x78, 0x41, 0x13, 0xa2, 0x25, 0x87, 0x01, 0x22, 0xe9, 0x09,
|
||||||
|
0x8c, 0x63, 0x61, 0x1e, 0xee, 0x44, 0x0f, 0xe7, 0x89, 0xc0, 0x0b, 0xf5,
|
||||||
|
0xc8, 0x68, 0xbb, 0xd0, 0x25, 0xda, 0x7f, 0x9e, 0x30, 0xf7, 0x02, 0x74,
|
||||||
|
0x93, 0xb1, 0xc7, 0x00, 0xb4, 0x10, 0x6c, 0xa6, 0x5c, 0x45, 0xe7, 0x6d,
|
||||||
|
0xdc, 0x94, 0xc0, 0x14, 0xe8, 0xcc, 0x5a, 0xfb, 0x55, 0xbd, 0x3e, 0x85,
|
||||||
|
0x54, 0x06, 0xae, 0x31, 0xf0, 0xda, 0x55, 0x91, 0x3e, 0xc3, 0x41, 0x60,
|
||||||
|
0x70, 0xd9,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPrivateKey1Secp384r1[] = {
|
||||||
|
0x30, 0x81, 0xa4, 0x02, 0x01, 0x01, 0x04, 0x30, 0xc1, 0xee, 0x91, 0x6a,
|
||||||
|
0xb3, 0x23, 0x90, 0xdf, 0x0a, 0x7a, 0x82, 0x86, 0x60, 0x72, 0x4f, 0xc0,
|
||||||
|
0x02, 0x64, 0x0c, 0x3f, 0xc8, 0xdf, 0x8c, 0x06, 0x7a, 0x89, 0x39, 0xa0,
|
||||||
|
0x9e, 0xb4, 0xf6, 0x7f, 0x3a, 0xcd, 0xf4, 0x69, 0xfb, 0xdb, 0x59, 0xdf,
|
||||||
|
0x29, 0x40, 0x90, 0x34, 0x16, 0x42, 0x3a, 0x06, 0xa0, 0x07, 0x06, 0x05,
|
||||||
|
0x2b, 0x81, 0x04, 0x00, 0x22, 0xa1, 0x64, 0x03, 0x62, 0x00, 0x04, 0xb0,
|
||||||
|
0x50, 0x2a, 0x13, 0x20, 0x3e, 0x66, 0x67, 0xdf, 0x11, 0x2a, 0xbc, 0x0f,
|
||||||
|
0x76, 0x69, 0x4b, 0xa1, 0x88, 0xec, 0xb8, 0x71, 0xcf, 0xc9, 0xbb, 0xd2,
|
||||||
|
0xbc, 0xf8, 0x53, 0xfd, 0x8b, 0x8d, 0x14, 0x6f, 0xda, 0xea, 0x60, 0x51,
|
||||||
|
0xc8, 0xd3, 0x3a, 0xd4, 0x75, 0x81, 0x05, 0x16, 0x03, 0x0b, 0xcb, 0x33,
|
||||||
|
0x2c, 0x8b, 0xe6, 0xd3, 0x57, 0x6c, 0xfb, 0x81, 0x4b, 0xfe, 0x79, 0x56,
|
||||||
|
0xf7, 0x6a, 0x2b, 0xca, 0xa7, 0x04, 0xe9, 0x37, 0xd6, 0x49, 0xe5, 0x8b,
|
||||||
|
0x2c, 0xe9, 0x8e, 0xcd, 0xe7, 0xe3, 0xc9, 0xf5, 0x4c, 0x90, 0x82, 0x5f,
|
||||||
|
0xf0, 0x53, 0xd2, 0xa4, 0x1a, 0xb3, 0x53, 0x3d, 0xa7, 0xa7, 0xfd,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPublicKey1Secp384r1[] = {
|
||||||
|
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
|
||||||
|
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
|
||||||
|
0xb0, 0x50, 0x2a, 0x13, 0x20, 0x3e, 0x66, 0x67, 0xdf, 0x11, 0x2a, 0xbc,
|
||||||
|
0x0f, 0x76, 0x69, 0x4b, 0xa1, 0x88, 0xec, 0xb8, 0x71, 0xcf, 0xc9, 0xbb,
|
||||||
|
0xd2, 0xbc, 0xf8, 0x53, 0xfd, 0x8b, 0x8d, 0x14, 0x6f, 0xda, 0xea, 0x60,
|
||||||
|
0x51, 0xc8, 0xd3, 0x3a, 0xd4, 0x75, 0x81, 0x05, 0x16, 0x03, 0x0b, 0xcb,
|
||||||
|
0x33, 0x2c, 0x8b, 0xe6, 0xd3, 0x57, 0x6c, 0xfb, 0x81, 0x4b, 0xfe, 0x79,
|
||||||
|
0x56, 0xf7, 0x6a, 0x2b, 0xca, 0xa7, 0x04, 0xe9, 0x37, 0xd6, 0x49, 0xe5,
|
||||||
|
0x8b, 0x2c, 0xe9, 0x8e, 0xcd, 0xe7, 0xe3, 0xc9, 0xf5, 0x4c, 0x90, 0x82,
|
||||||
|
0x5f, 0xf0, 0x53, 0xd2, 0xa4, 0x1a, 0xb3, 0x53, 0x3d, 0xa7, 0xa7, 0xfd,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPrivateKey1Secp256r1[] = {
|
||||||
|
0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x7b, 0xa9, 0xe0, 0xe6,
|
||||||
|
0xa5, 0x22, 0xfd, 0x5b, 0x7a, 0x73, 0xe7, 0x0e, 0x9d, 0x13, 0x97,
|
||||||
|
0x79, 0x35, 0x95, 0x56, 0x67, 0xd0, 0x35, 0x8e, 0xc6, 0xa3, 0xb5,
|
||||||
|
0xaa, 0x97, 0x64, 0xdb, 0xdb, 0xa9, 0xa0, 0x0a, 0x06, 0x08, 0x2a,
|
||||||
|
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42,
|
||||||
|
0x00, 0x04, 0x8a, 0xea, 0x3f, 0x16, 0xff, 0x24, 0xa9, 0xbf, 0x03,
|
||||||
|
0x28, 0x30, 0x15, 0xee, 0x52, 0x50, 0x9a, 0x55, 0x1c, 0x60, 0xc7,
|
||||||
|
0xa7, 0xcc, 0x4b, 0x99, 0x5b, 0x40, 0x55, 0xce, 0x46, 0x19, 0xd4,
|
||||||
|
0xd4, 0x5e, 0xfd, 0xe0, 0x68, 0x27, 0xea, 0x78, 0xf3, 0x07, 0x1f,
|
||||||
|
0x02, 0x4a, 0x78, 0x52, 0x44, 0xd3, 0xdf, 0xbe, 0xac, 0x5f, 0xa5,
|
||||||
|
0x1c, 0x8a, 0x49, 0x8d, 0xa6, 0x5a, 0xac, 0xa1, 0x25, 0x2b, 0xd1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPublicKey1Secp256r1[] = {
|
||||||
|
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
|
||||||
|
0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
|
||||||
|
0x42, 0x00, 0x04, 0x8a, 0xea, 0x3f, 0x16, 0xff, 0x24, 0xa9, 0xbf, 0x03,
|
||||||
|
0x28, 0x30, 0x15, 0xee, 0x52, 0x50, 0x9a, 0x55, 0x1c, 0x60, 0xc7, 0xa7,
|
||||||
|
0xcc, 0x4b, 0x99, 0x5b, 0x40, 0x55, 0xce, 0x46, 0x19, 0xd4, 0xd4, 0x5e,
|
||||||
|
0xfd, 0xe0, 0x68, 0x27, 0xea, 0x78, 0xf3, 0x07, 0x1f, 0x02, 0x4a, 0x78,
|
||||||
|
0x52, 0x44, 0xd3, 0xdf, 0xbe, 0xac, 0x5f, 0xa5, 0x1c, 0x8a, 0x49, 0x8d,
|
||||||
|
0xa6, 0x5a, 0xac, 0xa1, 0x25, 0x2b, 0xd1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPrivateKey2Secp521r1[] = {
|
||||||
|
0x30, 0x81, 0xdc, 0x02, 0x01, 0x01, 0x04, 0x42, 0x00, 0xbc, 0xe2, 0x3b,
|
||||||
|
0xae, 0xba, 0xc3, 0xa4, 0x43, 0x68, 0x2b, 0xee, 0x83, 0x83, 0x49, 0x52,
|
||||||
|
0x53, 0x22, 0xd8, 0xd3, 0xe2, 0xc6, 0x3c, 0xe5, 0xa1, 0xbb, 0x90, 0x03,
|
||||||
|
0x37, 0xc8, 0xea, 0x26, 0x98, 0x20, 0xe5, 0x04, 0xaf, 0x55, 0x0a, 0x0f,
|
||||||
|
0x40, 0x20, 0x71, 0x3e, 0xdd, 0x62, 0x88, 0xce, 0x74, 0x64, 0x2e, 0xbe,
|
||||||
|
0x66, 0x8e, 0x5a, 0xf5, 0x77, 0x23, 0x13, 0x43, 0xc2, 0x6e, 0xaf, 0xdf,
|
||||||
|
0x75, 0x9e, 0xa0, 0x07, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, 0xa1,
|
||||||
|
0x81, 0x89, 0x03, 0x81, 0x86, 0x00, 0x04, 0x00, 0xf5, 0x2b, 0x67, 0x95,
|
||||||
|
0x4c, 0xcb, 0xfc, 0x76, 0xe5, 0x4d, 0x13, 0x81, 0xbc, 0xcf, 0xd8, 0x9f,
|
||||||
|
0x2e, 0x8b, 0xfd, 0x74, 0x93, 0x56, 0xa3, 0x51, 0x56, 0xa6, 0x17, 0x32,
|
||||||
|
0xfc, 0x69, 0x15, 0xf3, 0x60, 0xca, 0xb4, 0x23, 0x66, 0x2c, 0x70, 0x08,
|
||||||
|
0x1b, 0x92, 0x9a, 0x0e, 0x2e, 0xeb, 0x7c, 0xb6, 0x08, 0xe1, 0xf1, 0xb0,
|
||||||
|
0x68, 0x44, 0x8b, 0x7c, 0x33, 0xd3, 0xd9, 0xbc, 0x0e, 0x90, 0x7b, 0x86,
|
||||||
|
0x57, 0x00, 0x08, 0x3e, 0xd4, 0x0d, 0x44, 0x3e, 0x31, 0x16, 0x72, 0xf9,
|
||||||
|
0x9f, 0xcb, 0x20, 0xa8, 0x3f, 0x00, 0xa3, 0xf7, 0x3c, 0xae, 0x90, 0x69,
|
||||||
|
0xe1, 0x01, 0xd7, 0x59, 0x42, 0xb6, 0xcf, 0xc8, 0x5f, 0x19, 0x64, 0x17,
|
||||||
|
0x1e, 0x42, 0x04, 0x92, 0xc2, 0x60, 0x9a, 0x06, 0x38, 0x5b, 0xf5, 0x4e,
|
||||||
|
0xab, 0xd6, 0x96, 0x46, 0xdb, 0x55, 0x64, 0xc8, 0xf2, 0x75, 0xc7, 0x36,
|
||||||
|
0xbe, 0x89, 0x8e, 0xb5, 0xcd, 0x2b, 0xb4};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPublicKey2Secp521r1[] = {
|
||||||
|
0x30, 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
|
||||||
|
0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86,
|
||||||
|
0x00, 0x04, 0x00, 0xf5, 0x2b, 0x67, 0x95, 0x4c, 0xcb, 0xfc, 0x76, 0xe5,
|
||||||
|
0x4d, 0x13, 0x81, 0xbc, 0xcf, 0xd8, 0x9f, 0x2e, 0x8b, 0xfd, 0x74, 0x93,
|
||||||
|
0x56, 0xa3, 0x51, 0x56, 0xa6, 0x17, 0x32, 0xfc, 0x69, 0x15, 0xf3, 0x60,
|
||||||
|
0xca, 0xb4, 0x23, 0x66, 0x2c, 0x70, 0x08, 0x1b, 0x92, 0x9a, 0x0e, 0x2e,
|
||||||
|
0xeb, 0x7c, 0xb6, 0x08, 0xe1, 0xf1, 0xb0, 0x68, 0x44, 0x8b, 0x7c, 0x33,
|
||||||
|
0xd3, 0xd9, 0xbc, 0x0e, 0x90, 0x7b, 0x86, 0x57, 0x00, 0x08, 0x3e, 0xd4,
|
||||||
|
0x0d, 0x44, 0x3e, 0x31, 0x16, 0x72, 0xf9, 0x9f, 0xcb, 0x20, 0xa8, 0x3f,
|
||||||
|
0x00, 0xa3, 0xf7, 0x3c, 0xae, 0x90, 0x69, 0xe1, 0x01, 0xd7, 0x59, 0x42,
|
||||||
|
0xb6, 0xcf, 0xc8, 0x5f, 0x19, 0x64, 0x17, 0x1e, 0x42, 0x04, 0x92, 0xc2,
|
||||||
|
0x60, 0x9a, 0x06, 0x38, 0x5b, 0xf5, 0x4e, 0xab, 0xd6, 0x96, 0x46, 0xdb,
|
||||||
|
0x55, 0x64, 0xc8, 0xf2, 0x75, 0xc7, 0x36, 0xbe, 0x89, 0x8e, 0xb5, 0xcd,
|
||||||
|
0x2b, 0xb4};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPrivateKey2Secp384r1[] = {
|
||||||
|
0x30, 0x81, 0xa4, 0x02, 0x01, 0x01, 0x04, 0x30, 0xb7, 0x43, 0x85, 0x40,
|
||||||
|
0x4c, 0x0b, 0x1e, 0x75, 0x11, 0xf9, 0x74, 0x3a, 0x77, 0xc5, 0x35, 0x76,
|
||||||
|
0x31, 0xc9, 0x33, 0x56, 0x88, 0x23, 0x7c, 0x3b, 0x4c, 0xbc, 0x6a, 0x9e,
|
||||||
|
0x8e, 0xc7, 0x0f, 0x4f, 0x25, 0xda, 0xeb, 0x60, 0xa2, 0x6e, 0x63, 0xbe,
|
||||||
|
0x1f, 0x11, 0x77, 0x6d, 0xf3, 0x69, 0x42, 0x62, 0xa0, 0x07, 0x06, 0x05,
|
||||||
|
0x2b, 0x81, 0x04, 0x00, 0x22, 0xa1, 0x64, 0x03, 0x62, 0x00, 0x04, 0x04,
|
||||||
|
0xa7, 0x50, 0xdc, 0xff, 0x2c, 0x47, 0x28, 0x83, 0x67, 0x92, 0x67, 0xbb,
|
||||||
|
0x4f, 0x69, 0xaf, 0xda, 0x7c, 0xd9, 0x79, 0xd8, 0x59, 0x8c, 0xd1, 0x4b,
|
||||||
|
0x84, 0xf0, 0x7e, 0x04, 0xd3, 0x1b, 0xf1, 0x40, 0x83, 0x19, 0xe2, 0x22,
|
||||||
|
0xcb, 0x8a, 0xee, 0x7f, 0x28, 0x5b, 0xea, 0xed, 0x24, 0x45, 0xae, 0xbc,
|
||||||
|
0x2e, 0xa4, 0x3f, 0xdf, 0x60, 0xf2, 0x7c, 0x8b, 0xa8, 0xb7, 0x03, 0xc7,
|
||||||
|
0x18, 0x68, 0x07, 0x0f, 0xfc, 0x8b, 0x0b, 0xec, 0x1e, 0xc6, 0xc5, 0x21,
|
||||||
|
0xfb, 0xab, 0x3a, 0x5a, 0x83, 0x59, 0x62, 0x0b, 0xda, 0xcd, 0xd5, 0x35,
|
||||||
|
0x7b, 0xf1, 0xd5, 0x51, 0xa4, 0x37, 0x97, 0x90, 0x1b, 0xe4, 0xf2};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPublicKey2Secp384r1[] = {
|
||||||
|
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
|
||||||
|
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
|
||||||
|
0x04, 0xa7, 0x50, 0xdc, 0xff, 0x2c, 0x47, 0x28, 0x83, 0x67, 0x92, 0x67,
|
||||||
|
0xbb, 0x4f, 0x69, 0xaf, 0xda, 0x7c, 0xd9, 0x79, 0xd8, 0x59, 0x8c, 0xd1,
|
||||||
|
0x4b, 0x84, 0xf0, 0x7e, 0x04, 0xd3, 0x1b, 0xf1, 0x40, 0x83, 0x19, 0xe2,
|
||||||
|
0x22, 0xcb, 0x8a, 0xee, 0x7f, 0x28, 0x5b, 0xea, 0xed, 0x24, 0x45, 0xae,
|
||||||
|
0xbc, 0x2e, 0xa4, 0x3f, 0xdf, 0x60, 0xf2, 0x7c, 0x8b, 0xa8, 0xb7, 0x03,
|
||||||
|
0xc7, 0x18, 0x68, 0x07, 0x0f, 0xfc, 0x8b, 0x0b, 0xec, 0x1e, 0xc6, 0xc5,
|
||||||
|
0x21, 0xfb, 0xab, 0x3a, 0x5a, 0x83, 0x59, 0x62, 0x0b, 0xda, 0xcd, 0xd5,
|
||||||
|
0x35, 0x7b, 0xf1, 0xd5, 0x51, 0xa4, 0x37, 0x97, 0x90, 0x1b, 0xe4, 0xf2};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPrivateKey2Secp256r1[] = {
|
||||||
|
0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x34, 0x9a, 0xf2, 0x95,
|
||||||
|
0x94, 0xd4, 0xca, 0xb9, 0xa0, 0x81, 0xe4, 0x1c, 0xf5, 0xde, 0x8d,
|
||||||
|
0x23, 0xf6, 0x79, 0xba, 0x3c, 0x6e, 0xc9, 0x0b, 0x56, 0x0f, 0x07,
|
||||||
|
0x5e, 0x9f, 0xe9, 0x38, 0x18, 0xfc, 0xa0, 0x0a, 0x06, 0x08, 0x2a,
|
||||||
|
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42,
|
||||||
|
0x00, 0x04, 0x7b, 0x2a, 0x61, 0x59, 0xe5, 0x1b, 0xb6, 0x30, 0x7b,
|
||||||
|
0x59, 0x98, 0x42, 0x59, 0x37, 0xfb, 0x46, 0xfe, 0x53, 0xfe, 0x32,
|
||||||
|
0xa1, 0x5c, 0x93, 0x36, 0x11, 0xb0, 0x5a, 0xae, 0xa4, 0x48, 0xe3,
|
||||||
|
0x20, 0x12, 0xce, 0x78, 0xa7, 0x7f, 0xfd, 0x73, 0x5e, 0x09, 0x77,
|
||||||
|
0x53, 0x77, 0x8f, 0xd6, 0x1b, 0x26, 0xfa, 0xc4, 0x2c, 0xc4, 0x11,
|
||||||
|
0xfa, 0x72, 0x6a, 0xbe, 0x94, 0x78, 0x4d, 0x74, 0x20, 0x27, 0x86};
|
||||||
|
|
||||||
|
static const unsigned char kTestECPublicKey2Secp256r1[] = {
|
||||||
|
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
|
||||||
|
0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
|
||||||
|
0x42, 0x00, 0x04, 0x7b, 0x2a, 0x61, 0x59, 0xe5, 0x1b, 0xb6, 0x30, 0x7b,
|
||||||
|
0x59, 0x98, 0x42, 0x59, 0x37, 0xfb, 0x46, 0xfe, 0x53, 0xfe, 0x32, 0xa1,
|
||||||
|
0x5c, 0x93, 0x36, 0x11, 0xb0, 0x5a, 0xae, 0xa4, 0x48, 0xe3, 0x20, 0x12,
|
||||||
|
0xce, 0x78, 0xa7, 0x7f, 0xfd, 0x73, 0x5e, 0x09, 0x77, 0x53, 0x77, 0x8f,
|
||||||
|
0xd6, 0x1b, 0x26, 0xfa, 0xc4, 0x2c, 0xc4, 0x11, 0xfa, 0x72, 0x6a, 0xbe,
|
||||||
|
0x94, 0x78, 0x4d, 0x74, 0x20, 0x27, 0x86};
|
||||||
|
|
||||||
|
ECTestKeys::ECTestKeys()
|
||||||
|
: private_test_key_1_secp521r1_(std::begin(kTestECPrivateKey1Secp521r1),
|
||||||
|
std::end(kTestECPrivateKey1Secp521r1)),
|
||||||
|
public_test_key_1_secp521r1_(std::begin(kTestECPublicKey1Secp521r1),
|
||||||
|
std::end(kTestECPublicKey1Secp521r1)),
|
||||||
|
private_test_key_1_secp384r1_(std::begin(kTestECPrivateKey1Secp384r1),
|
||||||
|
std::end(kTestECPrivateKey1Secp384r1)),
|
||||||
|
public_test_key_1_secp384r1_(std::begin(kTestECPublicKey1Secp384r1),
|
||||||
|
std::end(kTestECPublicKey1Secp384r1)),
|
||||||
|
private_test_key_1_secp256r1_(std::begin(kTestECPrivateKey1Secp256r1),
|
||||||
|
std::end(kTestECPrivateKey1Secp256r1)),
|
||||||
|
public_test_key_1_secp256r1_(std::begin(kTestECPublicKey1Secp256r1),
|
||||||
|
std::end(kTestECPublicKey1Secp256r1)),
|
||||||
|
private_test_key_2_secp521r1_(std::begin(kTestECPrivateKey2Secp521r1),
|
||||||
|
std::end(kTestECPrivateKey2Secp521r1)),
|
||||||
|
public_test_key_2_secp521r1_(std::begin(kTestECPublicKey2Secp521r1),
|
||||||
|
std::end(kTestECPublicKey2Secp521r1)),
|
||||||
|
private_test_key_2_secp384r1_(std::begin(kTestECPrivateKey2Secp384r1),
|
||||||
|
std::end(kTestECPrivateKey2Secp384r1)),
|
||||||
|
public_test_key_2_secp384r1_(std::begin(kTestECPublicKey2Secp384r1),
|
||||||
|
std::end(kTestECPublicKey2Secp384r1)),
|
||||||
|
private_test_key_2_secp256r1_(std::begin(kTestECPrivateKey2Secp256r1),
|
||||||
|
std::end(kTestECPrivateKey2Secp256r1)),
|
||||||
|
public_test_key_2_secp256r1_(std::begin(kTestECPublicKey2Secp256r1),
|
||||||
|
std::end(kTestECPublicKey2Secp256r1)) {}
|
||||||
|
} // namespace widevine
|
||||||
91
common/ec_test_keys.h
Normal file
91
common/ec_test_keys.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Test EC keys generated for testing purposes.
|
||||||
|
|
||||||
|
#ifndef COMMON_EC_TEST_KEYS_H_
|
||||||
|
#define COMMON_EC_TEST_KEYS_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class ECTestKeys {
|
||||||
|
public:
|
||||||
|
ECTestKeys();
|
||||||
|
|
||||||
|
const std::string& private_test_key_1_secp521r1() const {
|
||||||
|
return private_test_key_1_secp521r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& public_test_key_1_secp521r1() const {
|
||||||
|
return public_test_key_1_secp521r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& private_test_key_2_secp521r1() const {
|
||||||
|
return private_test_key_2_secp521r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& public_test_key_2_secp521r1() const {
|
||||||
|
return public_test_key_2_secp521r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& private_test_key_1_secp384r1() const {
|
||||||
|
return private_test_key_1_secp384r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& public_test_key_1_secp384r1() const {
|
||||||
|
return public_test_key_1_secp384r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& private_test_key_2_secp384r1() const {
|
||||||
|
return private_test_key_2_secp384r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& public_test_key_2_secp384r1() const {
|
||||||
|
return public_test_key_2_secp384r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& private_test_key_1_secp256r1() const {
|
||||||
|
return private_test_key_1_secp256r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& public_test_key_1_secp256r1() const {
|
||||||
|
return public_test_key_1_secp256r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& private_test_key_2_secp256r1() const {
|
||||||
|
return private_test_key_2_secp256r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& public_test_key_2_secp256r1() const {
|
||||||
|
return public_test_key_2_secp256r1_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ECTestKeys(const ECTestKeys&) = delete;
|
||||||
|
ECTestKeys& operator=(const ECTestKeys&) = delete;
|
||||||
|
|
||||||
|
std::string private_test_key_1_secp521r1_;
|
||||||
|
std::string public_test_key_1_secp521r1_;
|
||||||
|
std::string private_test_key_1_secp384r1_;
|
||||||
|
std::string public_test_key_1_secp384r1_;
|
||||||
|
std::string private_test_key_1_secp256r1_;
|
||||||
|
std::string public_test_key_1_secp256r1_;
|
||||||
|
std::string private_test_key_2_secp521r1_;
|
||||||
|
std::string public_test_key_2_secp521r1_;
|
||||||
|
std::string private_test_key_2_secp384r1_;
|
||||||
|
std::string public_test_key_2_secp384r1_;
|
||||||
|
std::string private_test_key_2_secp256r1_;
|
||||||
|
std::string public_test_key_2_secp256r1_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_EC_TEST_KEYS_H_
|
||||||
198
common/ec_util.cc
Normal file
198
common/ec_util.cc
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Implementation of EC utility functions.
|
||||||
|
|
||||||
|
#include "common/ec_util.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "openssl/ec.h"
|
||||||
|
#include "openssl/ec_key.h"
|
||||||
|
#include "openssl/pem.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/openssl_util.h"
|
||||||
|
#include "common/private_key_util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace ec_util {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Expected X9.62 format. <tag><X><Y>.
|
||||||
|
// tag = 0x04 indicating uncompressed format.
|
||||||
|
// size of X and Y equal. But differ per curve.
|
||||||
|
// Secp256R1 32 bytes per value.
|
||||||
|
// Secp384R1 48 bytes per value.
|
||||||
|
// Secp521R1 66 bytes per value.
|
||||||
|
const size_t kSecp256R1ExpectedPubKeySize = 256 / 8 * 2 + 1; // 65.
|
||||||
|
const size_t kSecp384R1ExpectedPubKeySize = 384 / 8 * 2 + 1; // 97.
|
||||||
|
// 521 has to be rounded up to next block size, 528.
|
||||||
|
const size_t kSecp521R1ExpectedPubKeySize = (521 + 7) / 8 * 2 + 1; // 133.
|
||||||
|
|
||||||
|
std::string OpenSSLErrorString(uint32_t error) {
|
||||||
|
char buf[ERR_ERROR_STRING_BUF_LEN];
|
||||||
|
ERR_error_string_n(error, buf, sizeof(buf));
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool SerializeECPrivateKey(const EC_KEY* private_key,
|
||||||
|
std::string* serialized_private_key) {
|
||||||
|
return private_key_util::SerializeKey<EC_KEY>(
|
||||||
|
private_key, i2d_ECPrivateKey_bio, serialized_private_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeserializeECPrivateKey(const std::string& serialized_private_key,
|
||||||
|
EC_KEY** private_key) {
|
||||||
|
return private_key_util::DeserializeKey<EC_KEY>(
|
||||||
|
serialized_private_key, d2i_ECPrivateKey_bio, private_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SerializeECPublicKey(const EC_KEY* public_key,
|
||||||
|
std::string* serialized_public_key) {
|
||||||
|
return private_key_util::SerializeKey<EC_KEY>(public_key, i2d_EC_PUBKEY_bio,
|
||||||
|
serialized_public_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeserializeECPublicKey(const std::string& serialized_public_key,
|
||||||
|
EC_KEY** public_key) {
|
||||||
|
return private_key_util::DeserializeKey<EC_KEY>(
|
||||||
|
serialized_public_key, d2i_EC_PUBKEY_bio, public_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetPublicKeyPointSize(ECPrivateKey::EllipticCurve curve) {
|
||||||
|
switch (curve) {
|
||||||
|
case widevine::ECPrivateKey::SECP256R1:
|
||||||
|
return kSecp256R1ExpectedPubKeySize;
|
||||||
|
case widevine::ECPrivateKey::SECP384R1:
|
||||||
|
return kSecp384R1ExpectedPubKeySize;
|
||||||
|
case widevine::ECPrivateKey::SECP521R1:
|
||||||
|
return kSecp521R1ExpectedPubKeySize;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetPublicKeyPoint(const EC_KEY* ec_key, std::string* encoded_key) {
|
||||||
|
CHECK(ec_key);
|
||||||
|
CHECK(encoded_key);
|
||||||
|
const EC_POINT* public_key = EC_KEY_get0_public_key(ec_key);
|
||||||
|
if (public_key == nullptr) {
|
||||||
|
LOG(ERROR) << "Could not retrieve EC_POINT from EC_KEY.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len =
|
||||||
|
EC_POINT_point2oct(EC_KEY_get0_group(ec_key), public_key,
|
||||||
|
POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
|
||||||
|
if (len == 0) {
|
||||||
|
LOG(ERROR) << "Unexpected error getting the size of the public key.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
encoded_key->resize(len);
|
||||||
|
uint8_t* encoded_data = reinterpret_cast<uint8_t*>(&(*encoded_key)[0]);
|
||||||
|
len = EC_POINT_point2oct(EC_KEY_get0_group(ec_key), public_key,
|
||||||
|
POINT_CONVERSION_UNCOMPRESSED, encoded_data,
|
||||||
|
encoded_key->size(), nullptr);
|
||||||
|
if (len == 0) {
|
||||||
|
LOG(ERROR) << "Unexpected error encoding the public key.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetPublicKeyFromKeyPoint(ECPrivateKey::EllipticCurve curve,
|
||||||
|
const std::string& key_point,
|
||||||
|
EC_KEY** public_key) {
|
||||||
|
CHECK(public_key);
|
||||||
|
ScopedECGROUP group(EC_GROUP_new_by_curve_name(CurveToNid(curve)));
|
||||||
|
if (group == nullptr) {
|
||||||
|
LOG(ERROR) << "Could not load the EC Group for curve " << curve;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedECPOINT point(EC_POINT_new(group.get()));
|
||||||
|
if (EC_POINT_oct2point(group.get(), point.get(),
|
||||||
|
reinterpret_cast<const uint8_t*>(key_point.data()),
|
||||||
|
key_point.size(), nullptr) == 0) {
|
||||||
|
LOG(ERROR) << "Failed to convert the serialized point to EC_POINT.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedECKEY key(EC_KEY_new_by_curve_name(ec_util::CurveToNid(curve)));
|
||||||
|
if (EC_KEY_set_public_key(key.get(), point.get()) == 0) {
|
||||||
|
LOG(ERROR) << "Failed to convert the EC_POINT to EC_KEY";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*public_key = key.release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECPrivateKey::EllipticCurve NidToCurve(int nid) {
|
||||||
|
switch (nid) {
|
||||||
|
case NID_X9_62_prime256v1:
|
||||||
|
return ECPrivateKey::SECP256R1;
|
||||||
|
case NID_secp384r1:
|
||||||
|
return ECPrivateKey::SECP384R1;
|
||||||
|
case NID_secp521r1:
|
||||||
|
return ECPrivateKey::SECP521R1;
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Unaccepted nid: " << nid;
|
||||||
|
return ECPrivateKey::UNDEFINED_CURVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CurveToNid(ECPrivateKey::EllipticCurve curve) {
|
||||||
|
switch (curve) {
|
||||||
|
case ECPrivateKey::SECP256R1:
|
||||||
|
// This is the ANSI X9.62 name for secp256r1. openssl uses prime256v1
|
||||||
|
// instead of secp256r1.
|
||||||
|
// https://tools.ietf.org/search/rfc4492#appendix-A
|
||||||
|
return NID_X9_62_prime256v1;
|
||||||
|
case ECPrivateKey::SECP384R1:
|
||||||
|
return NID_secp384r1;
|
||||||
|
case ECPrivateKey::SECP521R1:
|
||||||
|
return NID_secp521r1;
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "EllipticCurve " << curve << " not accepted";
|
||||||
|
return NID_undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(user): Benchmark ecc key generation and evaluate whether additional
|
||||||
|
// changes are needed to improve the implementation. Currently an ephemeral key
|
||||||
|
// pair is generated per license request. We will also test for the impact of
|
||||||
|
// multiple threads generating keys.
|
||||||
|
ScopedECKEY GenerateKeyWithCurve(ECPrivateKey::EllipticCurve curve) {
|
||||||
|
if (curve == ECPrivateKey::UNDEFINED_CURVE) {
|
||||||
|
LOG(ERROR) << "Requested key uses unsupported curve";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ScopedECKEY new_ec_key(EC_KEY_new_by_curve_name(ec_util::CurveToNid(curve)));
|
||||||
|
if (EC_KEY_generate_key(new_ec_key.get()) == 0) {
|
||||||
|
LOG(ERROR) << "Unable to generate private key: "
|
||||||
|
<< OpenSSLErrorString(ERR_get_error());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (EC_KEY_check_key(new_ec_key.get()) == 0) {
|
||||||
|
LOG(ERROR) << "Invalid private EC key: "
|
||||||
|
<< OpenSSLErrorString(ERR_get_error());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return new_ec_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ec_util
|
||||||
|
} // namespace widevine
|
||||||
87
common/ec_util.h
Normal file
87
common/ec_util.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Utility functions to deserialize and serialize EC keys.
|
||||||
|
|
||||||
|
#ifndef COMMON_EC_UTIL_H_
|
||||||
|
#define COMMON_EC_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "openssl/ec.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace ec_util {
|
||||||
|
|
||||||
|
// Serialize EC private key into DER encoded RFC5915 std::string.
|
||||||
|
// Parameter |private_key| is the EC private key to be serialized.
|
||||||
|
// Parameter |serialized_private_key| will hold the serialized private key.
|
||||||
|
// Caller retains ownership of all pointers and they cannot be nullptr.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool SerializeECPrivateKey(const EC_KEY* private_key,
|
||||||
|
std::string* serialized_private_key);
|
||||||
|
|
||||||
|
// Deserialized DER encoded RFC5915 std::string into an EC private key.
|
||||||
|
// Parameter |serialized_private_key| contains the serialized private key.
|
||||||
|
// Parameter |private_key| will hold the deserialized EC private key.
|
||||||
|
// Caller retains ownership of all pointers and they cannot be nullptr.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool DeserializeECPrivateKey(const std::string& serialized_private_key,
|
||||||
|
EC_KEY** private_key);
|
||||||
|
|
||||||
|
// Serialize EC public key into DER encoded RFC5208 std::string.
|
||||||
|
// Parameter |public_key| is the EC public key to be serialized.
|
||||||
|
// Parameter |serialized_public_key| will hold the serialized public key.
|
||||||
|
// Caller retains ownership of all pointers and they cannot be nullptr.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool SerializeECPublicKey(const EC_KEY* public_key,
|
||||||
|
std::string* serialized_public_key);
|
||||||
|
|
||||||
|
// Deserialized DER encoded RFC5208 std::string into an EC private key.
|
||||||
|
// Parameter |serialized_public_key| contains the serialized public key.
|
||||||
|
// Parameter |public_key| will hold the deserialized EC public key.
|
||||||
|
// Caller retains ownership of all pointers and they cannot be nullptr.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool DeserializeECPublicKey(const std::string& serialized_public_key,
|
||||||
|
EC_KEY** public_key);
|
||||||
|
|
||||||
|
// Returns the expected size of the encoded public key keypoint for the given
|
||||||
|
// key |curve|.
|
||||||
|
size_t GetPublicKeyPointSize(ECPrivateKey::EllipticCurve curve);
|
||||||
|
|
||||||
|
// Returns the public key from the |ec_key| encoded per X9.62.
|
||||||
|
bool GetPublicKeyPoint(const EC_KEY* ec_key, std::string* encoded_key);
|
||||||
|
|
||||||
|
// Returns a |public_key| for the given curve, deserialized from the
|
||||||
|
// X9.62 |key_point|. |key_point| is required to be in uncompressed format and
|
||||||
|
// the correct size for the given curve.
|
||||||
|
bool GetPublicKeyFromKeyPoint(ECPrivateKey::EllipticCurve curve,
|
||||||
|
const std::string& key_point,
|
||||||
|
EC_KEY** public_key);
|
||||||
|
|
||||||
|
// Convert |nid| to its corresponding EllipticCurve value if supported.
|
||||||
|
// Parameter |nid| corresponds to one of the macros in nid.h.
|
||||||
|
// Returns a valid EllipticCurve if |nid| is supported and UNDEFINED_CURVE
|
||||||
|
// otherwise.
|
||||||
|
ECPrivateKey::EllipticCurve NidToCurve(int nid);
|
||||||
|
|
||||||
|
// Convert |curve| to its corresponding NID value.
|
||||||
|
// Parameter |curve| is an accepted curve value.
|
||||||
|
// Returns the NID that corresponds to the curve in nid.h if it exists and
|
||||||
|
// NID_undef otherwise.
|
||||||
|
int CurveToNid(ECPrivateKey::EllipticCurve curve);
|
||||||
|
|
||||||
|
// Creates a new ECKey using the provided |curve|.
|
||||||
|
ScopedECKEY GenerateKeyWithCurve(ECPrivateKey::EllipticCurve curve);
|
||||||
|
|
||||||
|
} // namespace ec_util
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_EC_UTIL_H_
|
||||||
202
common/ec_util_test.cc
Normal file
202
common/ec_util_test.cc
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Unit tests for ec_util.
|
||||||
|
|
||||||
|
#include "common/ec_util.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
|
#include "common/openssl_util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace ec_util {
|
||||||
|
|
||||||
|
class ECUtilTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
ECTestKeys test_keys_;
|
||||||
|
|
||||||
|
// Given serialization and deserialization methods for an EC_KEY and a test
|
||||||
|
// key, this checks that that those methods can deserialize to a valid EC_KEY
|
||||||
|
// and serialize to the same test key.
|
||||||
|
template <bool (*SerializeECKey)(const EC_KEY*, std::string*),
|
||||||
|
bool (*DeserializeECKey)(const std::string&, EC_KEY** private_key)>
|
||||||
|
void SerializeDeserializeECKeyCheck(const std::string& test_key) {
|
||||||
|
EC_KEY* key = nullptr;
|
||||||
|
std::string serialized_key;
|
||||||
|
EXPECT_TRUE(DeserializeECKey(test_key, &key));
|
||||||
|
ASSERT_TRUE(key != nullptr);
|
||||||
|
EXPECT_TRUE(SerializeECKey(key, &serialized_key));
|
||||||
|
EXPECT_EQ(serialized_key, test_key);
|
||||||
|
EXPECT_EQ(EC_KEY_check_key(key), 1);
|
||||||
|
EC_KEY_free(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given serialization and deserialization methods for an EC_KEY and a test
|
||||||
|
// key, this method checks that deserialization fails for a bad input std::string
|
||||||
|
// and both serialization and deserialization fail on nullptrs.
|
||||||
|
template <bool (*SerializeECKey)(const EC_KEY*, std::string*),
|
||||||
|
bool (*DeserializeECKey)(const std::string&, EC_KEY** private_key)>
|
||||||
|
void SerializeDeserializeECKeyValidateInput(const std::string& test_key) {
|
||||||
|
EC_KEY* key = nullptr;
|
||||||
|
// Invalid key
|
||||||
|
EXPECT_FALSE(DeserializeECKey("this is a bad key", &key));
|
||||||
|
EXPECT_EQ(key, nullptr);
|
||||||
|
// Invalid pointers
|
||||||
|
EXPECT_TRUE(DeserializeECKey(test_key, &key));
|
||||||
|
EXPECT_FALSE(SerializeECKey(key, nullptr));
|
||||||
|
EC_KEY_free(key);
|
||||||
|
EXPECT_FALSE(DeserializeECKey(test_key, nullptr));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ECUtilTest, SerializeDeserializePrivateKey) {
|
||||||
|
SerializeDeserializeECKeyCheck<SerializeECPrivateKey,
|
||||||
|
DeserializeECPrivateKey>(
|
||||||
|
test_keys_.private_test_key_1_secp521r1());
|
||||||
|
SerializeDeserializeECKeyCheck<SerializeECPrivateKey,
|
||||||
|
DeserializeECPrivateKey>(
|
||||||
|
test_keys_.private_test_key_1_secp384r1());
|
||||||
|
SerializeDeserializeECKeyCheck<SerializeECPrivateKey,
|
||||||
|
DeserializeECPrivateKey>(
|
||||||
|
test_keys_.private_test_key_1_secp256r1());
|
||||||
|
SerializeDeserializeECKeyValidateInput<SerializeECPrivateKey,
|
||||||
|
DeserializeECPrivateKey>(
|
||||||
|
test_keys_.private_test_key_1_secp521r1());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ECUtilTest, SerializeDeserializePublicKey) {
|
||||||
|
SerializeDeserializeECKeyCheck<SerializeECPublicKey, DeserializeECPublicKey>(
|
||||||
|
test_keys_.public_test_key_1_secp521r1());
|
||||||
|
SerializeDeserializeECKeyCheck<SerializeECPublicKey, DeserializeECPublicKey>(
|
||||||
|
test_keys_.public_test_key_1_secp384r1());
|
||||||
|
SerializeDeserializeECKeyCheck<SerializeECPublicKey, DeserializeECPublicKey>(
|
||||||
|
test_keys_.public_test_key_1_secp256r1());
|
||||||
|
SerializeDeserializeECKeyValidateInput<SerializeECPublicKey,
|
||||||
|
DeserializeECPublicKey>(
|
||||||
|
test_keys_.public_test_key_1_secp521r1());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ECUtilTest, PublicKeyExtraction) {
|
||||||
|
EC_KEY* private_key = nullptr;
|
||||||
|
std::string serialized_public_key;
|
||||||
|
// Key 1
|
||||||
|
EXPECT_TRUE(DeserializeECPrivateKey(test_keys_.private_test_key_1_secp521r1(),
|
||||||
|
&private_key));
|
||||||
|
ASSERT_TRUE(private_key != nullptr);
|
||||||
|
EXPECT_TRUE(SerializeECPublicKey(private_key, &serialized_public_key));
|
||||||
|
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_secp521r1());
|
||||||
|
EC_KEY_free(private_key);
|
||||||
|
private_key = nullptr;
|
||||||
|
// Key 2
|
||||||
|
EXPECT_TRUE(DeserializeECPrivateKey(test_keys_.private_test_key_1_secp384r1(),
|
||||||
|
&private_key));
|
||||||
|
ASSERT_TRUE(private_key != nullptr);
|
||||||
|
EXPECT_TRUE(SerializeECPublicKey(private_key, &serialized_public_key));
|
||||||
|
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_secp384r1());
|
||||||
|
EC_KEY_free(private_key);
|
||||||
|
private_key = nullptr;
|
||||||
|
// Key 3
|
||||||
|
EXPECT_TRUE(DeserializeECPrivateKey(test_keys_.private_test_key_1_secp256r1(),
|
||||||
|
&private_key));
|
||||||
|
ASSERT_TRUE(private_key != nullptr);
|
||||||
|
EXPECT_TRUE(SerializeECPublicKey(private_key, &serialized_public_key));
|
||||||
|
EXPECT_EQ(serialized_public_key, test_keys_.public_test_key_1_secp256r1());
|
||||||
|
EC_KEY_free(private_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ECUtilTest, GetPublicKeyPointSizeAll) {
|
||||||
|
ASSERT_GT(ec_util::GetPublicKeyPointSize(ECPrivateKey::SECP256R1), 0);
|
||||||
|
ASSERT_GT(ec_util::GetPublicKeyPointSize(ECPrivateKey::SECP384R1), 0);
|
||||||
|
ASSERT_GT(ec_util::GetPublicKeyPointSize(ECPrivateKey::SECP521R1), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(0, ec_util::GetPublicKeyPointSize(ECPrivateKey::UNDEFINED_CURVE));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ECUtilTest, GetPublicKeyFromKeyPointDeath) {
|
||||||
|
EXPECT_DEATH(GetPublicKeyFromKeyPoint(ECPrivateKey::SECP256R1, "", nullptr),
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
|
||||||
|
class ECKeyPointTest : public ::testing::Test,
|
||||||
|
public ::testing::WithParamInterface<std::string> {
|
||||||
|
public:
|
||||||
|
static std::vector<std::string> GetTestKeyList() {
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
return {test_keys.public_test_key_1_secp256r1(),
|
||||||
|
test_keys.public_test_key_1_secp384r1(),
|
||||||
|
test_keys.public_test_key_1_secp521r1()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
serialized_public_key_ = GetParam();
|
||||||
|
|
||||||
|
EC_KEY* public_key = nullptr;
|
||||||
|
ASSERT_TRUE(DeserializeECPublicKey(serialized_public_key_, &public_key));
|
||||||
|
ASSERT_TRUE(public_key != nullptr);
|
||||||
|
ec_public_key_.reset(public_key);
|
||||||
|
|
||||||
|
curve_ = NidToCurve(
|
||||||
|
EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_public_key_.get())));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string serialized_public_key_;
|
||||||
|
ScopedECKEY ec_public_key_;
|
||||||
|
ECPrivateKey::EllipticCurve curve_;
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(ECUtilKeyPointTest, ECKeyPointTest,
|
||||||
|
::testing::ValuesIn(ECKeyPointTest::GetTestKeyList()));
|
||||||
|
|
||||||
|
TEST_P(ECKeyPointTest, GetPublicKeyPointDeath) {
|
||||||
|
EXPECT_DEATH(GetPublicKeyPoint(ec_public_key_.get(), nullptr), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyPointTest, GetPublicKeyFromKeyPointInvalidCurve) {
|
||||||
|
std::string encoded_key;
|
||||||
|
ASSERT_TRUE(GetPublicKeyPoint(ec_public_key_.get(), &encoded_key));
|
||||||
|
EC_KEY* new_public_key = nullptr;
|
||||||
|
EXPECT_FALSE(GetPublicKeyFromKeyPoint(ECPrivateKey::UNDEFINED_CURVE,
|
||||||
|
encoded_key, &new_public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyPointTest, GetPublicKeyFromKeyPointInvalidSize) {
|
||||||
|
EC_KEY* new_public_key = nullptr;
|
||||||
|
EXPECT_FALSE(GetPublicKeyFromKeyPoint(curve_, "", &new_public_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyPointTest, PublicKeyPointFormatTest) {
|
||||||
|
std::string encoded_key;
|
||||||
|
ASSERT_TRUE(GetPublicKeyPoint(ec_public_key_.get(), &encoded_key));
|
||||||
|
ASSERT_FALSE(encoded_key.empty());
|
||||||
|
EXPECT_EQ(4, encoded_key[0]);
|
||||||
|
EXPECT_EQ(GetPublicKeyPointSize(curve_), encoded_key.size());
|
||||||
|
|
||||||
|
// Create a new EC key from the key point.
|
||||||
|
EC_KEY* ec_key = nullptr;
|
||||||
|
ASSERT_TRUE(GetPublicKeyFromKeyPoint(curve_, encoded_key, &ec_key));
|
||||||
|
ASSERT_TRUE(ec_key != nullptr);
|
||||||
|
ScopedECKEY new_public_key(ec_key);
|
||||||
|
|
||||||
|
// EC_POINT_cmp returns 0 if the keys match.
|
||||||
|
ASSERT_EQ(
|
||||||
|
0, EC_POINT_cmp(EC_KEY_get0_group(ec_public_key_.get()),
|
||||||
|
EC_KEY_get0_public_key(ec_public_key_.get()),
|
||||||
|
EC_KEY_get0_public_key(new_public_key.get()), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ec_util
|
||||||
|
} // namespace widevine
|
||||||
@@ -55,15 +55,18 @@ static bool EncryptOrDecrypt3DesCbc(absl::string_view key,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
bool Encrypt3DesEcb(absl::string_view key, absl::string_view src,
|
||||||
|
std::string* dst) {
|
||||||
return EncryptOrDecrypt3DesCbc(key, src, dst, DES_ENCRYPT);
|
return EncryptOrDecrypt3DesCbc(key, src, dst, DES_ENCRYPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src,
|
||||||
|
std::string* dst) {
|
||||||
return EncryptOrDecrypt3DesCbc(key, src, dst, DES_DECRYPT);
|
return EncryptOrDecrypt3DesCbc(key, src, dst, DES_DECRYPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
bool EncryptAesEcb(absl::string_view key, absl::string_view src,
|
||||||
|
std::string* dst) {
|
||||||
CHECK(dst);
|
CHECK(dst);
|
||||||
dst->clear();
|
dst->clear();
|
||||||
if (src.size() % 16 != 0) {
|
if (src.size() % 16 != 0) {
|
||||||
@@ -86,7 +89,8 @@ bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* ds
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
bool DecryptAesEcb(absl::string_view key, absl::string_view src,
|
||||||
|
std::string* dst) {
|
||||||
CHECK(dst);
|
CHECK(dst);
|
||||||
dst->clear();
|
dst->clear();
|
||||||
if (src.size() % 16 != 0) {
|
if (src.size() % 16 != 0) {
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ namespace crypto_util {
|
|||||||
// unsuccessful. Key should be 16 bytes. The first 8 are key2 of the 3DES key
|
// 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
|
// bundle, and the last 8 bytes are key1 and key3. |src| must be a multiple of
|
||||||
// 8 bytes.
|
// 8 bytes.
|
||||||
bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
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)
|
// 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
|
// key. This is used for protecting content keys on some older Widevine
|
||||||
@@ -35,7 +36,8 @@ bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* d
|
|||||||
// Key should be 16 bytes. The first 8 are key2 of the 3DES key bundle,
|
// 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
|
// and the last 8 bytes are key1 and key3. |src| must be a multiple of
|
||||||
// 8 bytes.
|
// 8 bytes.
|
||||||
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src,
|
||||||
|
std::string* dst);
|
||||||
|
|
||||||
// Encrypts |src| into |dst| using AES ECB mode with the given
|
// Encrypts |src| into |dst| using AES ECB mode with the given
|
||||||
// key. This is used for protecting content keys on Widevine devices,
|
// key. This is used for protecting content keys on Widevine devices,
|
||||||
@@ -43,7 +45,8 @@ bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* d
|
|||||||
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
||||||
// fail if invalid key or data sizes are passed in.
|
// fail if invalid key or data sizes are passed in.
|
||||||
// Key must be 16 bytes, and src must be a multiple of 16 bytes.
|
// 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);
|
bool EncryptAesEcb(absl::string_view key, absl::string_view src,
|
||||||
|
std::string* dst);
|
||||||
|
|
||||||
// Decrypts |src| into |dst| using AES ECB mode with the given
|
// Decrypts |src| into |dst| using AES ECB mode with the given
|
||||||
// key. This is used for protecting content keys on Widevine devices,
|
// key. This is used for protecting content keys on Widevine devices,
|
||||||
@@ -51,7 +54,8 @@ bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* ds
|
|||||||
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
||||||
// fail if invalid key or data sizes are passed in.
|
// fail if invalid key or data sizes are passed in.
|
||||||
// Key must be 16 bytes, and src must be a multiple of 16 bytes.
|
// 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);
|
bool DecryptAesEcb(absl::string_view key, absl::string_view src,
|
||||||
|
std::string* dst);
|
||||||
|
|
||||||
} // namespace crypto_util
|
} // namespace crypto_util
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
239
common/ecies_crypto.cc
Normal file
239
common/ecies_crypto.cc
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/ecies_crypto.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
|
#include "openssl/ec.h"
|
||||||
|
#include "common/aes_cbc_util.h"
|
||||||
|
#include "common/crypto_util.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_util.h"
|
||||||
|
#include "common/openssl_util.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const char kZeroIv[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
const size_t kMacSizeBytes = 32;
|
||||||
|
const size_t kMasterKeySizeBytes = 32;
|
||||||
|
const size_t kEncryptionKeySizeBytes = kMasterKeySizeBytes;
|
||||||
|
const size_t kSigningKeySizeBytes = kMasterKeySizeBytes;
|
||||||
|
const size_t kEncryptionKeySizeBits = kEncryptionKeySizeBytes * 8;
|
||||||
|
const size_t kSigningKeySizeBits = kSigningKeySizeBytes * 8;
|
||||||
|
|
||||||
|
const char kWrappingKeyLabel[] = "ECIESEncryptionLabel";
|
||||||
|
const char kSigningKeyLabel[] = "ECIESSigningLabel";
|
||||||
|
|
||||||
|
bool DeriveWrappingAndSigningKeys(const std::string& key,
|
||||||
|
const std::string& context,
|
||||||
|
std::string* wrapping_key,
|
||||||
|
std::string* signing_key) {
|
||||||
|
if (wrapping_key == nullptr || signing_key == nullptr) return false;
|
||||||
|
if (key.size() != kMasterKeySizeBytes) {
|
||||||
|
LOG(ERROR) << "Invalid master key size";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*wrapping_key = widevine::crypto_util::DeriveKey(
|
||||||
|
key, kWrappingKeyLabel, context, kEncryptionKeySizeBits);
|
||||||
|
if (wrapping_key->empty()) {
|
||||||
|
LOG(WARNING) << "Failed to derive encryption key.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*signing_key = widevine::crypto_util::DeriveKey(
|
||||||
|
key, kSigningKeyLabel, context, kSigningKeySizeBits);
|
||||||
|
if (signing_key->empty()) {
|
||||||
|
LOG(WARNING) << "Failed to derive sigining key.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
EciesEncryptor::EciesEncryptor(std::unique_ptr<ECPublicKey> public_key,
|
||||||
|
ECKeySource* key_source)
|
||||||
|
: public_key_(std::move(public_key)), key_source_(key_source) {}
|
||||||
|
|
||||||
|
std::unique_ptr<EciesEncryptor> EciesEncryptor::Create(
|
||||||
|
const std::string& serialized_public_key, ECKeySource* key_source) {
|
||||||
|
if (key_source == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
std::unique_ptr<ECPublicKey> ec_key =
|
||||||
|
ECPublicKey::Create(serialized_public_key);
|
||||||
|
if (ec_key == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
std::unique_ptr<EciesEncryptor> encryptor(
|
||||||
|
new EciesEncryptor(std::move(ec_key), key_source));
|
||||||
|
return encryptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EciesEncryptor::Encrypt(const std::string& plaintext,
|
||||||
|
const std::string& context,
|
||||||
|
std::string* ecies_message) const {
|
||||||
|
if (ecies_message == nullptr) {
|
||||||
|
LOG(ERROR) << "ecies_message cannot be null";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string serialized_private_key;
|
||||||
|
std::string serialized_public_key;
|
||||||
|
if (!key_source_->GetECKey(public_key_->Curve(), &serialized_private_key,
|
||||||
|
&serialized_public_key)) {
|
||||||
|
LOG(WARNING) << "Failed to get key pair from key source.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the ephemeral public key to an encoded EC point.
|
||||||
|
std::unique_ptr<ECPublicKey> ephemeral_public_key =
|
||||||
|
ECPublicKey::Create(serialized_public_key);
|
||||||
|
std::string encoded_public_key;
|
||||||
|
if (!ephemeral_public_key->GetPointEncodedKey(&encoded_public_key)) {
|
||||||
|
LOG(ERROR) << "Could not encode the public key. ";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This condition is just an indication that the serialized_public_key doesn't
|
||||||
|
// match our size expectations. It shouldn't block the encryption operation.
|
||||||
|
size_t expected_size = ec_util::GetPublicKeyPointSize(public_key_->Curve());
|
||||||
|
if (encoded_public_key.size() != expected_size) {
|
||||||
|
LOG(WARNING) << "Unexpected key size for public key. "
|
||||||
|
<< "Curve " << public_key_->Curve() << ". Expected "
|
||||||
|
<< expected_size << ". Found " << encoded_public_key.size()
|
||||||
|
<< ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ECPrivateKey> ephemeral_private_key =
|
||||||
|
ECPrivateKey::Create(serialized_private_key);
|
||||||
|
|
||||||
|
std::string session_key;
|
||||||
|
if (!ephemeral_private_key->DeriveSharedSessionKey(*public_key_,
|
||||||
|
&session_key)) {
|
||||||
|
LOG(WARNING) << "Failed to derive shared session key.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string encryption_key;
|
||||||
|
std::string signing_key;
|
||||||
|
if (!DeriveWrappingAndSigningKeys(session_key, context, &encryption_key,
|
||||||
|
&signing_key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string zero_iv(kZeroIv, sizeof(kZeroIv));
|
||||||
|
std::string ciphertext =
|
||||||
|
crypto_util::EncryptAesCbc(encryption_key, zero_iv, plaintext);
|
||||||
|
if (ciphertext.empty()) {
|
||||||
|
LOG(WARNING) << "Failed to encrypt plaintext.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string signature =
|
||||||
|
crypto_util::CreateSignatureHmacSha256(signing_key, ciphertext);
|
||||||
|
if (signature.empty()) {
|
||||||
|
LOG(WARNING) << "Failed to sign plaintext.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ecies_message = encoded_public_key + ciphertext + signature;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<EciesDecryptor> EciesDecryptor::Create(
|
||||||
|
const std::string& serialized_private_key) {
|
||||||
|
std::unique_ptr<ECPrivateKey> ec_key =
|
||||||
|
ECPrivateKey::Create(serialized_private_key);
|
||||||
|
if (ec_key == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
std::unique_ptr<EciesDecryptor> decryptor(
|
||||||
|
new EciesDecryptor(std::move(ec_key)));
|
||||||
|
if (decryptor == nullptr) {
|
||||||
|
LOG(ERROR) << "Failed to create EciesDecryptor.";
|
||||||
|
}
|
||||||
|
return decryptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
EciesDecryptor::EciesDecryptor(std::unique_ptr<ECPrivateKey> private_key)
|
||||||
|
: private_key_(std::move(private_key)) {}
|
||||||
|
|
||||||
|
bool EciesDecryptor::Decrypt(const std::string& ecies_message,
|
||||||
|
const std::string& context,
|
||||||
|
std::string* plaintext) const {
|
||||||
|
if (plaintext == nullptr) {
|
||||||
|
LOG(ERROR) << "plaintext cannot be null";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the minimum std::string size.
|
||||||
|
size_t key_size = ec_util::GetPublicKeyPointSize(private_key_->Curve());
|
||||||
|
if (key_size + kMacSizeBytes > ecies_message.size()) {
|
||||||
|
LOG(ERROR) << "The size of the message is too small. Expected > "
|
||||||
|
<< key_size + kMacSizeBytes << ". found "
|
||||||
|
<< ecies_message.size();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ciphertext = ecies_message.substr(
|
||||||
|
key_size, ecies_message.size() - kMacSizeBytes - key_size);
|
||||||
|
std::string signature =
|
||||||
|
ecies_message.substr(ecies_message.size() - kMacSizeBytes, kMacSizeBytes);
|
||||||
|
|
||||||
|
std::unique_ptr<ECPublicKey> public_key = ECPublicKey::CreateFromKeyPoint(
|
||||||
|
private_key_->Curve(), ecies_message.substr(0, key_size));
|
||||||
|
if (public_key == nullptr) {
|
||||||
|
LOG(WARNING) << "Failed to deserialize public key.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string session_key;
|
||||||
|
if (!private_key_->DeriveSharedSessionKey(*public_key, &session_key)) {
|
||||||
|
LOG(WARNING) << "Failed to derive shared session key.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string encryption_key;
|
||||||
|
std::string signing_key;
|
||||||
|
if (!DeriveWrappingAndSigningKeys(session_key, context, &encryption_key,
|
||||||
|
&signing_key)) {
|
||||||
|
LOG(WARNING) << "Failed to derive shared wrapping and signing keys.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!crypto_util::VerifySignatureHmacSha256(signing_key, signature,
|
||||||
|
ciphertext)) {
|
||||||
|
LOG(WARNING) << "Failed to verify signature on ciphertext.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string zero_iv(kZeroIv, sizeof(kZeroIv));
|
||||||
|
// In theory, we should be able to decrypt a cipher block that was originally
|
||||||
|
// the empty string. But DecryptAesCbc uses an empty std::string to indicate an
|
||||||
|
// error. This means that we can't distinguish between an error and correctly
|
||||||
|
// decrypted empty string.
|
||||||
|
*plaintext = crypto_util::DecryptAesCbc(encryption_key, zero_iv, ciphertext);
|
||||||
|
if (plaintext->empty()) {
|
||||||
|
LOG(WARNING) << "Failed to decrypt plaintext.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine.
|
||||||
82
common/ecies_crypto.h
Normal file
82
common/ecies_crypto.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_ECIES_CRYPTO_H_
|
||||||
|
#define COMMON_ECIES_CRYPTO_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_key_source.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class EciesEncryptor {
|
||||||
|
public:
|
||||||
|
static std::unique_ptr<EciesEncryptor> Create(const std::string& public_key,
|
||||||
|
ECKeySource* key_source);
|
||||||
|
virtual ~EciesEncryptor() = default;
|
||||||
|
EciesEncryptor(const EciesEncryptor&) = delete;
|
||||||
|
EciesEncryptor& operator=(const EciesEncryptor&) = delete;
|
||||||
|
|
||||||
|
// Generates an encrypted EC-IES message using the public key, an ephemeral
|
||||||
|
// private key and context. This function uses AES 256 bit encryption with a
|
||||||
|
// master key derived from EC shared key generated from the public key and
|
||||||
|
// ephemeral private key.
|
||||||
|
// |plaintext| is the value to be encrypted.
|
||||||
|
// |context| is used as part of the key derivation.
|
||||||
|
// |ecies_message| is the concatenation of
|
||||||
|
// 1) the ephemeral public key.
|
||||||
|
// 2) the plaintext encrypted with the derived AES key using AES CBC,
|
||||||
|
// PKCS7 padding and a zerio iv.
|
||||||
|
// 3) The HMAC SHA256 of the cipher text.
|
||||||
|
// Returns false if there is a problem encrypting the content, true otherwise.
|
||||||
|
virtual bool Encrypt(const std::string& plaintext, const std::string& context,
|
||||||
|
std::string* ecies_message) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Creates the EciesEncryptor with a given ECKey. This is protected in order
|
||||||
|
// to support mock tests.
|
||||||
|
EciesEncryptor(std::unique_ptr<ECPublicKey> public_key,
|
||||||
|
ECKeySource* key_source);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<ECPublicKey> public_key_;
|
||||||
|
ECKeySource* key_source_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EciesDecryptor {
|
||||||
|
public:
|
||||||
|
static std::unique_ptr<EciesDecryptor> Create(
|
||||||
|
const std::string& serialized_private_key);
|
||||||
|
|
||||||
|
virtual ~EciesDecryptor() = default;
|
||||||
|
EciesDecryptor(const EciesDecryptor&) = delete;
|
||||||
|
EciesDecryptor& operator=(const EciesDecryptor&) = delete;
|
||||||
|
|
||||||
|
// Decrypts and verifies an EC-IES message using the private key, the
|
||||||
|
// ephemeral public key embedded in |ecies_message| and the |context|.
|
||||||
|
// This function uses a master AES key to decrypt the content and validate the
|
||||||
|
// signature. The content is encrypted with AES CBC, PKCS7 padded with a
|
||||||
|
// zero iv.
|
||||||
|
// |plaintext| will be populated iff decryption is successful and the
|
||||||
|
// signature is valid.
|
||||||
|
// Returns false if there is a problem decrypting the content, true otherwise.
|
||||||
|
virtual bool Decrypt(const std::string& ecies_message,
|
||||||
|
const std::string& context,
|
||||||
|
std::string* plaintext) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit EciesDecryptor(std::unique_ptr<ECPrivateKey> private_key);
|
||||||
|
std::unique_ptr<ECPrivateKey> private_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_ECIES_CRYPTO_H_
|
||||||
258
common/ecies_crypto_test.cc
Normal file
258
common/ecies_crypto_test.cc
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/ecies_crypto.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_key_source.h"
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
|
#include "common/ec_util.h"
|
||||||
|
#include "common/fake_ec_key_source.h"
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::Return;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const size_t kMacSizeBytes = 32;
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class EciesCryptoTest
|
||||||
|
: public ::testing::Test,
|
||||||
|
public ::testing::WithParamInterface<
|
||||||
|
std::tuple<std::string, std::string, std::string, std::string> > {
|
||||||
|
public:
|
||||||
|
static std::vector<
|
||||||
|
std::tuple<std::string, std::string, std::string, std::string> >
|
||||||
|
GetTestKeyList() {
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
|
||||||
|
std::vector<std::tuple<std::string, std::string, std::string, std::string> >
|
||||||
|
keys({std::make_tuple(test_keys.private_test_key_1_secp521r1(),
|
||||||
|
test_keys.public_test_key_1_secp521r1(),
|
||||||
|
test_keys.private_test_key_2_secp521r1(),
|
||||||
|
test_keys.public_test_key_2_secp521r1()),
|
||||||
|
std::make_tuple(test_keys.private_test_key_1_secp384r1(),
|
||||||
|
test_keys.public_test_key_1_secp384r1(),
|
||||||
|
test_keys.private_test_key_2_secp384r1(),
|
||||||
|
test_keys.public_test_key_2_secp384r1()),
|
||||||
|
std::make_tuple(test_keys.private_test_key_1_secp256r1(),
|
||||||
|
test_keys.public_test_key_1_secp256r1(),
|
||||||
|
test_keys.private_test_key_2_secp256r1(),
|
||||||
|
test_keys.public_test_key_2_secp256r1())});
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
EciesCryptoTest() {
|
||||||
|
test_private_key_ = std::get<0>(GetParam());
|
||||||
|
test_public_key_ = std::get<1>(GetParam());
|
||||||
|
test_ephemeral_private_key_ = std::get<2>(GetParam());
|
||||||
|
test_ephemeral_public_key_ = std::get<3>(GetParam());
|
||||||
|
private_key_ = ECPrivateKey::Create(test_private_key_);
|
||||||
|
public_key_ = ECPublicKey::Create(test_public_key_);
|
||||||
|
ephemeral_private_key_ = ECPrivateKey::Create(test_ephemeral_private_key_);
|
||||||
|
ephemeral_public_key_ = ECPublicKey::Create(test_ephemeral_public_key_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string test_private_key_;
|
||||||
|
std::string test_public_key_;
|
||||||
|
std::string test_ephemeral_private_key_;
|
||||||
|
std::string test_ephemeral_public_key_;
|
||||||
|
std::unique_ptr<ECPrivateKey> private_key_;
|
||||||
|
std::unique_ptr<ECPublicKey> public_key_;
|
||||||
|
std::unique_ptr<ECPrivateKey> ephemeral_private_key_;
|
||||||
|
std::unique_ptr<ECPublicKey> ephemeral_public_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(EciesCryptoTest, EciesEncryptSuccess) {
|
||||||
|
std::string serialized_public_key;
|
||||||
|
|
||||||
|
const std::string context = "test context";
|
||||||
|
const std::string plaintext = "test plaintext";
|
||||||
|
|
||||||
|
std::string ecies_message;
|
||||||
|
|
||||||
|
// Use the test ephemeral key in the key source.
|
||||||
|
FakeECKeySource fake_key_source;
|
||||||
|
ASSERT_TRUE(fake_key_source.SetKey(ephemeral_public_key_->Curve(),
|
||||||
|
test_ephemeral_private_key_,
|
||||||
|
test_ephemeral_public_key_));
|
||||||
|
std::unique_ptr<EciesEncryptor> encryptor =
|
||||||
|
EciesEncryptor::Create(test_public_key_, &fake_key_source);
|
||||||
|
|
||||||
|
ASSERT_TRUE(encryptor->Encrypt(plaintext, context, &ecies_message));
|
||||||
|
EXPECT_TRUE(ecies_message.find(plaintext) == std::string::npos);
|
||||||
|
|
||||||
|
// Verify the decrypted_message.
|
||||||
|
std::string decrypted_message;
|
||||||
|
std::unique_ptr<EciesDecryptor> decryptor =
|
||||||
|
EciesDecryptor::Create(test_private_key_);
|
||||||
|
ASSERT_TRUE(decryptor != nullptr);
|
||||||
|
ASSERT_TRUE(decryptor->Decrypt(ecies_message, context, &decrypted_message));
|
||||||
|
EXPECT_EQ(plaintext, decrypted_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(EciesCryptoTest, EciesDecryptShortEciesMessage) {
|
||||||
|
std::unique_ptr<EciesDecryptor> decryptor =
|
||||||
|
EciesDecryptor::Create(test_private_key_);
|
||||||
|
ASSERT_NE(nullptr, decryptor.get());
|
||||||
|
std::string decrypted_message;
|
||||||
|
std::string short_message(
|
||||||
|
ec_util::GetPublicKeyPointSize(private_key_->Curve()) + kMacSizeBytes - 1,
|
||||||
|
'a');
|
||||||
|
|
||||||
|
ASSERT_FALSE(
|
||||||
|
decryptor->Decrypt(short_message, "test context", &decrypted_message));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(EciesCryptoTest, EciesDecryptBadSignature) {
|
||||||
|
std::string serialized_public_key;
|
||||||
|
|
||||||
|
const std::string context = "test context";
|
||||||
|
const std::string plaintext = "test plaintext";
|
||||||
|
|
||||||
|
std::string ecies_message;
|
||||||
|
|
||||||
|
// Use the test ephemeral key in the key source.
|
||||||
|
FakeECKeySource fake_key_source;
|
||||||
|
ASSERT_TRUE(fake_key_source.SetKey(ephemeral_public_key_->Curve(),
|
||||||
|
test_ephemeral_private_key_,
|
||||||
|
test_ephemeral_public_key_));
|
||||||
|
std::unique_ptr<EciesEncryptor> encryptor =
|
||||||
|
EciesEncryptor::Create(test_public_key_, &fake_key_source);
|
||||||
|
|
||||||
|
ASSERT_TRUE(encryptor->Encrypt(plaintext, context, &ecies_message));
|
||||||
|
|
||||||
|
// Corrupt the signature (at end of message) and verify that decryption fails.
|
||||||
|
ecies_message[ecies_message.size() - 1]++;
|
||||||
|
std::string decrypted_message;
|
||||||
|
std::unique_ptr<EciesDecryptor> decryptor =
|
||||||
|
EciesDecryptor::Create(test_private_key_);
|
||||||
|
ASSERT_TRUE(decryptor != nullptr);
|
||||||
|
ASSERT_FALSE(decryptor->Decrypt(ecies_message, context, &decrypted_message));
|
||||||
|
EXPECT_EQ("", decrypted_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(EciesCryptoTest, EciesEncryptMismatchContext) {
|
||||||
|
std::string serialized_public_key;
|
||||||
|
|
||||||
|
const std::string context = "test context";
|
||||||
|
const std::string bogus_context = "bogus_context";
|
||||||
|
const std::string plaintext = "test plaintext";
|
||||||
|
|
||||||
|
std::string ecies_message;
|
||||||
|
|
||||||
|
// Use the test ephemeral key in the key source.
|
||||||
|
FakeECKeySource fake_key_source;
|
||||||
|
ASSERT_TRUE(fake_key_source.SetKey(ephemeral_public_key_->Curve(),
|
||||||
|
test_ephemeral_private_key_,
|
||||||
|
test_ephemeral_public_key_));
|
||||||
|
std::unique_ptr<EciesEncryptor> encryptor =
|
||||||
|
EciesEncryptor::Create(test_public_key_, &fake_key_source);
|
||||||
|
ASSERT_TRUE(encryptor != nullptr);
|
||||||
|
ASSERT_TRUE(encryptor->Encrypt(plaintext, context, &ecies_message));
|
||||||
|
EXPECT_GT(
|
||||||
|
ecies_message.size(),
|
||||||
|
kMacSizeBytes + ec_util::GetPublicKeyPointSize(public_key_->Curve()));
|
||||||
|
|
||||||
|
// Verify the decrypted_message using the invalid context.
|
||||||
|
std::string decrypted_message;
|
||||||
|
std::unique_ptr<EciesDecryptor> decryptor =
|
||||||
|
EciesDecryptor::Create(test_private_key_);
|
||||||
|
ASSERT_TRUE(decryptor != nullptr);
|
||||||
|
ASSERT_FALSE(
|
||||||
|
decryptor->Decrypt(ecies_message, bogus_context, &decrypted_message));
|
||||||
|
EXPECT_TRUE(decrypted_message.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
EciesCryptoTest, EciesCryptoTest,
|
||||||
|
::testing::ValuesIn(EciesCryptoTest::GetTestKeyList()));
|
||||||
|
|
||||||
|
TEST(EciesEncryptorTest, EciesEncryptBadPublicKey) {
|
||||||
|
// Use the test ephemeral key in the key source.
|
||||||
|
FakeECKeySource fake_key_source;
|
||||||
|
std::unique_ptr<EciesEncryptor> encryptor =
|
||||||
|
EciesEncryptor::Create("bad public key.", &fake_key_source);
|
||||||
|
ASSERT_TRUE(encryptor == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EciesEncryptorTest, EciesEncryptNullKeySource) {
|
||||||
|
std::unique_ptr<EciesEncryptor> encryptor =
|
||||||
|
EciesEncryptor::Create("bad public key.", nullptr);
|
||||||
|
ASSERT_TRUE(encryptor == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockEcKeySource : public ECKeySource {
|
||||||
|
public:
|
||||||
|
MockEcKeySource() = default;
|
||||||
|
MOCK_METHOD3(GetECKey,
|
||||||
|
bool(ECPrivateKey::EllipticCurve curve, std::string* private_key,
|
||||||
|
std::string* public_key));
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(EciesEncryptorTest, EciesEncryptKeysourceFail) {
|
||||||
|
MockEcKeySource mock_ec_key_source;
|
||||||
|
EXPECT_CALL(mock_ec_key_source, GetECKey(_, _, _)).WillOnce(Return(false));
|
||||||
|
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
std::unique_ptr<EciesEncryptor> encryptor = EciesEncryptor::Create(
|
||||||
|
test_keys.public_test_key_1_secp256r1(), &mock_ec_key_source);
|
||||||
|
ASSERT_TRUE(encryptor != nullptr);
|
||||||
|
std::string ecies_message;
|
||||||
|
ASSERT_FALSE(
|
||||||
|
encryptor->Encrypt("test plaintext", "test context", &ecies_message));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EciesEncryptorTest, EciesEncryptNullEciesMessage) {
|
||||||
|
FakeECKeySource fake_key_source;
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
std::unique_ptr<EciesEncryptor> encryptor = EciesEncryptor::Create(
|
||||||
|
test_keys.public_test_key_1_secp256r1(), &fake_key_source);
|
||||||
|
ASSERT_TRUE(encryptor != nullptr);
|
||||||
|
ASSERT_FALSE(encryptor->Encrypt("test plaintext", "test context", nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EciesDecryptorTest, EciesDecryptBadPrivateKey) {
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
std::string invalid_private_key(test_keys.private_test_key_1_secp521r1());
|
||||||
|
invalid_private_key[0]++;
|
||||||
|
|
||||||
|
std::unique_ptr<EciesDecryptor> decryptor =
|
||||||
|
EciesDecryptor::Create(invalid_private_key);
|
||||||
|
ASSERT_TRUE(decryptor == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EciesDecryptorTest, EciesDecryptEmptyEciesMessage) {
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
std::unique_ptr<EciesDecryptor> decryptor =
|
||||||
|
EciesDecryptor::Create(test_keys.private_test_key_1_secp521r1());
|
||||||
|
ASSERT_NE(nullptr, decryptor);
|
||||||
|
std::string decrypted_message;
|
||||||
|
ASSERT_FALSE(decryptor->Decrypt("", "test context", &decrypted_message));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EciesDecryptorTest, EciesDecryptNullPlaintext) {
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
std::unique_ptr<EciesDecryptor> decryptor =
|
||||||
|
EciesDecryptor::Create(test_keys.private_test_key_1_secp521r1());
|
||||||
|
ASSERT_NE(nullptr, decryptor);
|
||||||
|
std::string decrypted_message;
|
||||||
|
ASSERT_FALSE(decryptor->Decrypt("foo", "test context", nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
78
common/fake_ec_key_source.cc
Normal file
78
common/fake_ec_key_source.cc
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Implementation of fake EC key generator. Returns precomputed test keys.
|
||||||
|
|
||||||
|
#include "common/fake_ec_key_source.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
FakeECKeySource::FakeECKeySource() {
|
||||||
|
ECTestKeys ec_test_keys;
|
||||||
|
CHECK(SetKey(ECPrivateKey::SECP256R1,
|
||||||
|
ec_test_keys.private_test_key_1_secp256r1(),
|
||||||
|
ec_test_keys.public_test_key_1_secp256r1()));
|
||||||
|
|
||||||
|
CHECK(SetKey(ECPrivateKey::SECP384R1,
|
||||||
|
ec_test_keys.private_test_key_1_secp384r1(),
|
||||||
|
ec_test_keys.public_test_key_1_secp384r1()));
|
||||||
|
|
||||||
|
CHECK(SetKey(ECPrivateKey::SECP521R1,
|
||||||
|
ec_test_keys.private_test_key_1_secp521r1(),
|
||||||
|
ec_test_keys.public_test_key_1_secp521r1()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FakeECKeySource::SetKey(ECPrivateKey::EllipticCurve curve,
|
||||||
|
const std::string& private_key,
|
||||||
|
const std::string& public_key) {
|
||||||
|
std::unique_ptr<ECPrivateKey> pri_key = ECPrivateKey::Create(private_key);
|
||||||
|
std::unique_ptr<ECPublicKey> pub_key = ECPublicKey::Create(public_key);
|
||||||
|
if (pub_key == nullptr || pri_key == nullptr) {
|
||||||
|
LOG(ERROR) << "public key or private key was null.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!pri_key->MatchesPublicKey(*pub_key)) {
|
||||||
|
LOG(ERROR) << "Public and private keys do not match.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pri_key->Curve() != curve) {
|
||||||
|
LOG(ERROR) << "Curve does not match specified curve.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
test_keys_[curve] = std::make_pair(private_key, public_key);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FakeECKeySource::GetECKey(ECPrivateKey::EllipticCurve curve,
|
||||||
|
std::string* private_key,
|
||||||
|
std::string* public_key) {
|
||||||
|
CHECK(private_key);
|
||||||
|
CHECK(public_key);
|
||||||
|
|
||||||
|
const auto keys = test_keys_.find(curve);
|
||||||
|
if (keys == test_keys_.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tie(*private_key, *public_key) = keys->second;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
53
common/fake_ec_key_source.h
Normal file
53
common/fake_ec_key_source.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Declaration for fake EC key source used for testing.
|
||||||
|
|
||||||
|
#ifndef COMMON_FAKE_EC_KEY_SOURCE_H_
|
||||||
|
#define COMMON_FAKE_EC_KEY_SOURCE_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/ec_key_source.h"
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// This is a fake implementation of the ECKeySource. It returns the values
|
||||||
|
// specified in ec_test_keys.h.
|
||||||
|
class FakeECKeySource : public ECKeySource {
|
||||||
|
public:
|
||||||
|
FakeECKeySource();
|
||||||
|
FakeECKeySource(const FakeECKeySource&) = delete;
|
||||||
|
FakeECKeySource& operator=(const FakeECKeySource&) = delete;
|
||||||
|
|
||||||
|
// This method allows a caller to specify which keys to return from GetKeys.
|
||||||
|
// This overrides the default test keys.
|
||||||
|
bool SetKey(ECPrivateKey::EllipticCurve curve, const std::string& private_key,
|
||||||
|
const std::string& public_key);
|
||||||
|
|
||||||
|
// Gets precomputed test keys based on |curve|.
|
||||||
|
// Parameter |curve| is a standard curve which defines the parameters of the
|
||||||
|
// key generation.
|
||||||
|
// Parameter |private_key| is the serialized EC private key.
|
||||||
|
// Parameter |public_key| is the serialized EC public key.
|
||||||
|
// Caller retains ownership of all pointers and they cannot be nullptr.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool GetECKey(ECPrivateKey::EllipticCurve curve, std::string* private_key,
|
||||||
|
std::string* public_key) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<ECPrivateKey::EllipticCurve, std::pair<std::string, std::string>>
|
||||||
|
test_keys_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_FAKE_EC_KEY_SOURCE_H_
|
||||||
@@ -19,7 +19,8 @@ TEST(FileUtilTest, EmptyFileName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileUtilTest, BasicTest) {
|
TEST(FileUtilTest, BasicTest) {
|
||||||
const std::string file_path = absl::StrCat("/tmp", "/file_util_test");
|
const std::string file_path =
|
||||||
|
absl::StrCat("/tmp", "/file_util_test");
|
||||||
EXPECT_TRUE(SetContents(file_path, "test content"));
|
EXPECT_TRUE(SetContents(file_path, "test content"));
|
||||||
std::string contents;
|
std::string contents;
|
||||||
EXPECT_TRUE(GetContents(file_path, &contents));
|
EXPECT_TRUE(GetContents(file_path, &contents));
|
||||||
|
|||||||
56
common/keybox_client_cert.cc
Normal file
56
common/keybox_client_cert.cc
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/keybox_client_cert.h"
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "common/crypto_util.h"
|
||||||
|
#include "common/error_space.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
#include "common/signing_key_util.h"
|
||||||
|
#include "common/wvm_token_handler.h"
|
||||||
|
#include "protos/public/errors.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
Status KeyboxClientCert::Initialize(const std::string& keybox_token) {
|
||||||
|
system_id_ = WvmTokenHandler::GetSystemId(keybox_token);
|
||||||
|
serial_number_ = WvmTokenHandler::GetEncryptedUniqueId(keybox_token);
|
||||||
|
bool insecure_keybox = false;
|
||||||
|
Status status = WvmTokenHandler::DecryptDeviceKey(keybox_token, &device_key_,
|
||||||
|
nullptr, &insecure_keybox);
|
||||||
|
if (!status.ok()) {
|
||||||
|
Errors new_code = status.error_code() == error::NOT_FOUND
|
||||||
|
? MISSING_PRE_PROV_KEY
|
||||||
|
: KEYBOX_DECRYPT_ERROR;
|
||||||
|
return Status(error_space, new_code, status.error_message());
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status KeyboxClientCert::VerifySignature(
|
||||||
|
const std::string& message, const std::string& signature,
|
||||||
|
ProtocolVersion protocol_version) const {
|
||||||
|
DCHECK(!signing_key_.empty());
|
||||||
|
using crypto_util::VerifySignatureHmacSha256;
|
||||||
|
if (!VerifySignatureHmacSha256(
|
||||||
|
GetClientSigningKey(signing_key_, protocol_version), signature,
|
||||||
|
message)) {
|
||||||
|
return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboxClientCert::GenerateSigningKey(const std::string& message,
|
||||||
|
ProtocolVersion protocol_version) {
|
||||||
|
signing_key_ = crypto_util::DeriveKey(
|
||||||
|
key(), crypto_util::kSigningKeyLabel,
|
||||||
|
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||||
|
SigningKeyMaterialSizeBits(protocol_version));
|
||||||
|
}
|
||||||
|
} // namespace widevine
|
||||||
66
common/keybox_client_cert.h
Normal file
66
common/keybox_client_cert.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_KEYBOX_CLIENT_CERT_H_
|
||||||
|
#define COMMON_KEYBOX_CLIENT_CERT_H_
|
||||||
|
|
||||||
|
#include "common/client_cert.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
//
|
||||||
|
class KeyboxClientCert : public ClientCert {
|
||||||
|
public:
|
||||||
|
KeyboxClientCert() {}
|
||||||
|
~KeyboxClientCert() override {}
|
||||||
|
KeyboxClientCert(const KeyboxClientCert&) = delete;
|
||||||
|
KeyboxClientCert& operator=(const KeyboxClientCert&) = delete;
|
||||||
|
Status Initialize(const std::string& keybox_token);
|
||||||
|
|
||||||
|
Status VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature,
|
||||||
|
ProtocolVersion protocol_version) const override;
|
||||||
|
|
||||||
|
void GenerateSigningKey(const std::string& message,
|
||||||
|
ProtocolVersion protocol_version) override;
|
||||||
|
|
||||||
|
const std::string& encrypted_key() const override { return unimplemented_; }
|
||||||
|
const std::string& key() const override { return device_key_; }
|
||||||
|
const std::string& serial_number() const override { return serial_number_; }
|
||||||
|
const std::string& service_id() const override { return unimplemented_; }
|
||||||
|
const std::string& signing_key() const override { return signing_key_; }
|
||||||
|
const std::string& signer_serial_number() const override {
|
||||||
|
return unimplemented_;
|
||||||
|
}
|
||||||
|
uint32_t signer_creation_time_seconds() const override { return 0; }
|
||||||
|
bool signed_by_provisioner() const override { return false; }
|
||||||
|
uint32_t system_id() const override { return system_id_; }
|
||||||
|
widevine::ClientIdentification::TokenType type() const override {
|
||||||
|
return ClientIdentification::KEYBOX;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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>& keymap);
|
||||||
|
static bool IsSystemIdKnown(const uint32_t system_id);
|
||||||
|
static uint32_t GetSystemId(const std::string& keybox_bytes);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string unimplemented_;
|
||||||
|
std::string device_key_;
|
||||||
|
uint32_t system_id_;
|
||||||
|
std::string serial_number_;
|
||||||
|
std::string signing_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_KEYBOX_CLIENT_CERT_H_
|
||||||
50
common/local_ec_key_source.cc
Normal file
50
common/local_ec_key_source.cc
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Implementation of local EC key generator.
|
||||||
|
|
||||||
|
#include "common/local_ec_key_source.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "openssl/ec.h"
|
||||||
|
#include "openssl/nid.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_util.h"
|
||||||
|
|
||||||
|
using widevine::ec_util::SerializeECPrivateKey;
|
||||||
|
using widevine::ec_util::SerializeECPublicKey;
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
bool LocalECKeySource::GetECKey(ECPrivateKey::EllipticCurve curve,
|
||||||
|
std::string* private_key,
|
||||||
|
std::string* public_key) {
|
||||||
|
DCHECK(private_key);
|
||||||
|
DCHECK(public_key);
|
||||||
|
|
||||||
|
int nid = ec_util::CurveToNid(curve);
|
||||||
|
if (nid <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bssl::UniquePtr<EC_KEY> key_pair(EC_KEY_new_by_curve_name(nid));
|
||||||
|
if (key_pair == nullptr || !EC_KEY_generate_key(key_pair.get())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!SerializeECPrivateKey(key_pair.get(), private_key) ||
|
||||||
|
!SerializeECPublicKey(key_pair.get(), public_key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
42
common/local_ec_key_source.h
Normal file
42
common/local_ec_key_source.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Class to generate EC keys locally.
|
||||||
|
|
||||||
|
#ifndef COMMON_LOCAL_EC_KEY_SOURCE_H_
|
||||||
|
#define COMMON_LOCAL_EC_KEY_SOURCE_H_
|
||||||
|
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_key_source.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Class which generates new EC keys.
|
||||||
|
class LocalECKeySource : public ECKeySource {
|
||||||
|
public:
|
||||||
|
LocalECKeySource() = default;
|
||||||
|
|
||||||
|
// Creates an EC key pair using openssl and |curve|.
|
||||||
|
// Parameter |curve| is a standard curve which defines the parameters of the
|
||||||
|
// key generation.
|
||||||
|
// Parameter |private_key| is the serialized EC private key.
|
||||||
|
// Parameter |public_key| is the serialized EC public key.
|
||||||
|
// Caller retains ownership of all pointers and they cannot be nullptr.
|
||||||
|
// Returns true if successful, false otherwise.
|
||||||
|
bool GetECKey(ECPrivateKey::EllipticCurve curve, std::string* private_key,
|
||||||
|
std::string* public_key) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
LocalECKeySource(const LocalECKeySource&) = delete;
|
||||||
|
LocalECKeySource& operator=(const LocalECKeySource&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_LOCAL_EC_KEY_SOURCE_H_
|
||||||
64
common/local_ec_key_source_test.cc
Normal file
64
common/local_ec_key_source_test.cc
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Unit tests for LocalECKeySource.
|
||||||
|
|
||||||
|
#include "common/local_ec_key_source.h"
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
|
#include "common/ec_util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
using ec_util::DeserializeECPrivateKey;
|
||||||
|
using ec_util::DeserializeECPublicKey;
|
||||||
|
using ec_util::SerializeECPublicKey;
|
||||||
|
|
||||||
|
class LocalECKeySourceTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
void ValidateKeyPair(ECPrivateKey::EllipticCurve curve,
|
||||||
|
const std::string& private_key,
|
||||||
|
const std::string& public_key) {
|
||||||
|
EC_KEY* ec_key;
|
||||||
|
std::string expected_public_key;
|
||||||
|
|
||||||
|
int nid = ec_util::CurveToNid(curve);
|
||||||
|
ASSERT_GT(nid, 0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(DeserializeECPrivateKey(private_key, &ec_key));
|
||||||
|
ASSERT_TRUE(ec_key != nullptr);
|
||||||
|
EXPECT_EQ(nid, EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)));
|
||||||
|
EXPECT_TRUE(SerializeECPublicKey(ec_key, &expected_public_key));
|
||||||
|
EXPECT_EQ(expected_public_key, public_key);
|
||||||
|
EC_KEY_free(ec_key);
|
||||||
|
ec_key = nullptr;
|
||||||
|
EXPECT_TRUE(DeserializeECPublicKey(public_key, &ec_key));
|
||||||
|
ASSERT_TRUE(ec_key != nullptr);
|
||||||
|
EC_KEY_free(ec_key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(LocalECKeySourceTest, GenerateKeys) {
|
||||||
|
LocalECKeySource key_source;
|
||||||
|
std::string private_key;
|
||||||
|
std::string public_key;
|
||||||
|
|
||||||
|
std::vector<ECPrivateKey::EllipticCurve> curves = {ECPrivateKey::SECP256R1,
|
||||||
|
ECPrivateKey::SECP384R1,
|
||||||
|
ECPrivateKey::SECP521R1};
|
||||||
|
|
||||||
|
for (auto curve : curves) {
|
||||||
|
ASSERT_TRUE(key_source.GetECKey(curve, &private_key, &public_key));
|
||||||
|
ValidateKeyPair(curve, private_key, public_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
@@ -39,8 +39,8 @@ class MockRsaPublicKey : public RsaPublicKey {
|
|||||||
|
|
||||||
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
|
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
|
||||||
std::string* encrypted_message));
|
std::string* encrypted_message));
|
||||||
MOCK_CONST_METHOD2(VerifySignature,
|
MOCK_CONST_METHOD2(VerifySignature, bool(const std::string& message,
|
||||||
bool(const std::string& message, const std::string& signature));
|
const std::string& signature));
|
||||||
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
||||||
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
||||||
|
|
||||||
@@ -54,14 +54,16 @@ class MockRsaKeyFactory : public RsaKeyFactory {
|
|||||||
MockRsaKeyFactory() {}
|
MockRsaKeyFactory() {}
|
||||||
~MockRsaKeyFactory() override {}
|
~MockRsaKeyFactory() override {}
|
||||||
|
|
||||||
MOCK_CONST_METHOD1(CreateFromPkcs1PrivateKey,
|
MOCK_CONST_METHOD1(
|
||||||
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
|
CreateFromPkcs1PrivateKey,
|
||||||
MOCK_CONST_METHOD2(
|
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
|
||||||
CreateFromPkcs8PrivateKey,
|
MOCK_CONST_METHOD2(CreateFromPkcs8PrivateKey,
|
||||||
std::unique_ptr<RsaPrivateKey>(const std::string& private_key,
|
std::unique_ptr<RsaPrivateKey>(
|
||||||
const std::string& private_key_passphrase));
|
const std::string& private_key,
|
||||||
MOCK_CONST_METHOD1(CreateFromPkcs1PublicKey,
|
const std::string& private_key_passphrase));
|
||||||
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
|
MOCK_CONST_METHOD1(
|
||||||
|
CreateFromPkcs1PublicKey,
|
||||||
|
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;
|
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ using ScopedOpenSSLStackOnly =
|
|||||||
|
|
||||||
using ScopedBIGNUM = ScopedOpenSSLType<BIGNUM, BN_free>;
|
using ScopedBIGNUM = ScopedOpenSSLType<BIGNUM, BN_free>;
|
||||||
using ScopedBIO = ScopedOpenSSLType<BIO, BIO_vfree>;
|
using ScopedBIO = ScopedOpenSSLType<BIO, BIO_vfree>;
|
||||||
|
using ScopedECGROUP = ScopedOpenSSLType<EC_GROUP, EC_GROUP_free>;
|
||||||
|
using ScopedECKEY = ScopedOpenSSLType<EC_KEY, EC_KEY_free>;
|
||||||
|
using ScopedECPOINT = ScopedOpenSSLType<EC_POINT, EC_POINT_free>;
|
||||||
using ScopedPKCS7 = ScopedOpenSSLType<PKCS7, PKCS7_free>;
|
using ScopedPKCS7 = ScopedOpenSSLType<PKCS7, PKCS7_free>;
|
||||||
using ScopedPKEY = ScopedOpenSSLType<EVP_PKEY, EVP_PKEY_free>;
|
using ScopedPKEY = ScopedOpenSSLType<EVP_PKEY, EVP_PKEY_free>;
|
||||||
using ScopedRSA = ScopedOpenSSLType<RSA, RSA_free>;
|
using ScopedRSA = ScopedOpenSSLType<RSA, RSA_free>;
|
||||||
|
|||||||
54
common/output_protection_util.cc
Normal file
54
common/output_protection_util.cc
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/output_protection_util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace op_util {
|
||||||
|
|
||||||
|
Status VerifyDeviceCapabilities(
|
||||||
|
const ClientIdentification::ClientCapabilities& device_capabilities,
|
||||||
|
const License::KeyContainer::OutputProtection& output_protection,
|
||||||
|
bool* should_disable_analog_output) {
|
||||||
|
bool device_has_analog_output =
|
||||||
|
device_capabilities.analog_output_capabilities() !=
|
||||||
|
ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_NONE &&
|
||||||
|
device_capabilities.analog_output_capabilities() !=
|
||||||
|
ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_UNKNOWN;
|
||||||
|
|
||||||
|
if (should_disable_analog_output != nullptr) {
|
||||||
|
*should_disable_analog_output = false;
|
||||||
|
}
|
||||||
|
if (device_has_analog_output) {
|
||||||
|
if (output_protection.disable_analog_output() &&
|
||||||
|
!device_capabilities.can_disable_analog_output()) {
|
||||||
|
return Status(error::PERMISSION_DENIED,
|
||||||
|
"Analog out cannot be disabled on this device.");
|
||||||
|
}
|
||||||
|
if (output_protection.cgms_flags() !=
|
||||||
|
License::KeyContainer::OutputProtection::CGMS_NONE) {
|
||||||
|
if (device_capabilities.analog_output_capabilities() !=
|
||||||
|
ClientIdentification::ClientCapabilities::
|
||||||
|
ANALOG_OUTPUT_SUPPORTS_CGMS_A) {
|
||||||
|
if (!device_capabilities.can_disable_analog_output()) {
|
||||||
|
return Status(
|
||||||
|
error::PERMISSION_DENIED,
|
||||||
|
"Analog out cannot be disabled on this device, no CGMS.");
|
||||||
|
}
|
||||||
|
// This device does not have support for CGMS protections.
|
||||||
|
if (should_disable_analog_output != nullptr) {
|
||||||
|
*should_disable_analog_output = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace op_util
|
||||||
|
} // namespace widevine
|
||||||
31
common/output_protection_util.h
Normal file
31
common/output_protection_util.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_OUTPUT_PROTECTION_UTIL_H_
|
||||||
|
#define COMMON_OUTPUT_PROTECTION_UTIL_H_
|
||||||
|
|
||||||
|
#include "common/status.h"
|
||||||
|
#include "protos/public/client_identification.pb.h"
|
||||||
|
#include "protos/public/license_protocol.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace op_util {
|
||||||
|
|
||||||
|
// Verify the device meets the provider's output requirements. Set
|
||||||
|
// |should_disable_analog_output| to true if device does not meet analog output
|
||||||
|
// requirements, otherwise |should_disable_analog_error| is false including
|
||||||
|
// error cases.
|
||||||
|
Status VerifyDeviceCapabilities(
|
||||||
|
const ClientIdentification::ClientCapabilities& device_capabilities,
|
||||||
|
const License::KeyContainer::OutputProtection& output_protection,
|
||||||
|
bool* should_disable_analog_output);
|
||||||
|
|
||||||
|
} // namespace op_util
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_OUTPUT_PROTECTION_UTIL_H_
|
||||||
155
common/output_protection_util_test.cc
Normal file
155
common/output_protection_util_test.cc
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/output_protection_util.h"
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace op_util {
|
||||||
|
|
||||||
|
TEST(OutputProtectionUtilTest, AnalogOutputAllowed_DeviceWithoutAnalogOutput) {
|
||||||
|
License::KeyContainer::OutputProtection output_protection;
|
||||||
|
output_protection.set_disable_analog_output(false);
|
||||||
|
ClientIdentification::ClientCapabilities client_capabilities;
|
||||||
|
client_capabilities.set_analog_output_capabilities(
|
||||||
|
ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_UNKNOWN);
|
||||||
|
bool should_disable_output_protection = true;
|
||||||
|
ASSERT_OK(VerifyDeviceCapabilities(client_capabilities, output_protection,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
EXPECT_FALSE(should_disable_output_protection);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputProtectionUtilTest,
|
||||||
|
AnalogOutputNotAllowed_DeviceWithoutAnalogOutput) {
|
||||||
|
License::KeyContainer::OutputProtection output_protection;
|
||||||
|
output_protection.set_disable_analog_output(true);
|
||||||
|
ClientIdentification::ClientCapabilities client_capabilities;
|
||||||
|
client_capabilities.set_analog_output_capabilities(
|
||||||
|
ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_NONE);
|
||||||
|
bool should_disable_output_protection = true;
|
||||||
|
ASSERT_OK(VerifyDeviceCapabilities(client_capabilities, output_protection,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
EXPECT_FALSE(should_disable_output_protection);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputProtectionUtilTest, AnalogOutputNotAllowed_DeviceWithAnalogOutput) {
|
||||||
|
License::KeyContainer::OutputProtection output_protection;
|
||||||
|
output_protection.set_disable_analog_output(true);
|
||||||
|
ClientIdentification::ClientCapabilities client_capabilities;
|
||||||
|
client_capabilities.set_analog_output_capabilities(
|
||||||
|
ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_SUPPORTED);
|
||||||
|
client_capabilities.set_can_disable_analog_output(false);
|
||||||
|
bool should_disable_output_protection = true;
|
||||||
|
ASSERT_EQ(error::PERMISSION_DENIED,
|
||||||
|
VerifyDeviceCapabilities(client_capabilities, output_protection,
|
||||||
|
&should_disable_output_protection)
|
||||||
|
.error_code());
|
||||||
|
EXPECT_FALSE(should_disable_output_protection);
|
||||||
|
// Client has the ability to disable it's analog output.
|
||||||
|
client_capabilities.set_can_disable_analog_output(true);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(client_capabilities, output_protection,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputProtectionUtilTest, CGMSRequired) {
|
||||||
|
License::KeyContainer::OutputProtection output_protection;
|
||||||
|
output_protection.set_disable_analog_output(false);
|
||||||
|
output_protection.set_cgms_flags(
|
||||||
|
License::KeyContainer::OutputProtection::COPY_NEVER);
|
||||||
|
ClientIdentification::ClientCapabilities client_capabilities;
|
||||||
|
client_capabilities.set_analog_output_capabilities(
|
||||||
|
ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_SUPPORTED);
|
||||||
|
bool should_disable_output_protection = true;
|
||||||
|
EXPECT_EQ(error::PERMISSION_DENIED,
|
||||||
|
VerifyDeviceCapabilities(client_capabilities, output_protection,
|
||||||
|
&should_disable_output_protection)
|
||||||
|
.error_code());
|
||||||
|
client_capabilities.set_analog_output_capabilities(
|
||||||
|
ClientIdentification::ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
|
||||||
|
ASSERT_OK(VerifyDeviceCapabilities(client_capabilities, output_protection,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
EXPECT_FALSE(should_disable_output_protection);
|
||||||
|
}
|
||||||
|
// TODO(user): enable this test.
|
||||||
|
TEST(OutputProtectionUtilTest, DISABLED_VerifyDeviceHdcpCapabilities) {
|
||||||
|
ClientIdentification::ClientCapabilities device_capabilities;
|
||||||
|
License::KeyContainer::OutputProtection required;
|
||||||
|
|
||||||
|
// Device capabilities was not specified by the client.
|
||||||
|
device_capabilities.clear_max_hdcp_version();
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_NONE);
|
||||||
|
bool should_disable_output_protection;
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V1);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2_1);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2_2);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(
|
||||||
|
License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
|
||||||
|
// Device capabilities are too low for any HDCP. Only fail if the required
|
||||||
|
// protection is HDCP_NO_DIGITAL_OUTPUT.
|
||||||
|
device_capabilities.set_max_hdcp_version(
|
||||||
|
ClientIdentification::ClientCapabilities::HDCP_NONE);
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_NONE);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V1);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(
|
||||||
|
License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
|
||||||
|
EXPECT_EQ(error::PERMISSION_DENIED,
|
||||||
|
VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection)
|
||||||
|
.error_code());
|
||||||
|
|
||||||
|
// Device does not have any digital output ports. In this case, the CDM
|
||||||
|
// cannot enforce this situation. For now, allow all HDCP requests, the KCB
|
||||||
|
// will enforce the HDCP settings.
|
||||||
|
device_capabilities.set_max_hdcp_version(
|
||||||
|
ClientIdentification::ClientCapabilities::HDCP_NO_DIGITAL_OUTPUT);
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_NONE);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V1);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2_1);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(License::KeyContainer::OutputProtection::HDCP_V2_2);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
required.set_hdcp(
|
||||||
|
License::KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
|
||||||
|
EXPECT_OK(VerifyDeviceCapabilities(device_capabilities, required,
|
||||||
|
&should_disable_output_protection));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace op_util
|
||||||
|
} // namespace widevine
|
||||||
84
common/private_key_util.h
Normal file
84
common/private_key_util.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Shared private key utilities between RSA and EC.
|
||||||
|
|
||||||
|
#ifndef COMMON_PRIVATE_KEY_UTIL_H_
|
||||||
|
#define COMMON_PRIVATE_KEY_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "openssl/bio.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace private_key_util {
|
||||||
|
|
||||||
|
template <class Key>
|
||||||
|
bool SerializeKey(const Key* key, int (*serialization_func)(BIO*, Key*),
|
||||||
|
std::string* serialized_key) {
|
||||||
|
if (key == nullptr) {
|
||||||
|
LOG(ERROR) << "Key is nullptr.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (serialized_key == nullptr) {
|
||||||
|
LOG(ERROR) << "Pointer to hold serialized key is nullptr.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BIO* bio = BIO_new(BIO_s_mem());
|
||||||
|
if (bio == nullptr) {
|
||||||
|
LOG(ERROR) << "BIO_new returned nullptr";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
if (serialization_func(bio, const_cast<Key*>(key)) != 0) {
|
||||||
|
int serialized_size = BIO_pending(bio);
|
||||||
|
serialized_key->assign(serialized_size, 0);
|
||||||
|
if (BIO_read(bio, &(*serialized_key)[0], serialized_size) ==
|
||||||
|
serialized_size) {
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
LOG(ERROR) << "BIO_read failure";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(ERROR) << "Key serialization failure";
|
||||||
|
}
|
||||||
|
BIO_free(bio);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key>
|
||||||
|
bool DeserializeKey(const std::string& serialized_key,
|
||||||
|
Key* (*deserialization_func)(BIO*, Key**), Key** key) {
|
||||||
|
if (serialized_key.empty()) {
|
||||||
|
LOG(ERROR) << "Serialized key is empty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (key == nullptr) {
|
||||||
|
LOG(ERROR) << "Pointer to hold new key is nullptr.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
|
||||||
|
serialized_key.size());
|
||||||
|
if (bio == nullptr) {
|
||||||
|
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*key = deserialization_func(bio, nullptr);
|
||||||
|
BIO_free(bio);
|
||||||
|
if (*key == nullptr) {
|
||||||
|
LOG(ERROR) << "Key deserialization failure";
|
||||||
|
}
|
||||||
|
return *key != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace private_key_util
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_PRIVATE_KEY_UTIL_H_
|
||||||
@@ -185,7 +185,8 @@ Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
|||||||
|
|
||||||
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||||
const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) {
|
const ClientIdentification& client_id,
|
||||||
|
std::string* remote_attestation_cert_sn) {
|
||||||
if (!client_id.has_token()) {
|
if (!client_id.has_token()) {
|
||||||
return (Status(error_space, INVALID_MESSAGE,
|
return (Status(error_space, INVALID_MESSAGE,
|
||||||
"remote-attestation-token-missing"));
|
"remote-attestation-token-missing"));
|
||||||
@@ -217,9 +218,10 @@ Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
|||||||
status = ca_->VerifyCertChain(*cert_chain);
|
status = ca_->VerifyCertChain(*cert_chain);
|
||||||
ca_mutex_.ReaderUnlock();
|
ca_mutex_.ReaderUnlock();
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
|
return (Status(
|
||||||
std::string("remote-attestation-cert-chain-validation-failed: ") +
|
error_space, REMOTE_ATTESTATION_FAILED,
|
||||||
status.error_message()));
|
std::string("remote-attestation-cert-chain-validation-failed: ") +
|
||||||
|
status.error_message()));
|
||||||
}
|
}
|
||||||
// Verify the remote attestation signature.
|
// Verify the remote attestation signature.
|
||||||
std::unique_ptr<RsaPublicKey> leaf_key;
|
std::unique_ptr<RsaPublicKey> leaf_key;
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "base/macros.h"
|
|
||||||
#include "base/thread_annotations.h"
|
#include "base/thread_annotations.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "common/status.h"
|
#include "common/status.h"
|
||||||
@@ -33,6 +32,11 @@ namespace widevine {
|
|||||||
class RemoteAttestationVerifier {
|
class RemoteAttestationVerifier {
|
||||||
public:
|
public:
|
||||||
RemoteAttestationVerifier() : enable_test_certificates_(false) {}
|
RemoteAttestationVerifier() : enable_test_certificates_(false) {}
|
||||||
|
|
||||||
|
RemoteAttestationVerifier(const RemoteAttestationVerifier&) = delete;
|
||||||
|
RemoteAttestationVerifier& operator=(const RemoteAttestationVerifier&) =
|
||||||
|
delete;
|
||||||
|
|
||||||
virtual ~RemoteAttestationVerifier() {}
|
virtual ~RemoteAttestationVerifier() {}
|
||||||
|
|
||||||
// Singleton accessor.
|
// Singleton accessor.
|
||||||
@@ -82,9 +86,7 @@ class RemoteAttestationVerifier {
|
|||||||
|
|
||||||
bool enable_test_certificates_;
|
bool enable_test_certificates_;
|
||||||
absl::Mutex ca_mutex_;
|
absl::Mutex ca_mutex_;
|
||||||
std::unique_ptr<X509CA> ca_ GUARDED_BY(ca_mutex_);
|
std::unique_ptr<X509CA> ca_ ABSL_GUARDED_BY(ca_mutex_);
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(RemoteAttestationVerifier);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
107
common/rot_id_generator.cc
Normal file
107
common/rot_id_generator.cc
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Classes for generating and decrypting the root of trust id which is
|
||||||
|
// included in generated DRM Certificates.
|
||||||
|
|
||||||
|
#include "common/rot_id_generator.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "common/crypto_util.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/rot_id_util.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr char kRotIdLabel[] = "ROOT_OF_TRUST_ID ENCRYPTION LABEL";
|
||||||
|
constexpr int32_t kKeyId = 0;
|
||||||
|
|
||||||
|
std::string GenerateContext(uint32_t system_id) {
|
||||||
|
return absl::StrCat(kRotIdLabel, system_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
Status RootOfTrustIdGenerator::Generate(uint32_t system_id,
|
||||||
|
const std::string& unique_id,
|
||||||
|
RootOfTrustId* root_of_trust_id) const {
|
||||||
|
CHECK(root_of_trust_id != nullptr) << "root_of_trust_id was null.";
|
||||||
|
|
||||||
|
if (system_id == 0) {
|
||||||
|
return Status(error::INVALID_ARGUMENT, "system id should not be 0.");
|
||||||
|
}
|
||||||
|
if (unique_id.empty()) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
"The unique id should not be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
root_of_trust_id->set_version(
|
||||||
|
widevine::RootOfTrustId::ROOT_OF_TRUST_ID_VERSION_1);
|
||||||
|
root_of_trust_id->set_key_id(kKeyId);
|
||||||
|
if (!ecies_encryptor_->Encrypt(
|
||||||
|
unique_id, GenerateContext(system_id),
|
||||||
|
root_of_trust_id->mutable_encrypted_unique_id())) {
|
||||||
|
root_of_trust_id->Clear();
|
||||||
|
return Status(error::INTERNAL, "Encrypt failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string unique_id_hash = GenerateUniqueIdHash(unique_id);
|
||||||
|
if (unique_id_hash.empty()) {
|
||||||
|
root_of_trust_id->Clear();
|
||||||
|
return Status(error::INTERNAL, "Could not generate unique id hash.");
|
||||||
|
}
|
||||||
|
|
||||||
|
root_of_trust_id->set_unique_id_hash(GenerateRotIdHash(
|
||||||
|
root_of_trust_id->encrypted_unique_id(), system_id, unique_id_hash));
|
||||||
|
if (root_of_trust_id->unique_id_hash().empty()) {
|
||||||
|
root_of_trust_id->Clear();
|
||||||
|
return Status(error::INTERNAL, "Failed to generate revoked id hash.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RootOfTrustIdGenerator::GenerateUniqueIdHash(
|
||||||
|
const std::string& unique_id) const {
|
||||||
|
if (unique_id.empty()) {
|
||||||
|
LOG(WARNING) << "unique_id should not be empty.";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return Sha256_Hash(absl::StrCat(unique_id, wv_shared_salt_));
|
||||||
|
}
|
||||||
|
|
||||||
|
Status RootOfTrustIdDecryptor::DecryptUniqueId(
|
||||||
|
uint32_t system_id, const std::string& rot_encrypted_id,
|
||||||
|
std::string* unique_id) const {
|
||||||
|
CHECK(unique_id != nullptr) << "unique_id was null.";
|
||||||
|
if (system_id == 0) {
|
||||||
|
return Status(error::INVALID_ARGUMENT, "system id should not be 0.");
|
||||||
|
}
|
||||||
|
if (rot_encrypted_id.empty()) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
"The rot_encrypted_id should not be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ecies_decryptor_->Decrypt(rot_encrypted_id, GenerateContext(system_id),
|
||||||
|
unique_id)) {
|
||||||
|
return Status(error::INTERNAL, "Failed to decrypt rot_encrypted_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
91
common/rot_id_generator.h
Normal file
91
common/rot_id_generator.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Classes for generating and decrypting the root of trust id which is
|
||||||
|
// included in generated DRM Certificates.
|
||||||
|
|
||||||
|
#ifndef COMMON_ROT_ID_GENERATOR_H_
|
||||||
|
#define COMMON_ROT_ID_GENERATOR_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "common/ecies_crypto.h"
|
||||||
|
#include "common/rot_id_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// The RootOfTrustIdGenerator is used to create a root of trust id that is
|
||||||
|
// associated with the keybox or OEM X.509 certificate that was used as
|
||||||
|
// authentication during provisioning.
|
||||||
|
class RootOfTrustIdGenerator {
|
||||||
|
public:
|
||||||
|
// The constructor for creating the RootOfTrustIdGenerator. |ecies_encryptor|
|
||||||
|
// is used to encrypt the unique identifier. It must not be null. The
|
||||||
|
// |wv_shared_salt| is a secret salt used in creating the hash
|
||||||
|
// component of the Root of Trust Id. The secret salt is stored securely. The
|
||||||
|
// same value is used as part of generating all Root of Trust Ids.
|
||||||
|
// The |wv_shared_salt| is also used for creating the unique id hash
|
||||||
|
// values. The unique id hash values identify revoked devices and are
|
||||||
|
// published in the DCSL and consumed by the License SDK.
|
||||||
|
RootOfTrustIdGenerator(std::unique_ptr<EciesEncryptor> ecies_encryptor,
|
||||||
|
std::string wv_shared_salt)
|
||||||
|
: ecies_encryptor_(std::move(ecies_encryptor)),
|
||||||
|
wv_shared_salt_(std::move(wv_shared_salt)) {}
|
||||||
|
|
||||||
|
virtual ~RootOfTrustIdGenerator() {}
|
||||||
|
|
||||||
|
// Creates the root of trust identifier. This fills the fields of the
|
||||||
|
// |root_of_trust_id|. The fields are generated per the spec at go/wv-kb-id.
|
||||||
|
// The |system_id| is required and must match the system id to which the
|
||||||
|
// |unique_id| belongs.
|
||||||
|
// Returns INVALID_ARGUMENT if |system_id| is 0, |unique_id| is empty.
|
||||||
|
// |roof_of_trust_id| is owned by the caller and must not be null.
|
||||||
|
virtual Status Generate(uint32_t system_id, const std::string& unique_id,
|
||||||
|
RootOfTrustId* root_of_trust_id) const;
|
||||||
|
|
||||||
|
// Generates the hash of the |unique_id| per the spec in go/wv-kb-id. This
|
||||||
|
// unique id hash (aka inner hash) is the identifier used when revoking an
|
||||||
|
// individual device. The result of this hash is one of the values used to
|
||||||
|
// generate Root of Trust Id Hash.
|
||||||
|
// |unique_id| should not be empty. If it is, an empty hash
|
||||||
|
// is returned.
|
||||||
|
std::string GenerateUniqueIdHash(const std::string& unique_id) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<EciesEncryptor> ecies_encryptor_;
|
||||||
|
std::string wv_shared_salt_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The RootOfTrustIdDecryptor is used to decrypt the root of trust id. It
|
||||||
|
// requires an |ecies_decryptor| which must use the private key that matches
|
||||||
|
// the public key used with the RootOfTrustIdGenerator. |ecies_decryptor|
|
||||||
|
// must not be null. The RootOfTrustIdDecryptor will take ownership.
|
||||||
|
class RootOfTrustIdDecryptor {
|
||||||
|
public:
|
||||||
|
explicit RootOfTrustIdDecryptor(
|
||||||
|
std::unique_ptr<EciesDecryptor> ecies_decryptor)
|
||||||
|
: ecies_decryptor_(std::move(ecies_decryptor)) {}
|
||||||
|
|
||||||
|
// Decrypts the |rot_encrypted_id| using the |system_id| as part of the
|
||||||
|
// context. |unique_id| contains the decrypted value on success.
|
||||||
|
// |rot_encrypted_id| must not be empty. |unique_id| must not be null.
|
||||||
|
// Returns true on success, false on failure.
|
||||||
|
Status DecryptUniqueId(uint32_t system_id, const std::string& rot_encrypted_id,
|
||||||
|
std::string* unique_id) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<EciesDecryptor> ecies_decryptor_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
#endif // COMMON_ROT_ID_GENERATOR_H_
|
||||||
258
common/rot_id_generator_test.cc
Normal file
258
common/rot_id_generator_test.cc
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Unit tests for RootOfTrustIdGenerator and RootO
|
||||||
|
#include "common/rot_id_generator.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "google/protobuf/util/message_differencer.h"
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
|
#include "common/ecies_crypto.h"
|
||||||
|
#include "common/fake_ec_key_source.h"
|
||||||
|
#include "common/rot_id_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::NotNull;
|
||||||
|
using ::testing::Return;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr char kTestSharedSalt[] = "test shared salt";
|
||||||
|
constexpr char kTestUniqueId[] = "test unique id";
|
||||||
|
constexpr uint32_t kTestSystemId = 1234;
|
||||||
|
constexpr char kExpectedRotIdEncryptedIdHex[] =
|
||||||
|
"048aea3f16ff24a9bf03283015ee52509a551c60c7a7cc4b995b4055ce4619d4d45efde068"
|
||||||
|
"27ea78f3071f024a785244d3dfbeac5fa51c8a498da65aaca1252bd1f174166d9645b6ecd6"
|
||||||
|
"669aa0afb7b766682c02f794d67050f779ebd104a767bedef288be13d321ae79d7209b5cd3"
|
||||||
|
"4698";
|
||||||
|
|
||||||
|
constexpr char kExpectedRotIdHashHex[] =
|
||||||
|
"2c4dbc37092ab3e55897639d4f0dd3c824001d07eb69a3f9e6db3b846bf31828";
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class RootOfTrustIdGeneratorTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
RootOfTrustIdGeneratorTest() {
|
||||||
|
expected_root_of_trust_id_.set_encrypted_unique_id(
|
||||||
|
absl::HexStringToBytes(kExpectedRotIdEncryptedIdHex));
|
||||||
|
expected_root_of_trust_id_.set_unique_id_hash(
|
||||||
|
absl::HexStringToBytes(kExpectedRotIdHashHex));
|
||||||
|
expected_root_of_trust_id_.set_key_id(0);
|
||||||
|
expected_root_of_trust_id_.set_version(
|
||||||
|
RootOfTrustId::ROOT_OF_TRUST_ID_VERSION_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<EciesEncryptor> CreateEncryptor() {
|
||||||
|
return EciesEncryptor::Create(test_keys_.public_test_key_2_secp256r1(),
|
||||||
|
&fake_ec_key_source_);
|
||||||
|
}
|
||||||
|
std::unique_ptr<EciesDecryptor> CreateDecryptor() {
|
||||||
|
return EciesDecryptor::Create(test_keys_.private_test_key_2_secp256r1());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ECTestKeys test_keys_;
|
||||||
|
FakeECKeySource fake_ec_key_source_;
|
||||||
|
RootOfTrustId expected_root_of_trust_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockEciesEncryptor : public EciesEncryptor {
|
||||||
|
public:
|
||||||
|
static MockEciesEncryptor* Create() {
|
||||||
|
ECTestKeys test_keys;
|
||||||
|
std::unique_ptr<ECPublicKey> ec_key =
|
||||||
|
ECPublicKey::Create(test_keys.public_test_key_1_secp256r1());
|
||||||
|
return new MockEciesEncryptor(std::move(ec_key));
|
||||||
|
}
|
||||||
|
MOCK_CONST_METHOD3(Encrypt, bool(const std::string&, const std::string&,
|
||||||
|
std::string*));
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit MockEciesEncryptor(std::unique_ptr<ECPublicKey> ec_key)
|
||||||
|
: EciesEncryptor(std::move(ec_key), &fake_ec_key_source_) {}
|
||||||
|
|
||||||
|
FakeECKeySource fake_ec_key_source_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) {
|
||||||
|
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||||
|
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||||
|
|
||||||
|
// Generate the root of trust id.
|
||||||
|
RootOfTrustId root_of_trust_id;
|
||||||
|
ASSERT_OK(
|
||||||
|
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
expected_root_of_trust_id_, root_of_trust_id));
|
||||||
|
|
||||||
|
// Verify decrypted unique id.
|
||||||
|
std::string decrypted_unique_id;
|
||||||
|
EXPECT_OK(decryptor.DecryptUniqueId(kTestSystemId,
|
||||||
|
root_of_trust_id.encrypted_unique_id(),
|
||||||
|
&decrypted_unique_id));
|
||||||
|
EXPECT_EQ(kTestUniqueId, decrypted_unique_id);
|
||||||
|
|
||||||
|
// Verify hashed unique id matches.
|
||||||
|
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId);
|
||||||
|
EXPECT_TRUE(IsRotIdRevoked(root_of_trust_id.encrypted_unique_id(),
|
||||||
|
kTestSystemId, root_of_trust_id.unique_id_hash(),
|
||||||
|
{unique_id_hash}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
|
||||||
|
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||||
|
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||||
|
|
||||||
|
std::string rot_encrypted_id;
|
||||||
|
std::string rot_id_hash;
|
||||||
|
|
||||||
|
// Generate the root of trust id.
|
||||||
|
RootOfTrustId root_of_trust_id;
|
||||||
|
ASSERT_OK(
|
||||||
|
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
expected_root_of_trust_id_, root_of_trust_id));
|
||||||
|
|
||||||
|
// Generate a second root of trust id for the same unique id.
|
||||||
|
// This must generate a different looking id.
|
||||||
|
// First, assign a new ephemeral to the fake key source.
|
||||||
|
fake_ec_key_source_.SetKey(ECPrivateKey::SECP256R1,
|
||||||
|
test_keys_.private_test_key_2_secp256r1(),
|
||||||
|
test_keys_.public_test_key_2_secp256r1());
|
||||||
|
RootOfTrustId second_root_of_trust_id;
|
||||||
|
ASSERT_OK(generator.Generate(kTestSystemId, kTestUniqueId,
|
||||||
|
&second_root_of_trust_id));
|
||||||
|
EXPECT_FALSE(second_root_of_trust_id.encrypted_unique_id().empty());
|
||||||
|
EXPECT_NE(kTestUniqueId, second_root_of_trust_id.encrypted_unique_id());
|
||||||
|
EXPECT_FALSE(second_root_of_trust_id.unique_id_hash().empty());
|
||||||
|
|
||||||
|
// Verify that the second id does not equal the first.
|
||||||
|
EXPECT_NE(root_of_trust_id.encrypted_unique_id(),
|
||||||
|
second_root_of_trust_id.encrypted_unique_id());
|
||||||
|
EXPECT_NE(root_of_trust_id.unique_id_hash(),
|
||||||
|
second_root_of_trust_id.unique_id_hash());
|
||||||
|
|
||||||
|
// Verify second decrypted unique id.
|
||||||
|
std::string decrypted_unique_id;
|
||||||
|
EXPECT_OK(decryptor.DecryptUniqueId(
|
||||||
|
kTestSystemId, second_root_of_trust_id.encrypted_unique_id(),
|
||||||
|
&decrypted_unique_id));
|
||||||
|
EXPECT_EQ(kTestUniqueId, decrypted_unique_id);
|
||||||
|
|
||||||
|
// Verify hashed unique id matches.
|
||||||
|
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId);
|
||||||
|
EXPECT_TRUE(IsRotIdRevoked(
|
||||||
|
second_root_of_trust_id.encrypted_unique_id(), kTestSystemId,
|
||||||
|
second_root_of_trust_id.unique_id_hash(), {unique_id_hash}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdFailedEncryption) {
|
||||||
|
MockEciesEncryptor* mock_encryptor = MockEciesEncryptor::Create();
|
||||||
|
ASSERT_THAT(mock_encryptor, NotNull());
|
||||||
|
RootOfTrustIdGenerator generator(
|
||||||
|
std::unique_ptr<EciesEncryptor>(mock_encryptor), kTestSharedSalt);
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock_encryptor, Encrypt(_, _, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Return(false))
|
||||||
|
.RetiresOnSaturation();
|
||||||
|
|
||||||
|
std::string rot_encrypted_id;
|
||||||
|
std::string rot_id_hash;
|
||||||
|
|
||||||
|
// Attempt to generate the root of trust id.
|
||||||
|
RootOfTrustId root_of_trust_id;
|
||||||
|
EXPECT_EQ(error::INTERNAL,
|
||||||
|
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id)
|
||||||
|
.error_code());
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
RootOfTrustId::default_instance(), root_of_trust_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdEmptyIdFail) {
|
||||||
|
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||||
|
RootOfTrustId root_of_trust_id;
|
||||||
|
|
||||||
|
// Should fail because the id is blank.
|
||||||
|
EXPECT_EQ(
|
||||||
|
error::INVALID_ARGUMENT,
|
||||||
|
generator.Generate(kTestSystemId, "", &root_of_trust_id).error_code());
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
RootOfTrustId::default_instance(), root_of_trust_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdNullRotIdFail) {
|
||||||
|
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||||
|
|
||||||
|
// Should fail because the id is blank.
|
||||||
|
EXPECT_DEATH(generator.Generate(kTestSystemId, kTestUniqueId,
|
||||||
|
nullptr /* root of trust id*/),
|
||||||
|
"root_of_trust_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdMismatchFails) {
|
||||||
|
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||||
|
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||||
|
|
||||||
|
// Generate the root of trust id.
|
||||||
|
RootOfTrustId root_of_trust_id;
|
||||||
|
ASSERT_OK(
|
||||||
|
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
expected_root_of_trust_id_, root_of_trust_id));
|
||||||
|
|
||||||
|
// Attempt to decrypt with different system id. Should fail.
|
||||||
|
std::string decrypted_unique_id;
|
||||||
|
EXPECT_EQ(error::INTERNAL,
|
||||||
|
decryptor
|
||||||
|
.DecryptUniqueId(kTestSystemId + 1,
|
||||||
|
root_of_trust_id.encrypted_unique_id(),
|
||||||
|
&decrypted_unique_id)
|
||||||
|
.error_code());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RootOfTrustIdGeneratorTest, DecryptorBlankUniqueId) {
|
||||||
|
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||||
|
|
||||||
|
// Attempt to decrypt empty encrypted id.
|
||||||
|
std::string decrypted_unique_id;
|
||||||
|
EXPECT_EQ(error::INVALID_ARGUMENT,
|
||||||
|
decryptor.DecryptUniqueId(kTestSystemId, "", &decrypted_unique_id)
|
||||||
|
.error_code());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdNullDecryptedIdFails) {
|
||||||
|
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
|
||||||
|
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
|
||||||
|
|
||||||
|
// Generate the root of trust id.
|
||||||
|
RootOfTrustId root_of_trust_id;
|
||||||
|
ASSERT_OK(
|
||||||
|
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
|
||||||
|
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
|
||||||
|
expected_root_of_trust_id_, root_of_trust_id));
|
||||||
|
|
||||||
|
// Attempt to decrypt with a nullptr for the decrypted id.
|
||||||
|
std::string decrypted_unique_id;
|
||||||
|
EXPECT_DEATH(
|
||||||
|
decryptor.DecryptUniqueId(
|
||||||
|
kTestSystemId, root_of_trust_id.encrypted_unique_id(), nullptr),
|
||||||
|
"unique_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
51
common/rot_id_util.cc
Normal file
51
common/rot_id_util.cc
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Helper methods for the Root of Trust Id.
|
||||||
|
|
||||||
|
#include "common/rot_id_util.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "common/crypto_util.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/sha_util.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
|
||||||
|
const std::string& rot_id_hash,
|
||||||
|
const std::vector<std::string>& revoked_ids) {
|
||||||
|
// This could conceivably happen for legacy DRM certificates without a ROT id.
|
||||||
|
// No need to match if there's nothing to match against.
|
||||||
|
if (encrypted_unique_id.empty() || rot_id_hash.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& revoked_id : revoked_ids) {
|
||||||
|
std::string revoked_hash =
|
||||||
|
GenerateRotIdHash(encrypted_unique_id, system_id, revoked_id);
|
||||||
|
if (rot_id_hash == revoked_hash) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
|
||||||
|
const std::string& unique_id_hash) {
|
||||||
|
if (salt.empty() || unique_id_hash.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return Sha256_Hash(absl::StrCat(salt, system_id, unique_id_hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
45
common/rot_id_util.h
Normal file
45
common/rot_id_util.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Helper methods for the Root of Trust Id.
|
||||||
|
|
||||||
|
#ifndef COMMON_ROT_ID_UTIL_H_
|
||||||
|
#define COMMON_ROT_ID_UTIL_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/local_ec_key_source.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// Helper function that compares the |rot_id_hash| to a hash of each of the
|
||||||
|
// |revoked_ids|. The |revoked_ids| are the unique id hash (aka inner hash)
|
||||||
|
// values as defined in the spec at go/wv-kb-id. The |encrypted_unique_id| and
|
||||||
|
// |system_id| are used to compute the hash of each of the |revoked_ids|.
|
||||||
|
// Returns true if any of the revoked_ids match.
|
||||||
|
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
|
||||||
|
const std::string& rot_id_hash,
|
||||||
|
const std::vector<std::string>& revoked_ids);
|
||||||
|
|
||||||
|
// Helper function that generates the hash for the ROT id from the
|
||||||
|
// |unique_id_hash|, the |system_id| and the |salt|. |salt| is typically an
|
||||||
|
// encrypted unique id. Since we use an ephemeral eliptic curve key as part of
|
||||||
|
// the encrypted unique id, the value is effectively random can be used as a
|
||||||
|
// salt.
|
||||||
|
// Returns the hash value on success.
|
||||||
|
// If |salt| or |unique_id_hash| are empty, this will return an empty
|
||||||
|
// string.
|
||||||
|
std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
|
||||||
|
const std::string& unique_id_hash);
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
#endif // COMMON_ROT_ID_UTIL_H_
|
||||||
66
common/rot_id_util_test.cc
Normal file
66
common/rot_id_util_test.cc
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Unit tests for the rot_id_util helper methods.
|
||||||
|
|
||||||
|
#include "common/rot_id_util.h"
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr char kFakeEncryptedId[] = "fake encrypted id";
|
||||||
|
constexpr char kFakeUniqueIdHash[] = "fake unique_id hash";
|
||||||
|
|
||||||
|
// This is the ROT ID Hash generated from the fake values.
|
||||||
|
constexpr char kRotIdHashHex[] =
|
||||||
|
"0a757dde0f1080b60f34bf8e46af573ce987b5ed1c831b44952e2feed5243a95";
|
||||||
|
|
||||||
|
constexpr uint32_t kFakeSystemId = 1234;
|
||||||
|
constexpr uint32_t kOtherFakeSystemId = 9876;
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
TEST(RotIdUtilTest, IsRotIdRevokedMatches) {
|
||||||
|
ASSERT_TRUE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId,
|
||||||
|
absl::HexStringToBytes(kRotIdHashHex),
|
||||||
|
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RotIdUtilTest, IsRotIdRevokedNoMatchSystemId) {
|
||||||
|
ASSERT_FALSE(
|
||||||
|
IsRotIdRevoked(kFakeEncryptedId, kOtherFakeSystemId,
|
||||||
|
absl::HexStringToBytes(kRotIdHashHex),
|
||||||
|
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RotIdUtilTest, IsRotIdRevokedNoMatch) {
|
||||||
|
ASSERT_FALSE(IsRotIdRevoked(
|
||||||
|
kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash,
|
||||||
|
{"NO MATCH UNIQUE ID HASH 1", "NO MATCH UNIQUE ID HASH 2"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RotIdUtilTest, IsRotIdRevokedEmptyList) {
|
||||||
|
ASSERT_FALSE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId,
|
||||||
|
kFakeUniqueIdHash,
|
||||||
|
{/* Intentionally empty vector */}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test really only ensures the stability of the implementation. If the
|
||||||
|
// hash ever changes, then it will introduce problems into the ecosystem.
|
||||||
|
TEST(RotIdUtilTest, GenerateRotIdHashSuccess) {
|
||||||
|
ASSERT_EQ(
|
||||||
|
absl::HexStringToBytes(kRotIdHashHex),
|
||||||
|
GenerateRotIdHash(kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
@@ -172,6 +172,11 @@ RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key)
|
|||||||
CHECK(key_ != nullptr);
|
CHECK(key_ != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RsaPublicKey::RsaPublicKey(const RsaPrivateKey& rsa_key)
|
||||||
|
: key_(RSAPublicKey_dup(rsa_key.key_)) {
|
||||||
|
CHECK(key_ != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
RsaPublicKey::~RsaPublicKey() { RSA_free(key_); }
|
RsaPublicKey::~RsaPublicKey() { RSA_free(key_); }
|
||||||
|
|
||||||
RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) {
|
RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) {
|
||||||
@@ -248,8 +253,8 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RsaPublicKey::VerifySignatureSha256Pkcs7(const std::string& message,
|
bool RsaPublicKey::VerifySignatureSha256Pkcs7(
|
||||||
const std::string& signature) const {
|
const std::string& message, const std::string& signature) const {
|
||||||
if (message.empty()) {
|
if (message.empty()) {
|
||||||
LOG(ERROR) << "Empty signature verification message";
|
LOG(ERROR) << "Empty signature verification message";
|
||||||
return false;
|
return false;
|
||||||
@@ -291,7 +296,8 @@ std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs1PrivateKey(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
|
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
|
||||||
const std::string& private_key, const std::string& private_key_passphrase) const {
|
const std::string& private_key,
|
||||||
|
const std::string& private_key_passphrase) const {
|
||||||
std::string pkcs1_key;
|
std::string pkcs1_key;
|
||||||
const bool result =
|
const bool result =
|
||||||
private_key_passphrase.empty()
|
private_key_passphrase.empty()
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "base/macros.h"
|
|
||||||
#include "openssl/rsa.h"
|
#include "openssl/rsa.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
@@ -79,7 +78,13 @@ class RsaPrivateKey {
|
|||||||
class RsaPublicKey {
|
class RsaPublicKey {
|
||||||
public:
|
public:
|
||||||
explicit RsaPublicKey(RSA* key);
|
explicit RsaPublicKey(RSA* key);
|
||||||
RsaPublicKey(const RsaPublicKey&);
|
|
||||||
|
// Copy constructor.
|
||||||
|
RsaPublicKey(const RsaPublicKey& rsa_key);
|
||||||
|
|
||||||
|
// Construct RsaPublicKey object from RsaPrivateKey.
|
||||||
|
explicit RsaPublicKey(const RsaPrivateKey& rsa_key);
|
||||||
|
|
||||||
virtual ~RsaPublicKey();
|
virtual ~RsaPublicKey();
|
||||||
|
|
||||||
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
|
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
|
||||||
@@ -132,6 +137,10 @@ class RsaPublicKey {
|
|||||||
class RsaKeyFactory {
|
class RsaKeyFactory {
|
||||||
public:
|
public:
|
||||||
RsaKeyFactory();
|
RsaKeyFactory();
|
||||||
|
|
||||||
|
RsaKeyFactory(const RsaKeyFactory&) = delete;
|
||||||
|
RsaKeyFactory& operator=(const RsaKeyFactory&) = delete;
|
||||||
|
|
||||||
virtual ~RsaKeyFactory();
|
virtual ~RsaKeyFactory();
|
||||||
|
|
||||||
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
|
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
|
||||||
@@ -141,14 +150,12 @@ class RsaKeyFactory {
|
|||||||
// Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or
|
// Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or
|
||||||
// EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty).
|
// EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty).
|
||||||
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs8PrivateKey(
|
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs8PrivateKey(
|
||||||
const std::string& private_key, const std::string& private_key_passphrase) const;
|
const std::string& private_key,
|
||||||
|
const std::string& private_key_passphrase) const;
|
||||||
|
|
||||||
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
|
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
|
||||||
virtual std::unique_ptr<RsaPublicKey> CreateFromPkcs1PublicKey(
|
virtual std::unique_ptr<RsaPublicKey> CreateFromPkcs1PublicKey(
|
||||||
const std::string& public_key) const;
|
const std::string& public_key) const;
|
||||||
|
|
||||||
private:
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -161,6 +161,19 @@ TEST_F(RsaKeyTest, EncryptAndDecrypt_2048) {
|
|||||||
factory_.CreateFromPkcs1PublicKey(public_key));
|
factory_.CreateFromPkcs1PublicKey(public_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(RsaKeyTest, RsaPublicKeyFromPrivateKey) {
|
||||||
|
std::unique_ptr<RsaPrivateKey> private_key(
|
||||||
|
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
|
||||||
|
ASSERT_TRUE(private_key);
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key(new RsaPublicKey(*private_key));
|
||||||
|
ASSERT_TRUE(public_key);
|
||||||
|
|
||||||
|
EXPECT_TRUE(private_key->MatchesPublicKey(*public_key));
|
||||||
|
EXPECT_TRUE(public_key->MatchesPrivateKey(*private_key));
|
||||||
|
|
||||||
|
TestEncryption(std::move(private_key), std::move(public_key));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(RsaKeyTest, SignAndVerify_3072) {
|
TEST_F(RsaKeyTest, SignAndVerify_3072) {
|
||||||
const std::string& private_key = test_keys_.private_test_key_1_3072_bits();
|
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();
|
const std::string& public_key = test_keys_.public_test_key_1_3072_bits();
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// License Agreement. For a copy of this agreement, please contact
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
// widevine-licensing@google.com.
|
// widevine-licensing@google.com.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
//
|
//
|
||||||
// Description:
|
// Description:
|
||||||
// RSA utility functions for serializing and deserializing RSA keys,
|
// RSA utility functions for serializing and deserializing RSA keys,
|
||||||
@@ -14,12 +13,14 @@
|
|||||||
#include "common/rsa_util.h"
|
#include "common/rsa_util.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "openssl/pem.h"
|
#include "openssl/pem.h"
|
||||||
#include "openssl/x509.h"
|
#include "openssl/x509.h"
|
||||||
|
#include "common/private_key_util.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int BigNumGreaterThanPow2(const BIGNUM* b, int n) {
|
int BigNumGreaterThanPow2(const BIGNUM* b, int n) {
|
||||||
@@ -34,92 +35,28 @@ int BigNumGreaterThanPow2(const BIGNUM* b, int n) {
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace rsa_util {
|
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,
|
bool SerializeRsaPrivateKey(const RSA* private_key,
|
||||||
std::string* serialized_private_key) {
|
std::string* serialized_private_key) {
|
||||||
return SerializeRsaKey(private_key, serialized_private_key, true);
|
return private_key_util::SerializeKey<RSA>(private_key, i2d_RSAPrivateKey_bio,
|
||||||
|
serialized_private_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeserializeRsaPrivateKey(const std::string& serialized_private_key,
|
bool DeserializeRsaPrivateKey(const std::string& serialized_private_key,
|
||||||
RSA** private_key) {
|
RSA** private_key) {
|
||||||
return DeserializeRsaKey(serialized_private_key, private_key, true);
|
return private_key_util::DeserializeKey<RSA>(
|
||||||
|
serialized_private_key, d2i_RSAPrivateKey_bio, private_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SerializeRsaPublicKey(const RSA* public_key,
|
bool SerializeRsaPublicKey(const RSA* public_key,
|
||||||
std::string* serialized_public_key) {
|
std::string* serialized_public_key) {
|
||||||
return SerializeRsaKey(public_key, serialized_public_key, false);
|
return private_key_util::SerializeKey<RSA>(public_key, i2d_RSAPublicKey_bio,
|
||||||
|
serialized_public_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
|
bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
|
||||||
RSA** public_key) {
|
RSA** public_key) {
|
||||||
return DeserializeRsaKey(serialized_public_key, public_key, false);
|
return private_key_util::DeserializeKey<RSA>(
|
||||||
|
serialized_public_key, d2i_RSAPublicKey_bio, public_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SerializePrivateKeyInfo(const RSA* private_key,
|
bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||||
@@ -327,9 +264,9 @@ int get_password(char* buf, int size, int rwflag, void* u) {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key,
|
bool DeserializeEncryptedPrivateKeyInfo(
|
||||||
const std::string& passphrase,
|
const std::string& serialized_private_key, const std::string& passphrase,
|
||||||
RSA** private_key) {
|
RSA** private_key) {
|
||||||
if (serialized_private_key.empty()) {
|
if (serialized_private_key.empty()) {
|
||||||
LOG(ERROR) << "Serialized RSAEncryptedPrivateKeyInfo is empty.";
|
LOG(ERROR) << "Serialized RSAEncryptedPrivateKeyInfo is empty.";
|
||||||
return false;
|
return false;
|
||||||
@@ -349,8 +286,8 @@ bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_ke
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool success = false;
|
bool success = false;
|
||||||
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, nullptr, get_password,
|
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(
|
||||||
const_cast<std::string*>(&passphrase));
|
bio, nullptr, get_password, const_cast<std::string*>(&passphrase));
|
||||||
if (evp == nullptr) {
|
if (evp == nullptr) {
|
||||||
LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned nullptr.";
|
LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned nullptr.";
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|||||||
@@ -119,9 +119,9 @@ bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
|
|||||||
// which is not allocated if the method fails. This parameter must not be
|
// which is not allocated if the method fails. This parameter must not be
|
||||||
// NULL.
|
// NULL.
|
||||||
// Returns true if successful, false otherwise.
|
// Returns true if successful, false otherwise.
|
||||||
bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key,
|
bool DeserializeEncryptedPrivateKeyInfo(
|
||||||
const std::string& passphrase,
|
const std::string& serialized_private_key, const std::string& passphrase,
|
||||||
RSA** private_key);
|
RSA** private_key);
|
||||||
|
|
||||||
// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8
|
// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8
|
||||||
// EncryptedPrivateKeyInfo.
|
// EncryptedPrivateKeyInfo.
|
||||||
|
|||||||
185
common/security_profile_list.cc
Normal file
185
common/security_profile_list.cc
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2020 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 the SecurityProfileList class.
|
||||||
|
|
||||||
|
#include "common/security_profile_list.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/client_id_util.h"
|
||||||
|
#include "protos/public/client_identification.pb.h"
|
||||||
|
#include "protos/public/provisioned_device_info.pb.h"
|
||||||
|
#include "protos/public/security_profile.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
using ClientCapabilities = ClientIdentification::ClientCapabilities;
|
||||||
|
|
||||||
|
const char kModDrmMake[] = "company_name";
|
||||||
|
const char kModDrmModel[] = "model_name";
|
||||||
|
|
||||||
|
int SecurityProfileList::Init() { return AddDefaultProfiles(); }
|
||||||
|
|
||||||
|
int SecurityProfileList::AddDefaultProfiles() {
|
||||||
|
const uint32_t oemcrypto_8 = 8;
|
||||||
|
const uint32_t oemcrypto_12 = 12;
|
||||||
|
const bool make_model_not_verified = false;
|
||||||
|
SecurityProfile profile;
|
||||||
|
|
||||||
|
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_1,
|
||||||
|
ClientCapabilities::HDCP_NONE,
|
||||||
|
ClientCapabilities::ANALOG_OUTPUT_UNKNOWN, oemcrypto_8,
|
||||||
|
make_model_not_verified, ProvisionedDeviceInfo::LEVEL_3,
|
||||||
|
kResourceTierLow, &profile);
|
||||||
|
InsertProfile(profile);
|
||||||
|
|
||||||
|
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_2,
|
||||||
|
ClientCapabilities::HDCP_NONE,
|
||||||
|
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A,
|
||||||
|
oemcrypto_12, make_model_not_verified,
|
||||||
|
ProvisionedDeviceInfo::LEVEL_2, kResourceTierLow, &profile);
|
||||||
|
InsertProfile(profile);
|
||||||
|
|
||||||
|
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_3,
|
||||||
|
ClientCapabilities::HDCP_V1,
|
||||||
|
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A,
|
||||||
|
oemcrypto_12, make_model_not_verified,
|
||||||
|
ProvisionedDeviceInfo::LEVEL_1, kResourceTierMed, &profile);
|
||||||
|
InsertProfile(profile);
|
||||||
|
|
||||||
|
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_4,
|
||||||
|
ClientCapabilities::HDCP_V2_2,
|
||||||
|
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A,
|
||||||
|
oemcrypto_12, make_model_not_verified,
|
||||||
|
ProvisionedDeviceInfo::LEVEL_1, kResourceTierHigh, &profile);
|
||||||
|
InsertProfile(profile);
|
||||||
|
absl::ReaderMutexLock lock(&mutex_);
|
||||||
|
return security_profiles_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
SecurityProfile::Level SecurityProfileList::GetProfileLevel(
|
||||||
|
const ClientIdentification& client_id,
|
||||||
|
const ProvisionedDeviceInfo& device_info,
|
||||||
|
SecurityProfile::DrmInfo* drm_info) const {
|
||||||
|
// Iterate through each SP starting from the strictest first.
|
||||||
|
absl::ReaderMutexLock lock(&mutex_);
|
||||||
|
for (auto& profile : security_profiles_) {
|
||||||
|
if (profile.min_security_requirements().security_level() <
|
||||||
|
device_info.security_level()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (profile.min_security_requirements().oemcrypto_version() >
|
||||||
|
client_id.client_capabilities().oem_crypto_api_version()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (profile.min_output_requirements().hdcp_version() >
|
||||||
|
client_id.client_capabilities().max_hdcp_version()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (profile.min_output_requirements().analog_output_capabilities() >
|
||||||
|
client_id.client_capabilities().analog_output_capabilities()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (profile.min_security_requirements().resource_rating_tier() >
|
||||||
|
client_id.client_capabilities().resource_rating_tier()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (drm_info != nullptr) {
|
||||||
|
GetDrmInfo(client_id, device_info, drm_info);
|
||||||
|
}
|
||||||
|
return profile.level();
|
||||||
|
}
|
||||||
|
return SecurityProfile::SECURITY_PROFILE_LEVEL_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SecurityProfileList::GetDrmInfo(const ClientIdentification& client_id,
|
||||||
|
const ProvisionedDeviceInfo& device_info,
|
||||||
|
SecurityProfile::DrmInfo* drm_info) const {
|
||||||
|
if (drm_info == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
drm_info->mutable_output()->set_hdcp_version(
|
||||||
|
client_id.client_capabilities().max_hdcp_version());
|
||||||
|
drm_info->mutable_output()->set_analog_output_capabilities(
|
||||||
|
client_id.client_capabilities().analog_output_capabilities());
|
||||||
|
drm_info->mutable_security()->set_oemcrypto_version(
|
||||||
|
client_id.client_capabilities().oem_crypto_api_version());
|
||||||
|
drm_info->mutable_security()->set_resource_rating_tier(
|
||||||
|
client_id.client_capabilities().resource_rating_tier());
|
||||||
|
drm_info->mutable_security()->set_security_level(
|
||||||
|
device_info.security_level());
|
||||||
|
drm_info->mutable_security()->set_request_model_info_status(false);
|
||||||
|
drm_info->mutable_request_model_info()->set_manufacturer(
|
||||||
|
GetClientInfo(client_id, kModDrmMake));
|
||||||
|
drm_info->mutable_request_model_info()->set_model(
|
||||||
|
GetClientInfo(client_id, kModDrmModel));
|
||||||
|
drm_info->set_system_id(device_info.system_id());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SecurityProfileList::PopulateProfile(
|
||||||
|
const SecurityProfile::Level profile_level,
|
||||||
|
const ClientCapabilities::HdcpVersion min_hdcp_version,
|
||||||
|
const ClientCapabilities::AnalogOutputCapabilities
|
||||||
|
analog_output_capabilities,
|
||||||
|
const uint32_t min_oemcrypto_version, const bool make_model_verified,
|
||||||
|
const ProvisionedDeviceInfo::WvSecurityLevel security_level,
|
||||||
|
const uint32_t resource_rating_tier,
|
||||||
|
SecurityProfile* profile_to_create) const {
|
||||||
|
if (profile_to_create == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
profile_to_create->set_level(profile_level);
|
||||||
|
profile_to_create->mutable_min_output_requirements()->set_hdcp_version(
|
||||||
|
min_hdcp_version);
|
||||||
|
profile_to_create->mutable_min_output_requirements()
|
||||||
|
->set_analog_output_capabilities(analog_output_capabilities);
|
||||||
|
profile_to_create->mutable_min_security_requirements()->set_oemcrypto_version(
|
||||||
|
min_oemcrypto_version);
|
||||||
|
profile_to_create->mutable_min_security_requirements()->set_security_level(
|
||||||
|
security_level);
|
||||||
|
profile_to_create->mutable_min_security_requirements()
|
||||||
|
->set_resource_rating_tier(resource_rating_tier);
|
||||||
|
profile_to_create->mutable_min_security_requirements()
|
||||||
|
->set_request_model_info_status(make_model_verified);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SecurityProfileList::GetProfile(SecurityProfile::Level level,
|
||||||
|
SecurityProfile* security_profile) {
|
||||||
|
absl::ReaderMutexLock lock(&mutex_);
|
||||||
|
for (auto& profile : security_profiles_) {
|
||||||
|
if (profile.level() == level) {
|
||||||
|
if (security_profile != nullptr) {
|
||||||
|
*security_profile = profile;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SecurityProfileList::InsertProfile(
|
||||||
|
const SecurityProfile& profile_to_insert) {
|
||||||
|
// Check if profile already exist.
|
||||||
|
if (GetProfile(profile_to_insert.level(), nullptr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
absl::WriterMutexLock lock(&mutex_);
|
||||||
|
security_profiles_.push_back(profile_to_insert);
|
||||||
|
sort(security_profiles_.begin(), security_profiles_.end(),
|
||||||
|
CompareProfileLevel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SecurityProfileList::CompareProfileLevel(const SecurityProfile& p1,
|
||||||
|
const SecurityProfile& p2) {
|
||||||
|
// Profiles are sorted from highest to lowest (strictest) level.
|
||||||
|
return (p1.level() > p2.level());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
96
common/security_profile_list.h
Normal file
96
common/security_profile_list.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2020 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:
|
||||||
|
// Container of Widevine security profiles. Security profiles indicate the
|
||||||
|
// level of security of a device based on the device's output protections,
|
||||||
|
// version of OEMCrypto and security level.
|
||||||
|
|
||||||
|
#ifndef COMMON_SECURITY_PROFILE_LIST_H_
|
||||||
|
#define COMMON_SECURITY_PROFILE_LIST_H_
|
||||||
|
|
||||||
|
#include "absl/synchronization/mutex.h"
|
||||||
|
#include "protos/public/client_identification.pb.h"
|
||||||
|
#include "protos/public/provisioned_device_info.pb.h"
|
||||||
|
#include "protos/public/security_profile.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
using ClientCapabilities = ClientIdentification::ClientCapabilities;
|
||||||
|
|
||||||
|
const uint32_t kResourceTierLow = 1;
|
||||||
|
const uint32_t kResourceTierMed = 2;
|
||||||
|
const uint32_t kResourceTierHigh = 3;
|
||||||
|
|
||||||
|
// The SecurityProfileList will hold all security profiles. During license
|
||||||
|
// acquisition, information from the client and information from the server are
|
||||||
|
// combined to deternmine the device's security profile level.
|
||||||
|
|
||||||
|
class SecurityProfileList {
|
||||||
|
public:
|
||||||
|
SecurityProfileList() {}
|
||||||
|
~SecurityProfileList() {}
|
||||||
|
|
||||||
|
// Initialize the security profile list. The list is initially empty, this
|
||||||
|
// function will populate the list with default profiles. The size of the
|
||||||
|
// list is returned.
|
||||||
|
int Init();
|
||||||
|
|
||||||
|
// Add the specified profile to the existing list of profiles. Returns true
|
||||||
|
// if successfully inserted, false if unable to insert.
|
||||||
|
bool InsertProfile(const SecurityProfile& profile_to_insert);
|
||||||
|
|
||||||
|
// Return the highest security level based on the device capabilities.
|
||||||
|
// If |drm_info| is not null, |drm_info| is populated with the device data.
|
||||||
|
SecurityProfile::Level GetProfileLevel(
|
||||||
|
const ClientIdentification& client_id,
|
||||||
|
const ProvisionedDeviceInfo& device_info,
|
||||||
|
SecurityProfile::DrmInfo* drm_info) const;
|
||||||
|
|
||||||
|
// Return the device security capabilities. |drm_info| is populated with
|
||||||
|
// data from |client_id| and |device_info|. |drm_info| must not be null and
|
||||||
|
// is owned by the caller.
|
||||||
|
bool GetDrmInfo(const ClientIdentification& client_id,
|
||||||
|
const ProvisionedDeviceInfo& device_info,
|
||||||
|
SecurityProfile::DrmInfo* drm_info) const;
|
||||||
|
|
||||||
|
// Populate |profile_to_create| with the specified output protections and
|
||||||
|
// security parameters. All input parameters are used hence should be set.
|
||||||
|
bool PopulateProfile(
|
||||||
|
const SecurityProfile::Level profile_level,
|
||||||
|
const ClientCapabilities::HdcpVersion min_hdcp_version,
|
||||||
|
const ClientCapabilities::AnalogOutputCapabilities
|
||||||
|
analog_output_capabilities,
|
||||||
|
const uint32_t min_oemcrypto_version, const bool make_model_verified,
|
||||||
|
const ProvisionedDeviceInfo::WvSecurityLevel security_level,
|
||||||
|
const uint32_t resource_rating_tier,
|
||||||
|
SecurityProfile* profile_to_create) const;
|
||||||
|
|
||||||
|
// Return true if a profile exist matching the specified |level|.
|
||||||
|
// |security_profile| is owned by the caller and is populated if a profile
|
||||||
|
// exist.
|
||||||
|
bool GetProfile(SecurityProfile::Level level,
|
||||||
|
SecurityProfile* security_profile);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Initialize the list with Widevine default profiles. The size of the
|
||||||
|
// profile list after the additions is returned.
|
||||||
|
int AddDefaultProfiles();
|
||||||
|
|
||||||
|
static bool CompareProfileLevel(const SecurityProfile& p1,
|
||||||
|
const SecurityProfile& p2);
|
||||||
|
|
||||||
|
mutable absl::Mutex mutex_;
|
||||||
|
// Widevine security profiles
|
||||||
|
std::vector<SecurityProfile> security_profiles_ GUARDED_BY(mutex_);
|
||||||
|
// Custom security profiles
|
||||||
|
std::map<std::string, SecurityProfile> custom_security_profiles_
|
||||||
|
GUARDED_BY(mutex_);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
#endif // COMMON_SECURITY_PROFILE_LIST_H_
|
||||||
157
common/security_profile_list_test.cc
Normal file
157
common/security_profile_list_test.cc
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2020 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/security_profile_list.h"
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "google/protobuf/util/message_differencer.h"
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "protos/public/security_profile.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace security_profile {
|
||||||
|
|
||||||
|
const char kMakeName[] = "company_name";
|
||||||
|
const char kMakeValue[] = "Google";
|
||||||
|
const char kModelName[] = "model_name";
|
||||||
|
const char kModelValue[] = "model1";
|
||||||
|
const uint32_t kSystemId = 1234;
|
||||||
|
|
||||||
|
class SecurityProfileListTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
SecurityProfileListTest() {}
|
||||||
|
~SecurityProfileListTest() override {}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
const uint32_t oemcrypto_12 = 12;
|
||||||
|
const bool make_model_not_verified = false;
|
||||||
|
const ClientIdentification::ClientCapabilities::HdcpVersion hdcp_version =
|
||||||
|
ClientCapabilities::HDCP_V2_2;
|
||||||
|
test_profile_1_.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_1);
|
||||||
|
test_profile_1_.mutable_min_output_requirements()->set_hdcp_version(
|
||||||
|
hdcp_version);
|
||||||
|
test_profile_1_.mutable_min_output_requirements()
|
||||||
|
->set_analog_output_capabilities(
|
||||||
|
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
|
||||||
|
test_profile_1_.mutable_min_security_requirements()->set_oemcrypto_version(
|
||||||
|
oemcrypto_12);
|
||||||
|
test_profile_1_.mutable_min_security_requirements()->set_security_level(
|
||||||
|
ProvisionedDeviceInfo::LEVEL_1);
|
||||||
|
test_profile_1_.mutable_min_security_requirements()
|
||||||
|
->set_resource_rating_tier(kResourceTierHigh);
|
||||||
|
test_profile_1_.mutable_min_security_requirements()
|
||||||
|
->set_request_model_info_status(make_model_not_verified);
|
||||||
|
|
||||||
|
ClientIdentification_NameValue *nv = client_id_.add_client_info();
|
||||||
|
nv->set_name(kMakeName);
|
||||||
|
nv->set_value(kMakeValue);
|
||||||
|
nv = client_id_.add_client_info();
|
||||||
|
nv->set_name(kModelName);
|
||||||
|
nv->set_value(kModelValue);
|
||||||
|
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(
|
||||||
|
oemcrypto_12);
|
||||||
|
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
|
||||||
|
hdcp_version);
|
||||||
|
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
|
||||||
|
kResourceTierHigh);
|
||||||
|
|
||||||
|
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
|
||||||
|
device_info_.set_system_id(kSystemId);
|
||||||
|
}
|
||||||
|
SecurityProfile test_profile_1_;
|
||||||
|
SecurityProfileList profile_list_;
|
||||||
|
ClientIdentification client_id_;
|
||||||
|
ProvisionedDeviceInfo device_info_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(SecurityProfileListTest, InsertProfile) {
|
||||||
|
// This test will not initialize the SecurityProfileList, hence it's empty.
|
||||||
|
// Insert test profile 1 into the list.
|
||||||
|
EXPECT_TRUE(profile_list_.InsertProfile(test_profile_1_));
|
||||||
|
// Should not allow insertion of an alreadfy existing level.
|
||||||
|
EXPECT_FALSE(profile_list_.InsertProfile(test_profile_1_));
|
||||||
|
SecurityProfile profile;
|
||||||
|
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_1,
|
||||||
|
profile_list_.GetProfile(test_profile_1_.level(), &profile));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
google::protobuf::util::MessageDifferencer::Equals(test_profile_1_, profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SecurityProfileListTest, GetDrmInfo) {
|
||||||
|
SecurityProfile::DrmInfo drm_info;
|
||||||
|
ASSERT_TRUE(profile_list_.GetDrmInfo(client_id_, device_info_, &drm_info));
|
||||||
|
EXPECT_EQ(client_id_.client_capabilities().max_hdcp_version(),
|
||||||
|
drm_info.output().hdcp_version());
|
||||||
|
EXPECT_EQ(client_id_.client_capabilities().analog_output_capabilities(),
|
||||||
|
drm_info.output().analog_output_capabilities());
|
||||||
|
EXPECT_EQ(client_id_.client_capabilities().oem_crypto_api_version(),
|
||||||
|
drm_info.security().oemcrypto_version());
|
||||||
|
EXPECT_EQ(client_id_.client_capabilities().resource_rating_tier(),
|
||||||
|
drm_info.security().resource_rating_tier());
|
||||||
|
|
||||||
|
EXPECT_EQ(device_info_.security_level(),
|
||||||
|
drm_info.security().security_level());
|
||||||
|
EXPECT_EQ(device_info_.system_id(), drm_info.system_id());
|
||||||
|
|
||||||
|
// make_mode status is currently hard-coded to false.
|
||||||
|
EXPECT_EQ(false, drm_info.security().request_model_info_status());
|
||||||
|
EXPECT_EQ(kMakeValue, drm_info.request_model_info().manufacturer());
|
||||||
|
EXPECT_EQ(kModelValue, drm_info.request_model_info().model());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SecurityProfileListTest, ProfileLevels) {
|
||||||
|
SecurityProfile::DrmInfo drm_info;
|
||||||
|
profile_list_.Init();
|
||||||
|
|
||||||
|
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
|
||||||
|
ClientCapabilities::HDCP_NONE);
|
||||||
|
client_id_.mutable_client_capabilities()->set_analog_output_capabilities(
|
||||||
|
ClientCapabilities::ANALOG_OUTPUT_UNKNOWN);
|
||||||
|
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(7);
|
||||||
|
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
|
||||||
|
kResourceTierLow);
|
||||||
|
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_3);
|
||||||
|
|
||||||
|
// Lowest profile level requires OEMCrypto version 8.
|
||||||
|
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_UNDEFINED,
|
||||||
|
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
|
||||||
|
|
||||||
|
// Move up to profile 1
|
||||||
|
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(8);
|
||||||
|
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_1,
|
||||||
|
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
|
||||||
|
|
||||||
|
// Move up to profile 2
|
||||||
|
client_id_.mutable_client_capabilities()->set_analog_output_capabilities(
|
||||||
|
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
|
||||||
|
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(12);
|
||||||
|
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_2);
|
||||||
|
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_2,
|
||||||
|
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
|
||||||
|
|
||||||
|
// Move up to profile 3
|
||||||
|
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
|
||||||
|
ClientCapabilities::HDCP_V1);
|
||||||
|
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
|
||||||
|
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
|
||||||
|
kResourceTierMed);
|
||||||
|
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_3,
|
||||||
|
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
|
||||||
|
|
||||||
|
// Move up to profile 4
|
||||||
|
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
|
||||||
|
ClientCapabilities::HDCP_V2_2);
|
||||||
|
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
|
||||||
|
kResourceTierHigh);
|
||||||
|
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_4,
|
||||||
|
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace security_profile
|
||||||
|
} // namespace widevine
|
||||||
@@ -27,7 +27,8 @@ std::string Sha512_Hash(const std::string& message);
|
|||||||
// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based,
|
// 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
|
// 16-byte binary representation. Name_space is a GUID prefix; name is a unique
|
||||||
// name in the namespace.
|
// name in the namespace.
|
||||||
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name);
|
std::string GenerateSha1Uuid(const std::string& name_space,
|
||||||
|
const std::string& name);
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ TEST(ShaUtilTest, Sha1Empty) {
|
|||||||
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
|
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
|
||||||
0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
|
0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
|
||||||
};
|
};
|
||||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha1_Hash(""));
|
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||||
|
Sha1_Hash(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ShaUtilTest, Sha256Empty) {
|
TEST(ShaUtilTest, Sha256Empty) {
|
||||||
@@ -26,7 +27,8 @@ TEST(ShaUtilTest, Sha256Empty) {
|
|||||||
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
|
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
|
||||||
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
||||||
};
|
};
|
||||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha256_Hash(""));
|
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||||
|
Sha256_Hash(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ShaUtilTest, Sha1) {
|
TEST(ShaUtilTest, Sha1) {
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace signature_util {
|
namespace signature_util {
|
||||||
|
|
||||||
Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
Status GenerateAesSignature(const std::string& message,
|
||||||
|
const std::string& aes_key,
|
||||||
const std::string& aes_iv, std::string* signature) {
|
const std::string& aes_iv, std::string* signature) {
|
||||||
if (signature == nullptr) {
|
if (signature == nullptr) {
|
||||||
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
|
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
|
||||||
@@ -36,7 +37,8 @@ Status GenerateAesSignature(const std::string& message, const std::string& aes_k
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
|
Status GenerateRsaSignature(const std::string& message,
|
||||||
|
const std::string& private_key,
|
||||||
std::string* signature) {
|
std::string* signature) {
|
||||||
if (signature == nullptr) {
|
if (signature == nullptr) {
|
||||||
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
|
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
|
||||||
|
|||||||
@@ -19,13 +19,15 @@ namespace signature_util {
|
|||||||
// Generates an AES signature of |message| using |aes_key| and |aes_iv|.
|
// Generates an AES signature of |message| using |aes_key| and |aes_iv|.
|
||||||
// Signature is returned via |signature| if generation was successful.
|
// Signature is returned via |signature| if generation was successful.
|
||||||
// Returns a Status that carries the details of error if generation failed.
|
// Returns a Status that carries the details of error if generation failed.
|
||||||
Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
Status GenerateAesSignature(const std::string& message,
|
||||||
|
const std::string& aes_key,
|
||||||
const std::string& aes_iv, std::string* signature);
|
const std::string& aes_iv, std::string* signature);
|
||||||
|
|
||||||
// Generates a RSA signature of |message| using |private_key|.
|
// Generates a RSA signature of |message| using |private_key|.
|
||||||
// Signature is returned via |sigature| if generation was successful.
|
// Signature is returned via |sigature| if generation was successful.
|
||||||
// Returns a Status that carries the details of error if generation failed.
|
// Returns a Status that carries the details of error if generation failed.
|
||||||
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
|
Status GenerateRsaSignature(const std::string& message,
|
||||||
|
const std::string& private_key,
|
||||||
std::string* signature);
|
std::string* signature);
|
||||||
|
|
||||||
} // namespace signature_util
|
} // namespace signature_util
|
||||||
|
|||||||
69
common/signer_public_key.cc
Normal file
69
common/signer_public_key.cc
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/signer_public_key.h"
|
||||||
|
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// SignerPublicKeyImpl is a generic implementation of SignerPublicKey. The
|
||||||
|
// initialization details are in the SignerPublicKey factory method.
|
||||||
|
template <typename T>
|
||||||
|
class SignerPublicKeyImpl : public SignerPublicKey {
|
||||||
|
public:
|
||||||
|
explicit SignerPublicKeyImpl(std::unique_ptr<T> signer_public_key)
|
||||||
|
: signer_public_key_(std::move(signer_public_key)) {}
|
||||||
|
~SignerPublicKeyImpl() override {}
|
||||||
|
SignerPublicKeyImpl(const SignerPublicKeyImpl&) = delete;
|
||||||
|
SignerPublicKeyImpl& operator=(const SignerPublicKeyImpl&) = delete;
|
||||||
|
|
||||||
|
bool VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature) const override {
|
||||||
|
if (!signer_public_key_->VerifySignature(message, signature)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<T> signer_public_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<SignerPublicKey> SignerPublicKey::Create(
|
||||||
|
const std::string& signer_public_key, DrmCertificate::Algorithm algorithm) {
|
||||||
|
switch (algorithm) {
|
||||||
|
case DrmCertificate::RSA: {
|
||||||
|
std::unique_ptr<RsaPublicKey> public_key(
|
||||||
|
RsaPublicKey::Create(signer_public_key));
|
||||||
|
if (public_key == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return absl::make_unique<SignerPublicKeyImpl<RsaPublicKey>>(
|
||||||
|
std::move(public_key));
|
||||||
|
}
|
||||||
|
// All supported ECC curves are specified here.
|
||||||
|
case DrmCertificate::ECC_SECP256R1:
|
||||||
|
case DrmCertificate::ECC_SECP384R1:
|
||||||
|
case DrmCertificate::ECC_SECP521R1: {
|
||||||
|
std::unique_ptr<ECPublicKey> public_key =
|
||||||
|
ECPublicKey::Create(signer_public_key);
|
||||||
|
if (public_key == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return absl::make_unique<SignerPublicKeyImpl<ECPublicKey>>(
|
||||||
|
std::move(public_key));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
40
common/signer_public_key.h
Normal file
40
common/signer_public_key.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_SIGNER_PUBLIC_KEY_H_
|
||||||
|
#define COMMON_SIGNER_PUBLIC_KEY_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
// SignerPublicKey is implemented for each provisioning key type that are
|
||||||
|
// defined in video/widevine/proto/public.drm_certificate.proto.
|
||||||
|
class SignerPublicKey {
|
||||||
|
public:
|
||||||
|
SignerPublicKey() = default;
|
||||||
|
virtual ~SignerPublicKey() = default;
|
||||||
|
SignerPublicKey(const SignerPublicKey&) = delete;
|
||||||
|
SignerPublicKey& operator=(const SignerPublicKey&) = delete;
|
||||||
|
|
||||||
|
// Verify message using |signer_public_key_|.
|
||||||
|
virtual bool VerifySignature(const std::string& message,
|
||||||
|
const std::string& signature) const = 0;
|
||||||
|
|
||||||
|
// A factory method to create a SignerPublicKey. The |algorithm| is used to
|
||||||
|
// create an appropriate SignerPublicKey for the key type.
|
||||||
|
// The returned pointer is a nullptr if the key cannot be deserialized.
|
||||||
|
static std::unique_ptr<SignerPublicKey> Create(
|
||||||
|
const std::string& signer_public_key,
|
||||||
|
DrmCertificate::Algorithm algorithm);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
#endif // COMMON_SIGNER_PUBLIC_KEY_H_
|
||||||
78
common/signer_public_key_test.cc
Normal file
78
common/signer_public_key_test.cc
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/signer_public_key.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "common/ec_key.h"
|
||||||
|
#include "common/ec_test_keys.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
#include "common/rsa_test_keys.h"
|
||||||
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
static const char kMessage[] = "The rain in Spain falls mainly in the blank?";
|
||||||
|
|
||||||
|
class SignerPublicKeyTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
RsaTestKeys rsa_test_keys_;
|
||||||
|
ECTestKeys ec_test_keys_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(SignerPublicKeyTest, RSA) {
|
||||||
|
std::unique_ptr<RsaPrivateKey> private_key(
|
||||||
|
RsaPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits()));
|
||||||
|
|
||||||
|
std::string signature;
|
||||||
|
ASSERT_TRUE(private_key->GenerateSignature(kMessage, &signature));
|
||||||
|
|
||||||
|
std::unique_ptr<SignerPublicKey> public_key = SignerPublicKey::Create(
|
||||||
|
rsa_test_keys_.public_test_key_1_3072_bits(), DrmCertificate::RSA);
|
||||||
|
ASSERT_NE(public_key, nullptr);
|
||||||
|
EXPECT_TRUE(public_key->VerifySignature(kMessage, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SignerPublicKeyTest, ECC) {
|
||||||
|
std::unique_ptr<ECPrivateKey> private_key =
|
||||||
|
ECPrivateKey::Create(ec_test_keys_.private_test_key_1_secp521r1());
|
||||||
|
|
||||||
|
std::string signature;
|
||||||
|
ASSERT_TRUE(private_key->GenerateSignature(kMessage, &signature));
|
||||||
|
|
||||||
|
std::unique_ptr<SignerPublicKey> public_key =
|
||||||
|
SignerPublicKey::Create(ec_test_keys_.public_test_key_1_secp521r1(),
|
||||||
|
DrmCertificate::ECC_SECP521R1);
|
||||||
|
ASSERT_NE(public_key, nullptr);
|
||||||
|
EXPECT_TRUE(public_key->VerifySignature(kMessage, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SignerPublicKeyTest, IncorrectAlgorithm) {
|
||||||
|
std::unique_ptr<SignerPublicKey> rsa_public_key =
|
||||||
|
SignerPublicKey::Create(rsa_test_keys_.public_test_key_1_3072_bits(),
|
||||||
|
DrmCertificate::ECC_SECP521R1);
|
||||||
|
ASSERT_EQ(rsa_public_key, nullptr);
|
||||||
|
|
||||||
|
std::unique_ptr<SignerPublicKey> ec_public_key = SignerPublicKey::Create(
|
||||||
|
ec_test_keys_.public_test_key_1_secp521r1(), DrmCertificate::RSA);
|
||||||
|
ASSERT_EQ(ec_public_key, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SignerPublicKeyTest, BadKey) {
|
||||||
|
std::unique_ptr<SignerPublicKey> rsa_public_key =
|
||||||
|
SignerPublicKey::Create("GobbletyGook", DrmCertificate::RSA);
|
||||||
|
ASSERT_EQ(rsa_public_key, nullptr);
|
||||||
|
|
||||||
|
std::unique_ptr<SignerPublicKey> ec_public_key = SignerPublicKey::Create(
|
||||||
|
"MoreGobbletyGook", DrmCertificate::ECC_SECP521R1);
|
||||||
|
ASSERT_EQ(ec_public_key, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
@@ -26,7 +26,7 @@ uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version) {
|
|||||||
|
|
||||||
using crypto_util::kSigningKeySizeBytes;
|
using crypto_util::kSigningKeySizeBytes;
|
||||||
std::string GetClientSigningKey(const std::string& derived_key,
|
std::string GetClientSigningKey(const std::string& derived_key,
|
||||||
ProtocolVersion protocol_version) {
|
ProtocolVersion protocol_version) {
|
||||||
if (protocol_version == VERSION_2_0) {
|
if (protocol_version == VERSION_2_0) {
|
||||||
DCHECK(derived_key.size() >= kSigningKeySizeBytes);
|
DCHECK(derived_key.size() >= kSigningKeySizeBytes);
|
||||||
return derived_key.substr(0, kSigningKeySizeBytes);
|
return derived_key.substr(0, kSigningKeySizeBytes);
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "base/macros.h"
|
|
||||||
#include "protos/public/license_protocol.pb.h"
|
#include "protos/public/license_protocol.pb.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
@@ -44,7 +43,7 @@ uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version);
|
|||||||
// keys are returned. Keys that are 256 bits in length are returned
|
// keys are returned. Keys that are 256 bits in length are returned
|
||||||
// in there entirety, version 2.0 keys.
|
// in there entirety, version 2.0 keys.
|
||||||
std::string GetClientSigningKey(const std::string& derived_key,
|
std::string GetClientSigningKey(const std::string& derived_key,
|
||||||
ProtocolVersion protocol_version);
|
ProtocolVersion protocol_version);
|
||||||
|
|
||||||
// Returns the server portion of the derived_key. The server portion
|
// Returns the server portion of the derived_key. The server portion
|
||||||
// is the first 256 bits of the key.
|
// is the first 256 bits of the key.
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ const char* kFrontKeyHex =
|
|||||||
const char* kBackKeyHex =
|
const char* kBackKeyHex =
|
||||||
"0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d";
|
"0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d";
|
||||||
|
|
||||||
std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) {
|
std::string GenerateDerivedKey(
|
||||||
|
widevine::ProtocolVersion protocol_version) {
|
||||||
if (protocol_version == widevine::VERSION_2_0) {
|
if (protocol_version == widevine::VERSION_2_0) {
|
||||||
return absl::HexStringToBytes(kFrontKeyHex);
|
return absl::HexStringToBytes(kFrontKeyHex);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ enum StatusCode {
|
|||||||
// instead for those errors).
|
// instead for those errors).
|
||||||
PERMISSION_DENIED = 7,
|
PERMISSION_DENIED = 7,
|
||||||
|
|
||||||
|
// The operation was rejected because the system is not in a state
|
||||||
|
// required for the operation's execution. For example, the directory
|
||||||
|
// to be deleted is non-empty, an rmdir operation is applied to
|
||||||
|
// a non-directory, etc.
|
||||||
|
FAILED_PRECONDITION = 9,
|
||||||
|
|
||||||
// Operation is not implemented or not supported/enabled in this service.
|
// Operation is not implemented or not supported/enabled in this service.
|
||||||
UNIMPLEMENTED = 12,
|
UNIMPLEMENTED = 12,
|
||||||
|
|
||||||
@@ -70,7 +76,8 @@ class Status {
|
|||||||
const std::string& error_message)
|
const std::string& error_message)
|
||||||
: error_space_(e), status_code_(c), error_message_(error_message) {}
|
: error_space_(e), status_code_(c), error_message_(error_message) {}
|
||||||
|
|
||||||
Status(const util::ErrorSpace* e, int error, const std::string& error_message)
|
Status(const util::ErrorSpace* e, int error,
|
||||||
|
const std::string& error_message)
|
||||||
: error_space_(e), status_code_(error), error_message_(error_message) {}
|
: error_space_(e), status_code_(error), error_message_(error_message) {}
|
||||||
|
|
||||||
bool ok() const { return status_code_ == error::OK; }
|
bool ok() const { return status_code_ == error::OK; }
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace string_util {
|
namespace string_util {
|
||||||
|
|
||||||
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) {
|
Status BitsetStringToBinaryString(const std::string& bitset,
|
||||||
|
std::string* output) {
|
||||||
if (output == nullptr) {
|
if (output == nullptr) {
|
||||||
return Status(error::INTERNAL, "output is nullptr.");
|
return Status(error::INTERNAL, "output is nullptr.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ namespace string_util {
|
|||||||
|
|
||||||
// Converts std::string representation of a bitset to its binary equivalent string.
|
// Converts std::string representation of a bitset to its binary equivalent string.
|
||||||
// For example, converts "01110100011001010111001101110100" to "test".
|
// For example, converts "01110100011001010111001101110100" to "test".
|
||||||
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output);
|
Status BitsetStringToBinaryString(const std::string& bitset,
|
||||||
|
std::string* output);
|
||||||
|
|
||||||
} // namespace string_util
|
} // namespace string_util
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
static const unsigned char kTestRootCertificate[] = {
|
static const unsigned char kTestRootCertificate[] = {
|
||||||
0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
|
0x0a, 0x9b, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
|
||||||
0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5,
|
0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5,
|
||||||
0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78,
|
0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78,
|
||||||
0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f,
|
0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f,
|
||||||
@@ -47,42 +47,43 @@ static const unsigned char kTestRootCertificate[] = {
|
|||||||
0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33,
|
0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33,
|
||||||
0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67,
|
0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67,
|
||||||
0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02,
|
0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02,
|
||||||
0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a,
|
0x03, 0x01, 0x00, 0x01, 0x48, 0x01, 0x12, 0x80, 0x03, 0x45, 0x3d, 0x03,
|
||||||
0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35,
|
0x60, 0xd1, 0x13, 0x9e, 0xcd, 0x69, 0x5f, 0xd5, 0xa7, 0x62, 0x12, 0x28,
|
||||||
0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe,
|
0x49, 0x4a, 0x73, 0x05, 0x1b, 0xf3, 0xd4, 0x4e, 0x54, 0x3f, 0x5f, 0x43,
|
||||||
0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49,
|
0x2c, 0x17, 0x56, 0xbf, 0xc3, 0xb9, 0xe1, 0xb8, 0xb7, 0xc7, 0xd6, 0x52,
|
||||||
0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6,
|
0x8e, 0xfb, 0x1c, 0x24, 0x9b, 0x84, 0x13, 0x08, 0xec, 0x0b, 0xd9, 0xfa,
|
||||||
0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58,
|
0xe3, 0x9d, 0x37, 0x55, 0x72, 0x69, 0xfc, 0x39, 0x50, 0xbb, 0x49, 0x86,
|
||||||
0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce,
|
0xe2, 0x85, 0x01, 0x20, 0x3e, 0x08, 0x2c, 0xdc, 0xee, 0x36, 0x04, 0xff,
|
||||||
0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27,
|
0x24, 0x50, 0x88, 0x17, 0xfb, 0x8e, 0x86, 0xf6, 0xc5, 0xd6, 0xc5, 0x5b,
|
||||||
0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5,
|
0x32, 0xe1, 0x3f, 0xff, 0x9c, 0x23, 0xd8, 0x84, 0x61, 0x26, 0x1d, 0x46,
|
||||||
0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c,
|
0x82, 0x99, 0x3f, 0x1a, 0x5a, 0xc7, 0xd5, 0x97, 0x6d, 0xdb, 0x3a, 0x80,
|
||||||
0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e,
|
0xef, 0x80, 0x2d, 0x11, 0x06, 0xf2, 0x14, 0x2b, 0x40, 0x61, 0x6f, 0x91,
|
||||||
0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12,
|
0xea, 0x8a, 0xc5, 0xde, 0xad, 0x68, 0x31, 0xda, 0x11, 0x82, 0x11, 0x2b,
|
||||||
0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54,
|
0x19, 0x3c, 0x89, 0xbc, 0x4a, 0xed, 0x87, 0x44, 0x1b, 0x79, 0xa9, 0x22,
|
||||||
0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8,
|
0xb7, 0x81, 0xb3, 0xa9, 0xa2, 0x9b, 0x77, 0xf9, 0x40, 0x31, 0x4a, 0x9a,
|
||||||
0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1,
|
0x5a, 0x9d, 0x56, 0xf9, 0x81, 0x2f, 0x9b, 0xe1, 0xd1, 0xca, 0xe7, 0xc5,
|
||||||
0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1,
|
0xdc, 0x43, 0x92, 0x96, 0x5a, 0x22, 0x07, 0xcd, 0x0e, 0xec, 0x70, 0xe8,
|
||||||
0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8,
|
0xd7, 0xdb, 0x52, 0xbe, 0x23, 0x23, 0x4c, 0xb8, 0x9e, 0x0a, 0x94, 0x64,
|
||||||
0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4,
|
0xa7, 0xc8, 0xd8, 0x30, 0x78, 0xb9, 0x31, 0x8f, 0x5f, 0x98, 0x71, 0x24,
|
||||||
0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9,
|
0xbd, 0xc2, 0xdc, 0x52, 0xf5, 0x0a, 0xf7, 0x0d, 0x48, 0x58, 0x6b, 0xdd,
|
||||||
0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd,
|
0xa9, 0x95, 0xc6, 0x03, 0x13, 0x39, 0x87, 0xf8, 0x7a, 0x0e, 0x32, 0xd5,
|
||||||
0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9,
|
0x77, 0x46, 0x59, 0x12, 0xae, 0x52, 0xd1, 0x48, 0xdf, 0x4c, 0xdd, 0xbf,
|
||||||
0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f,
|
0xd7, 0xcc, 0x38, 0x1e, 0x07, 0x35, 0x3f, 0x1b, 0xe5, 0xa4, 0x2a, 0x01,
|
||||||
0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92,
|
0x77, 0x22, 0xe6, 0x02, 0x90, 0x4d, 0x8b, 0x02, 0x75, 0x07, 0x36, 0xb0,
|
||||||
0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15,
|
0xfa, 0x82, 0xf6, 0x7e, 0x74, 0xde, 0xba, 0xfa, 0x0e, 0x5a, 0x9a, 0x70,
|
||||||
0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0,
|
0x50, 0xf4, 0x42, 0x05, 0xb1, 0xca, 0xc7, 0x18, 0xb7, 0x76, 0xff, 0x04,
|
||||||
0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9,
|
0x8e, 0x2e, 0xe3, 0x44, 0x41, 0x38, 0x16, 0xa4, 0x34, 0x84, 0x66, 0x72,
|
||||||
0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1,
|
0x0f, 0xc8, 0x2f, 0x9c, 0xe1, 0x5f, 0xe6, 0x35, 0x79, 0x64, 0x67, 0xa0,
|
||||||
0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2,
|
0x53, 0x89, 0x4c, 0x51, 0xc8, 0x34, 0x6e, 0x70, 0xba, 0xfe, 0xdd, 0xca,
|
||||||
0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c,
|
0xc2, 0xc6, 0x91, 0x8b, 0x08, 0x5e, 0x25, 0x96, 0xd0, 0x0d, 0xe7, 0xee,
|
||||||
0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36,
|
0x25, 0x92, 0x39, 0xa3, 0xba, 0xa4, 0x0b, 0xab, 0xa4, 0x2e, 0x16, 0xfc,
|
||||||
0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35,
|
0xad, 0xed, 0xcf, 0x12, 0xda, 0x9b, 0xe9, 0x67, 0x4d, 0xb2, 0x4e, 0xe9,
|
||||||
0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7,
|
0xb3, 0xe8, 0x53, 0xc8, 0x5a, 0xc7, 0xbd, 0x69, 0xa7, 0x12, 0x4e, 0x43,
|
||||||
0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44};
|
0x20, 0x62, 0x34, 0xb0, 0xbd, 0xb2, 0xea, 0x95, 0xf6,
|
||||||
|
};
|
||||||
|
|
||||||
const unsigned char kTestIntermediateCertificate[] = {
|
const unsigned char kTestIntermediateCertificate[] = {
|
||||||
0x0a, 0xaf, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
|
0x0a, 0xb1, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
|
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
|
||||||
0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
|
0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
|
||||||
0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a,
|
0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a,
|
||||||
@@ -107,42 +108,92 @@ const unsigned char kTestIntermediateCertificate[] = {
|
|||||||
0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d,
|
0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d,
|
||||||
0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb,
|
0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb,
|
||||||
0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01,
|
0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01,
|
||||||
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40,
|
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x48, 0x01, 0x12, 0x80, 0x03, 0x06,
|
||||||
0xa8, 0xd0, 0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf,
|
0xe2, 0xc2, 0x94, 0x0e, 0x81, 0x87, 0x59, 0xe3, 0xe8, 0x15, 0x7f, 0xc6,
|
||||||
0xc9, 0x26, 0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4,
|
0xff, 0x6b, 0xc8, 0x7e, 0x0c, 0xd9, 0x9b, 0x40, 0x34, 0x22, 0x44, 0x00,
|
||||||
0x33, 0x8e, 0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b,
|
0xdf, 0x0e, 0x9e, 0xcd, 0xb9, 0x1d, 0x3d, 0xfe, 0x5a, 0xb9, 0x28, 0xdc,
|
||||||
0xc3, 0x4f, 0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51,
|
0x94, 0x43, 0xc4, 0x1c, 0x66, 0xa9, 0x8a, 0xa4, 0x61, 0xdf, 0x8a, 0xf3,
|
||||||
0xce, 0x45, 0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4,
|
0x7c, 0xf0, 0xbe, 0x66, 0xe9, 0xdf, 0x65, 0x93, 0x6c, 0xc7, 0xb5, 0x1a,
|
||||||
0x5a, 0x02, 0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9,
|
0x76, 0x07, 0x40, 0xde, 0xa1, 0xc5, 0x40, 0xde, 0xac, 0x5b, 0x9f, 0x32,
|
||||||
0xee, 0xc9, 0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb,
|
0xbb, 0xd4, 0xf2, 0x09, 0x13, 0x20, 0xbe, 0xee, 0xf4, 0xb5, 0xb0, 0xec,
|
||||||
0x1f, 0x7a, 0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9,
|
0xeb, 0x1e, 0xfa, 0x03, 0x1b, 0x9d, 0x5a, 0xa0, 0x2f, 0x71, 0x1a, 0x76,
|
||||||
0x26, 0xbe, 0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25,
|
0xe7, 0x6f, 0x71, 0x7d, 0x3a, 0x7d, 0x8c, 0x46, 0xaf, 0x93, 0x94, 0x47,
|
||||||
0xdb, 0x85, 0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba,
|
0x27, 0xec, 0x1b, 0x1e, 0xd7, 0x8c, 0x7c, 0xec, 0x42, 0xaf, 0x55, 0x82,
|
||||||
0xb9, 0x3d, 0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80,
|
0x3b, 0x6d, 0x07, 0x24, 0xb3, 0xfa, 0x2d, 0x1e, 0x12, 0x02, 0x94, 0x04,
|
||||||
0x12, 0x72, 0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98,
|
0x23, 0xeb, 0xf3, 0x74, 0x04, 0x7e, 0x2a, 0x7f, 0x00, 0x34, 0x2b, 0x5c,
|
||||||
0xab, 0x73, 0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1,
|
0x5b, 0x10, 0xe7, 0x36, 0x52, 0xde, 0x9f, 0x56, 0x10, 0xe3, 0x0b, 0xa5,
|
||||||
0x75, 0x79, 0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98,
|
0x29, 0x85, 0xa5, 0x95, 0xed, 0xf5, 0x39, 0x0a, 0x03, 0x51, 0x29, 0x64,
|
||||||
0xe8, 0x40, 0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f,
|
0xa1, 0x4f, 0x38, 0xde, 0x3b, 0x4d, 0x0a, 0xf3, 0x7e, 0x37, 0x14, 0xce,
|
||||||
0x6f, 0x23, 0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30,
|
0xdf, 0x9d, 0x86, 0x16, 0xad, 0x62, 0xa8, 0xf8, 0xa7, 0xc2, 0xa4, 0xc1,
|
||||||
0xaa, 0xcf, 0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b,
|
0xe2, 0xd6, 0x40, 0xa4, 0x7b, 0x20, 0x1b, 0x6d, 0x7c, 0x97, 0x0b, 0x73,
|
||||||
0x7f, 0x91, 0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a,
|
0x85, 0xbf, 0xdb, 0xc3, 0xa1, 0xf5, 0xd4, 0xb7, 0x95, 0xf2, 0xe7, 0x10,
|
||||||
0x98, 0xad, 0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e,
|
0x77, 0xc6, 0x82, 0xb2, 0x68, 0x24, 0x31, 0xdc, 0x69, 0x43, 0x56, 0xf5,
|
||||||
0x6e, 0x9d, 0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04,
|
0x76, 0x20, 0x0a, 0x82, 0x1a, 0x98, 0xb3, 0x02, 0x0f, 0x67, 0xcd, 0x4f,
|
||||||
0x4f, 0x32, 0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52,
|
0xab, 0x43, 0x44, 0xbd, 0xdb, 0x07, 0xd3, 0xff, 0x8b, 0x68, 0x33, 0x24,
|
||||||
0xfc, 0xcc, 0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa,
|
0x35, 0xe5, 0xc6, 0x1a, 0x94, 0x14, 0x4f, 0x40, 0xef, 0x92, 0xfb, 0xfd,
|
||||||
0xb3, 0x77, 0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d,
|
0x72, 0x15, 0xd4, 0x10, 0x60, 0x22, 0x3e, 0x60, 0x49, 0x3d, 0x58, 0xc6,
|
||||||
0x3c, 0x74, 0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f,
|
0x3d, 0x28, 0x70, 0x55, 0x32, 0xd5, 0x78, 0x03, 0x51, 0xff, 0xd6, 0x4f,
|
||||||
0x13, 0x97, 0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94,
|
0x4e, 0x89, 0x0e, 0x50, 0x85, 0x6e, 0x1c, 0x6a, 0x5f, 0x11, 0xd0, 0xf5,
|
||||||
0x3b, 0x31, 0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b,
|
0xee, 0xe5, 0x1c, 0xa8, 0xb2, 0xdb, 0x26, 0x93, 0xb1, 0xe2, 0xc1, 0x05,
|
||||||
0x65, 0xe9, 0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d,
|
0xe0, 0x7f, 0x16, 0xe7, 0x9c, 0xcf, 0xe7, 0xb7, 0x7e, 0xaa, 0x96, 0x21,
|
||||||
0x0a, 0xfd, 0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4,
|
0x64, 0x39, 0x6d, 0x7a, 0xdc, 0x70, 0x6e, 0xc8, 0xf5, 0x44, 0x2e, 0x9f,
|
||||||
0x83, 0xcf, 0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16,
|
0xc1, 0xe9, 0x46, 0x8c, 0x1b, 0x58, 0xec, 0x73, 0x1b, 0x9a, 0x04, 0xcb,
|
||||||
0x7b, 0x66, 0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d,
|
0x68, 0x58, 0x21, 0x0e, 0xd6, 0xd7, 0x7a, 0x2b, 0x60, 0x02, 0x20, 0x7b,
|
||||||
0x94, 0x5d, 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b,
|
0x85, 0xe5, 0x84, 0x2c, 0x5f, 0x24, 0x90, 0x2d, 0xc5, 0x19, 0xea, 0xf3,
|
||||||
0x55, 0xf8, 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
|
0x91, 0x78, 0xc2, 0xa7, 0x36, 0x5a, 0x72, 0x64, 0x45, 0x13, 0x49,
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char kTestIntermediateCertificateWithECKey[] = {
|
||||||
|
0x0a, 0x9a, 0x01, 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, 0x78, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a,
|
||||||
|
0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00,
|
||||||
|
0x22, 0x03, 0x62, 0x00, 0x04, 0xb0, 0x50, 0x2a, 0x13, 0x20, 0x3e, 0x66,
|
||||||
|
0x67, 0xdf, 0x11, 0x2a, 0xbc, 0x0f, 0x76, 0x69, 0x4b, 0xa1, 0x88, 0xec,
|
||||||
|
0xb8, 0x71, 0xcf, 0xc9, 0xbb, 0xd2, 0xbc, 0xf8, 0x53, 0xfd, 0x8b, 0x8d,
|
||||||
|
0x14, 0x6f, 0xda, 0xea, 0x60, 0x51, 0xc8, 0xd3, 0x3a, 0xd4, 0x75, 0x81,
|
||||||
|
0x05, 0x16, 0x03, 0x0b, 0xcb, 0x33, 0x2c, 0x8b, 0xe6, 0xd3, 0x57, 0x6c,
|
||||||
|
0xfb, 0x81, 0x4b, 0xfe, 0x79, 0x56, 0xf7, 0x6a, 0x2b, 0xca, 0xa7, 0x04,
|
||||||
|
0xe9, 0x37, 0xd6, 0x49, 0xe5, 0x8b, 0x2c, 0xe9, 0x8e, 0xcd, 0xe7, 0xe3,
|
||||||
|
0xc9, 0xf5, 0x4c, 0x90, 0x82, 0x5f, 0xf0, 0x53, 0xd2, 0xa4, 0x1a, 0xb3,
|
||||||
|
0x53, 0x3d, 0xa7, 0xa7, 0xfd, 0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x48,
|
||||||
|
0x03, 0x12, 0x80, 0x03, 0x42, 0x90, 0xc4, 0x87, 0x0b, 0x55, 0x78, 0xb5,
|
||||||
|
0x25, 0x64, 0x23, 0xf2, 0x6a, 0x28, 0x7b, 0x1e, 0x12, 0xeb, 0x94, 0x08,
|
||||||
|
0x4f, 0xce, 0x6b, 0x53, 0x35, 0xda, 0xa6, 0xf3, 0x90, 0x3b, 0x1b, 0xa8,
|
||||||
|
0x2f, 0x17, 0x8e, 0x09, 0x12, 0x4b, 0xc6, 0x10, 0xfc, 0x8a, 0x63, 0xda,
|
||||||
|
0xf6, 0x7e, 0x18, 0x3e, 0x49, 0x4c, 0x85, 0x5b, 0x2c, 0xcb, 0x09, 0x67,
|
||||||
|
0x3b, 0xd3, 0xf3, 0x90, 0xe7, 0x4e, 0x06, 0x2f, 0x25, 0xbe, 0x22, 0x7f,
|
||||||
|
0xd6, 0x5c, 0xd5, 0xda, 0xac, 0x60, 0x29, 0x83, 0x53, 0x54, 0x73, 0x0d,
|
||||||
|
0x96, 0xca, 0x50, 0x6e, 0x45, 0xd6, 0x3c, 0xe6, 0xab, 0xf7, 0xe8, 0x69,
|
||||||
|
0x9e, 0xe2, 0x8e, 0xfd, 0x46, 0x11, 0x40, 0x9d, 0xfc, 0xd8, 0x2d, 0xe0,
|
||||||
|
0x94, 0xbc, 0x05, 0x74, 0x3c, 0x0a, 0xdd, 0x64, 0x37, 0x93, 0x9d, 0x5a,
|
||||||
|
0x08, 0xfe, 0x5f, 0xae, 0xa3, 0xc6, 0x48, 0xbd, 0x8a, 0x4e, 0x3c, 0xac,
|
||||||
|
0x7c, 0x66, 0xad, 0xc4, 0x7b, 0x7b, 0x89, 0xd9, 0xae, 0xf5, 0x8d, 0xf3,
|
||||||
|
0x0e, 0x3b, 0x1c, 0xb6, 0xf0, 0xff, 0x52, 0x22, 0xbc, 0xdd, 0x1e, 0xe5,
|
||||||
|
0x90, 0xe1, 0x09, 0xe2, 0x65, 0x42, 0x70, 0x4b, 0xfa, 0xf0, 0x41, 0x41,
|
||||||
|
0x53, 0xa2, 0x2c, 0x32, 0xc4, 0x1a, 0x42, 0x0d, 0xbe, 0x8d, 0x5b, 0x14,
|
||||||
|
0xae, 0x8f, 0xca, 0x85, 0xda, 0xfb, 0xe1, 0x25, 0x71, 0xc6, 0x8a, 0x3c,
|
||||||
|
0xe1, 0x99, 0x09, 0x30, 0x9d, 0xa7, 0xec, 0x10, 0x7b, 0x43, 0xee, 0xf8,
|
||||||
|
0xa5, 0x58, 0x9d, 0xd7, 0x31, 0x6a, 0x22, 0x45, 0xf5, 0x0c, 0x30, 0xb2,
|
||||||
|
0x77, 0x05, 0x13, 0x10, 0xfd, 0xc1, 0xf9, 0x13, 0xc2, 0x88, 0xc8, 0x71,
|
||||||
|
0xd9, 0x14, 0x5f, 0xc9, 0xde, 0x96, 0xe7, 0x55, 0xb9, 0x4a, 0xb0, 0x18,
|
||||||
|
0x22, 0x17, 0x9f, 0x95, 0xe2, 0xa7, 0x66, 0x9b, 0xfd, 0x38, 0xf9, 0x5c,
|
||||||
|
0xa0, 0xaa, 0xf4, 0x60, 0xee, 0x00, 0x53, 0x87, 0x29, 0x63, 0x53, 0x55,
|
||||||
|
0xfb, 0x32, 0x7a, 0x80, 0x56, 0xea, 0xaa, 0x95, 0x22, 0x08, 0x9e, 0x25,
|
||||||
|
0x53, 0x50, 0xb4, 0xd0, 0x07, 0x3c, 0x29, 0x3f, 0x03, 0xab, 0x68, 0xf5,
|
||||||
|
0xa5, 0xc6, 0xd2, 0x73, 0xf6, 0xee, 0xa2, 0x6c, 0xec, 0xd4, 0xf3, 0x20,
|
||||||
|
0xdc, 0x56, 0x00, 0x3d, 0xea, 0x57, 0x14, 0xc7, 0x90, 0x86, 0x82, 0x1b,
|
||||||
|
0x14, 0x57, 0x68, 0xec, 0x24, 0x0e, 0x8d, 0x6b, 0xcc, 0x5f, 0x7a, 0x53,
|
||||||
|
0xac, 0x60, 0x20, 0x5f, 0xe7, 0x79, 0xb6, 0x2c, 0xfb, 0x23, 0xa3, 0x43,
|
||||||
|
0x91, 0x0f, 0x53, 0x38, 0x0e, 0xcc, 0x27, 0xaf, 0x57, 0x01, 0x26, 0xd8,
|
||||||
|
0x01, 0x41, 0x27, 0x63, 0xca, 0x9f, 0xf5, 0xa7, 0x43, 0x26, 0x74, 0x59,
|
||||||
|
0xec, 0xce, 0x71, 0x09, 0x0d, 0xda, 0x5d, 0x63, 0xef, 0xfd, 0x6e, 0x92,
|
||||||
|
0x53, 0x12, 0xbc, 0x6a, 0x5b, 0x4d, 0x4a, 0x43, 0x04, 0x5d, 0x8e, 0x93,
|
||||||
|
0xd7, 0x89, 0x21, 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
const unsigned char kTestUserDrmCertificate[] = {
|
const unsigned char kTestUserDrmCertificate[] = {
|
||||||
0x0a, 0xc1, 0x02, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42,
|
0x0a, 0xc3, 0x02, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42,
|
||||||
0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18,
|
0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18,
|
||||||
0x91, 0xab, 0x4b, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
|
0x91, 0xab, 0x4b, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
|
||||||
0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51,
|
0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51,
|
||||||
@@ -169,89 +220,282 @@ const unsigned char kTestUserDrmCertificate[] = {
|
|||||||
0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01,
|
0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01,
|
||||||
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65,
|
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65,
|
||||||
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
|
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
0x12, 0x80, 0x02, 0x62, 0xd5, 0x8b, 0xb6, 0x52, 0x94, 0xcb, 0x25, 0xba,
|
0x48, 0x01, 0x12, 0x80, 0x02, 0x23, 0x61, 0xfb, 0xd0, 0xf4, 0xcf, 0xf2,
|
||||||
0x68, 0x44, 0xdc, 0x6f, 0x03, 0xf6, 0x24, 0xc5, 0xba, 0x46, 0xd1, 0xa1,
|
0x58, 0xd7, 0xb0, 0x79, 0x4e, 0x4e, 0xf3, 0x2c, 0x83, 0x63, 0x34, 0x6c,
|
||||||
0x83, 0xe7, 0xaf, 0x94, 0x96, 0x8b, 0x57, 0x89, 0xd6, 0xa0, 0x99, 0x6f,
|
0x49, 0x80, 0xdd, 0x85, 0xf4, 0xa5, 0x23, 0x89, 0x95, 0x0c, 0x8f, 0xf6,
|
||||||
0xed, 0xac, 0xfe, 0x6c, 0x9d, 0x80, 0x1c, 0xae, 0x34, 0xda, 0x49, 0x4f,
|
0xc6, 0xdc, 0x90, 0x8b, 0x83, 0xd3, 0x0b, 0x1c, 0x34, 0xd2, 0xa0, 0x08,
|
||||||
0x10, 0x22, 0x3c, 0xdd, 0x77, 0xa0, 0x9a, 0x79, 0x73, 0x68, 0x66, 0xa4,
|
0xdc, 0x05, 0x76, 0x8f, 0xff, 0xa3, 0x2e, 0xf8, 0x93, 0x9e, 0xe6, 0xf3,
|
||||||
0x6d, 0x1e, 0x82, 0xbf, 0xce, 0x06, 0x1a, 0x83, 0xcd, 0xa3, 0xed, 0x91,
|
0x62, 0x0f, 0x70, 0x1c, 0x31, 0x15, 0xbb, 0x98, 0xf4, 0xa6, 0x22, 0x2c,
|
||||||
0xbe, 0xb1, 0xfe, 0xf3, 0xde, 0x63, 0x96, 0xd5, 0x24, 0x44, 0x46, 0x94,
|
0x90, 0x59, 0xc2, 0x16, 0x48, 0xe0, 0x5a, 0xb8, 0x94, 0x6f, 0xde, 0x80,
|
||||||
0x7f, 0xc2, 0x14, 0x19, 0x42, 0x08, 0x64, 0xef, 0x93, 0x81, 0x7a, 0x54,
|
0xaf, 0x83, 0x8e, 0x77, 0x6a, 0xa4, 0xf4, 0x9b, 0xf8, 0x76, 0xd1, 0x1b,
|
||||||
0x8b, 0x6e, 0xd9, 0xf5, 0x14, 0x88, 0x6c, 0x39, 0x6f, 0x0f, 0x70, 0x91,
|
0x6d, 0x87, 0x85, 0x35, 0xd9, 0xd0, 0x62, 0x55, 0xfe, 0x11, 0xed, 0x4a,
|
||||||
0x97, 0xd4, 0x24, 0x73, 0x9d, 0x12, 0x7a, 0xc8, 0x83, 0xd7, 0x2b, 0xc7,
|
0x6c, 0xc9, 0x14, 0x67, 0x72, 0xb6, 0x46, 0x56, 0xbc, 0x81, 0xac, 0xe6,
|
||||||
0xb7, 0xe1, 0x20, 0x6c, 0x28, 0x11, 0x6f, 0x56, 0x82, 0xf6, 0x1c, 0x4f,
|
0xf0, 0x7a, 0x0e, 0x57, 0x95, 0x4d, 0x53, 0xf5, 0x33, 0x2e, 0xa5, 0x7e,
|
||||||
0x2d, 0x51, 0x0f, 0xd6, 0xd4, 0x14, 0xea, 0xac, 0x28, 0x66, 0xeb, 0x37,
|
0x71, 0x8e, 0x04, 0x64, 0x50, 0x88, 0x6b, 0xb9, 0x6e, 0xbc, 0x6b, 0x74,
|
||||||
0xca, 0x00, 0x49, 0xff, 0xed, 0x8e, 0x8c, 0x3e, 0x4b, 0x9b, 0x12, 0x0e,
|
0xfc, 0x69, 0xa3, 0x81, 0x30, 0x1f, 0xac, 0x9d, 0x7b, 0xa0, 0xf5, 0x7f,
|
||||||
0xbf, 0xcd, 0xb7, 0xe6, 0xed, 0xd6, 0x1f, 0x88, 0xe8, 0x99, 0x68, 0x1a,
|
0x42, 0xfd, 0x14, 0xca, 0x89, 0x5b, 0xb0, 0xcd, 0xa2, 0x4b, 0xef, 0xcf,
|
||||||
0xf8, 0xbb, 0xa2, 0x33, 0xfa, 0xb6, 0x21, 0xdf, 0xba, 0x24, 0x5c, 0x19,
|
0x84, 0x8f, 0xe8, 0xe4, 0xf7, 0xd2, 0x63, 0xe2, 0x95, 0x94, 0x45, 0xd5,
|
||||||
0xa2, 0xe7, 0x6f, 0x61, 0x90, 0x78, 0x21, 0xca, 0x2f, 0x84, 0xab, 0x9f,
|
0xc2, 0xe3, 0x99, 0xfc, 0x34, 0xcb, 0x6a, 0x15, 0x74, 0x6e, 0x16, 0xe3,
|
||||||
0xff, 0x37, 0x14, 0x33, 0x83, 0x43, 0x98, 0xeb, 0xa9, 0x88, 0xde, 0xad,
|
0x6f, 0x8e, 0xe7, 0x9b, 0x01, 0xed, 0x7f, 0xf8, 0x90, 0xc6, 0x87, 0xf4,
|
||||||
0x3a, 0xd9, 0xe2, 0x5c, 0x26, 0xd3, 0x95, 0x72, 0xba, 0x8c, 0x77, 0xdf,
|
0x9e, 0x45, 0x64, 0x09, 0xf9, 0xaa, 0x46, 0xe4, 0x83, 0x3b, 0x4f, 0x36,
|
||||||
0x90, 0x67, 0x4e, 0xbc, 0xda, 0x83, 0x09, 0x22, 0x70, 0x51, 0x84, 0x70,
|
0xdb, 0x32, 0x72, 0x00, 0xcf, 0x3c, 0x4c, 0x41, 0x67, 0x59, 0xf2, 0x93,
|
||||||
0x31, 0x25, 0x8b, 0xae, 0x5e, 0x19, 0xba, 0x97, 0xd7, 0x1f, 0x6a, 0xd7,
|
0xff, 0x4e, 0x07, 0x22, 0x6e, 0x5a, 0x03, 0xf5, 0xe1, 0x48, 0x72, 0x9d,
|
||||||
0x95, 0xcf, 0xde, 0x8f, 0x93, 0x69, 0x88, 0x11, 0xbe, 0x8c, 0x6a, 0xfb,
|
0x2f, 0xfc, 0xcd, 0x38, 0x5f, 0x2d, 0x69, 0x47, 0xd3, 0xa8, 0x09, 0x8e,
|
||||||
0x3c, 0x13, 0x87, 0x0e, 0x6c, 0xa5, 0xa0, 0x1a, 0xb5, 0x05, 0x0a, 0xaf,
|
0xd5, 0x9b, 0x24, 0x45, 0x43, 0x08, 0xca, 0xc6, 0xed, 0x1a, 0xb7, 0x05,
|
||||||
0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
|
0x0a, 0xb1, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||||
0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18, 0xb2, 0x92,
|
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
|
||||||
0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
|
0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
|
||||||
0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40,
|
0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a,
|
||||||
0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7,
|
0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd,
|
||||||
0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56,
|
0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67,
|
||||||
0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e,
|
0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d,
|
||||||
0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34,
|
0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f,
|
||||||
0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31,
|
0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f,
|
||||||
0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e,
|
0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf,
|
||||||
0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39,
|
0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73,
|
||||||
0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2,
|
0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5,
|
||||||
0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54,
|
0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13,
|
||||||
0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3,
|
0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad,
|
||||||
0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71,
|
0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57,
|
||||||
0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96,
|
0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24,
|
||||||
0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a,
|
0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06,
|
||||||
0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c,
|
0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61,
|
||||||
0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f,
|
0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8,
|
||||||
0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca,
|
0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb,
|
||||||
0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77,
|
0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b,
|
||||||
0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27,
|
0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b,
|
||||||
0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c,
|
0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d,
|
||||||
0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c,
|
0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb,
|
||||||
0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x28, 0xd2,
|
0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01,
|
||||||
0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40, 0xa8, 0xd0,
|
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x48, 0x01, 0x12, 0x80, 0x03, 0x06,
|
||||||
0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf, 0xc9, 0x26,
|
0xe2, 0xc2, 0x94, 0x0e, 0x81, 0x87, 0x59, 0xe3, 0xe8, 0x15, 0x7f, 0xc6,
|
||||||
0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4, 0x33, 0x8e,
|
0xff, 0x6b, 0xc8, 0x7e, 0x0c, 0xd9, 0x9b, 0x40, 0x34, 0x22, 0x44, 0x00,
|
||||||
0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b, 0xc3, 0x4f,
|
0xdf, 0x0e, 0x9e, 0xcd, 0xb9, 0x1d, 0x3d, 0xfe, 0x5a, 0xb9, 0x28, 0xdc,
|
||||||
0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51, 0xce, 0x45,
|
0x94, 0x43, 0xc4, 0x1c, 0x66, 0xa9, 0x8a, 0xa4, 0x61, 0xdf, 0x8a, 0xf3,
|
||||||
0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4, 0x5a, 0x02,
|
0x7c, 0xf0, 0xbe, 0x66, 0xe9, 0xdf, 0x65, 0x93, 0x6c, 0xc7, 0xb5, 0x1a,
|
||||||
0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9, 0xee, 0xc9,
|
0x76, 0x07, 0x40, 0xde, 0xa1, 0xc5, 0x40, 0xde, 0xac, 0x5b, 0x9f, 0x32,
|
||||||
0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb, 0x1f, 0x7a,
|
0xbb, 0xd4, 0xf2, 0x09, 0x13, 0x20, 0xbe, 0xee, 0xf4, 0xb5, 0xb0, 0xec,
|
||||||
0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9, 0x26, 0xbe,
|
0xeb, 0x1e, 0xfa, 0x03, 0x1b, 0x9d, 0x5a, 0xa0, 0x2f, 0x71, 0x1a, 0x76,
|
||||||
0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25, 0xdb, 0x85,
|
0xe7, 0x6f, 0x71, 0x7d, 0x3a, 0x7d, 0x8c, 0x46, 0xaf, 0x93, 0x94, 0x47,
|
||||||
0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba, 0xb9, 0x3d,
|
0x27, 0xec, 0x1b, 0x1e, 0xd7, 0x8c, 0x7c, 0xec, 0x42, 0xaf, 0x55, 0x82,
|
||||||
0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80, 0x12, 0x72,
|
0x3b, 0x6d, 0x07, 0x24, 0xb3, 0xfa, 0x2d, 0x1e, 0x12, 0x02, 0x94, 0x04,
|
||||||
0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98, 0xab, 0x73,
|
0x23, 0xeb, 0xf3, 0x74, 0x04, 0x7e, 0x2a, 0x7f, 0x00, 0x34, 0x2b, 0x5c,
|
||||||
0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1, 0x75, 0x79,
|
0x5b, 0x10, 0xe7, 0x36, 0x52, 0xde, 0x9f, 0x56, 0x10, 0xe3, 0x0b, 0xa5,
|
||||||
0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98, 0xe8, 0x40,
|
0x29, 0x85, 0xa5, 0x95, 0xed, 0xf5, 0x39, 0x0a, 0x03, 0x51, 0x29, 0x64,
|
||||||
0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f, 0x6f, 0x23,
|
0xa1, 0x4f, 0x38, 0xde, 0x3b, 0x4d, 0x0a, 0xf3, 0x7e, 0x37, 0x14, 0xce,
|
||||||
0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30, 0xaa, 0xcf,
|
0xdf, 0x9d, 0x86, 0x16, 0xad, 0x62, 0xa8, 0xf8, 0xa7, 0xc2, 0xa4, 0xc1,
|
||||||
0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b, 0x7f, 0x91,
|
0xe2, 0xd6, 0x40, 0xa4, 0x7b, 0x20, 0x1b, 0x6d, 0x7c, 0x97, 0x0b, 0x73,
|
||||||
0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a, 0x98, 0xad,
|
0x85, 0xbf, 0xdb, 0xc3, 0xa1, 0xf5, 0xd4, 0xb7, 0x95, 0xf2, 0xe7, 0x10,
|
||||||
0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e, 0x6e, 0x9d,
|
0x77, 0xc6, 0x82, 0xb2, 0x68, 0x24, 0x31, 0xdc, 0x69, 0x43, 0x56, 0xf5,
|
||||||
0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04, 0x4f, 0x32,
|
0x76, 0x20, 0x0a, 0x82, 0x1a, 0x98, 0xb3, 0x02, 0x0f, 0x67, 0xcd, 0x4f,
|
||||||
0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52, 0xfc, 0xcc,
|
0xab, 0x43, 0x44, 0xbd, 0xdb, 0x07, 0xd3, 0xff, 0x8b, 0x68, 0x33, 0x24,
|
||||||
0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa, 0xb3, 0x77,
|
0x35, 0xe5, 0xc6, 0x1a, 0x94, 0x14, 0x4f, 0x40, 0xef, 0x92, 0xfb, 0xfd,
|
||||||
0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d, 0x3c, 0x74,
|
0x72, 0x15, 0xd4, 0x10, 0x60, 0x22, 0x3e, 0x60, 0x49, 0x3d, 0x58, 0xc6,
|
||||||
0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f, 0x13, 0x97,
|
0x3d, 0x28, 0x70, 0x55, 0x32, 0xd5, 0x78, 0x03, 0x51, 0xff, 0xd6, 0x4f,
|
||||||
0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94, 0x3b, 0x31,
|
0x4e, 0x89, 0x0e, 0x50, 0x85, 0x6e, 0x1c, 0x6a, 0x5f, 0x11, 0xd0, 0xf5,
|
||||||
0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b, 0x65, 0xe9,
|
0xee, 0xe5, 0x1c, 0xa8, 0xb2, 0xdb, 0x26, 0x93, 0xb1, 0xe2, 0xc1, 0x05,
|
||||||
0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d, 0x0a, 0xfd,
|
0xe0, 0x7f, 0x16, 0xe7, 0x9c, 0xcf, 0xe7, 0xb7, 0x7e, 0xaa, 0x96, 0x21,
|
||||||
0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4, 0x83, 0xcf,
|
0x64, 0x39, 0x6d, 0x7a, 0xdc, 0x70, 0x6e, 0xc8, 0xf5, 0x44, 0x2e, 0x9f,
|
||||||
0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16, 0x7b, 0x66,
|
0xc1, 0xe9, 0x46, 0x8c, 0x1b, 0x58, 0xec, 0x73, 0x1b, 0x9a, 0x04, 0xcb,
|
||||||
0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d, 0x94, 0x5d,
|
0x68, 0x58, 0x21, 0x0e, 0xd6, 0xd7, 0x7a, 0x2b, 0x60, 0x02, 0x20, 0x7b,
|
||||||
0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, 0x55, 0xf8,
|
0x85, 0xe5, 0x84, 0x2c, 0x5f, 0x24, 0x90, 0x2d, 0xc5, 0x19, 0xea, 0xf3,
|
||||||
0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
|
0x91, 0x78, 0xc2, 0xa7, 0x36, 0x5a, 0x72, 0x64, 0x45, 0x13, 0x49,
|
||||||
|
};
|
||||||
|
|
||||||
const unsigned char kTestDrmServiceCertificate[] = {
|
const unsigned char kTestUserDrmCertificateWithECKey[] = {
|
||||||
0x0a, 0xbc, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32,
|
0x0a, 0x8f, 0x01, 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, 0x5b, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
|
||||||
|
0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
|
||||||
|
0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x8a, 0xea, 0x3f, 0x16,
|
||||||
|
0xff, 0x24, 0xa9, 0xbf, 0x03, 0x28, 0x30, 0x15, 0xee, 0x52, 0x50, 0x9a,
|
||||||
|
0x55, 0x1c, 0x60, 0xc7, 0xa7, 0xcc, 0x4b, 0x99, 0x5b, 0x40, 0x55, 0xce,
|
||||||
|
0x46, 0x19, 0xd4, 0xd4, 0x5e, 0xfd, 0xe0, 0x68, 0x27, 0xea, 0x78, 0xf3,
|
||||||
|
0x07, 0x1f, 0x02, 0x4a, 0x78, 0x52, 0x44, 0xd3, 0xdf, 0xbe, 0xac, 0x5f,
|
||||||
|
0xa5, 0x1c, 0x8a, 0x49, 0x8d, 0xa6, 0x5a, 0xac, 0xa1, 0x25, 0x2b, 0xd1,
|
||||||
|
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65,
|
||||||
|
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
|
0x48, 0x02, 0x12, 0x66, 0x30, 0x64, 0x02, 0x30, 0x4c, 0x7a, 0x79, 0x4a,
|
||||||
|
0x18, 0xf2, 0x2f, 0x9f, 0x29, 0x3b, 0x6f, 0x8f, 0x8f, 0xe4, 0xe0, 0xf2,
|
||||||
|
0xd9, 0x38, 0x18, 0x8a, 0x9a, 0x88, 0x85, 0x95, 0x72, 0xb7, 0x3c, 0xb6,
|
||||||
|
0x47, 0xa4, 0x6b, 0x0a, 0x56, 0x4a, 0x38, 0x1d, 0x2f, 0x4a, 0xc6, 0x61,
|
||||||
|
0x97, 0x35, 0x81, 0x87, 0x4b, 0xca, 0xdc, 0x20, 0x02, 0x30, 0x28, 0x4e,
|
||||||
|
0xf1, 0x23, 0x6d, 0x3f, 0x4f, 0x29, 0x29, 0x86, 0x75, 0x46, 0x83, 0x03,
|
||||||
|
0xa0, 0xe7, 0x23, 0x07, 0x2b, 0x28, 0x4d, 0xa9, 0x72, 0xb6, 0x5e, 0x3b,
|
||||||
|
0xd2, 0x90, 0x05, 0xd3, 0x33, 0x35, 0x99, 0xdc, 0xe9, 0x54, 0xa0, 0x6e,
|
||||||
|
0xca, 0x38, 0x63, 0x4d, 0x95, 0xab, 0x99, 0x77, 0x87, 0x38, 0x1a, 0xa0,
|
||||||
|
0x04, 0x0a, 0x9a, 0x01, 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, 0x78, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07,
|
||||||
|
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04,
|
||||||
|
0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xb0, 0x50, 0x2a, 0x13, 0x20, 0x3e,
|
||||||
|
0x66, 0x67, 0xdf, 0x11, 0x2a, 0xbc, 0x0f, 0x76, 0x69, 0x4b, 0xa1, 0x88,
|
||||||
|
0xec, 0xb8, 0x71, 0xcf, 0xc9, 0xbb, 0xd2, 0xbc, 0xf8, 0x53, 0xfd, 0x8b,
|
||||||
|
0x8d, 0x14, 0x6f, 0xda, 0xea, 0x60, 0x51, 0xc8, 0xd3, 0x3a, 0xd4, 0x75,
|
||||||
|
0x81, 0x05, 0x16, 0x03, 0x0b, 0xcb, 0x33, 0x2c, 0x8b, 0xe6, 0xd3, 0x57,
|
||||||
|
0x6c, 0xfb, 0x81, 0x4b, 0xfe, 0x79, 0x56, 0xf7, 0x6a, 0x2b, 0xca, 0xa7,
|
||||||
|
0x04, 0xe9, 0x37, 0xd6, 0x49, 0xe5, 0x8b, 0x2c, 0xe9, 0x8e, 0xcd, 0xe7,
|
||||||
|
0xe3, 0xc9, 0xf5, 0x4c, 0x90, 0x82, 0x5f, 0xf0, 0x53, 0xd2, 0xa4, 0x1a,
|
||||||
|
0xb3, 0x53, 0x3d, 0xa7, 0xa7, 0xfd, 0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04,
|
||||||
|
0x48, 0x03, 0x12, 0x80, 0x03, 0x42, 0x90, 0xc4, 0x87, 0x0b, 0x55, 0x78,
|
||||||
|
0xb5, 0x25, 0x64, 0x23, 0xf2, 0x6a, 0x28, 0x7b, 0x1e, 0x12, 0xeb, 0x94,
|
||||||
|
0x08, 0x4f, 0xce, 0x6b, 0x53, 0x35, 0xda, 0xa6, 0xf3, 0x90, 0x3b, 0x1b,
|
||||||
|
0xa8, 0x2f, 0x17, 0x8e, 0x09, 0x12, 0x4b, 0xc6, 0x10, 0xfc, 0x8a, 0x63,
|
||||||
|
0xda, 0xf6, 0x7e, 0x18, 0x3e, 0x49, 0x4c, 0x85, 0x5b, 0x2c, 0xcb, 0x09,
|
||||||
|
0x67, 0x3b, 0xd3, 0xf3, 0x90, 0xe7, 0x4e, 0x06, 0x2f, 0x25, 0xbe, 0x22,
|
||||||
|
0x7f, 0xd6, 0x5c, 0xd5, 0xda, 0xac, 0x60, 0x29, 0x83, 0x53, 0x54, 0x73,
|
||||||
|
0x0d, 0x96, 0xca, 0x50, 0x6e, 0x45, 0xd6, 0x3c, 0xe6, 0xab, 0xf7, 0xe8,
|
||||||
|
0x69, 0x9e, 0xe2, 0x8e, 0xfd, 0x46, 0x11, 0x40, 0x9d, 0xfc, 0xd8, 0x2d,
|
||||||
|
0xe0, 0x94, 0xbc, 0x05, 0x74, 0x3c, 0x0a, 0xdd, 0x64, 0x37, 0x93, 0x9d,
|
||||||
|
0x5a, 0x08, 0xfe, 0x5f, 0xae, 0xa3, 0xc6, 0x48, 0xbd, 0x8a, 0x4e, 0x3c,
|
||||||
|
0xac, 0x7c, 0x66, 0xad, 0xc4, 0x7b, 0x7b, 0x89, 0xd9, 0xae, 0xf5, 0x8d,
|
||||||
|
0xf3, 0x0e, 0x3b, 0x1c, 0xb6, 0xf0, 0xff, 0x52, 0x22, 0xbc, 0xdd, 0x1e,
|
||||||
|
0xe5, 0x90, 0xe1, 0x09, 0xe2, 0x65, 0x42, 0x70, 0x4b, 0xfa, 0xf0, 0x41,
|
||||||
|
0x41, 0x53, 0xa2, 0x2c, 0x32, 0xc4, 0x1a, 0x42, 0x0d, 0xbe, 0x8d, 0x5b,
|
||||||
|
0x14, 0xae, 0x8f, 0xca, 0x85, 0xda, 0xfb, 0xe1, 0x25, 0x71, 0xc6, 0x8a,
|
||||||
|
0x3c, 0xe1, 0x99, 0x09, 0x30, 0x9d, 0xa7, 0xec, 0x10, 0x7b, 0x43, 0xee,
|
||||||
|
0xf8, 0xa5, 0x58, 0x9d, 0xd7, 0x31, 0x6a, 0x22, 0x45, 0xf5, 0x0c, 0x30,
|
||||||
|
0xb2, 0x77, 0x05, 0x13, 0x10, 0xfd, 0xc1, 0xf9, 0x13, 0xc2, 0x88, 0xc8,
|
||||||
|
0x71, 0xd9, 0x14, 0x5f, 0xc9, 0xde, 0x96, 0xe7, 0x55, 0xb9, 0x4a, 0xb0,
|
||||||
|
0x18, 0x22, 0x17, 0x9f, 0x95, 0xe2, 0xa7, 0x66, 0x9b, 0xfd, 0x38, 0xf9,
|
||||||
|
0x5c, 0xa0, 0xaa, 0xf4, 0x60, 0xee, 0x00, 0x53, 0x87, 0x29, 0x63, 0x53,
|
||||||
|
0x55, 0xfb, 0x32, 0x7a, 0x80, 0x56, 0xea, 0xaa, 0x95, 0x22, 0x08, 0x9e,
|
||||||
|
0x25, 0x53, 0x50, 0xb4, 0xd0, 0x07, 0x3c, 0x29, 0x3f, 0x03, 0xab, 0x68,
|
||||||
|
0xf5, 0xa5, 0xc6, 0xd2, 0x73, 0xf6, 0xee, 0xa2, 0x6c, 0xec, 0xd4, 0xf3,
|
||||||
|
0x20, 0xdc, 0x56, 0x00, 0x3d, 0xea, 0x57, 0x14, 0xc7, 0x90, 0x86, 0x82,
|
||||||
|
0x1b, 0x14, 0x57, 0x68, 0xec, 0x24, 0x0e, 0x8d, 0x6b, 0xcc, 0x5f, 0x7a,
|
||||||
|
0x53, 0xac, 0x60, 0x20, 0x5f, 0xe7, 0x79, 0xb6, 0x2c, 0xfb, 0x23, 0xa3,
|
||||||
|
0x43, 0x91, 0x0f, 0x53, 0x38, 0x0e, 0xcc, 0x27, 0xaf, 0x57, 0x01, 0x26,
|
||||||
|
0xd8, 0x01, 0x41, 0x27, 0x63, 0xca, 0x9f, 0xf5, 0xa7, 0x43, 0x26, 0x74,
|
||||||
|
0x59, 0xec, 0xce, 0x71, 0x09, 0x0d, 0xda, 0x5d, 0x63, 0xef, 0xfd, 0x6e,
|
||||||
|
0x92, 0x53, 0x12, 0xbc, 0x6a, 0x5b, 0x4d, 0x4a, 0x43, 0x04, 0x5d, 0x8e,
|
||||||
|
0x93, 0xd7, 0x89, 0x21, 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char kTestUserDrmCertificateWithRotId[] = {
|
||||||
|
0x0a, 0xdf, 0x03, 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,
|
||||||
|
0x48, 0x01, 0x52, 0x99, 0x01, 0x08, 0x01, 0x10, 0x00, 0x1a, 0x71, 0x04,
|
||||||
|
0x8a, 0xea, 0x3f, 0x16, 0xff, 0x24, 0xa9, 0xbf, 0x03, 0x28, 0x30, 0x15,
|
||||||
|
0xee, 0x52, 0x50, 0x9a, 0x55, 0x1c, 0x60, 0xc7, 0xa7, 0xcc, 0x4b, 0x99,
|
||||||
|
0x5b, 0x40, 0x55, 0xce, 0x46, 0x19, 0xd4, 0xd4, 0x5e, 0xfd, 0xe0, 0x68,
|
||||||
|
0x27, 0xea, 0x78, 0xf3, 0x07, 0x1f, 0x02, 0x4a, 0x78, 0x52, 0x44, 0xd3,
|
||||||
|
0xdf, 0xbe, 0xac, 0x5f, 0xa5, 0x1c, 0x8a, 0x49, 0x8d, 0xa6, 0x5a, 0xac,
|
||||||
|
0xa1, 0x25, 0x2b, 0xd1, 0xd9, 0xeb, 0xa7, 0x91, 0x26, 0x46, 0x3a, 0xe4,
|
||||||
|
0x5b, 0x06, 0x6b, 0x77, 0x83, 0xa9, 0x0f, 0xa3, 0xf3, 0x2a, 0x39, 0x36,
|
||||||
|
0xc1, 0xbd, 0x37, 0xeb, 0xd7, 0x83, 0x3e, 0xbd, 0x17, 0x53, 0x82, 0x69,
|
||||||
|
0xc3, 0xe4, 0x48, 0xb5, 0x0d, 0x8c, 0xe1, 0x30, 0x17, 0xef, 0x01, 0x88,
|
||||||
|
0x30, 0x62, 0x5a, 0xb3, 0x22, 0x20, 0x91, 0x69, 0x9e, 0xbc, 0xa2, 0x5c,
|
||||||
|
0xd4, 0x51, 0x79, 0xfd, 0xbc, 0x2f, 0x92, 0xcd, 0x48, 0x2d, 0xd3, 0x30,
|
||||||
|
0xe6, 0x1e, 0xbd, 0x4e, 0x23, 0x96, 0x2b, 0xb0, 0x3a, 0xfc, 0xb4, 0x7b,
|
||||||
|
0x0e, 0x3d, 0x12, 0x80, 0x02, 0xa0, 0xe0, 0x2b, 0x1d, 0xe4, 0x28, 0x7b,
|
||||||
|
0x57, 0x64, 0x3f, 0xe2, 0xf7, 0x10, 0xda, 0x97, 0x7f, 0x49, 0xf4, 0xd2,
|
||||||
|
0x57, 0xc5, 0xf5, 0xe1, 0xd9, 0xd9, 0xbb, 0x34, 0xac, 0x90, 0x0c, 0xfa,
|
||||||
|
0x9f, 0x0d, 0xd7, 0x14, 0xfc, 0xbb, 0xc9, 0xfa, 0xa5, 0x41, 0xd8, 0x87,
|
||||||
|
0xa5, 0xef, 0x39, 0xf8, 0x70, 0x4f, 0x93, 0xfc, 0xb8, 0x62, 0xb8, 0x7e,
|
||||||
|
0x6e, 0x9f, 0x25, 0xe9, 0x47, 0x08, 0xe6, 0x89, 0xe9, 0xc2, 0x41, 0x6a,
|
||||||
|
0xc6, 0x0f, 0x84, 0x71, 0xa7, 0x90, 0xf2, 0x86, 0x7b, 0xbc, 0x99, 0xca,
|
||||||
|
0xf1, 0xd4, 0xa5, 0xc0, 0x9f, 0x4e, 0x53, 0x27, 0xde, 0x3e, 0x4b, 0x7b,
|
||||||
|
0x7d, 0x6a, 0xa4, 0xaa, 0x53, 0x9c, 0x4f, 0xff, 0xf6, 0x61, 0xbe, 0x4d,
|
||||||
|
0xa7, 0x9b, 0xa6, 0xc9, 0xb1, 0x00, 0x57, 0xd6, 0x47, 0xb2, 0x0e, 0xf6,
|
||||||
|
0x56, 0x1b, 0xc3, 0x7d, 0xe0, 0x97, 0x85, 0x93, 0xe9, 0xa4, 0x43, 0xfa,
|
||||||
|
0x02, 0xd9, 0x40, 0x97, 0x22, 0x86, 0x15, 0xd8, 0x24, 0x92, 0x35, 0x32,
|
||||||
|
0x15, 0xc2, 0x19, 0xbc, 0x32, 0x21, 0x3f, 0x8c, 0xdf, 0x9f, 0x5c, 0x3a,
|
||||||
|
0x57, 0x73, 0x4f, 0x25, 0x3b, 0xa1, 0x88, 0x6f, 0xbb, 0x4a, 0xb7, 0xe5,
|
||||||
|
0xc6, 0x33, 0x1b, 0x59, 0xa9, 0xe9, 0xc3, 0x0b, 0xdb, 0xe9, 0x41, 0xb0,
|
||||||
|
0x06, 0x49, 0xdf, 0x0a, 0xa9, 0x85, 0xf5, 0xc7, 0xe6, 0x2c, 0x20, 0x25,
|
||||||
|
0x50, 0x45, 0xf2, 0x86, 0x57, 0xdb, 0x3f, 0x28, 0x0b, 0xd6, 0xc4, 0xac,
|
||||||
|
0xb0, 0x1c, 0xc3, 0xed, 0x7a, 0x8d, 0xa9, 0x83, 0x20, 0x43, 0xc7, 0x42,
|
||||||
|
0x03, 0xca, 0x23, 0xc8, 0xf7, 0xbf, 0x2e, 0x70, 0x9a, 0xff, 0x23, 0xff,
|
||||||
|
0x17, 0x22, 0xca, 0xe0, 0x58, 0x2f, 0xd3, 0x0d, 0xa4, 0xa2, 0x90, 0x6c,
|
||||||
|
0xf1, 0x78, 0x7c, 0xee, 0x1a, 0xe7, 0x0c, 0xe2, 0x89, 0xf0, 0x5b, 0x9a,
|
||||||
|
0x24, 0x4e, 0x10, 0xcf, 0x58, 0xa1, 0xdb, 0x3f, 0x1b, 0x1a, 0xb7, 0x05,
|
||||||
|
0x0a, 0xb1, 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, 0x48, 0x01, 0x12, 0x80, 0x03, 0x06,
|
||||||
|
0xe2, 0xc2, 0x94, 0x0e, 0x81, 0x87, 0x59, 0xe3, 0xe8, 0x15, 0x7f, 0xc6,
|
||||||
|
0xff, 0x6b, 0xc8, 0x7e, 0x0c, 0xd9, 0x9b, 0x40, 0x34, 0x22, 0x44, 0x00,
|
||||||
|
0xdf, 0x0e, 0x9e, 0xcd, 0xb9, 0x1d, 0x3d, 0xfe, 0x5a, 0xb9, 0x28, 0xdc,
|
||||||
|
0x94, 0x43, 0xc4, 0x1c, 0x66, 0xa9, 0x8a, 0xa4, 0x61, 0xdf, 0x8a, 0xf3,
|
||||||
|
0x7c, 0xf0, 0xbe, 0x66, 0xe9, 0xdf, 0x65, 0x93, 0x6c, 0xc7, 0xb5, 0x1a,
|
||||||
|
0x76, 0x07, 0x40, 0xde, 0xa1, 0xc5, 0x40, 0xde, 0xac, 0x5b, 0x9f, 0x32,
|
||||||
|
0xbb, 0xd4, 0xf2, 0x09, 0x13, 0x20, 0xbe, 0xee, 0xf4, 0xb5, 0xb0, 0xec,
|
||||||
|
0xeb, 0x1e, 0xfa, 0x03, 0x1b, 0x9d, 0x5a, 0xa0, 0x2f, 0x71, 0x1a, 0x76,
|
||||||
|
0xe7, 0x6f, 0x71, 0x7d, 0x3a, 0x7d, 0x8c, 0x46, 0xaf, 0x93, 0x94, 0x47,
|
||||||
|
0x27, 0xec, 0x1b, 0x1e, 0xd7, 0x8c, 0x7c, 0xec, 0x42, 0xaf, 0x55, 0x82,
|
||||||
|
0x3b, 0x6d, 0x07, 0x24, 0xb3, 0xfa, 0x2d, 0x1e, 0x12, 0x02, 0x94, 0x04,
|
||||||
|
0x23, 0xeb, 0xf3, 0x74, 0x04, 0x7e, 0x2a, 0x7f, 0x00, 0x34, 0x2b, 0x5c,
|
||||||
|
0x5b, 0x10, 0xe7, 0x36, 0x52, 0xde, 0x9f, 0x56, 0x10, 0xe3, 0x0b, 0xa5,
|
||||||
|
0x29, 0x85, 0xa5, 0x95, 0xed, 0xf5, 0x39, 0x0a, 0x03, 0x51, 0x29, 0x64,
|
||||||
|
0xa1, 0x4f, 0x38, 0xde, 0x3b, 0x4d, 0x0a, 0xf3, 0x7e, 0x37, 0x14, 0xce,
|
||||||
|
0xdf, 0x9d, 0x86, 0x16, 0xad, 0x62, 0xa8, 0xf8, 0xa7, 0xc2, 0xa4, 0xc1,
|
||||||
|
0xe2, 0xd6, 0x40, 0xa4, 0x7b, 0x20, 0x1b, 0x6d, 0x7c, 0x97, 0x0b, 0x73,
|
||||||
|
0x85, 0xbf, 0xdb, 0xc3, 0xa1, 0xf5, 0xd4, 0xb7, 0x95, 0xf2, 0xe7, 0x10,
|
||||||
|
0x77, 0xc6, 0x82, 0xb2, 0x68, 0x24, 0x31, 0xdc, 0x69, 0x43, 0x56, 0xf5,
|
||||||
|
0x76, 0x20, 0x0a, 0x82, 0x1a, 0x98, 0xb3, 0x02, 0x0f, 0x67, 0xcd, 0x4f,
|
||||||
|
0xab, 0x43, 0x44, 0xbd, 0xdb, 0x07, 0xd3, 0xff, 0x8b, 0x68, 0x33, 0x24,
|
||||||
|
0x35, 0xe5, 0xc6, 0x1a, 0x94, 0x14, 0x4f, 0x40, 0xef, 0x92, 0xfb, 0xfd,
|
||||||
|
0x72, 0x15, 0xd4, 0x10, 0x60, 0x22, 0x3e, 0x60, 0x49, 0x3d, 0x58, 0xc6,
|
||||||
|
0x3d, 0x28, 0x70, 0x55, 0x32, 0xd5, 0x78, 0x03, 0x51, 0xff, 0xd6, 0x4f,
|
||||||
|
0x4e, 0x89, 0x0e, 0x50, 0x85, 0x6e, 0x1c, 0x6a, 0x5f, 0x11, 0xd0, 0xf5,
|
||||||
|
0xee, 0xe5, 0x1c, 0xa8, 0xb2, 0xdb, 0x26, 0x93, 0xb1, 0xe2, 0xc1, 0x05,
|
||||||
|
0xe0, 0x7f, 0x16, 0xe7, 0x9c, 0xcf, 0xe7, 0xb7, 0x7e, 0xaa, 0x96, 0x21,
|
||||||
|
0x64, 0x39, 0x6d, 0x7a, 0xdc, 0x70, 0x6e, 0xc8, 0xf5, 0x44, 0x2e, 0x9f,
|
||||||
|
0xc1, 0xe9, 0x46, 0x8c, 0x1b, 0x58, 0xec, 0x73, 0x1b, 0x9a, 0x04, 0xcb,
|
||||||
|
0x68, 0x58, 0x21, 0x0e, 0xd6, 0xd7, 0x7a, 0x2b, 0x60, 0x02, 0x20, 0x7b,
|
||||||
|
0x85, 0xe5, 0x84, 0x2c, 0x5f, 0x24, 0x90, 0x2d, 0xc5, 0x19, 0xea, 0xf3,
|
||||||
|
0x91, 0x78, 0xc2, 0xa7, 0x36, 0x5a, 0x72, 0x64, 0x45, 0x13, 0x49};
|
||||||
|
|
||||||
|
const unsigned char kTestDrmServiceCertificateLicenseSdk[] = {
|
||||||
|
0x0a, 0xc0, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32,
|
||||||
0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18,
|
0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18,
|
||||||
0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02,
|
0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02,
|
||||||
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54,
|
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54,
|
||||||
@@ -277,39 +521,166 @@ const unsigned char kTestDrmServiceCertificate[] = {
|
|||||||
0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b,
|
0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b,
|
||||||
0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00,
|
0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00,
|
||||||
0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76,
|
0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76,
|
||||||
0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0x6e, 0xc3,
|
0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x40, 0x01, 0x48, 0x01, 0x12,
|
||||||
0x5a, 0x17, 0xa8, 0xf9, 0xef, 0xee, 0x67, 0x4d, 0x0a, 0xef, 0x57, 0x5e,
|
0x80, 0x03, 0x20, 0x54, 0x65, 0x70, 0x93, 0x53, 0x0a, 0x76, 0x95, 0xe5,
|
||||||
0xbc, 0x59, 0x3d, 0x22, 0x84, 0xa0, 0x0a, 0xf5, 0x84, 0x26, 0xb7, 0x8b,
|
0xaa, 0x48, 0x94, 0xa1, 0xb8, 0x25, 0xa6, 0xa4, 0x5a, 0x56, 0x59, 0x8d,
|
||||||
0xab, 0x91, 0x3e, 0x4b, 0xb9, 0x91, 0x3c, 0x50, 0xc9, 0x08, 0x2f, 0x97,
|
0x80, 0x64, 0x7c, 0xb2, 0x1e, 0xeb, 0xda, 0xfb, 0x30, 0x26, 0x09, 0xc0,
|
||||||
0x0a, 0x91, 0xb5, 0x48, 0xe4, 0xba, 0xfd, 0x7b, 0xbd, 0xf0, 0xba, 0x08,
|
0x25, 0x65, 0xdb, 0xb8, 0x7d, 0x0e, 0xe0, 0x2b, 0xfb, 0xfe, 0xe6, 0x3a,
|
||||||
0xb3, 0x29, 0xb4, 0x23, 0x74, 0xaf, 0x3f, 0xe9, 0x77, 0x78, 0x3f, 0xdc,
|
0x5b, 0x9b, 0x1f, 0xbb, 0xa4, 0x89, 0xf5, 0x7c, 0x52, 0x6f, 0x52, 0xe3,
|
||||||
0x3d, 0x8a, 0x37, 0xec, 0x1c, 0x3a, 0xff, 0x60, 0x8e, 0x10, 0x72, 0xaa,
|
0xb3, 0xc7, 0x27, 0x9d, 0xca, 0x01, 0x78, 0x08, 0x9b, 0x59, 0x37, 0x9f,
|
||||||
0x97, 0x98, 0x56, 0xa0, 0x35, 0xa9, 0xbf, 0x43, 0x21, 0x6a, 0x15, 0x88,
|
0x27, 0x52, 0x90, 0x80, 0x22, 0xfb, 0x0d, 0xca, 0x57, 0xc2, 0xd9, 0x89,
|
||||||
0xba, 0xc0, 0x68, 0x01, 0x7b, 0xd7, 0x88, 0x2f, 0x1a, 0xc5, 0x1f, 0x54,
|
0xb6, 0x69, 0x45, 0x1f, 0x15, 0x23, 0xf1, 0xf8, 0x39, 0xbb, 0x45, 0xb9,
|
||||||
0xf0, 0xea, 0x36, 0xb7, 0xed, 0x49, 0x78, 0x09, 0xb1, 0x07, 0x46, 0xfe,
|
0x39, 0xe5, 0x1e, 0x8b, 0x71, 0x82, 0x25, 0x4a, 0x32, 0xc2, 0x44, 0xee,
|
||||||
0xf4, 0xfa, 0x16, 0x0c, 0x46, 0x91, 0xe2, 0xa9, 0xe0, 0x8e, 0x97, 0xe5,
|
0x76, 0x91, 0x61, 0xa2, 0xe2, 0x7a, 0xb4, 0x68, 0x56, 0xaf, 0x33, 0xe4,
|
||||||
0xea, 0x2f, 0xd9, 0x94, 0x1e, 0xe7, 0xba, 0x28, 0x98, 0x92, 0xae, 0xb8,
|
0x97, 0x44, 0xfe, 0x6d, 0x70, 0x85, 0x4f, 0x16, 0x1a, 0xda, 0xa4, 0x30,
|
||||||
0xb6, 0x6e, 0xf6, 0xd2, 0x50, 0xd3, 0x5b, 0x25, 0x12, 0x68, 0x5e, 0x07,
|
0x66, 0xf7, 0x95, 0xe9, 0x7b, 0x84, 0x42, 0xd6, 0x7c, 0x4b, 0x05, 0xca,
|
||||||
0x82, 0x64, 0x27, 0xfe, 0x1a, 0xcd, 0x38, 0xa8, 0x00, 0x53, 0x8c, 0x69,
|
0x67, 0x2f, 0xf4, 0xdc, 0x81, 0x9b, 0x7c, 0x80, 0xc4, 0x9e, 0x25, 0x98,
|
||||||
0x51, 0x75, 0x71, 0xc2, 0x6a, 0x5f, 0x05, 0x13, 0x77, 0x2b, 0xc8, 0x6c,
|
0x84, 0xc4, 0x43, 0x35, 0x13, 0xf4, 0x9d, 0x57, 0x02, 0x1e, 0x67, 0x86,
|
||||||
0xab, 0xd2, 0x64, 0x27, 0xbd, 0x21, 0xfc, 0x33, 0x0a, 0x3a, 0x53, 0xa6,
|
0x00, 0x6c, 0x46, 0xde, 0x91, 0x9f, 0x1f, 0x42, 0xbb, 0xa7, 0xd1, 0xb8,
|
||||||
0x28, 0x1c, 0x2a, 0xad, 0x23, 0x0a, 0x95, 0xe4, 0x38, 0x6b, 0x9b, 0x3e,
|
0x80, 0x2c, 0x33, 0x51, 0x87, 0x93, 0x6d, 0x75, 0x03, 0xb0, 0x42, 0xc9,
|
||||||
0x77, 0x7d, 0x96, 0x20, 0x42, 0xf5, 0x18, 0xbe, 0xb0, 0x78, 0xe4, 0xf0,
|
0xe6, 0xa1, 0xc7, 0xa5, 0xd3, 0x40, 0xe7, 0x99, 0x6d, 0x07, 0x78, 0x13,
|
||||||
0x95, 0x6c, 0xd5, 0x30, 0xd6, 0xfc, 0x04, 0xe2, 0xf7, 0xff, 0x06, 0x6b,
|
0x8a, 0x01, 0x4d, 0x3e, 0xb4, 0x9a, 0x1b, 0x52, 0xb7, 0xac, 0x6d, 0x27,
|
||||||
0xaf, 0xf1, 0x9c, 0x10, 0xa6, 0xdb, 0xed, 0x4a, 0x18, 0x68, 0x87, 0xda,
|
0xda, 0x5c, 0xa2, 0x78, 0x01, 0xe3, 0x4d, 0x5d, 0x0a, 0xd0, 0xc7, 0xb5,
|
||||||
0x43, 0x2c, 0x60, 0xc6, 0x0a, 0x72, 0x1e, 0x9f, 0x4b, 0x05, 0x80, 0x15,
|
0x73, 0xcf, 0x6e, 0xdd, 0x89, 0xc6, 0xd4, 0x9c, 0xc7, 0xfa, 0x87, 0xe9,
|
||||||
0x17, 0x84, 0xf1, 0xee, 0xcc, 0x80, 0x25, 0x33, 0x87, 0x74, 0x02, 0x8c,
|
0x74, 0x01, 0xe9, 0xdd, 0x16, 0x0f, 0x3a, 0x8e, 0x38, 0x8d, 0x0b, 0x5a,
|
||||||
0xa1, 0xbb, 0xd9, 0x29, 0x33, 0x97, 0xbd, 0x5b, 0x1c, 0xed, 0xcc, 0x47,
|
0xc8, 0x01, 0xca, 0xb2, 0x7f, 0xcb, 0xe3, 0x25, 0xaa, 0x10, 0xc9, 0x4f,
|
||||||
0xda, 0x73, 0xae, 0xb1, 0x75, 0xac, 0xf7, 0x39, 0xbe, 0x67, 0xc3, 0xaf,
|
0x5a, 0x17, 0xe0, 0x31, 0x30, 0x34, 0xe8, 0xe6, 0x06, 0x27, 0xb3, 0x26,
|
||||||
0x60, 0x07, 0xf5, 0xba, 0x81, 0xf4, 0x42, 0xad, 0x28, 0x8d, 0xe6, 0x63,
|
0xee, 0x44, 0x5d, 0x34, 0x2d, 0xc0, 0xff, 0x98, 0x1d, 0x33, 0x99, 0x96,
|
||||||
0xea, 0x8a, 0x0e, 0x71, 0x53, 0x6e, 0x62, 0x8a, 0x23, 0x4f, 0xad, 0x2a,
|
0x29, 0xa9, 0xc6, 0x31, 0xc1, 0xe1, 0x2f, 0xb9, 0x3a, 0xd2, 0x80, 0x16,
|
||||||
0x9a, 0xf6, 0xeb, 0xa8, 0x82, 0x83, 0xbb, 0x5f, 0xc9, 0x86, 0xd8, 0x76,
|
0xc2, 0x4a, 0x38, 0x58, 0x9f, 0x78, 0x7b, 0x11, 0x6c, 0x4e, 0xb0, 0x6b,
|
||||||
0xb9, 0xf3, 0xe7, 0x32, 0xdd, 0xe0, 0x44, 0x6a, 0xab, 0x78, 0xa0, 0x8c,
|
0x3b, 0x8f, 0x77, 0x59, 0xb7, 0xca, 0x08, 0x85, 0xc5, 0xe2, 0x03, 0xa7,
|
||||||
0xa4, 0x99, 0x6f, 0x71, 0x42, 0x8b, 0x31, 0x32, 0xbb, 0x80, 0x36, 0x61,
|
0x33, 0xe7, 0x34, 0xc5, 0x64, 0x37, 0x9b, 0x19, 0x48, 0x54, 0xa7, 0xe5,
|
||||||
0x1c, 0xe5, 0x6d, 0x87, 0xf2, 0x68, 0xca, 0xcd, 0xe0, 0x5f, 0xa2, 0x68,
|
0x74, 0xe3, 0xa9, 0xfc, 0xe7, 0x6f, 0x9f, 0x04, 0xd4, 0xbd, 0x4a, 0x70,
|
||||||
0x5b, 0xfc, 0x73, 0xc9, 0x26, 0x2b, 0x13, 0x05, 0x1c, 0xde, 0x19, 0xdf,
|
0xc9, 0x09, 0x67, 0x1a, 0xc5, 0x7b, 0xe9, 0x88, 0x71, 0xcb, 0x96, 0x46,
|
||||||
0x34, 0xba, 0xf5, 0xec, 0xaf, 0x26, 0xfb, 0x64, 0xc4, 0x38, 0x7e, 0xdb,
|
0x2f, 0x5e, 0x47, 0x19, 0x48, 0xc0, 0xc6, 0xc1, 0xef, 0xb1, 0x26, 0x95,
|
||||||
0x51, 0x28, 0x49, 0xa7, 0x12, 0x88, 0xa5, 0x6d, 0xa2, 0xfa};
|
0x0c, 0x16, 0xed, 0x88, 0x4d, 0xaf, 0x96, 0x3f, 0xa2, 0xf8, 0xc7, 0x38,
|
||||||
|
0x95, 0x48,
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char kTestDrmServiceCertificateAllTypes[] = {
|
||||||
|
0x0a, 0xc4, 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, 0x40, 0x01, 0x40, 0x02, 0x40,
|
||||||
|
0x03, 0x48, 0x01, 0x12, 0x80, 0x03, 0x72, 0x7c, 0xb3, 0x19, 0x62, 0x88,
|
||||||
|
0x4d, 0xc5, 0x46, 0x11, 0xcc, 0x6b, 0x32, 0x47, 0x65, 0x7a, 0x5e, 0xe7,
|
||||||
|
0xf7, 0x58, 0x7e, 0xbc, 0x76, 0xa6, 0x55, 0x75, 0x32, 0x19, 0xaa, 0xf6,
|
||||||
|
0xb7, 0x02, 0x16, 0x75, 0xfd, 0x05, 0x0b, 0x1c, 0x2c, 0xc5, 0x44, 0x6c,
|
||||||
|
0xcf, 0x96, 0x02, 0x93, 0x23, 0x60, 0x4b, 0xd4, 0x97, 0x81, 0xa4, 0xe5,
|
||||||
|
0x5f, 0x0a, 0xe0, 0x5f, 0x53, 0x23, 0x66, 0x19, 0x70, 0xe0, 0x8c, 0x08,
|
||||||
|
0x19, 0x88, 0xf2, 0xda, 0xd9, 0x7c, 0x69, 0x52, 0xc0, 0x8a, 0x7a, 0x62,
|
||||||
|
0x81, 0xaa, 0x2b, 0x0e, 0xe8, 0x28, 0x21, 0xce, 0x4a, 0x63, 0x52, 0x23,
|
||||||
|
0xab, 0xea, 0x65, 0xf9, 0x0f, 0x8a, 0xd2, 0x77, 0xa5, 0x94, 0x05, 0x77,
|
||||||
|
0x8b, 0x35, 0x99, 0x8b, 0xa9, 0xdd, 0xe4, 0x69, 0x00, 0xbe, 0x4d, 0xd2,
|
||||||
|
0xce, 0x6c, 0xe4, 0xf9, 0x34, 0x97, 0xaa, 0x59, 0xd8, 0xc9, 0x58, 0x11,
|
||||||
|
0xa9, 0xb4, 0x9b, 0x0e, 0xce, 0xb7, 0x15, 0x73, 0x6b, 0xb5, 0x5f, 0x87,
|
||||||
|
0xea, 0xf1, 0x1c, 0x8d, 0x66, 0x5a, 0x28, 0x7e, 0x9a, 0x01, 0x15, 0xfb,
|
||||||
|
0xb1, 0x92, 0x28, 0x3b, 0x62, 0x6a, 0xcb, 0xb0, 0xe7, 0x11, 0x76, 0x44,
|
||||||
|
0x5a, 0x97, 0x4b, 0x1a, 0x26, 0xbe, 0x3c, 0xa6, 0x33, 0xd8, 0x99, 0x7b,
|
||||||
|
0x97, 0xb2, 0xec, 0xa3, 0x18, 0xdf, 0x6b, 0xeb, 0x9f, 0x43, 0x14, 0x0a,
|
||||||
|
0x49, 0xdb, 0xb0, 0xe2, 0xc0, 0xe0, 0xe3, 0x73, 0x66, 0x0f, 0x14, 0x1c,
|
||||||
|
0x48, 0x21, 0x28, 0x3a, 0x90, 0xfd, 0x32, 0x1d, 0x35, 0x9a, 0x11, 0x6c,
|
||||||
|
0xf8, 0x39, 0xd1, 0xcb, 0x45, 0x76, 0xc0, 0x5e, 0xc5, 0xa8, 0xba, 0xd9,
|
||||||
|
0x09, 0xfc, 0x44, 0xf2, 0x9e, 0xaf, 0x95, 0xc4, 0xe0, 0x06, 0x0e, 0xbf,
|
||||||
|
0x99, 0x6e, 0x57, 0xfa, 0xa8, 0xcd, 0x2b, 0x4b, 0x97, 0x62, 0xf7, 0x92,
|
||||||
|
0x14, 0x8f, 0xf4, 0x8c, 0xba, 0xb1, 0xb4, 0x8e, 0x07, 0xd2, 0x7b, 0x93,
|
||||||
|
0xd1, 0xbf, 0x90, 0x60, 0xe0, 0xbf, 0x1a, 0x3b, 0xd2, 0xee, 0xad, 0xf8,
|
||||||
|
0x4f, 0x5b, 0xee, 0xe5, 0xf4, 0x8e, 0x97, 0x5b, 0x24, 0xd2, 0xa6, 0x80,
|
||||||
|
0x5c, 0x09, 0x27, 0x8e, 0x14, 0xa9, 0xcc, 0xff, 0x5a, 0xc1, 0xb4, 0x5f,
|
||||||
|
0xb5, 0x07, 0x04, 0xd0, 0x3a, 0xef, 0xa9, 0x45, 0xa1, 0x23, 0x0f, 0xc2,
|
||||||
|
0x13, 0x4f, 0xc8, 0xd4, 0x7f, 0xef, 0x54, 0x5c, 0xcc, 0xdb, 0xdf, 0x89,
|
||||||
|
0x4c, 0x31, 0x8f, 0x27, 0x50, 0x2b, 0x99, 0xd8, 0xee, 0xdf, 0x8b, 0x06,
|
||||||
|
0x93, 0xc0, 0xd0, 0x59, 0xf5, 0x66, 0xaf, 0x4b, 0x98, 0x68, 0xb6, 0x8c,
|
||||||
|
0xd7, 0x70, 0xad, 0x69, 0x60, 0xca, 0xae, 0x64, 0xc2, 0x53, 0xcb, 0xa1,
|
||||||
|
0x39, 0xb7, 0x08, 0x50, 0x31, 0x12, 0xc1, 0x02, 0x08, 0x58, 0x59, 0xc8,
|
||||||
|
0x69, 0x9e, 0x43, 0xac, 0x60, 0x7d, 0x4c, 0x8b, 0x1a, 0xe8, 0xa8, 0x3d,
|
||||||
|
0x65, 0x29, 0x83, 0x85, 0x9b, 0x8e,
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char kTestDrmServiceCertificateNoType[] = {
|
||||||
|
0x0a, 0xbe, 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, 0x48, 0x01, 0x12, 0x80, 0x03,
|
||||||
|
0x21, 0xa4, 0x86, 0x36, 0x40, 0x9d, 0x0f, 0x7d, 0x31, 0x8a, 0xbe, 0x98,
|
||||||
|
0x85, 0x0f, 0x4a, 0xe6, 0x5e, 0x2e, 0x94, 0xc3, 0x4a, 0x69, 0x26, 0x55,
|
||||||
|
0x76, 0xf6, 0x11, 0xfe, 0xd4, 0x1d, 0xfc, 0xb3, 0xbe, 0x94, 0x6a, 0x5c,
|
||||||
|
0x51, 0x2d, 0xda, 0x95, 0x70, 0x6f, 0x62, 0x23, 0x69, 0xfa, 0x3d, 0xce,
|
||||||
|
0x09, 0x0c, 0x2f, 0xc7, 0x30, 0xac, 0xf2, 0x46, 0x6a, 0x14, 0x42, 0x24,
|
||||||
|
0x8f, 0x2a, 0xb4, 0xa7, 0x32, 0xbb, 0x8c, 0x02, 0xe2, 0x1f, 0x63, 0xfd,
|
||||||
|
0xf4, 0x40, 0x14, 0xd9, 0x12, 0x88, 0xa6, 0x46, 0x15, 0x67, 0x85, 0xa3,
|
||||||
|
0xe2, 0xe4, 0x88, 0x5f, 0xdd, 0x20, 0xcd, 0xa7, 0xd1, 0x26, 0x71, 0x63,
|
||||||
|
0x89, 0x2e, 0x1a, 0x8a, 0x52, 0x59, 0xba, 0x3b, 0x09, 0x8a, 0x1c, 0x67,
|
||||||
|
0x0c, 0xfd, 0xc9, 0xad, 0x7b, 0x4f, 0xd8, 0x0e, 0x47, 0x9e, 0x47, 0x44,
|
||||||
|
0x20, 0x67, 0x7a, 0xec, 0x3b, 0xfe, 0x58, 0xf2, 0x25, 0xb2, 0x72, 0xe1,
|
||||||
|
0x05, 0xbd, 0x7c, 0x02, 0xc9, 0xdb, 0x40, 0x3c, 0x90, 0x04, 0xab, 0xa7,
|
||||||
|
0xb6, 0x06, 0xb2, 0x30, 0x64, 0x42, 0x84, 0x3e, 0x89, 0xd7, 0xd5, 0x90,
|
||||||
|
0xaa, 0x14, 0x00, 0xa6, 0x45, 0x26, 0xe1, 0x16, 0xa8, 0x24, 0x65, 0x01,
|
||||||
|
0xe1, 0x0e, 0xd3, 0xff, 0x68, 0x0c, 0x0f, 0x8f, 0xc3, 0x68, 0xed, 0x82,
|
||||||
|
0x7c, 0xae, 0x21, 0x97, 0x6a, 0x5a, 0x55, 0x76, 0x4b, 0x4e, 0x65, 0x9b,
|
||||||
|
0xbd, 0x18, 0x8b, 0xf4, 0xc4, 0x28, 0xab, 0x6c, 0x52, 0xc7, 0xfc, 0xb3,
|
||||||
|
0x12, 0x79, 0x3c, 0x29, 0x82, 0x15, 0xd1, 0x45, 0xae, 0x0b, 0xbc, 0x46,
|
||||||
|
0x13, 0x24, 0x80, 0xb1, 0x6a, 0x15, 0x7f, 0x68, 0x91, 0xfe, 0xa6, 0x5d,
|
||||||
|
0x7a, 0xdb, 0x81, 0xc4, 0x14, 0xf0, 0x7d, 0x59, 0x15, 0x7c, 0x11, 0x6d,
|
||||||
|
0xe5, 0x97, 0xc2, 0x5e, 0x00, 0xfc, 0x36, 0x4f, 0xf7, 0xc1, 0xe6, 0x62,
|
||||||
|
0x92, 0x03, 0x92, 0x4d, 0x91, 0x61, 0x7e, 0xdc, 0xe9, 0xd3, 0x16, 0x17,
|
||||||
|
0xa7, 0x21, 0xb8, 0xf7, 0x27, 0xc4, 0x72, 0xe9, 0xff, 0x66, 0x60, 0x65,
|
||||||
|
0x50, 0x6d, 0x2d, 0xe9, 0x02, 0xf2, 0x48, 0xbc, 0xfa, 0x7b, 0x3e, 0xdc,
|
||||||
|
0x51, 0x4a, 0xd8, 0x2a, 0xd2, 0x32, 0x95, 0xff, 0x32, 0xf0, 0x68, 0xae,
|
||||||
|
0x74, 0xb3, 0xaf, 0x93, 0x79, 0x2e, 0x4e, 0xd8, 0x23, 0x61, 0xf4, 0xca,
|
||||||
|
0x34, 0x3f, 0x2d, 0x12, 0x5d, 0xef, 0x05, 0xd0, 0xdc, 0x72, 0x0b, 0x02,
|
||||||
|
0xc4, 0x2d, 0x97, 0x65, 0x6d, 0x44, 0x25, 0x50, 0x8e, 0xd1, 0x34, 0x8b,
|
||||||
|
0xc3, 0xff, 0xca, 0x17, 0xcb, 0x5c, 0x64, 0x8e, 0xd1, 0xe5, 0x81, 0xb2,
|
||||||
|
0x5c, 0xd6, 0xd3, 0x2a, 0x0c, 0x74, 0x41, 0x14, 0xc5, 0x27, 0x3c, 0x3f,
|
||||||
|
0x2b, 0xf8, 0xba, 0x33, 0x0b, 0xf4, 0x88, 0x9c, 0x8b, 0x75, 0xf5, 0xf0,
|
||||||
|
0x29, 0x08, 0x23, 0x91, 0xe1, 0xcc, 0xc3, 0x6e, 0x5e, 0x60, 0x5d, 0x5a,
|
||||||
|
};
|
||||||
|
|
||||||
TestDrmCertificates::TestDrmCertificates()
|
TestDrmCertificates::TestDrmCertificates()
|
||||||
: test_root_certificate_(
|
: test_root_certificate_(
|
||||||
@@ -318,11 +689,32 @@ TestDrmCertificates::TestDrmCertificates()
|
|||||||
test_intermediate_certificate_(
|
test_intermediate_certificate_(
|
||||||
kTestIntermediateCertificate,
|
kTestIntermediateCertificate,
|
||||||
kTestIntermediateCertificate + sizeof(kTestIntermediateCertificate)),
|
kTestIntermediateCertificate + sizeof(kTestIntermediateCertificate)),
|
||||||
|
test_intermediate_certificate_with_ec_key_(
|
||||||
|
kTestIntermediateCertificateWithECKey,
|
||||||
|
kTestIntermediateCertificateWithECKey +
|
||||||
|
sizeof(kTestIntermediateCertificateWithECKey)),
|
||||||
test_user_device_certificate_(
|
test_user_device_certificate_(
|
||||||
kTestUserDrmCertificate,
|
kTestUserDrmCertificate,
|
||||||
kTestUserDrmCertificate + sizeof(kTestUserDrmCertificate)),
|
kTestUserDrmCertificate + sizeof(kTestUserDrmCertificate)),
|
||||||
test_service_certificate_(
|
test_user_device_certificate_with_ec_key_(
|
||||||
kTestDrmServiceCertificate,
|
kTestUserDrmCertificateWithECKey,
|
||||||
kTestDrmServiceCertificate + sizeof(kTestDrmServiceCertificate)) {}
|
kTestUserDrmCertificateWithECKey +
|
||||||
|
sizeof(kTestUserDrmCertificateWithECKey)),
|
||||||
|
test_user_device_certificate_with_rot_id_(
|
||||||
|
kTestUserDrmCertificateWithRotId,
|
||||||
|
kTestUserDrmCertificateWithRotId +
|
||||||
|
sizeof(kTestUserDrmCertificateWithRotId)),
|
||||||
|
test_service_certificate_license_sdk_(
|
||||||
|
kTestDrmServiceCertificateLicenseSdk,
|
||||||
|
kTestDrmServiceCertificateLicenseSdk +
|
||||||
|
sizeof(kTestDrmServiceCertificateLicenseSdk)),
|
||||||
|
test_service_certificate_all_types_(
|
||||||
|
kTestDrmServiceCertificateAllTypes,
|
||||||
|
kTestDrmServiceCertificateAllTypes +
|
||||||
|
sizeof(kTestDrmServiceCertificateAllTypes)),
|
||||||
|
test_service_certificate_no_type_(
|
||||||
|
kTestDrmServiceCertificateNoType,
|
||||||
|
kTestDrmServiceCertificateNoType +
|
||||||
|
sizeof(kTestDrmServiceCertificateNoType)) {}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -14,41 +14,78 @@
|
|||||||
#define COMMON_TEST_DRM_CERTIFICATES_H_
|
#define COMMON_TEST_DRM_CERTIFICATES_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "base/macros.h"
|
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
class TestDrmCertificates {
|
class TestDrmCertificates {
|
||||||
public:
|
public:
|
||||||
TestDrmCertificates();
|
TestDrmCertificates();
|
||||||
|
|
||||||
|
TestDrmCertificates(const TestDrmCertificates&) = delete;
|
||||||
|
TestDrmCertificates& operator=(const TestDrmCertificates&) = delete;
|
||||||
|
|
||||||
virtual ~TestDrmCertificates() {}
|
virtual ~TestDrmCertificates() {}
|
||||||
|
|
||||||
// returns a test root certificate
|
// returns a test root certificate
|
||||||
const std::string& test_root_certificate() const { return test_root_certificate_; }
|
const std::string& test_root_certificate() const {
|
||||||
|
return test_root_certificate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a test intermediate certificate with an RSA key
|
||||||
|
|
||||||
// returns a test intermediate certificate
|
|
||||||
const std::string& test_intermediate_certificate() const {
|
const std::string& test_intermediate_certificate() const {
|
||||||
return test_intermediate_certificate_;
|
return test_intermediate_certificate_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns an user device certificate
|
// returns a test intermediate certificate with an EC key
|
||||||
|
const std::string& test_intermediate_certificate_with_ec_key() const {
|
||||||
|
return test_intermediate_certificate_with_ec_key_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a user device certificate with an RSA key
|
||||||
|
|
||||||
const std::string& test_user_device_certificate() const {
|
const std::string& test_user_device_certificate() const {
|
||||||
return test_user_device_certificate_;
|
return test_user_device_certificate_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns a service certificate
|
// returns a user device certificate with an EC key
|
||||||
const std::string& test_service_certificate() const {
|
const std::string& test_user_device_certificate_with_ec_key() const {
|
||||||
return test_service_certificate_;
|
return test_user_device_certificate_with_ec_key_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a user device certificate with a Root of Trust Id.
|
||||||
|
const std::string& test_user_device_certificate_with_rot_id() const {
|
||||||
|
return test_user_device_certificate_with_rot_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a service certificate with license sdk service type
|
||||||
|
const std::string& test_service_certificate_license_sdk() const {
|
||||||
|
return test_service_certificate_license_sdk_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a service certificate with all service types
|
||||||
|
const std::string& test_service_certificate_all_types() const {
|
||||||
|
return test_service_certificate_all_types_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a service certificate prior to a change requiring the service
|
||||||
|
// type to be specified.
|
||||||
|
const std::string& test_service_certificate_no_type() const {
|
||||||
|
return test_service_certificate_no_type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string test_root_certificate_;
|
const std::string test_root_certificate_;
|
||||||
const std::string test_intermediate_certificate_;
|
const std::string test_intermediate_certificate_;
|
||||||
|
const std::string test_intermediate_certificate_with_ec_key_;
|
||||||
const std::string test_user_device_certificate_;
|
const std::string test_user_device_certificate_;
|
||||||
const std::string test_service_certificate_;
|
const std::string test_user_device_certificate_with_ec_key_;
|
||||||
|
const std::string test_user_device_certificate_with_rot_id_;
|
||||||
DISALLOW_COPY_AND_ASSIGN(TestDrmCertificates);
|
const std::string test_service_certificate_license_sdk_;
|
||||||
|
const std::string test_service_certificate_all_types_;
|
||||||
|
const std::string test_service_certificate_no_type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // COMMON_TEST_DRM_CERTIFICATES_H_
|
#endif // COMMON_TEST_DRM_CERTIFICATES_H_
|
||||||
|
|||||||
@@ -253,11 +253,11 @@ Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
|
|||||||
Status status = ca_cert->LoadDer(
|
Status status = ca_cert->LoadDer(
|
||||||
cert_type == kCertificateTypeProduction
|
cert_type == kCertificateTypeProduction
|
||||||
? std::string(reinterpret_cast<const char*>(
|
? std::string(reinterpret_cast<const char*>(
|
||||||
kProdVmpCodeSigningDrmRootCertificate),
|
kProdVmpCodeSigningDrmRootCertificate),
|
||||||
sizeof(kProdVmpCodeSigningDrmRootCertificate))
|
sizeof(kProdVmpCodeSigningDrmRootCertificate))
|
||||||
: std::string(reinterpret_cast<const char*>(
|
: std::string(reinterpret_cast<const char*>(
|
||||||
kDevVmpCodeSigningDrmRootCertificate),
|
kDevVmpCodeSigningDrmRootCertificate),
|
||||||
sizeof(kDevVmpCodeSigningDrmRootCertificate)));
|
sizeof(kDevVmpCodeSigningDrmRootCertificate)));
|
||||||
if (!status.ok()) return status;
|
if (!status.ok()) return status;
|
||||||
|
|
||||||
ca_.reset(new X509CA(ca_cert.release()));
|
ca_.reset(new X509CA(ca_cert.release()));
|
||||||
|
|||||||
@@ -167,7 +167,8 @@ class VmpCheckerTest : public ::testing::Test {
|
|||||||
// |kSameAsPrevious| (empty), then the binary is signed using the same
|
// |kSameAsPrevious| (empty), then the binary is signed using the same
|
||||||
// certificate as the previously added binary. This means that the first
|
// certificate as the previously added binary. This means that the first
|
||||||
// call to this function should not use |kSameAsPrevious|.
|
// call to this function should not use |kSameAsPrevious|.
|
||||||
void AddVmpBinary(const std::string& signing_cert, const std::string& file_name,
|
void AddVmpBinary(const std::string& signing_cert,
|
||||||
|
const std::string& file_name,
|
||||||
const std::string& binary_hash, uint32_t flags) {
|
const std::string& binary_hash, uint32_t flags) {
|
||||||
DCHECK(!signing_cert.empty() || !vmp_data_.certificates().empty());
|
DCHECK(!signing_cert.empty() || !vmp_data_.certificates().empty());
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,8 @@ PreprovKeysMap* PreprovKeysMap::GetSingleton() {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
||||||
const std::string& key_bytes, Cipher cipher,
|
const std::string& key_bytes,
|
||||||
|
Cipher cipher,
|
||||||
const std::string& model_filter)
|
const std::string& model_filter)
|
||||||
: system_id(system_id),
|
: system_id(system_id),
|
||||||
key_bytes(key_bytes),
|
key_bytes(key_bytes),
|
||||||
@@ -93,7 +94,8 @@ WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
|||||||
model_filter(model_filter) {}
|
model_filter(model_filter) {}
|
||||||
|
|
||||||
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
||||||
const std::string& key_bytes, Cipher cipher)
|
const std::string& key_bytes,
|
||||||
|
Cipher cipher)
|
||||||
: system_id(system_id), key_bytes(key_bytes), cipher(cipher) {}
|
: system_id(system_id), key_bytes(key_bytes), cipher(cipher) {}
|
||||||
|
|
||||||
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
||||||
@@ -230,8 +232,10 @@ Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
|||||||
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
|
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
|
||||||
}
|
}
|
||||||
if (version) {
|
if (version) {
|
||||||
|
// Bytes 0-3 contain the keybox version.
|
||||||
*version = BigEndian::Load32(token.data());
|
*version = BigEndian::Load32(token.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This was checked at initialization, so if it fails now something is wrong.
|
// This was checked at initialization, so if it fails now something is wrong.
|
||||||
CHECK_EQ(preprov_key.size(), kPreProvisioningKeySizeBytes);
|
CHECK_EQ(preprov_key.size(), kPreProvisioningKeySizeBytes);
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/macros.h"
|
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "common/status.h"
|
#include "common/status.h"
|
||||||
|
|
||||||
@@ -34,6 +33,9 @@ namespace widevine {
|
|||||||
// INTERNAL_ERROR - something went wrong that shouldn't have been able to.
|
// INTERNAL_ERROR - something went wrong that shouldn't have been able to.
|
||||||
class WvmTokenHandler {
|
class WvmTokenHandler {
|
||||||
public:
|
public:
|
||||||
|
WvmTokenHandler(const WvmTokenHandler&) = delete;
|
||||||
|
WvmTokenHandler& operator=(const WvmTokenHandler&) = delete;
|
||||||
|
|
||||||
// Cipher type to use for encrypting asset keys. This matches the enum in
|
// Cipher type to use for encrypting asset keys. This matches the enum in
|
||||||
// video/widevine/lockbox/public/key.proto.
|
// video/widevine/lockbox/public/key.proto.
|
||||||
enum Cipher {
|
enum Cipher {
|
||||||
@@ -71,22 +73,23 @@ class WvmTokenHandler {
|
|||||||
// insecure_out may be null; if not, *insecure_out will be set to the
|
// insecure_out may be null; if not, *insecure_out will be set to the
|
||||||
// decrypted value of the 'insecure keybox' flag.
|
// decrypted value of the 'insecure keybox' flag.
|
||||||
static Status DecryptDeviceKey(absl::string_view token,
|
static Status DecryptDeviceKey(absl::string_view token,
|
||||||
std::string* device_key_out, Cipher* cipher_out,
|
std::string* device_key_out,
|
||||||
bool* insecure_out);
|
Cipher* cipher_out, bool* insecure_out);
|
||||||
// Same as above, except takes in the make/model from the license request.
|
// 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
|
// For legacy WVM license, we have some special cases where we need to inspect
|
||||||
// the make/model as we apply alternate keys.
|
// the make/model as we apply alternate keys.
|
||||||
static Status DecryptDeviceKey(absl::string_view token,
|
static Status DecryptDeviceKey(absl::string_view token,
|
||||||
const std::string& make_model,
|
const std::string& make_model,
|
||||||
std::string* device_key_out, Cipher* cipher_out,
|
std::string* device_key_out,
|
||||||
bool* insecure_out);
|
Cipher* cipher_out, bool* insecure_out);
|
||||||
|
|
||||||
// Decrypt a token using the preprov key for its system ID, and use the
|
// 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
|
// decrypted device key to encrypt the given asset key. Returns the encrypted
|
||||||
// asset key in result.
|
// asset key in result.
|
||||||
static Status GetEncryptedAssetKey(absl::string_view token,
|
static Status GetEncryptedAssetKey(absl::string_view token,
|
||||||
absl::string_view raw_asset_key,
|
absl::string_view raw_asset_key,
|
||||||
const std::string& make_model, std::string* result);
|
const std::string& make_model,
|
||||||
|
std::string* result);
|
||||||
|
|
||||||
// Extract the system ID component of a token (bytes 4-8).
|
// Extract the system ID component of a token (bytes 4-8).
|
||||||
static uint32_t GetSystemId(absl::string_view token);
|
static uint32_t GetSystemId(absl::string_view token);
|
||||||
@@ -115,9 +118,6 @@ class WvmTokenHandler {
|
|||||||
static Status EncryptAssetKey(absl::string_view device_key,
|
static Status EncryptAssetKey(absl::string_view device_key,
|
||||||
absl::string_view raw_asset_key, Cipher cipher,
|
absl::string_view raw_asset_key, Cipher cipher,
|
||||||
std::string* result);
|
std::string* result);
|
||||||
|
|
||||||
private:
|
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -203,7 +203,8 @@ bool X509Cert::IsCaCertificate() const {
|
|||||||
return X509_check_ca(openssl_cert_) != 0;
|
return X509_check_ca(openssl_cert_) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const {
|
bool X509Cert::GetV3BooleanExtension(const std::string& oid,
|
||||||
|
bool* value) const {
|
||||||
ScopedAsn1Object extension_name(OBJ_txt2obj(oid.c_str(), 1));
|
ScopedAsn1Object extension_name(OBJ_txt2obj(oid.c_str(), 1));
|
||||||
int ext_pos = X509_get_ext_by_OBJ(openssl_cert_, extension_name.get(), -1);
|
int ext_pos = X509_get_ext_by_OBJ(openssl_cert_, extension_name.get(), -1);
|
||||||
if (ext_pos < 0) return false;
|
if (ext_pos < 0) return false;
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/macros.h"
|
|
||||||
#include "base/thread_annotations.h"
|
#include "base/thread_annotations.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "openssl/pem.h"
|
#include "openssl/pem.h"
|
||||||
@@ -39,6 +38,10 @@ class X509Cert {
|
|||||||
static std::unique_ptr<X509Cert> FromOpenSslCert(ScopedX509 openssl_cert_);
|
static std::unique_ptr<X509Cert> FromOpenSslCert(ScopedX509 openssl_cert_);
|
||||||
|
|
||||||
X509Cert();
|
X509Cert();
|
||||||
|
|
||||||
|
X509Cert(const X509Cert&) = delete;
|
||||||
|
X509Cert& operator=(const X509Cert&) = delete;
|
||||||
|
|
||||||
virtual ~X509Cert();
|
virtual ~X509Cert();
|
||||||
|
|
||||||
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
||||||
@@ -98,14 +101,15 @@ class X509Cert {
|
|||||||
std::string subject_name_;
|
std::string subject_name_;
|
||||||
|
|
||||||
friend class X509CertChain;
|
friend class X509CertChain;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(X509Cert);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class which holds a chain of X.509 certificates.
|
// Class which holds a chain of X.509 certificates.
|
||||||
class X509CertChain {
|
class X509CertChain {
|
||||||
public:
|
public:
|
||||||
X509CertChain() {}
|
X509CertChain() = default;
|
||||||
|
X509CertChain(const X509CertChain&) = delete;
|
||||||
|
X509CertChain& operator=(const X509CertChain&) = delete;
|
||||||
|
|
||||||
virtual ~X509CertChain();
|
virtual ~X509CertChain();
|
||||||
|
|
||||||
// Loads a chain of PEM-encoded X.509 certificates. Takes a single parameter,
|
// Loads a chain of PEM-encoded X.509 certificates. Takes a single parameter,
|
||||||
@@ -135,8 +139,6 @@ class X509CertChain {
|
|||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
std::vector<X509Cert*> cert_chain_;
|
std::vector<X509Cert*> cert_chain_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(X509CertChain);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// CA class which holds the root CA cert, and verifies certificate chains.
|
// CA class which holds the root CA cert, and verifies certificate chains.
|
||||||
@@ -144,6 +146,10 @@ class X509CA {
|
|||||||
public:
|
public:
|
||||||
// New object assumes ownership of |ca_cert|.
|
// New object assumes ownership of |ca_cert|.
|
||||||
explicit X509CA(X509Cert* ca_cert);
|
explicit X509CA(X509Cert* ca_cert);
|
||||||
|
|
||||||
|
X509CA(const X509CA&) = delete;
|
||||||
|
X509CA& operator=(const X509CA&) = delete;
|
||||||
|
|
||||||
virtual ~X509CA();
|
virtual ~X509CA();
|
||||||
|
|
||||||
// Does X.509 PKI validation of |cert| against the root CA certificate
|
// Does X.509 PKI validation of |cert| against the root CA certificate
|
||||||
@@ -166,9 +172,7 @@ class X509CA {
|
|||||||
|
|
||||||
std::unique_ptr<X509Cert> ca_cert_;
|
std::unique_ptr<X509Cert> ca_cert_;
|
||||||
absl::Mutex openssl_store_mutex_;
|
absl::Mutex openssl_store_mutex_;
|
||||||
X509_STORE* openssl_store_ GUARDED_BY(openssl_store_mutex_);
|
X509_STORE* openssl_store_ ABSL_GUARDED_BY(openssl_store_mutex_);
|
||||||
|
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(X509CA);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ cc_binary(
|
|||||||
"//base",
|
"//base",
|
||||||
"//common:status",
|
"//common:status",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
|
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace cas {
|
namespace cas {
|
||||||
|
|
||||||
const char kTestEcmgChannelSetup[] = {
|
constexpr char kTestEcmgChannelSetup[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x00', '\x01', // message_type - Channel_setup
|
'\x00', '\x01', // message_type - Channel_setup
|
||||||
'\x00', '\x0e', // message_length
|
'\x00', '\x0e', // message_length
|
||||||
@@ -26,7 +26,55 @@ const char kTestEcmgChannelSetup[] = {
|
|||||||
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
|
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestEcmgChannelStatus[] = {
|
constexpr char kTestEcmgChannelSetupWithPrivateParameters[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x00', '\x01', // message_type - Channel_setup
|
||||||
|
'\x00', '\x70', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x01', // parameter_type- SUPER_CAS_ID
|
||||||
|
'\x00', '\x04', // parameter_length
|
||||||
|
'\x4a', '\xd4', '\x00', '\x00', // parameter_value
|
||||||
|
'\x80', '\x00', // parameter_type - AGE_RESTRICTION
|
||||||
|
'\x00', '\x01', // parameter_length
|
||||||
|
'\x00', // parameter_value
|
||||||
|
'\x80', '\x01', // parameter_type - CRYPTO_MODE
|
||||||
|
'\x00', '\x07', // parameter_length
|
||||||
|
'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value
|
||||||
|
'\x80', '\x04', // parameter_type - TRACK_TYPES
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'S', 'D', // parameter_value
|
||||||
|
'\x80', '\x04', // parameter_type - TRACK_TYPES
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'H', 'D', // parameter_value
|
||||||
|
'\x80', '\x02', // parameter_type - CONTENT_ID
|
||||||
|
'\x00', '\x09', // parameter_length
|
||||||
|
'C', 'a', 's', 'T', 's', 'F', 'a', 'k',
|
||||||
|
'e', // parameter_value - CasTsFake
|
||||||
|
'\x80', '\x03', // parameter_type - CONTENT_PROVIDER
|
||||||
|
'\x00', '\x0d', // parameter_length
|
||||||
|
'w', 'i', 'd', 'e', 'v', 'i', 'n', 'e',
|
||||||
|
'_', 't', 'e', 's', 't', // parameter_value - widevine_test
|
||||||
|
'\x80', '\x06', // parameter_type - CONTENT_IV
|
||||||
|
'\x00', '\x10', // parameter_length
|
||||||
|
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
|
||||||
|
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
|
||||||
|
'\x80', '\x06', // parameter_type - CONTENT_IV
|
||||||
|
'\x00', '\x10', // parameter_length
|
||||||
|
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
|
||||||
|
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
|
||||||
|
|
||||||
|
constexpr char kTestEcmgChannelTest[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x00', '\x02', // message_type - Channel_test
|
||||||
|
'\x00', '\x06', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr char kTestEcmgChannelStatus[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x00', '\x03', // message_type - Channel_status
|
'\x00', '\x03', // message_type - Channel_status
|
||||||
'\x00', '\x39', // message_length
|
'\x00', '\x39', // message_length
|
||||||
@@ -62,7 +110,28 @@ const char kTestEcmgChannelStatus[] = {
|
|||||||
'\x00', '\x64' // parameter_value
|
'\x00', '\x64' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestEcmgStreamSetup[] = {
|
constexpr char kTestEcmgStreamSetupWithPrivateParameters[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x01', '\x01', // message_type - Stream_setup
|
||||||
|
'\x00', '\x1e', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x19', // parameter_type - ECM_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x02', // parameter_value
|
||||||
|
'\x00', '\x10', // parameter_type - nominal_CP_duration
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x64', // parameter_value
|
||||||
|
'\x80', '\x05', // parameter_type - STREAM_TRACK_TYPE
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'S', 'D', // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr char kTestEcmgStreamSetup[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x01', '\x01', // message_type - Stream_setup
|
'\x01', '\x01', // message_type - Stream_setup
|
||||||
'\x00', '\x18', // message_length
|
'\x00', '\x18', // message_length
|
||||||
@@ -80,7 +149,19 @@ const char kTestEcmgStreamSetup[] = {
|
|||||||
'\x00', '\x64' // parameter_value
|
'\x00', '\x64' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestEcmgStreamStatus[] = {
|
constexpr char kTestEcmgStreamTest[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x01', '\x02', // message_type - Stream_test
|
||||||
|
'\x00', '\x0c', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr char kTestEcmgStreamStatus[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x01', '\x03', // message_type - Stream_status
|
'\x01', '\x03', // message_type - Stream_status
|
||||||
'\x00', '\x17', // message_length
|
'\x00', '\x17', // message_length
|
||||||
@@ -98,7 +179,7 @@ const char kTestEcmgStreamStatus[] = {
|
|||||||
'\x01' // parameter_value
|
'\x01' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestEcmgCwProvision[] = {
|
constexpr char kTestEcmgCwProvision[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x02', '\x01', // message_type - CW_provision
|
'\x02', '\x01', // message_type - CW_provision
|
||||||
'\x00', '\x44', // message_length
|
'\x00', '\x44', // message_length
|
||||||
@@ -125,8 +206,87 @@ const char kTestEcmgCwProvision[] = {
|
|||||||
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
|
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
|
||||||
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
|
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
|
||||||
|
|
||||||
|
constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x02', '\x01', // message_type - CW_provision
|
||||||
|
'\x00', '\xaa', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x12', // parameter_type - CP_number
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x13', // parameter_type - CP_duration
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x64', // parameter_value
|
||||||
|
'\x00', '\x14', // parameter_type - CP_CW_Combination
|
||||||
|
'\x00', '\x12', // parameter_length
|
||||||
|
'\x00', '\x00', // parameter_value - CP (2 bytes) then CW next (16 bytes)
|
||||||
|
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
|
||||||
|
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
|
||||||
|
'\x00', '\x14', // parameter_type - CP_CW_Combination
|
||||||
|
'\x00', '\x12', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value - CP (2 bytes) then CW next (16 bytes)
|
||||||
|
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
|
||||||
|
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
|
||||||
|
'\x00', '\x0d', // parameter_type - access_criteria
|
||||||
|
'\x00', '\x62', // parameter_length
|
||||||
|
'\x80', '\x00', // access_criteria parameter_type - AGE_RESTRICTION
|
||||||
|
'\x00', '\x01', // parameter_length
|
||||||
|
'\x00', // parameter_value
|
||||||
|
'\x80', '\x01', // access_criteria parameter_type - CRYPTO_MODE
|
||||||
|
'\x00', '\x07', // parameter_length
|
||||||
|
'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value
|
||||||
|
'\x80', '\x04', // access_criteria parameter_type - TRACK_TYPES
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'S', 'D', // parameter_value
|
||||||
|
'\x80', '\x05', // access_criteria parameter_type - STREAM_TRACK_TYPE
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'S', 'D', // parameter_value
|
||||||
|
'\x80', '\x02', // access_criteria parameter_type - CONTENT_ID
|
||||||
|
'\x00', '\x09', // parameter_length
|
||||||
|
'C', 'a', 's', 'T', 's', 'F', 'a', 'k',
|
||||||
|
'e', // parameter_value - CasTsFake
|
||||||
|
'\x80', '\x03', // access_criteria parameter_type - CONTENT_PROVIDER
|
||||||
|
'\x00', '\x0d', // parameter_length
|
||||||
|
'w', 'i', 'd', 'e', 'v', 'i', 'n', 'e',
|
||||||
|
'_', 't', 'e', 's', 't', // parameter_value - widevine_test
|
||||||
|
'\x80', '\x06', // access_criteria parameter_type - CONTENT_IV
|
||||||
|
'\x00', '\x10', // parameter_length
|
||||||
|
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
|
||||||
|
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
|
||||||
|
'\x80', '\x06', // access_criteria parameter_type - CONTENT_IV
|
||||||
|
'\x00', '\x10', // parameter_length
|
||||||
|
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
|
||||||
|
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
|
||||||
|
|
||||||
|
constexpr char kTestEcmgCwProvisionSingleKey[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x02', '\x01', // message_type - CW_provision
|
||||||
|
'\x00', '\x2e', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x12', // parameter_type - CP_number
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x00', // parameter_value
|
||||||
|
'\x00', '\x13', // parameter_type - CP_duration
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x64', // parameter_value
|
||||||
|
'\x00', '\x14', // parameter_type - CP_CW_Combination
|
||||||
|
'\x00', '\x12', // parameter_length
|
||||||
|
'\x00', '\x00', // parameter_value - CP (2 bytes) then CW next (16 bytes)
|
||||||
|
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
|
||||||
|
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f'};
|
||||||
|
|
||||||
// CW is encrypted using hardcoded fixed entitlement key.
|
// CW is encrypted using hardcoded fixed entitlement key.
|
||||||
const char kTestEcmgEcmResponse[] = {
|
constexpr char kTestEcmgEcmResponse[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x02', '\x02', // message_type - ECM_response
|
'\x02', '\x02', // message_type - ECM_response
|
||||||
'\x00', '\xd2', // message_length
|
'\x00', '\xd2', // message_length
|
||||||
@@ -142,8 +302,8 @@ const char kTestEcmgEcmResponse[] = {
|
|||||||
'\x00', '\x15', // parameter_type - ECM_datagram
|
'\x00', '\x15', // parameter_type - ECM_datagram
|
||||||
'\x00', '\xbc', // parameter_length
|
'\x00', '\xbc', // parameter_length
|
||||||
// parameter_value - ECM_datagram
|
// parameter_value - ECM_datagram
|
||||||
'\x47', '\x40', '\x02', '\x10', '\x00', '\x80', '\x70', '\x95', '\x4a',
|
'\x47', '\x40', '\x00', '\x10', '\x00', '\x80', '\x70', '\xa5', '\x4a',
|
||||||
'\xd4', '\x01', '\x05', '\x80', '\x66', '\x61', '\x6b', '\x65', '\x5f',
|
'\xd4', '\x02', '\x0b', '\xc0', '\x66', '\x61', '\x6b', '\x65', '\x5f',
|
||||||
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x31', '\x2e', '\x2e',
|
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x31', '\x2e', '\x2e',
|
||||||
'\x2e', '\x2e', '\xef', '\x40', '\x57', '\x48', '\xa7', '\xad', '\xdd',
|
'\x2e', '\x2e', '\xef', '\x40', '\x57', '\x48', '\xa7', '\xad', '\xdd',
|
||||||
'\x34', '\x73', '\xfe', '\x5d', '\x1c', '\x65', '\xa0', '\xbf', '\x93',
|
'\x34', '\x73', '\xfe', '\x5d', '\x1c', '\x65', '\xa0', '\xbf', '\x93',
|
||||||
@@ -164,7 +324,7 @@ const char kTestEcmgEcmResponse[] = {
|
|||||||
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||||
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'};
|
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'};
|
||||||
|
|
||||||
const char kTestEcmgStreamCloseRequest[] = {
|
constexpr char kTestEcmgStreamClose[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x01', '\x04', // message_type - Stream_close_request
|
'\x01', '\x04', // message_type - Stream_close_request
|
||||||
'\x00', '\x0c', // message_length
|
'\x00', '\x0c', // message_length
|
||||||
@@ -176,7 +336,7 @@ const char kTestEcmgStreamCloseRequest[] = {
|
|||||||
'\x00', '\x01' // parameter_value
|
'\x00', '\x01' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestEcmgStreamCloseResponse[] = {
|
constexpr char kTestEcmgStreamCloseResponse[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x01', '\x05', // message_type - Stream_close_response
|
'\x01', '\x05', // message_type - Stream_close_response
|
||||||
'\x00', '\x0c', // message_length
|
'\x00', '\x0c', // message_length
|
||||||
@@ -188,7 +348,7 @@ const char kTestEcmgStreamCloseResponse[] = {
|
|||||||
'\x00', '\x01' // parameter_value
|
'\x00', '\x01' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kTestEcmgChannelClose[] = {
|
constexpr char kTestEcmgChannelClose[] = {
|
||||||
'\x03', // protocol_version
|
'\x03', // protocol_version
|
||||||
'\x00', '\x04', // message_type - Channel_close
|
'\x00', '\x04', // message_type - Channel_close
|
||||||
'\x00', '\x06', // message_length
|
'\x00', '\x06', // message_length
|
||||||
@@ -197,6 +357,33 @@ const char kTestEcmgChannelClose[] = {
|
|||||||
'\x00', '\x01' // parameter_value
|
'\x00', '\x01' // parameter_value
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr char kTestChannelErrorResponse[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x00', '\x05', // message_type - Channel_error_response
|
||||||
|
'\x00', '\x0c', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x70', '\x00', // parameter_type - error_status
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x00' // parameter_value: actual value varies.
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr char kTestStreamErrorResponse[] = {
|
||||||
|
'\x03', // protocol_version
|
||||||
|
'\x01', '\x06', // message_type - Stream_error_response
|
||||||
|
'\x00', '\x12', // message_length
|
||||||
|
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x00', '\x0f', // parameter_type - ECM_stream_id
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x01', // parameter_value
|
||||||
|
'\x70', '\x00', // parameter_type - error_status
|
||||||
|
'\x00', '\x02', // parameter_length
|
||||||
|
'\x00', '\x00' // parameter_value: actual value varies.
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace cas
|
} // namespace cas
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ cc_library(
|
|||||||
"//common:status",
|
"//common:status",
|
||||||
"//common:string_util",
|
"//common:string_util",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_cc_proto",
|
||||||
"//protos/public:media_cas_proto",
|
"//protos/public:media_cas_encryption_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ cc_test(
|
|||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"//common:status",
|
"//common:status",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ cc_test(
|
|||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/memory",
|
"@abseil_repo//absl/memory",
|
||||||
"//common:aes_cbc_util",
|
"//common:aes_cbc_util",
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,19 +85,23 @@ cc_library(
|
|||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
":ecm",
|
":ecm",
|
||||||
":ecm_generator",
|
|
||||||
":fixed_key_fetcher",
|
":fixed_key_fetcher",
|
||||||
|
":key_fetcher",
|
||||||
":mpeg2ts",
|
":mpeg2ts",
|
||||||
":simulcrypt_util",
|
":simulcrypt_util",
|
||||||
":util",
|
":util",
|
||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"@abseil_repo//absl/container:node_hash_map",
|
||||||
"@abseil_repo//absl/memory",
|
"@abseil_repo//absl/memory",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
|
"@abseil_repo//absl/strings:str_format",
|
||||||
"//common:crypto_util",
|
"//common:crypto_util",
|
||||||
|
"//common:random_util",
|
||||||
"//common:status",
|
"//common:status",
|
||||||
"//example:constants",
|
"//example:constants",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_ecm",
|
"//media_cas_packager_sdk/public:wv_cas_ecm",
|
||||||
|
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
|
||||||
"//media_cas_packager_sdk/public:wv_cas_types",
|
"//media_cas_packager_sdk/public:wv_cas_types",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -108,8 +112,12 @@ cc_test(
|
|||||||
srcs = ["ecmg_client_handler_test.cc"],
|
srcs = ["ecmg_client_handler_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
":ecmg_client_handler",
|
":ecmg_client_handler",
|
||||||
|
":simulcrypt_util",
|
||||||
|
":util",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/memory",
|
"@abseil_repo//absl/memory",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"@abseil_repo//absl/strings:str_format",
|
||||||
"//example:test_ecmg_messages",
|
"//example:test_ecmg_messages",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -127,6 +135,7 @@ cc_library(
|
|||||||
":util",
|
":util",
|
||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"@abseil_repo//absl/strings:str_format",
|
||||||
"//common:status",
|
"//common:status",
|
||||||
"//example:test_emmg_messages",
|
"//example:test_emmg_messages",
|
||||||
],
|
],
|
||||||
@@ -140,6 +149,7 @@ cc_test(
|
|||||||
":emmg",
|
":emmg",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/memory",
|
"@abseil_repo//absl/memory",
|
||||||
|
"@abseil_repo//absl/strings:str_format",
|
||||||
"//example:test_emmg_messages",
|
"//example:test_emmg_messages",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -155,7 +165,7 @@ cc_library(
|
|||||||
deps = [
|
deps = [
|
||||||
":key_fetcher",
|
":key_fetcher",
|
||||||
"//common:status",
|
"//common:status",
|
||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -37,14 +37,12 @@ static constexpr int kNumBitsCaSystemIdField = 16;
|
|||||||
static constexpr int kNumBitsEcmVersionField = 8;
|
static constexpr int kNumBitsEcmVersionField = 8;
|
||||||
|
|
||||||
// Byte 3
|
// Byte 3
|
||||||
static constexpr int kNumBitsEcmGenerationCountField = 5;
|
// Unused bits (mbz, must be zero)
|
||||||
|
static constexpr int kNumBitsUnusedFieldByte3 = 3;
|
||||||
// Values for Decrypt Mode are from enum CryptoMode
|
// Values for Decrypt Mode are from enum CryptoMode
|
||||||
static constexpr int kNumBitsDecryptModeField = 2;
|
static constexpr int kNumBitsDecryptModeField = 4;
|
||||||
static constexpr int kNumBitsRotationEnabledField = 1;
|
static constexpr int kNumBitsRotationEnabledField = 1;
|
||||||
|
|
||||||
static constexpr int kMaxGeneration =
|
|
||||||
(1 << kNumBitsEcmGenerationCountField) - 1;
|
|
||||||
|
|
||||||
// Byte 4
|
// Byte 4
|
||||||
// Size of IVs.
|
// Size of IVs.
|
||||||
// Values for IV size fields are from enum EcmIvSize
|
// Values for IV size fields are from enum EcmIvSize
|
||||||
@@ -52,8 +50,12 @@ static constexpr int kMaxGeneration =
|
|||||||
// always be set to 1.
|
// always be set to 1.
|
||||||
static constexpr int kNumBitsWrappedKeyIvSizeField = 1;
|
static constexpr int kNumBitsWrappedKeyIvSizeField = 1;
|
||||||
static constexpr int kNumBitsContentIvSizeField = 1;
|
static constexpr int kNumBitsContentIvSizeField = 1;
|
||||||
|
static constexpr int kNumBitsAgeRestriction = 5;
|
||||||
// Unused bits (mbz, must be zero)
|
// Unused bits (mbz, must be zero)
|
||||||
static constexpr int kNumBitsUnusedField = 6;
|
static constexpr int kNumBitsUnusedFieldByte4 = 1;
|
||||||
|
|
||||||
|
// Total bits equals number of bytes * 8.
|
||||||
|
static constexpr int kBitSetTotalSize = 40;
|
||||||
|
|
||||||
// Remaining bytes (starting from the 6th byte) hold entitled key info.
|
// Remaining bytes (starting from the 6th byte) hold entitled key info.
|
||||||
static constexpr size_t kKeyIdSizeBytes = 16;
|
static constexpr size_t kKeyIdSizeBytes = 16;
|
||||||
@@ -71,7 +73,7 @@ static constexpr int kWvCasCaSystemId = 0x4AD4;
|
|||||||
|
|
||||||
// Version - this should be incremented if there are non-backwards compatible
|
// Version - this should be incremented if there are non-backwards compatible
|
||||||
// changes to the ECM.
|
// changes to the ECM.
|
||||||
static constexpr int kEcmVersion = 1;
|
static constexpr int kEcmVersion = 2;
|
||||||
|
|
||||||
// Settings for RotationEnabled field.
|
// Settings for RotationEnabled field.
|
||||||
static constexpr int kRotationDisabled = 0;
|
static constexpr int kRotationDisabled = 0;
|
||||||
@@ -144,10 +146,7 @@ Status Ecm::Initialize(const std::string& content_id,
|
|||||||
content_provider_ = content_provider;
|
content_provider_ = content_provider;
|
||||||
paired_keys_required_ = ecm_init_parameters.key_rotation_enabled;
|
paired_keys_required_ = ecm_init_parameters.key_rotation_enabled;
|
||||||
track_types_ = ecm_init_parameters.track_types;
|
track_types_ = ecm_init_parameters.track_types;
|
||||||
// Generation is incremented before the ECM is generated.
|
age_restriction_ = ecm_init_parameters.age_restriction;
|
||||||
// Initializing to kMaxGeneration ensures the first generated ECM has a gen
|
|
||||||
// count of zero.
|
|
||||||
generation_ = kMaxGeneration;
|
|
||||||
|
|
||||||
// Construct and return CasEncryptionRequest message for caller to use.
|
// Construct and return CasEncryptionRequest message for caller to use.
|
||||||
Status status = CreateEntitlementRequest(key_request_message);
|
Status status = CreateEntitlementRequest(key_request_message);
|
||||||
@@ -162,6 +161,43 @@ Status Ecm::Initialize(const std::string& content_id,
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status Ecm::Initialize(
|
||||||
|
const EcmInitParameters& ecm_init_parameters,
|
||||||
|
const std::vector<InjectedEntitlementKeyInfo>& injected_entitlements) {
|
||||||
|
if (initialized_) {
|
||||||
|
return {error::INTERNAL, "Already initialized."};
|
||||||
|
}
|
||||||
|
if (ecm_init_parameters.track_types.empty()) {
|
||||||
|
return {error::INVALID_ARGUMENT,
|
||||||
|
"Parameter track_ids must be set with one or more Track IDs."};
|
||||||
|
}
|
||||||
|
if (!ConvertIvSizeParam(ecm_init_parameters.content_iv_size,
|
||||||
|
&content_iv_size_)) {
|
||||||
|
return {error::INVALID_ARGUMENT,
|
||||||
|
"Parameter content_iv_size must be kIvSize8 or kIvSize16."};
|
||||||
|
}
|
||||||
|
|
||||||
|
crypto_mode_ = ecm_init_parameters.crypto_mode;
|
||||||
|
paired_keys_required_ = ecm_init_parameters.key_rotation_enabled;
|
||||||
|
track_types_ = ecm_init_parameters.track_types;
|
||||||
|
age_restriction_ = ecm_init_parameters.age_restriction;
|
||||||
|
|
||||||
|
ClearEntitlementKeys();
|
||||||
|
for (const auto& entitlement : injected_entitlements) {
|
||||||
|
PushEntitlementKey(entitlement.track_type, entitlement.is_even_key,
|
||||||
|
{entitlement.key_id, entitlement.key_value});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckEntitlementKeys()) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
"Improper injected entitlement keys.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything is set up including entitlement keys.
|
||||||
|
initialized_ = true;
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
Status Ecm::ProcessCasEncryptionResponse(const std::string& response) {
|
Status Ecm::ProcessCasEncryptionResponse(const std::string& response) {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return {error::INTERNAL, "Not initialized."};
|
return {error::INTERNAL, "Not initialized."};
|
||||||
@@ -173,8 +209,8 @@ Status Ecm::ProcessCasEncryptionResponse(const std::string& response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::GenerateEcm(EntitledKeyInfo* even_key, EntitledKeyInfo* odd_key,
|
Status Ecm::GenerateEcm(EntitledKeyInfo* even_key, EntitledKeyInfo* odd_key,
|
||||||
const std::string& track_type, std::string* serialized_ecm,
|
const std::string& track_type,
|
||||||
uint32_t* generation) {
|
std::string* serialized_ecm) const {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return {error::INTERNAL, "Not initialized."};
|
return {error::INTERNAL, "Not initialized."};
|
||||||
}
|
}
|
||||||
@@ -190,11 +226,12 @@ Status Ecm::GenerateEcm(EntitledKeyInfo* even_key, EntitledKeyInfo* odd_key,
|
|||||||
keys.push_back(even_key);
|
keys.push_back(even_key);
|
||||||
keys.push_back(odd_key);
|
keys.push_back(odd_key);
|
||||||
|
|
||||||
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
|
return GenerateEcmCommon(keys, track_type, serialized_ecm);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::GenerateSingleKeyEcm(EntitledKeyInfo* key, const std::string& track_type,
|
Status Ecm::GenerateSingleKeyEcm(EntitledKeyInfo* key,
|
||||||
std::string* serialized_ecm, uint32_t* generation) {
|
const std::string& track_type,
|
||||||
|
std::string* serialized_ecm) const {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return {error::INTERNAL, "Not initialized."};
|
return {error::INTERNAL, "Not initialized."};
|
||||||
}
|
}
|
||||||
@@ -209,18 +246,15 @@ Status Ecm::GenerateSingleKeyEcm(EntitledKeyInfo* key, const std::string& track_
|
|||||||
std::vector<EntitledKeyInfo*> keys;
|
std::vector<EntitledKeyInfo*> keys;
|
||||||
keys.push_back(key);
|
keys.push_back(key);
|
||||||
|
|
||||||
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
|
return GenerateEcmCommon(keys, track_type, serialized_ecm);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
|
Status Ecm::GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
|
||||||
const std::string& track_type, std::string* serialized_ecm,
|
const std::string& track_type,
|
||||||
uint32_t* generation) {
|
std::string* serialized_ecm) const {
|
||||||
if (serialized_ecm == nullptr) {
|
if (serialized_ecm == nullptr) {
|
||||||
return {error::INVALID_ARGUMENT, "No return ecm std::string pointer."};
|
return {error::INVALID_ARGUMENT, "No return ecm std::string pointer."};
|
||||||
}
|
}
|
||||||
if (generation == nullptr) {
|
|
||||||
return {error::INVALID_ARGUMENT, "No return generation pointer."};
|
|
||||||
}
|
|
||||||
|
|
||||||
Status status = ValidateKeys(keys);
|
Status status = ValidateKeys(keys);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
@@ -240,29 +274,13 @@ Status Ecm::GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
|
|||||||
// TODO(user): validate inputs, compare against current values
|
// TODO(user): validate inputs, compare against current values
|
||||||
// TODO(user): replace current values.
|
// TODO(user): replace current values.
|
||||||
|
|
||||||
// Updates complete, we are complete and consistent.
|
|
||||||
// Update generation before serializing.
|
|
||||||
uint32_t previous_generation = generation_;
|
|
||||||
IncrementGeneration();
|
|
||||||
|
|
||||||
// Generate TS packet payload for ECM, pass back to caller.
|
// Generate TS packet payload for ECM, pass back to caller.
|
||||||
serialized_ecm->assign(SerializeEcm(keys));
|
serialized_ecm->assign(SerializeEcm(keys));
|
||||||
if (kMaxEcmSizeBytes < serialized_ecm->size()) {
|
|
||||||
generation_ = previous_generation;
|
|
||||||
serialized_ecm->clear();
|
|
||||||
return Status(error::INTERNAL, "Maximum size of ECM has been exceeded.");
|
|
||||||
}
|
|
||||||
*generation = generation_;
|
|
||||||
|
|
||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ecm::IncrementGeneration() {
|
|
||||||
generation_ = (generation_ >= kMaxGeneration) ? 0 : generation_ + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status Ecm::WrapEntitledKeys(const std::string& track_type,
|
Status Ecm::WrapEntitledKeys(const std::string& track_type,
|
||||||
const std::vector<EntitledKeyInfo*> keys) {
|
const std::vector<EntitledKeyInfo*>& keys) const {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return {error::INTERNAL, "Not initialized."};
|
return {error::INTERNAL, "Not initialized."};
|
||||||
}
|
}
|
||||||
@@ -296,8 +314,10 @@ Status Ecm::WrapEntitledKeys(const std::string& track_type,
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::WrapKey(const std::string& wrapping_key, const std::string& wrapping_iv,
|
Status Ecm::WrapKey(const std::string& wrapping_key,
|
||||||
const std::string& key_value, std::string* wrapped_key) {
|
const std::string& wrapping_iv,
|
||||||
|
const std::string& key_value,
|
||||||
|
std::string* wrapped_key) const {
|
||||||
Status status = ValidateKeyValue(wrapping_key, kWrappingKeySizeBytes);
|
Status status = ValidateKeyValue(wrapping_key, kWrappingKeySizeBytes);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return status;
|
return status;
|
||||||
@@ -319,7 +339,7 @@ Status Ecm::WrapKey(const std::string& wrapping_key, const std::string& wrapping
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) {
|
Status Ecm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) const {
|
||||||
for (const auto& key : keys) {
|
for (const auto& key : keys) {
|
||||||
Status status;
|
Status status;
|
||||||
status = ValidateKeyId(key->key_id);
|
status = ValidateKeyId(key->key_id);
|
||||||
@@ -342,7 +362,8 @@ Status Ecm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) {
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys) {
|
Status Ecm::ValidateWrappedKeys(
|
||||||
|
const std::vector<EntitledKeyInfo*>& keys) const {
|
||||||
for (const auto& key : keys) {
|
for (const auto& key : keys) {
|
||||||
Status status;
|
Status status;
|
||||||
status = ValidateKeyId(key->key_id);
|
status = ValidateKeyId(key->key_id);
|
||||||
@@ -366,14 +387,15 @@ Status Ecm::ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys) {
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::ValidateKeyId(const std::string& key_id) {
|
Status Ecm::ValidateKeyId(const std::string& key_id) const {
|
||||||
if (key_id.size() != kKeyIdSizeBytes) {
|
if (key_id.size() != kKeyIdSizeBytes) {
|
||||||
return {error::INVALID_ARGUMENT, "Key ID must be 16 bytes."};
|
return {error::INVALID_ARGUMENT, "Key ID must be 16 bytes."};
|
||||||
}
|
}
|
||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::ValidateKeyValue(const std::string& key_value, size_t key_value_size) {
|
Status Ecm::ValidateKeyValue(const std::string& key_value,
|
||||||
|
size_t key_value_size) const {
|
||||||
if (key_value.size() != key_value_size) {
|
if (key_value.size() != key_value_size) {
|
||||||
return Status(
|
return Status(
|
||||||
error::INVALID_ARGUMENT,
|
error::INVALID_ARGUMENT,
|
||||||
@@ -382,19 +404,18 @@ Status Ecm::ValidateKeyValue(const std::string& key_value, size_t key_value_size
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::ValidateIv(const std::string& iv, size_t size) {
|
Status Ecm::ValidateIv(const std::string& iv, size_t size) const {
|
||||||
if (iv.size() != size) {
|
if (iv.size() != size) {
|
||||||
return {error::INVALID_ARGUMENT, "IV is wrong size."};
|
return {error::INVALID_ARGUMENT, "IV is wrong size."};
|
||||||
}
|
}
|
||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Ecm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
|
std::string Ecm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) const {
|
||||||
// Five bytes (40 bits including padding)
|
// Five bytes (40 bits including padding)
|
||||||
std::bitset<kNumBitsCaSystemIdField> ca_system_id(kWvCasCaSystemId);
|
std::bitset<kNumBitsCaSystemIdField> ca_system_id(kWvCasCaSystemId);
|
||||||
std::bitset<kNumBitsEcmVersionField> ecm_version(kEcmVersion);
|
std::bitset<kNumBitsEcmVersionField> ecm_version(kEcmVersion);
|
||||||
std::bitset<kNumBitsEcmGenerationCountField> ecm_generation_count(
|
std::bitset<kNumBitsUnusedFieldByte3> unused(kUnusedZero);
|
||||||
generation());
|
|
||||||
std::bitset<kNumBitsDecryptModeField> decrypt_mode(
|
std::bitset<kNumBitsDecryptModeField> decrypt_mode(
|
||||||
static_cast<int>(crypto_mode()));
|
static_cast<int>(crypto_mode()));
|
||||||
std::bitset<kNumBitsRotationEnabledField> rotation_enabled(
|
std::bitset<kNumBitsRotationEnabledField> rotation_enabled(
|
||||||
@@ -403,21 +424,23 @@ std::string Ecm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
|
|||||||
IvSizeFieldValue(kWrappedKeyIvSizeBytes));
|
IvSizeFieldValue(kWrappedKeyIvSizeBytes));
|
||||||
std::bitset<kNumBitsContentIvSizeField> content_iv_size(
|
std::bitset<kNumBitsContentIvSizeField> content_iv_size(
|
||||||
IvSizeFieldValue(this->content_iv_size()));
|
IvSizeFieldValue(this->content_iv_size()));
|
||||||
std::bitset<kNumBitsUnusedField> padding(kUnusedZero);
|
std::bitset<kNumBitsAgeRestriction> age_restriction(
|
||||||
|
static_cast<int>(this->age_restriction()));
|
||||||
|
std::bitset<kNumBitsUnusedFieldByte4> padding(kUnusedZero);
|
||||||
|
|
||||||
// Converts bitset to string.
|
// Converts bitset to string.
|
||||||
std::string ecm_bitset = absl::StrCat(
|
std::string ecm_bitset = absl::StrCat(
|
||||||
ca_system_id.to_string(), ecm_version.to_string(),
|
ca_system_id.to_string(), ecm_version.to_string(), unused.to_string(),
|
||||||
ecm_generation_count.to_string(), decrypt_mode.to_string(),
|
decrypt_mode.to_string(), rotation_enabled.to_string(),
|
||||||
rotation_enabled.to_string(), wrapped_key_iv_size.to_string(),
|
wrapped_key_iv_size.to_string(), content_iv_size.to_string(),
|
||||||
content_iv_size.to_string(), padding.to_string());
|
age_restriction.to_string(), padding.to_string());
|
||||||
if (ecm_bitset.size() != 40) {
|
if (ecm_bitset.size() != kBitSetTotalSize) {
|
||||||
LOG(FATAL) << "ECM bitset incorret size: " << ecm_bitset.size();
|
LOG(FATAL) << "ECM bitset incorrect size: " << ecm_bitset.size();
|
||||||
}
|
}
|
||||||
std::string serialized_ecm;
|
std::string serialized_ecm;
|
||||||
Status status =
|
Status status =
|
||||||
string_util::BitsetStringToBinaryString(ecm_bitset, &serialized_ecm);
|
string_util::BitsetStringToBinaryString(ecm_bitset, &serialized_ecm);
|
||||||
if (!status.ok()) {
|
if (!status.ok() || serialized_ecm.empty()) {
|
||||||
LOG(FATAL) << "Failed to convert ECM bitset to std::string";
|
LOG(FATAL) << "Failed to convert ECM bitset to std::string";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +452,7 @@ std::string Ecm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
|
|||||||
return serialized_ecm;
|
return serialized_ecm;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Ecm::CreateEntitlementRequest(std::string* request_string) {
|
Status Ecm::CreateEntitlementRequest(std::string* request_string) const {
|
||||||
CasEncryptionRequest request;
|
CasEncryptionRequest request;
|
||||||
|
|
||||||
request.set_content_id(content_id_);
|
request.set_content_id(content_id_);
|
||||||
@@ -460,7 +483,7 @@ Status Ecm::ParseEntitlementResponse(const std::string& response_string) {
|
|||||||
if (!response.ParseFromString(signed_response.response())) {
|
if (!response.ParseFromString(signed_response.response())) {
|
||||||
return {error::INTERNAL, "Failure parsing signed response."};
|
return {error::INTERNAL, "Failure parsing signed response."};
|
||||||
}
|
}
|
||||||
if (response.status() != CasEncryptionResponse_Status_OK) {
|
if (response.status() != CasEncryptionResponse::OK) {
|
||||||
return Status(error::INTERNAL, absl::StrCat("Failure reported by server: ",
|
return Status(error::INTERNAL, absl::StrCat("Failure reported by server: ",
|
||||||
response.status(), " : ",
|
response.status(), " : ",
|
||||||
response.status_message()));
|
response.status_message()));
|
||||||
@@ -507,13 +530,13 @@ Status Ecm::ParseEntitlementResponse(const std::string& response_string) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (paired_keys_required()) {
|
if (paired_keys_required()) {
|
||||||
if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_EVEN) {
|
if (key.key_slot() == CasEncryptionResponse::KeyInfo::EVEN) {
|
||||||
PushEntitlementKey(key.track_type(), /* is_even_key= */ true, ekey);
|
PushEntitlementKey(key.track_type(), /* is_even_key= */ true, ekey);
|
||||||
} else if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_ODD) {
|
} else if (key.key_slot() == CasEncryptionResponse::KeyInfo::ODD) {
|
||||||
PushEntitlementKey(key.track_type(), /* is_even_key= */ false, ekey);
|
PushEntitlementKey(key.track_type(), /* is_even_key= */ false, ekey);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_SINGLE) {
|
if (key.key_slot() == CasEncryptionResponse::KeyInfo::SINGLE) {
|
||||||
PushEntitlementKey(key.track_type(), /* is_even_key= */ true, ekey);
|
PushEntitlementKey(key.track_type(), /* is_even_key= */ true, ekey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user