Update to support OEMCrypto v16 with ODK
This commit is contained in:
2
BUILD
2
BUILD
@@ -15,6 +15,8 @@ pkg_tar(
|
|||||||
strip_prefix = "/",
|
strip_prefix = "/",
|
||||||
files = [
|
files = [
|
||||||
"run_tests.sh",
|
"run_tests.sh",
|
||||||
|
"//common:provisioning_sdk_binary_release_files",
|
||||||
|
"//common/python:provisioning_sdk_binary_release_files",
|
||||||
"//example:binary_release_files",
|
"//example:binary_release_files",
|
||||||
"//protos/public:binary_release_files",
|
"//protos/public:binary_release_files",
|
||||||
"//provisioning_sdk/public:binary_release_files",
|
"//provisioning_sdk/public:binary_release_files",
|
||||||
|
|||||||
28
WORKSPACE
28
WORKSPACE
@@ -1,5 +1,6 @@
|
|||||||
workspace(name = "provisioning_sdk")
|
workspace(name = "provisioning_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(
|
||||||
@@ -10,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(
|
||||||
@@ -61,7 +81,7 @@ bind(
|
|||||||
|
|
||||||
bind(
|
bind(
|
||||||
name = "protobuf",
|
name = "protobuf",
|
||||||
actual = "@protobuf_repo//:protobuf",
|
actual = "@com_google_protobuf//:protobuf",
|
||||||
)
|
)
|
||||||
|
|
||||||
bind(
|
bind(
|
||||||
|
|||||||
668
common/BUILD
668
common/BUILD
@@ -16,10 +16,67 @@ filegroup(
|
|||||||
name = "binary_release_files",
|
name = "binary_release_files",
|
||||||
srcs = [
|
srcs = [
|
||||||
"certificate_type.h",
|
"certificate_type.h",
|
||||||
|
"default_device_security_profile_list.h",
|
||||||
|
"security_profile_list.h",
|
||||||
"status.h",
|
"status.h",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "provisioning_sdk_binary_release_files",
|
||||||
|
srcs = [
|
||||||
|
"certificate_type.h",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "playready_interface",
|
||||||
|
hdrs = ["playready_interface.h"],
|
||||||
|
deps = [
|
||||||
|
"//util:error_space",
|
||||||
|
"//protos/public:license_protocol_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "playready_sdk_impl",
|
||||||
|
hdrs = ["playready_sdk_impl.h"],
|
||||||
|
deps = [
|
||||||
|
":playready_interface",
|
||||||
|
"//protos/public:license_protocol_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "content_id_util",
|
||||||
|
srcs = ["content_id_util.cc"],
|
||||||
|
hdrs = ["content_id_util.h"],
|
||||||
|
deps = [
|
||||||
|
":error_space",
|
||||||
|
":status",
|
||||||
|
"//base",
|
||||||
|
"//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,16 +89,89 @@ 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",
|
||||||
|
":device_status_list",
|
||||||
|
"//base",
|
||||||
|
"@com_google_protobuf//:protobuf",
|
||||||
|
"@abseil_repo//absl/synchronization",
|
||||||
|
"//protos/public:client_identification_cc_proto",
|
||||||
|
"//protos/public:device_certificate_status_cc_proto",
|
||||||
|
"//protos/public:device_common_cc_proto",
|
||||||
|
"//protos/public:device_security_profile_data_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 = [
|
||||||
|
":client_id_util",
|
||||||
|
":security_profile_list",
|
||||||
|
"//base",
|
||||||
|
"@com_google_protobuf//:protobuf",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"//protos/public:device_common_cc_proto",
|
||||||
|
"//protos/public:device_security_profile_data_cc_proto",
|
||||||
|
"//protos/public:security_profile_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "default_device_security_profile_list",
|
||||||
|
srcs = ["default_device_security_profile_list.cc"],
|
||||||
|
hdrs = ["default_device_security_profile_list.h"],
|
||||||
|
deps = [
|
||||||
|
":client_id_util",
|
||||||
|
":device_status_list",
|
||||||
|
":security_profile_list",
|
||||||
|
"//base",
|
||||||
|
"@com_google_protobuf//:protobuf",
|
||||||
|
"//protos/public:client_identification_cc_proto",
|
||||||
|
"//protos/public:device_certificate_status_cc_proto",
|
||||||
|
"//protos/public:device_common_cc_proto",
|
||||||
|
"//protos/public:provisioned_device_info_cc_proto",
|
||||||
|
"//protos/public:security_profile_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "default_device_security_profile_list_test",
|
||||||
|
timeout = "short",
|
||||||
|
srcs = ["default_device_security_profile_list_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":client_id_util",
|
||||||
|
":default_device_security_profile_list",
|
||||||
|
"//base",
|
||||||
|
"@com_google_protobuf//:protobuf",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"//protos/public:device_common_cc_proto",
|
||||||
|
"//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"],
|
||||||
|
copts = ["-fvisibility=default"],
|
||||||
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",
|
||||||
],
|
],
|
||||||
|
# Make sure SDKs links in symbols defined in this
|
||||||
|
# target.
|
||||||
|
alwayslink = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
@@ -55,12 +185,26 @@ 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",
|
||||||
|
"dual_certificate_client_cert.cc",
|
||||||
|
"dual_certificate_client_cert.h",
|
||||||
|
"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",
|
||||||
|
":hash_algorithm",
|
||||||
|
":openssl_util",
|
||||||
":random_util",
|
":random_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
@@ -68,16 +212,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 +227,22 @@ cc_test(
|
|||||||
srcs = ["client_cert_test.cc"],
|
srcs = ["client_cert_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
":client_cert",
|
":client_cert",
|
||||||
":drm_root_certificate",
|
":ec_key",
|
||||||
|
":ec_test_keys",
|
||||||
":error_space",
|
":error_space",
|
||||||
|
":hash_algorithm",
|
||||||
|
":hash_algorithm_util",
|
||||||
|
":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 +252,32 @@ 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",
|
":hash_algorithm",
|
||||||
|
":hash_algorithm_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:device_common_cc_proto",
|
||||||
|
"//protos/public:provisioned_device_info_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -137,15 +288,21 @@ cc_test(
|
|||||||
deps = [
|
deps = [
|
||||||
":client_cert",
|
":client_cert",
|
||||||
":device_status_list",
|
":device_status_list",
|
||||||
|
":hash_algorithm",
|
||||||
|
":hash_algorithm_util",
|
||||||
|
":rsa_key",
|
||||||
|
":rsa_test_keys",
|
||||||
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
|
"@com_google_protobuf//: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 +312,21 @@ cc_library(
|
|||||||
hdrs = ["drm_root_certificate.h"],
|
hdrs = ["drm_root_certificate.h"],
|
||||||
deps = [
|
deps = [
|
||||||
":certificate_type",
|
":certificate_type",
|
||||||
|
":ec_key",
|
||||||
":error_space",
|
":error_space",
|
||||||
|
":hash_algorithm",
|
||||||
|
":hash_algorithm_util",
|
||||||
":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,16 +336,21 @@ 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",
|
||||||
|
":hash_algorithm",
|
||||||
|
":hash_algorithm_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":rsa_test_keys",
|
":rsa_test_keys",
|
||||||
":test_drm_certificates",
|
":test_drm_certificates",
|
||||||
"//base",
|
"//base",
|
||||||
"@protobuf_repo//:protobuf",
|
"@com_google_protobuf//: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 +360,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 +387,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",
|
||||||
],
|
],
|
||||||
@@ -243,9 +421,12 @@ cc_library(
|
|||||||
srcs = ["rsa_key.cc"],
|
srcs = ["rsa_key.cc"],
|
||||||
hdrs = ["rsa_key.h"],
|
hdrs = ["rsa_key.h"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":hash_algorithm",
|
||||||
":rsa_util",
|
":rsa_util",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
"//base",
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
"//external:openssl",
|
"//external:openssl",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -256,6 +437,7 @@ cc_test(
|
|||||||
timeout = "short",
|
timeout = "short",
|
||||||
srcs = ["rsa_key_test.cc"],
|
srcs = ["rsa_key_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":hash_algorithm",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":rsa_test_keys",
|
":rsa_test_keys",
|
||||||
":rsa_util",
|
":rsa_util",
|
||||||
@@ -269,9 +451,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(
|
||||||
@@ -279,11 +458,179 @@ cc_library(
|
|||||||
testonly = 1,
|
testonly = 1,
|
||||||
hdrs = ["mock_rsa_key.h"],
|
hdrs = ["mock_rsa_key.h"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":hash_algorithm",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
"//testing:gunit",
|
"//testing:gunit",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "ec_util",
|
||||||
|
srcs = ["ec_util.cc"],
|
||||||
|
hdrs = [
|
||||||
|
"ec_key.h",
|
||||||
|
"ec_util.h",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":hash_algorithm",
|
||||||
|
":openssl_util",
|
||||||
|
":private_key_util",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"@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",
|
||||||
|
":hash_algorithm",
|
||||||
|
":openssl_util",
|
||||||
|
":sha_util",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"@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",
|
||||||
|
":hash_algorithm",
|
||||||
|
":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 +660,6 @@ cc_library(
|
|||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"//external:openssl",
|
"//external:openssl",
|
||||||
"//util/endian",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -326,6 +672,7 @@ cc_test(
|
|||||||
"//testing:gunit",
|
"//testing:gunit",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
|
"//external:openssl",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -417,10 +764,10 @@ cc_library(
|
|||||||
hdrs = ["signature_util.h"],
|
hdrs = ["signature_util.h"],
|
||||||
deps = [
|
deps = [
|
||||||
":aes_cbc_util",
|
":aes_cbc_util",
|
||||||
|
":hash_algorithm",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":sha_util",
|
":sha_util",
|
||||||
":status",
|
":status",
|
||||||
"//base",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -431,7 +778,7 @@ cc_library(
|
|||||||
deps = [
|
deps = [
|
||||||
":crypto_util",
|
":crypto_util",
|
||||||
"//base",
|
"//base",
|
||||||
"//protos/public:license_protocol_proto",
|
"//protos/public:license_protocol_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -445,7 +792,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 +801,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 +852,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",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -525,11 +868,12 @@ cc_library(
|
|||||||
":status",
|
":status",
|
||||||
":x509_cert",
|
":x509_cert",
|
||||||
"//base",
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"@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",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -546,13 +890,15 @@ cc_library(
|
|||||||
":rsa_util",
|
":rsa_util",
|
||||||
":status",
|
":status",
|
||||||
"//base",
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"@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:external_license_cc_proto",
|
||||||
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -564,19 +910,21 @@ cc_test(
|
|||||||
":aes_cbc_util",
|
":aes_cbc_util",
|
||||||
":drm_root_certificate",
|
":drm_root_certificate",
|
||||||
":drm_service_certificate",
|
":drm_service_certificate",
|
||||||
|
":hash_algorithm_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":rsa_test_keys",
|
":rsa_test_keys",
|
||||||
":rsa_util",
|
":rsa_util",
|
||||||
":test_drm_certificates",
|
":test_drm_certificates",
|
||||||
"//base",
|
"//base",
|
||||||
"@protobuf_repo//:protobuf",
|
"@com_google_protobuf//: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:external_license_cc_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:license_server_sdk_cc_proto",
|
||||||
|
"//protos/public:signed_drm_certificate_cc_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -587,9 +935,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,11 +944,11 @@ 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",
|
||||||
"//base",
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"@abseil_repo//absl/strings",
|
"@abseil_repo//absl/strings",
|
||||||
"@abseil_repo//absl/synchronization",
|
"@abseil_repo//absl/synchronization",
|
||||||
"//external:openssl",
|
"//external:openssl",
|
||||||
@@ -629,7 +975,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",
|
||||||
],
|
],
|
||||||
@@ -642,12 +987,13 @@ cc_library(
|
|||||||
deps = [
|
deps = [
|
||||||
":certificate_type",
|
":certificate_type",
|
||||||
":error_space",
|
":error_space",
|
||||||
|
":hash_algorithm_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":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",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -656,13 +1002,14 @@ cc_test(
|
|||||||
timeout = "short",
|
timeout = "short",
|
||||||
srcs = ["vmp_checker_test.cc"],
|
srcs = ["vmp_checker_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":hash_algorithm_util",
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":vmp_checker",
|
":vmp_checker",
|
||||||
"//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 +1017,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 +1025,161 @@ cc_test(
|
|||||||
srcs = ["string_util_test.cc"],
|
srcs = ["string_util_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
":string_util",
|
":string_util",
|
||||||
"//base",
|
"//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",
|
||||||
|
"@com_google_protobuf//: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",
|
||||||
|
":hash_algorithm",
|
||||||
|
":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",
|
||||||
|
":hash_algorithm",
|
||||||
|
":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",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"//common/oemcrypto_core_message/odk:kdo",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "core_message_util_test",
|
||||||
|
srcs = ["core_message_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":core_message_util",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "hash_algorithm",
|
||||||
|
hdrs = ["hash_algorithm.h"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "hash_algorithm_util",
|
||||||
|
srcs = ["hash_algorithm_util.cc"],
|
||||||
|
hdrs = ["hash_algorithm_util.h"],
|
||||||
|
deps = [
|
||||||
|
":hash_algorithm",
|
||||||
|
"//base",
|
||||||
|
"//protos/public:hash_algorithm_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "hash_algorithm_util_test",
|
||||||
|
srcs = ["hash_algorithm_util_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":hash_algorithm",
|
||||||
|
":hash_algorithm_util",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/aes_cbc_util.h"
|
#include "common/aes_cbc_util.h"
|
||||||
|
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
|
|
||||||
@@ -29,16 +30,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 +68,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 +128,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.
|
||||||
|
|||||||
280
common/certificate_client_cert.cc
Normal file
280
common/certificate_client_cert.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.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#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/license_protocol.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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
const std::string& signature) const override {
|
||||||
|
CHECK(rsa_public_key_);
|
||||||
|
|
||||||
|
if (!rsa_public_key_->VerifySignature(message, hash_algorithm, 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_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignedMessage::SessionKeyType session_key_type() const override {
|
||||||
|
return SignedMessage::WRAPPED_AES_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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
const std::string& signature) const override {
|
||||||
|
CHECK(client_ecc_public_key_);
|
||||||
|
|
||||||
|
if (!client_ecc_public_key_->VerifySignature(message, hash_algorithm,
|
||||||
|
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_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignedMessage::SessionKeyType session_key_type() const override {
|
||||||
|
return SignedMessage::EPHEMERAL_ECC_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, HashAlgorithm hash_algorithm,
|
||||||
|
const std::string& signature, ProtocolVersion protocol_version) const {
|
||||||
|
return algorithm_->VerifySignature(
|
||||||
|
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||||
|
hash_algorithm, 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
|
||||||
114
common/certificate_client_cert.h
Normal file
114
common/certificate_client_cert.h
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 "common/hash_algorithm.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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Returns information on the type session key used in this format. This value
|
||||||
|
// is intended to be included in the SignedMessage::session_key_type field of
|
||||||
|
// a license response.
|
||||||
|
virtual SignedMessage::SessionKeyType session_key_type() 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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
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(); }
|
||||||
|
SignedMessage::SessionKeyType key_type() const override {
|
||||||
|
return algorithm_->session_key_type();
|
||||||
|
}
|
||||||
|
bool using_dual_certificate() const override { return false; }
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
const std::string& encrypted_unique_id() const override {
|
||||||
|
return device_cert_.rot_id().encrypted_unique_id();
|
||||||
|
}
|
||||||
|
const std::string& unique_id_hash() const override {
|
||||||
|
return device_cert_.rot_id().unique_id_hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
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,20 @@
|
|||||||
|
|
||||||
#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/dual_certificate_client_cert.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 +31,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 +53,78 @@ 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(const DrmRootCertificate* root_certificate,
|
||||||
if (keybox_bytes.size() < kKeyboxSizeBytes) {
|
const widevine::ClientIdentification& client_id,
|
||||||
return Status(error_space, INVALID_KEYBOX_TOKEN,
|
std::unique_ptr<ClientCert>* client_cert) {
|
||||||
"keybox-token-is-too-short");
|
CHECK(client_cert);
|
||||||
}
|
|
||||||
|
|
||||||
set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes));
|
switch (client_id.type()) {
|
||||||
set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes));
|
case ClientIdentification::KEYBOX:
|
||||||
bool insecure_keybox = false;
|
return CreateWithKeybox(client_id.token(), client_cert);
|
||||||
Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &device_key_,
|
case ClientIdentification::DRM_DEVICE_CERTIFICATE:
|
||||||
nullptr, &insecure_keybox);
|
if (!client_id.has_device_credentials()) {
|
||||||
if (!status.ok()) {
|
return CreateWithDrmCertificate(root_certificate, client_id.token(),
|
||||||
Errors new_code = status.error_code() == error::NOT_FOUND
|
client_cert);
|
||||||
? MISSING_PRE_PROV_KEY
|
}
|
||||||
: KEYBOX_DECRYPT_ERROR;
|
// Assumes |client_id.token| is the signing cert and
|
||||||
return Status(error_space, new_code, status.error_message());
|
// |client_id.device_credentials().token| is the encryption cert.
|
||||||
}
|
if (client_id.device_credentials().type() !=
|
||||||
return OkStatus();
|
ClientIdentification::DRM_DEVICE_CERTIFICATE)
|
||||||
}
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"unsupported-encryption-certificate");
|
||||||
|
|
||||||
Status KeyboxClientCert::VerifySignature(const std::string& message,
|
return CreateWithDualDrmCertificates(
|
||||||
const std::string& signature,
|
root_certificate, client_id.token(),
|
||||||
ProtocolVersion protocol_version) {
|
client_id.device_credentials().token(), client_cert);
|
||||||
DCHECK(!signing_key().empty());
|
default:
|
||||||
using crypto_util::VerifySignatureHmacSha256;
|
return Status(error_space, error::UNIMPLEMENTED,
|
||||||
if (!VerifySignatureHmacSha256(
|
"client-type-not-implemented");
|
||||||
GetClientSigningKey(signing_key(), protocol_version), signature,
|
|
||||||
message)) {
|
|
||||||
return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
|
|
||||||
}
|
|
||||||
return OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
CertificateClientCert::CertificateClientCert() {}
|
|
||||||
|
|
||||||
CertificateClientCert::~CertificateClientCert() {}
|
|
||||||
|
|
||||||
Status CertificateClientCert::Initialize(
|
|
||||||
const DrmRootCertificate* drm_root_certificate,
|
|
||||||
const std::string& serialized_certificate) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status CertificateClientCert::VerifySignature(
|
// Creates a Device Certificate based ClientCert. The |client_cert| is a
|
||||||
const std::string& message, const std::string& signature,
|
// caller supplied unique_ptr to receive the new ClientCert.
|
||||||
ProtocolVersion protocol_version) {
|
Status ClientCert::CreateWithDrmCertificate(
|
||||||
CHECK(rsa_public_key_);
|
const DrmRootCertificate* root_certificate,
|
||||||
|
const std::string& drm_certificate,
|
||||||
if (!rsa_public_key_->VerifySignature(
|
std::unique_ptr<ClientCert>* client_cert) {
|
||||||
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
CHECK(root_certificate);
|
||||||
signature)) {
|
CHECK(client_cert);
|
||||||
return Status(error_space, INVALID_SIGNATURE, "");
|
auto device_cert = absl::make_unique<CertificateClientCert>();
|
||||||
|
Status status = device_cert->Initialize(root_certificate, drm_certificate);
|
||||||
|
if (status.ok()) {
|
||||||
|
*client_cert = std::move(device_cert);
|
||||||
}
|
}
|
||||||
return OkStatus();
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status ClientCert::CreateWithDualDrmCertificates(
|
||||||
|
const DrmRootCertificate* root_certificate,
|
||||||
|
const std::string& signing_drm_certificate,
|
||||||
|
const std::string& encryption_drm_certificate,
|
||||||
|
std::unique_ptr<ClientCert>* client_cert) {
|
||||||
|
CHECK(root_certificate);
|
||||||
|
CHECK(client_cert);
|
||||||
|
auto device_cert = absl::make_unique<DualCertificateClientCert>();
|
||||||
|
Status status = device_cert->Initialize(
|
||||||
|
root_certificate, signing_drm_certificate, encryption_drm_certificate);
|
||||||
|
if (status.ok()) {
|
||||||
|
*client_cert = std::move(device_cert);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
|
||||||
|
std::unique_ptr<ClientCert>* client_cert) {
|
||||||
|
CHECK(client_cert);
|
||||||
|
auto kbx_cert = absl::make_unique<KeyboxClientCert>();
|
||||||
|
Status status = kbx_cert->Initialize(keybox_token);
|
||||||
|
if (status.ok()) {
|
||||||
|
*client_cert = std::move(kbx_cert);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // 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,93 @@
|
|||||||
#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 "absl/strings/str_cat.h"
|
||||||
|
#include "common/drm_root_certificate.h"
|
||||||
|
#include "common/error_space.h"
|
||||||
|
#include "common/hash_algorithm.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/errors.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 Device Certificate from the supplied |client_id|.
|
||||||
static Status Create(
|
static Status Create(const DrmRootCertificate* root_certificate,
|
||||||
const DrmRootCertificate* root_certificate,
|
const widevine::ClientIdentification& client_id,
|
||||||
widevine::ClientIdentification::TokenType token_type,
|
std::unique_ptr<ClientCert>* client_cert);
|
||||||
const std::string& token, ClientCert** client_cert);
|
|
||||||
// Creates a Keybox based ClientCert.
|
|
||||||
static Status CreateWithKeybox(const std::string& keybox_token,
|
|
||||||
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);
|
// Creates a Device Certificate using the supplied certificates.
|
||||||
|
// The|signing_drm_certificate| will be used to verify an incoming request.
|
||||||
|
// The |encryption_drm_certificate| will be used to define the session key
|
||||||
|
// used to protect a response message.
|
||||||
|
static Status CreateWithDualDrmCertificates(
|
||||||
|
const DrmRootCertificate* root_certificate,
|
||||||
|
const std::string& signing_drm_certificate,
|
||||||
|
const std::string& encryption_drm_certificate,
|
||||||
|
std::unique_ptr<ClientCert>* client_cert);
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
std::unique_ptr<ClientCert>* client_cert);
|
||||||
|
|
||||||
|
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;
|
HashAlgorithm hash_algorithm,
|
||||||
|
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 SignedMessage::SessionKeyType key_type() const = 0;
|
||||||
virtual const std::string& public_key() const { return public_key_; }
|
virtual bool using_dual_certificate() const = 0;
|
||||||
virtual const std::string& serial_number() const { return serial_number_; }
|
virtual const std::string& serial_number() const = 0;
|
||||||
virtual void set_serial_number(const std::string& serial_number) {
|
virtual const std::string& service_id() const = 0;
|
||||||
serial_number_ = serial_number;
|
virtual const std::string& signing_key() const = 0;
|
||||||
}
|
virtual const std::string& signer_serial_number() const = 0;
|
||||||
virtual const std::string& signer_serial_number() const {
|
virtual uint32_t signer_creation_time_seconds() const = 0;
|
||||||
return signer_serial_number_;
|
virtual bool signed_by_provisioner() const = 0;
|
||||||
}
|
virtual uint32_t system_id() const = 0;
|
||||||
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 const std::string& encrypted_unique_id() const = 0;
|
||||||
virtual bool signed_by_provisioner() const { return signed_by_provisioner_; }
|
virtual const std::string& unique_id_hash() const = 0;
|
||||||
|
virtual Status SystemIdUnknownError() const {
|
||||||
protected:
|
return Status(
|
||||||
ClientCert() {}
|
error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||||
|
absl::StrCat("device-certificate-status-unknown: ", system_id()));
|
||||||
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) {
|
virtual Status SystemIdRevokedError() const {
|
||||||
service_id_ = service_id;
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||||
|
absl::StrCat("device-certificate-revoked: ", system_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,49 @@
|
|||||||
|
|
||||||
#include "common/client_cert.h"
|
#include "common/client_cert.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <tuple>
|
||||||
#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_key.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "common/ec_test_keys.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/hash_algorithm.h"
|
||||||
|
#include "common/hash_algorithm_util.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;
|
||||||
|
|
||||||
|
const HashAlgorithm kSha256 = HashAlgorithm::kSha256;
|
||||||
|
|
||||||
// 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<
|
||||||
using ::testing::_;
|
std::tuple<DrmCertificate::Algorithm, DrmCertificate::Algorithm>> {
|
||||||
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(
|
||||||
@@ -73,16 +81,30 @@ class ClientCertTest : public ::testing::Test {
|
|||||||
class TestCertificateAndData {
|
class TestCertificateAndData {
|
||||||
public:
|
public:
|
||||||
const std::string certificate_;
|
const std::string certificate_;
|
||||||
|
const std::string encryption_certificate_;
|
||||||
const std::string expected_serial_number_;
|
const std::string expected_serial_number_;
|
||||||
uint32_t expected_system_id_;
|
uint32_t expected_system_id_;
|
||||||
Status expected_status_;
|
Status expected_status_;
|
||||||
|
SignedMessage::SessionKeyType expected_key_type_;
|
||||||
TestCertificateAndData(const std::string& certificate,
|
TestCertificateAndData(const std::string& certificate,
|
||||||
const std::string& expected_serial_number,
|
const std::string& expected_serial_number,
|
||||||
uint32_t expected_system_id, Status expected_status)
|
uint32_t expected_system_id, Status expected_status)
|
||||||
: 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),
|
||||||
|
expected_key_type_(SignedMessage::WRAPPED_AES_KEY) {}
|
||||||
|
TestCertificateAndData(const std::string& certificate,
|
||||||
|
const std::string& encryption_certificate,
|
||||||
|
const std::string& expected_serial_number,
|
||||||
|
uint32_t expected_system_id, Status expected_status,
|
||||||
|
SignedMessage::SessionKeyType expected_key_type)
|
||||||
|
: certificate_(certificate),
|
||||||
|
encryption_certificate_(encryption_certificate),
|
||||||
|
expected_serial_number_(expected_serial_number),
|
||||||
|
expected_system_id_(expected_system_id),
|
||||||
|
expected_status_(expected_status),
|
||||||
|
expected_key_type_(expected_key_type) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
void TestBasicValidation(const TestTokenAndKeys& expectation,
|
void TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||||
@@ -91,32 +113,42 @@ 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,
|
||||||
std::string* signature);
|
const std::string& private_key,
|
||||||
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate,
|
HashAlgorithm hash_algorithm, std::string* signature);
|
||||||
SignedDrmCertificate* signer,
|
std::unique_ptr<SignedDrmCertificate> SignCertificate(
|
||||||
const std::string& private_key);
|
const DrmCertificate& certificate, const SignedDrmCertificate* signer,
|
||||||
DrmCertificate* GenerateProvisionerCertificate(uint32_t system_id,
|
const std::string& private_key);
|
||||||
const std::string& serial_number,
|
std::unique_ptr<DrmCertificate> GenerateProvisionerCertificate(
|
||||||
const std::string& provider_id);
|
uint32_t system_id, const std::string& serial_number,
|
||||||
SignedDrmCertificate* GenerateSignedProvisionerCertificate(
|
const std::string& provider_id);
|
||||||
uint32_t system_id, const std::string& serial_number, const std::string& service_id);
|
std::unique_ptr<SignedDrmCertificate> GenerateSignedProvisionerCertificate(
|
||||||
DrmCertificate* GenerateIntermediateCertificate(uint32_t system_id,
|
uint32_t system_id, const std::string& serial_number,
|
||||||
const std::string& serial_number);
|
const std::string& service_id);
|
||||||
SignedDrmCertificate* GenerateSignedIntermediateCertificate(
|
std::unique_ptr<DrmCertificate> GenerateIntermediateCertificate(
|
||||||
|
uint32_t system_id, const std::string& serial_number);
|
||||||
|
std::unique_ptr<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,
|
std::unique_ptr<DrmCertificate> GenerateDrmCertificate(
|
||||||
const std::string& serial_number);
|
uint32_t system_id, const std::string& serial_number,
|
||||||
SignedDrmCertificate* GenerateSignedDrmCertificate(
|
DrmCertificate::Algorithm = DrmCertificate::RSA);
|
||||||
|
std::unique_ptr<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 GetECCPrivateKey(DrmCertificate::Algorithm algorithm);
|
||||||
|
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 +156,25 @@ 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.
|
ClientIdentification client_id;
|
||||||
for (int i = 0; i < 2; i++) {
|
client_id.set_type(ClientIdentification::KEYBOX);
|
||||||
if (i == 0) {
|
client_id.set_token(expectation.token_);
|
||||||
status =
|
|
||||||
ClientCert::Create(root_cert_.get(), ClientIdentification::KEYBOX,
|
status = ClientCert::Create(root_cert_.get(), client_id, &keybox_cert);
|
||||||
expectation.token_, &client_cert_ptr);
|
if (expect_success) {
|
||||||
} else {
|
ASSERT_EQ(OkStatus(), status);
|
||||||
status =
|
ASSERT_TRUE(keybox_cert.get());
|
||||||
ClientCert::CreateWithKeybox(expectation.token_, &client_cert_ptr);
|
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
|
||||||
}
|
EXPECT_EQ(expectation.expected_serial_number_,
|
||||||
std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
|
keybox_cert->serial_number());
|
||||||
if (expect_success) {
|
if (compare_device_key) {
|
||||||
ASSERT_EQ(OkStatus(), status);
|
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
|
||||||
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,14 +187,26 @@ 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(),
|
ClientIdentification client_id;
|
||||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
expectation.certificate_, &client_cert_ptr);
|
client_id.set_token(expectation.certificate_);
|
||||||
std::unique_ptr<ClientCert> drm_certificate_cert(client_cert_ptr);
|
if (!expectation.encryption_certificate_.empty()) {
|
||||||
|
client_id.mutable_device_credentials()->set_token(
|
||||||
|
expectation.encryption_certificate_);
|
||||||
|
client_id.mutable_device_credentials()->set_type(
|
||||||
|
ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
status =
|
||||||
|
ClientCert::Create(root_cert_.get(), client_id, &drm_certificate_cert);
|
||||||
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());
|
||||||
|
if (!expectation.encryption_certificate_.empty()) {
|
||||||
|
ASSERT_TRUE(drm_certificate_cert->using_dual_certificate());
|
||||||
|
}
|
||||||
|
ASSERT_EQ(expectation.expected_key_type_, drm_certificate_cert->key_type());
|
||||||
if (compare_data) {
|
if (compare_data) {
|
||||||
ASSERT_EQ(expectation.expected_serial_number_,
|
ASSERT_EQ(expectation.expected_serial_number_,
|
||||||
drm_certificate_cert->signer_serial_number());
|
drm_certificate_cert->signer_serial_number());
|
||||||
@@ -183,90 +220,154 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
|
|||||||
|
|
||||||
void ClientCertTest::GenerateSignature(const std::string& message,
|
void ClientCertTest::GenerateSignature(const std::string& message,
|
||||||
const std::string& private_key,
|
const std::string& private_key,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
std::string* signature) {
|
std::string* signature) {
|
||||||
std::unique_ptr<RsaPrivateKey> rsa_private_key(
|
std::unique_ptr<RsaPrivateKey> rsa_private_key(
|
||||||
RsaPrivateKey::Create(private_key));
|
RsaPrivateKey::Create(private_key));
|
||||||
ASSERT_TRUE(rsa_private_key != nullptr);
|
ASSERT_TRUE(rsa_private_key != nullptr);
|
||||||
rsa_private_key->GenerateSignature(message, signature);
|
rsa_private_key->GenerateSignature(message, hash_algorithm, signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The caller relinquishes ownership of |signer|, which may also be nullptr.
|
// The caller retains ownership of |signer|, which may also be nullptr.
|
||||||
SignedDrmCertificate* ClientCertTest::SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> ClientCertTest::SignCertificate(
|
||||||
const DrmCertificate& certificate, SignedDrmCertificate* signer,
|
const DrmCertificate& certificate, const SignedDrmCertificate* signer,
|
||||||
const std::string& private_key) {
|
const std::string& private_key) {
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_certificate(
|
std::unique_ptr<SignedDrmCertificate> signed_certificate(
|
||||||
new SignedDrmCertificate);
|
new SignedDrmCertificate);
|
||||||
signed_certificate->set_drm_certificate(certificate.SerializeAsString());
|
signed_certificate->set_drm_certificate(certificate.SerializeAsString());
|
||||||
GenerateSignature(signed_certificate->drm_certificate(), private_key,
|
GenerateSignature(
|
||||||
signed_certificate->mutable_signature());
|
signed_certificate->drm_certificate(), private_key,
|
||||||
|
HashAlgorithmProtoToEnum(signed_certificate->hash_algorithm()),
|
||||||
|
signed_certificate->mutable_signature());
|
||||||
if (signer != nullptr) {
|
if (signer != nullptr) {
|
||||||
signed_certificate->set_allocated_signer(signer);
|
*(signed_certificate->mutable_signer()) = *signer;
|
||||||
}
|
}
|
||||||
return signed_certificate.release();
|
return signed_certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
|
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::GetECCPrivateKey(
|
||||||
|
DrmCertificate::Algorithm algorithm) {
|
||||||
|
ECTestKeys keys;
|
||||||
|
switch (algorithm) {
|
||||||
|
case DrmCertificate::ECC_SECP256R1:
|
||||||
|
return keys.private_test_key_1_secp256r1();
|
||||||
|
case DrmCertificate::ECC_SECP384R1:
|
||||||
|
return keys.private_test_key_1_secp384r1();
|
||||||
|
case DrmCertificate::ECC_SECP521R1:
|
||||||
|
return keys.private_test_key_1_secp521r1();
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<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;
|
||||||
}
|
}
|
||||||
|
|
||||||
SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
|
std::unique_ptr<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(
|
std::unique_ptr<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);
|
||||||
return drm_certificate.release();
|
drm_certificate->set_algorithm(algorithm);
|
||||||
|
return drm_certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
|
std::unique_ptr<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,
|
||||||
return signed_drm_certificate.release();
|
GetPrivateKeyByCertType(DrmCertificate::DEVICE_MODEL)));
|
||||||
|
return signed_drm_certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
|
std::unique_ptr<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);
|
||||||
return provisioner_certificate.release();
|
return provisioner_certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
|
std::unique_ptr<SignedDrmCertificate>
|
||||||
uint32_t system_id, const std::string& serial_number, const std::string& service_id) {
|
ClientCertTest::GenerateSignedProvisionerCertificate(
|
||||||
|
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,20 +397,52 @@ 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> intermediate_certificate =
|
||||||
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
|
GenerateSignedIntermediateCertificate(nullptr, system_id, serial_number,
|
||||||
nullptr, system_id, serial_number),
|
kNoSigner);
|
||||||
system_id, serial_number + "-device"));
|
std::unique_ptr<SignedDrmCertificate> signed_cert =
|
||||||
|
GenerateSignedDrmCertificate(intermediate_certificate.get(), system_id,
|
||||||
|
serial_number + "-device1",
|
||||||
|
std::get<0>(GetParam()));
|
||||||
|
SignedMessage::SessionKeyType expected_key_type =
|
||||||
|
std::get<0>(GetParam()) != DrmCertificate::RSA
|
||||||
|
? SignedMessage::EPHEMERAL_ECC_PUBLIC_KEY
|
||||||
|
: SignedMessage::WRAPPED_AES_KEY;
|
||||||
|
std::unique_ptr<SignedDrmCertificate> encryption_certificate;
|
||||||
|
if (std::get<1>(GetParam()) != DrmCertificate::UNKNOWN_ALGORITHM) {
|
||||||
|
encryption_certificate = GenerateSignedDrmCertificate(
|
||||||
|
intermediate_certificate.get(), system_id, serial_number + "-device2",
|
||||||
|
std::get<1>(GetParam()));
|
||||||
|
expected_key_type = std::get<1>(GetParam()) != DrmCertificate::RSA
|
||||||
|
? SignedMessage::EPHEMERAL_ECC_PUBLIC_KEY
|
||||||
|
: SignedMessage::WRAPPED_AES_KEY;
|
||||||
|
}
|
||||||
const TestCertificateAndData kValidCertificateAndExpectedData(
|
const TestCertificateAndData kValidCertificateAndExpectedData(
|
||||||
signed_cert->SerializeAsString(), serial_number, system_id, OkStatus());
|
signed_cert->SerializeAsString(),
|
||||||
|
encryption_certificate == nullptr
|
||||||
|
? std::string()
|
||||||
|
: encryption_certificate->SerializeAsString(),
|
||||||
|
serial_number, system_id, OkStatus(), expected_key_type);
|
||||||
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::Combine(testing::Values(DrmCertificate::RSA,
|
||||||
|
DrmCertificate::ECC_SECP256R1,
|
||||||
|
DrmCertificate::ECC_SECP384R1,
|
||||||
|
DrmCertificate::ECC_SECP521R1),
|
||||||
|
testing::Values(DrmCertificate::UNKNOWN_ALGORITHM,
|
||||||
|
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[] = {
|
||||||
// This tests a malformed, but appropriately sized keybox.
|
// This tests a malformed, but appropriately sized keybox.
|
||||||
@@ -347,69 +480,80 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
|||||||
std::unique_ptr<SignedDrmCertificate> invalid_drm_cert(
|
std::unique_ptr<SignedDrmCertificate> invalid_drm_cert(
|
||||||
new SignedDrmCertificate);
|
new SignedDrmCertificate);
|
||||||
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
|
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
|
||||||
GenerateSignature(invalid_drm_cert->drm_certificate(),
|
GenerateSignature(
|
||||||
test_rsa_keys_.private_test_key_2_2048_bits(),
|
invalid_drm_cert->drm_certificate(),
|
||||||
invalid_drm_cert->mutable_signature());
|
test_rsa_keys_.private_test_key_2_2048_bits(),
|
||||||
|
HashAlgorithmProtoToEnum(invalid_drm_cert->hash_algorithm()),
|
||||||
|
invalid_drm_cert->mutable_signature());
|
||||||
invalid_drm_cert->set_allocated_signer(
|
invalid_drm_cert->set_allocated_signer(
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn,
|
||||||
|
kNoSigner)
|
||||||
|
.release());
|
||||||
// Invalid device public key.
|
// Invalid device public key.
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert = 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)
|
||||||
|
.get(),
|
||||||
|
test_rsa_keys_.private_test_key_2_2048_bits());
|
||||||
// Invalid serialized intermediate certificate.
|
// Invalid serialized intermediate certificate.
|
||||||
signed_signer.reset(
|
signed_signer = GenerateSignedIntermediateCertificate(nullptr, system_id,
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
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(),
|
||||||
|
HashAlgorithmProtoToEnum(signed_signer->hash_algorithm()),
|
||||||
signed_signer->mutable_signature());
|
signed_signer->mutable_signature());
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert = GenerateDrmCertificate(system_id, device_sn);
|
||||||
std::unique_ptr<SignedDrmCertificate> invalid_signer(
|
std::unique_ptr<SignedDrmCertificate> invalid_signer(
|
||||||
SignCertificate(*dev_cert, signed_signer.release(),
|
SignCertificate(*dev_cert, signed_signer.get(),
|
||||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// Invalid signer public key.
|
// Invalid signer public key.
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert = GenerateDrmCertificate(system_id, device_sn);
|
||||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
signer_cert = GenerateIntermediateCertificate(system_id, signer_sn);
|
||||||
signer_cert->set_public_key("bad-signer-public-key");
|
signer_cert->set_public_key("bad-signer-public-key");
|
||||||
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
|
||||||
*dev_cert,
|
*dev_cert,
|
||||||
SignCertificate(*signer_cert, nullptr,
|
SignCertificate(*signer_cert, nullptr,
|
||||||
test_rsa_keys_.private_test_key_1_3072_bits()),
|
test_rsa_keys_.private_test_key_1_3072_bits())
|
||||||
|
.get(),
|
||||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// 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(
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
nullptr, system_id, signer_sn, kNoSigner)
|
||||||
system_id, device_sn));
|
.get(),
|
||||||
|
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.
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert = GenerateDrmCertificate(system_id, device_sn);
|
||||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
signer_cert = GenerateIntermediateCertificate(system_id, signer_sn);
|
||||||
signer_cert->clear_system_id();
|
signer_cert->clear_system_id();
|
||||||
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
|
||||||
*dev_cert,
|
*dev_cert,
|
||||||
SignCertificate(*signer_cert, nullptr,
|
SignCertificate(*signer_cert, nullptr,
|
||||||
test_rsa_keys_.private_test_key_1_3072_bits()),
|
test_rsa_keys_.private_test_key_1_3072_bits())
|
||||||
|
.get(),
|
||||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
// Missing signer serial number.
|
// Missing signer serial number.
|
||||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
dev_cert = GenerateDrmCertificate(system_id, device_sn);
|
||||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
signer_cert = GenerateIntermediateCertificate(system_id, signer_sn);
|
||||||
signer_cert->clear_serial_number();
|
signer_cert->clear_serial_number();
|
||||||
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
|
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
|
||||||
*dev_cert,
|
*dev_cert,
|
||||||
SignCertificate(*signer_cert, nullptr,
|
SignCertificate(*signer_cert, nullptr,
|
||||||
test_rsa_keys_.private_test_key_1_3072_bits()),
|
test_rsa_keys_.private_test_key_1_3072_bits())
|
||||||
|
.get(),
|
||||||
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 = GenerateDrmCertificate(system_id, device_sn);
|
||||||
signed_signer.reset(
|
signed_signer = GenerateSignedIntermediateCertificate(nullptr, system_id,
|
||||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
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.get(),
|
||||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||||
|
|
||||||
const TestCertificateAndData kInvalidCertificate[] = {
|
const TestCertificateAndData kInvalidCertificate[] = {
|
||||||
@@ -453,8 +597,12 @@ 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);
|
ClientIdentification client_id;
|
||||||
|
client_id.set_type(ClientIdentification::KEYBOX);
|
||||||
|
client_id.set_token(token);
|
||||||
|
Status status =
|
||||||
|
ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr);
|
||||||
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
|
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,28 +613,28 @@ TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
|
|||||||
const std::string intermediate_serial_number("intermediate-serial-number");
|
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||||
const std::string provisioner_serial_number("provisioner-serial-number");
|
const std::string provisioner_serial_number("provisioner-serial-number");
|
||||||
|
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
|
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert =
|
||||||
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
|
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
|
||||||
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.get(), 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.get(), system_id,
|
||||||
system_id, device_serial_number));
|
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> drm_cert;
|
||||||
|
ClientIdentification client_id;
|
||||||
|
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
|
client_id.set_token(serialized_cert);
|
||||||
|
|
||||||
EXPECT_OK(ClientCert::Create(root_cert_.get(),
|
EXPECT_OK(ClientCert::Create(root_cert_.get(), client_id, &drm_cert));
|
||||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
ASSERT_TRUE(drm_cert);
|
||||||
serialized_cert, &client_cert_ptr));
|
|
||||||
ASSERT_TRUE(client_cert_ptr != nullptr);
|
|
||||||
std::unique_ptr<ClientCert> drm_cert(client_cert_ptr);
|
|
||||||
|
|
||||||
EXPECT_EQ(service_id, drm_cert->service_id());
|
EXPECT_EQ(service_id, drm_cert->service_id());
|
||||||
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
|
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
|
||||||
@@ -501,27 +649,28 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertEmptyServiceId) {
|
|||||||
const std::string intermediate_serial_number("intermediate-serial-number");
|
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||||
const std::string provisioner_serial_number("provisioner-serial-number");
|
const std::string provisioner_serial_number("provisioner-serial-number");
|
||||||
|
|
||||||
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
|
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert =
|
||||||
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
|
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
|
||||||
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.get(), 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.get(), system_id,
|
||||||
system_id, device_serial_number));
|
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;
|
||||||
|
ClientIdentification client_id;
|
||||||
|
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
|
client_id.set_token(serialized_cert);
|
||||||
|
|
||||||
EXPECT_EQ("missing-provisioning-service-id",
|
EXPECT_EQ("missing-provisioning-service-id",
|
||||||
ClientCert::Create(root_cert_.get(),
|
ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
|
||||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
|
||||||
serialized_cert, &client_cert_ptr)
|
|
||||||
.error_message());
|
.error_message());
|
||||||
EXPECT_FALSE(client_cert_ptr);
|
EXPECT_FALSE(client_cert_ptr);
|
||||||
}
|
}
|
||||||
@@ -534,31 +683,127 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
|
|||||||
const std::string intermediate_serial_number("intermediate-serial-number");
|
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||||
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.get(), 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.get(), system_id,
|
||||||
system_id, device_serial_number));
|
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;
|
||||||
|
ClientIdentification client_id;
|
||||||
|
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
|
client_id.set_token(serialized_cert);
|
||||||
|
|
||||||
// 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(), client_id, &client_cert_ptr)
|
||||||
ASSERT_EQ("cache-miss-invalid-signature",
|
.error_message());
|
||||||
ClientCert::Create(root_cert_.get(),
|
EXPECT_FALSE(client_cert_ptr);
|
||||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
}
|
||||||
serialized_cert, &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.get(), system_id, intermediate_serial_number1,
|
||||||
|
kProvisionerSigner);
|
||||||
|
|
||||||
|
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2 =
|
||||||
|
GenerateSignedIntermediateCertificate(
|
||||||
|
signed_intermediate_cert1.get(), system_id,
|
||||||
|
intermediate_serial_number2, kDeviceModelSigner);
|
||||||
|
|
||||||
|
std::unique_ptr<SignedDrmCertificate> signed_device_cert =
|
||||||
|
GenerateSignedDrmCertificate(signed_intermediate_cert2.get(), system_id,
|
||||||
|
device_serial_number);
|
||||||
|
|
||||||
|
std::string serialized_cert;
|
||||||
|
signed_device_cert->SerializeToString(&serialized_cert);
|
||||||
|
std::unique_ptr<ClientCert> client_cert_ptr = nullptr;
|
||||||
|
ClientIdentification client_id;
|
||||||
|
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
|
client_id.set_token(serialized_cert);
|
||||||
|
|
||||||
|
ASSERT_EQ("certificate-chain-size-exceeded",
|
||||||
|
ClientCert::Create(root_cert_.get(), client_id, &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.get(), system_id,
|
||||||
|
intermediate_serial_number);
|
||||||
|
|
||||||
|
std::unique_ptr<SignedDrmCertificate> signed_drm_cert =
|
||||||
|
GenerateSignedDrmCertificate(signed_intermediate_cert.get(), system_id,
|
||||||
|
drm_serial_number);
|
||||||
|
std::string serialized_cert;
|
||||||
|
signed_drm_cert->SerializeToString(&serialized_cert);
|
||||||
|
std::unique_ptr<ClientCert> client_cert_ptr;
|
||||||
|
ClientIdentification client_id;
|
||||||
|
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
|
client_id.set_token(serialized_cert);
|
||||||
|
|
||||||
|
EXPECT_EQ("device-cert-must-be-leaf",
|
||||||
|
ClientCert::Create(root_cert_.get(), client_id, &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.get(), system_id, intermediate_serial_number,
|
||||||
|
kProvisionerSigner);
|
||||||
|
std::string serialized_cert;
|
||||||
|
signed_intermediate_cert->SerializeToString(&serialized_cert);
|
||||||
|
std::unique_ptr<ClientCert> client_cert_ptr;
|
||||||
|
ClientIdentification client_id;
|
||||||
|
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
|
client_id.set_token(serialized_cert);
|
||||||
|
// Leaf certificate must be a device certificate.
|
||||||
|
EXPECT_EQ("expected-device-certificate-type",
|
||||||
|
ClientCert::Create(root_cert_.get(), client_id, &client_cert_ptr)
|
||||||
.error_message());
|
.error_message());
|
||||||
EXPECT_FALSE(client_cert_ptr);
|
EXPECT_FALSE(client_cert_ptr);
|
||||||
}
|
}
|
||||||
@@ -566,11 +811,11 @@ 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(
|
ClientIdentification client_id;
|
||||||
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
|
client_id.set_token(test_drm_certs_.test_user_device_certificate());
|
||||||
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
|
ASSERT_OK(ClientCert::Create(root_cert_.get(), client_id, &client_cert));
|
||||||
|
|
||||||
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()));
|
||||||
@@ -578,25 +823,27 @@ TEST_F(ClientCertTest, Protocol21WithDrmCert) {
|
|||||||
|
|
||||||
// Success
|
// Success
|
||||||
std::string signature;
|
std::string signature;
|
||||||
ASSERT_TRUE(private_key->GenerateSignature(message, &signature));
|
ASSERT_TRUE(private_key->GenerateSignature(message, kSha256, &signature));
|
||||||
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_1));
|
EXPECT_OK(
|
||||||
|
client_cert->VerifySignature(message, kSha256, signature, VERSION_2_1));
|
||||||
|
|
||||||
// Failure
|
// Failure
|
||||||
ASSERT_EQ(256, signature.size());
|
ASSERT_EQ(256, signature.size());
|
||||||
++signature[127];
|
++signature[127];
|
||||||
EXPECT_FALSE(
|
EXPECT_FALSE(
|
||||||
client_cert->VerifySignature(message, signature, VERSION_2_1).ok());
|
client_cert->VerifySignature(message, kSha256, signature, VERSION_2_1)
|
||||||
|
.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ClientCertTest, Protocol22WithDrmCert) {
|
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(
|
ClientIdentification client_id;
|
||||||
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
client_id.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
|
client_id.set_token(test_drm_certs_.test_user_device_certificate());
|
||||||
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
|
ASSERT_OK(ClientCert::Create(root_cert_.get(), client_id, &client_cert));
|
||||||
|
|
||||||
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()));
|
||||||
@@ -604,14 +851,17 @@ TEST_F(ClientCertTest, Protocol22WithDrmCert) {
|
|||||||
|
|
||||||
// Success
|
// Success
|
||||||
std::string signature;
|
std::string signature;
|
||||||
ASSERT_TRUE(private_key->GenerateSignature(message_hash, &signature));
|
ASSERT_TRUE(
|
||||||
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_2));
|
private_key->GenerateSignature(message_hash, kSha256, &signature));
|
||||||
|
EXPECT_OK(
|
||||||
|
client_cert->VerifySignature(message, kSha256, signature, VERSION_2_2));
|
||||||
|
|
||||||
// Failure
|
// Failure
|
||||||
ASSERT_EQ(256, signature.size());
|
ASSERT_EQ(256, signature.size());
|
||||||
++signature[127];
|
++signature[127];
|
||||||
EXPECT_FALSE(
|
EXPECT_FALSE(
|
||||||
client_cert->VerifySignature(message, signature, VERSION_2_2).ok());
|
client_cert->VerifySignature(message, kSha256, signature, VERSION_2_2)
|
||||||
|
.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -10,12 +10,24 @@
|
|||||||
|
|
||||||
#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 {
|
||||||
|
|
||||||
|
const char kModDrmMake[] = "company_name";
|
||||||
|
const char kModDrmModel[] = "model_name";
|
||||||
|
const char kModDrmDeviceName[] = "device_name";
|
||||||
|
const char kModDrmProductName[] = "product_name";
|
||||||
|
const char kModDrmBuildInfo[] = "build_info";
|
||||||
|
const char kModDrmOemCryptoSecurityPatchLevel[] =
|
||||||
|
"oem_crypto_security_patch_level";
|
||||||
|
|
||||||
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
|
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||||
absl::string_view value) {
|
absl::string_view value) {
|
||||||
ClientIdentification_NameValue* nv = client_id->add_client_info();
|
ClientIdentification_NameValue* nv = client_id->add_client_info();
|
||||||
@@ -37,12 +49,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 +99,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
|
||||||
|
|||||||
@@ -19,6 +19,13 @@
|
|||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
|
extern const char kModDrmMake[];
|
||||||
|
extern const char kModDrmModel[];
|
||||||
|
extern const char kModDrmDeviceName[];
|
||||||
|
extern const char kModDrmProductName[];
|
||||||
|
extern const char kModDrmBuildInfo[];
|
||||||
|
extern const char kModDrmOemCryptoSecurityPatchLevel[];
|
||||||
|
|
||||||
// Append the given name/value pair to client_id->client_info(). Does not
|
// Append the given name/value pair to client_id->client_info(). Does not
|
||||||
// check for duplicates.
|
// check for duplicates.
|
||||||
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
|
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||||
@@ -32,12 +39,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 +64,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_
|
||||||
|
|||||||
83
common/content_id_util.cc
Normal file
83
common/content_id_util.cc
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 "glog/logging.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 {
|
||||||
|
|
||||||
|
Status GetContentIdFromExternalLicenseRequest(
|
||||||
|
const ExternalLicenseRequest& external_license_request,
|
||||||
|
std::string* content_id) {
|
||||||
|
WidevinePsshData pssh_data;
|
||||||
|
Status status = ParsePsshData(external_license_request, &pssh_data);
|
||||||
|
*content_id = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status ParsePsshData(ExternalLicenseRequest external_license_request,
|
||||||
|
WidevinePsshData* widevine_pssh_data) {
|
||||||
|
if (!external_license_request.has_content_id()) {
|
||||||
|
std::string error = "ExternalLicenseRequest does not include ContentId";
|
||||||
|
LOG(ERROR) << error
|
||||||
|
<< ", request = " << external_license_request.ShortDebugString();
|
||||||
|
return Status(error_space, MISSING_CONTENT_ID, error);
|
||||||
|
}
|
||||||
|
ContentInfo content_info;
|
||||||
|
Status status =
|
||||||
|
ParseContentId(external_license_request.content_id(), &content_info);
|
||||||
|
if (!status.ok()) {
|
||||||
|
std::string error =
|
||||||
|
"Unable to retrieve ContentId from ExternalLicenseRequest";
|
||||||
|
LOG(ERROR) << error << ", status = " << status
|
||||||
|
<< ", request = " << external_license_request.ShortDebugString();
|
||||||
|
return Status(error_space, MISSING_CONTENT_ID, error);
|
||||||
|
}
|
||||||
|
switch (external_license_request.content_id().init_data().init_data_type()) {
|
||||||
|
case LicenseRequest::ContentIdentification::InitData::WEBM:
|
||||||
|
widevine_pssh_data->ParseFromString(
|
||||||
|
content_info.content_info_entry(0).key_ids(0));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*widevine_pssh_data =
|
||||||
|
content_info.content_info_entry(0).pssh().widevine_data();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (widevine_pssh_data->content_id().empty()) {
|
||||||
|
std::string error =
|
||||||
|
"Missing ContentId within Pssh data for ExternalLicenseRequest";
|
||||||
|
LOG(ERROR) << error
|
||||||
|
<< ", request = " << external_license_request.ShortDebugString();
|
||||||
|
return Status(error_space, MISSING_CONTENT_ID, error);
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
} // namespace widevine
|
||||||
37
common/content_id_util.h
Normal file
37
common/content_id_util.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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"
|
||||||
|
#include "protos/public/widevine_pssh.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);
|
||||||
|
|
||||||
|
// Returns OK if successful and |widevine_pssh_data| will be populated by
|
||||||
|
// parsing |external_license_request|. Else, error and |widevine_pssh_data|
|
||||||
|
// will not be set within this method.
|
||||||
|
Status ParsePsshData(ExternalLicenseRequest external_license_request,
|
||||||
|
WidevinePsshData* widevine_pssh_data);
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_CONTENT_ID_UTIL_H_
|
||||||
82
common/content_id_util_test.cc
Normal file
82
common/content_id_util_test.cc
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.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "common/content_id_util.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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 ExternalLicenseType 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(PLAYREADY_LICENSE_NEW,
|
||||||
|
kPlayReadyChallenge, kContentId),
|
||||||
|
&content_id));
|
||||||
|
EXPECT_EQ(kContentId, content_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ContentIdUtil, GetContentIdFailureWithIncorrectType) {
|
||||||
|
std::string content_id;
|
||||||
|
SignedMessage signed_message = BuildSignedExternalLicenseRequest(
|
||||||
|
PLAYREADY_LICENSE_NEW, 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(
|
||||||
|
PLAYREADY_LICENSE_NEW, 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
|
||||||
94
common/core_message_util.cc
Normal file
94
common/core_message_util.cc
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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::CoreCommonRequestFromMessage;
|
||||||
|
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 {
|
||||||
|
bool GetCoreProvisioningResponse(
|
||||||
|
const std::string& serialized_provisioning_response,
|
||||||
|
const std::string& request_core_message,
|
||||||
|
std::string* response_core_message) {
|
||||||
|
if (request_core_message.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
oemcrypto_core_message::ODK_ProvisioningRequest odk_provisioning_request;
|
||||||
|
if (CoreProvisioningRequestFromMessage(request_core_message,
|
||||||
|
&odk_provisioning_request)) {
|
||||||
|
return CreateCoreProvisioningResponseFromProto(
|
||||||
|
serialized_provisioning_response, odk_provisioning_request,
|
||||||
|
response_core_message);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetCoreRenewalOrReleaseLicenseResponse(
|
||||||
|
uint64_t renewal_duration_seconds, const std::string& request_core_message,
|
||||||
|
std::string* response_core_message) {
|
||||||
|
if (request_core_message.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
oemcrypto_core_message::ODK_RenewalRequest odk_renewal_request;
|
||||||
|
if (!CoreRenewalRequestFromMessage(request_core_message,
|
||||||
|
&odk_renewal_request)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return CreateCoreRenewalResponse(
|
||||||
|
odk_renewal_request, renewal_duration_seconds, response_core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetCoreNewLicenseResponse(const std::string& license,
|
||||||
|
const std::string& request_core_message,
|
||||||
|
const bool nonce_required,
|
||||||
|
std::string* response_core_message) {
|
||||||
|
if (request_core_message.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
oemcrypto_core_message::ODK_LicenseRequest odk_license_request;
|
||||||
|
if (!CoreLicenseRequestFromMessage(request_core_message,
|
||||||
|
&odk_license_request)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string core_request_sha256 = Sha256_Hash(request_core_message);
|
||||||
|
return CreateCoreLicenseResponseFromProto(license, odk_license_request,
|
||||||
|
core_request_sha256, nonce_required,
|
||||||
|
response_core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetCoreVersion(const std::string& request_core_message,
|
||||||
|
uint16_t* odk_major_version, uint16_t* odk_minor_version) {
|
||||||
|
if (odk_major_version == nullptr || odk_minor_version == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
oemcrypto_core_message::ODK_CommonRequest odk_common_request;
|
||||||
|
if (!CoreCommonRequestFromMessage(request_core_message,
|
||||||
|
&odk_common_request)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*odk_major_version = odk_common_request.api_major_version;
|
||||||
|
*odk_minor_version = odk_common_request.api_minor_version;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace core_message_util
|
||||||
|
} // namespace widevine
|
||||||
47
common/core_message_util.h
Normal file
47
common/core_message_util.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace core_message_util {
|
||||||
|
// Gets the |response_core_message| by parsing |request_core_message| and
|
||||||
|
// |serialized_provisioning_response|. The output is held in
|
||||||
|
// |response_core_message|.
|
||||||
|
bool GetCoreProvisioningResponse(
|
||||||
|
const std::string& serialized_provisioning_response,
|
||||||
|
const std::string& request_core_message,
|
||||||
|
std::string* response_core_message);
|
||||||
|
|
||||||
|
// Gets the |response_core_message| by parsing |request_core_message| for
|
||||||
|
// release and renewal response. The output is held in |response_core_message|.
|
||||||
|
bool GetCoreRenewalOrReleaseLicenseResponse(
|
||||||
|
uint64_t renewal_duration_seconds, const std::string& request_core_message,
|
||||||
|
std::string* response_core_message);
|
||||||
|
|
||||||
|
// Gets the |response_core_message| by parsing |request_core_message| and
|
||||||
|
// |license| for new license response. The output is held in
|
||||||
|
// |response_core_message|.
|
||||||
|
bool GetCoreNewLicenseResponse(const std::string& license,
|
||||||
|
const std::string& request_core_message,
|
||||||
|
const bool nonce_required,
|
||||||
|
std::string* response_core_message);
|
||||||
|
|
||||||
|
// Populates the |odk_major_version| and |odk_minor_version| from the ODK core
|
||||||
|
// message sent in the license request by parsing |request_core_message|.
|
||||||
|
bool GetCoreVersion(const std::string& request_core_message,
|
||||||
|
uint16_t* odk_major_version, uint16_t* odk_minor_version);
|
||||||
|
|
||||||
|
} // namespace core_message_util
|
||||||
|
} // namespace widevine
|
||||||
|
#endif // COMMON_CORE_MESSAGE_UTIL_H_
|
||||||
75
common/core_message_util_test.cc
Normal file
75
common/core_message_util_test.cc
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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:
|
||||||
|
// Unit tests for the code_message_util helper functions.
|
||||||
|
|
||||||
|
#include "common/core_message_util.h"
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Core message header format:
|
||||||
|
// message_type : 4 bytes
|
||||||
|
// message_length : 4 bytes
|
||||||
|
// minor_version : 2 bytes
|
||||||
|
// major_version : 2 bytes
|
||||||
|
// nonce : 4 bytes
|
||||||
|
// session_id : 4 bytes
|
||||||
|
const char kv16CoreMessage[] = "0000000100000014000300100000000000000000";
|
||||||
|
const char kv16CoreMessageRenewal[] =
|
||||||
|
"000000030000001c000300100000000000000000000000000000005a";
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace core_message_util {
|
||||||
|
|
||||||
|
class CoreMessageUtilTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
CoreMessageUtilTest() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(CoreMessageUtilTest, GetODKVersionFromNewLicenseRequest) {
|
||||||
|
uint16_t major_version = 0;
|
||||||
|
uint16_t minor_version = 0;
|
||||||
|
std::string oemcrypto_core_message = absl::HexStringToBytes(kv16CoreMessage);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
GetCoreVersion(oemcrypto_core_message, &major_version, &minor_version));
|
||||||
|
EXPECT_EQ(major_version, 16);
|
||||||
|
EXPECT_EQ(minor_version, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CoreMessageUtilTest, GetODKVersionFromRenewLicenseRequest) {
|
||||||
|
uint16_t major_version = 0;
|
||||||
|
uint16_t minor_version = 0;
|
||||||
|
std::string oemcrypto_core_message =
|
||||||
|
absl::HexStringToBytes(kv16CoreMessageRenewal);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
GetCoreVersion(oemcrypto_core_message, &major_version, &minor_version));
|
||||||
|
EXPECT_EQ(major_version, 16);
|
||||||
|
EXPECT_EQ(minor_version, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CoreMessageUtilTest, GetODKVersionFromBogusRequest) {
|
||||||
|
uint16_t major_version = 0;
|
||||||
|
uint16_t minor_version = 0;
|
||||||
|
std::string oemcrypto_core_message = "bogus message";
|
||||||
|
EXPECT_FALSE(
|
||||||
|
GetCoreVersion(oemcrypto_core_message, &major_version, &minor_version));
|
||||||
|
EXPECT_EQ(major_version, 0);
|
||||||
|
EXPECT_EQ(minor_version, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace core_message_util
|
||||||
|
} // namespace widevine
|
||||||
@@ -11,13 +11,13 @@
|
|||||||
#include "common/crypto_util.h"
|
#include "common/crypto_util.h"
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "openssl/aes.h"
|
#include "openssl/aes.h"
|
||||||
#include "openssl/cmac.h"
|
#include "openssl/cmac.h"
|
||||||
#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 {
|
||||||
@@ -38,9 +38,16 @@ const char kGroupKeyLabel[] = "GROUP_ENCRYPTION";
|
|||||||
// a real group master key in keystore.
|
// a real group master key in keystore.
|
||||||
// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy
|
// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy
|
||||||
// like VerifySignatureHmacSha1.
|
// like VerifySignatureHmacSha1.
|
||||||
|
// TODO(user): Revert logging signature in VerifySignatureHmacSha256.
|
||||||
|
// function.
|
||||||
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 +58,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 +81,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 +101,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 +167,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);
|
||||||
|
|||||||
135
common/default_device_security_profile_list.cc
Normal file
135
common/default_device_security_profile_list.cc
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 DefaultDeviceSecurityProfileList class.
|
||||||
|
|
||||||
|
#include "common/default_device_security_profile_list.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "google/protobuf/text_format.h"
|
||||||
|
#include "common/client_id_util.h"
|
||||||
|
#include "common/device_status_list.h"
|
||||||
|
#include "protos/public/client_identification.pb.h"
|
||||||
|
#include "protos/public/device_certificate_status.pb.h"
|
||||||
|
#include "protos/public/device_common.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 kWidevine[] = "widevine";
|
||||||
|
|
||||||
|
// Definition of Widevine default device security profiles.
|
||||||
|
// TODO(user): Add an OWNER file with per-file access to restrict changes to the
|
||||||
|
// profile definition.
|
||||||
|
const char kWidevineProfileMin[] =
|
||||||
|
(" name: \"minimum\""
|
||||||
|
" min_output_requirements {"
|
||||||
|
" hdcp_version: HDCP_NONE"
|
||||||
|
" analog_output_capabilities: ANALOG_OUTPUT_UNKNOWN"
|
||||||
|
" }"
|
||||||
|
" min_security_requirements {"
|
||||||
|
" oemcrypto_api_version: 0"
|
||||||
|
" security_level: LEVEL_3"
|
||||||
|
" resource_rating_tier: 0"
|
||||||
|
" vulnerability_level: VULNERABILITY_HIGH"
|
||||||
|
" }"
|
||||||
|
" owner: \"Widevine\"");
|
||||||
|
|
||||||
|
const char kWidevineProfileLow[] =
|
||||||
|
(" name: \"low\""
|
||||||
|
" min_output_requirements {"
|
||||||
|
" hdcp_version: HDCP_NONE"
|
||||||
|
" analog_output_capabilities: ANALOG_OUTPUT_UNKNOWN"
|
||||||
|
" }"
|
||||||
|
" min_security_requirements {"
|
||||||
|
" oemcrypto_api_version: 8"
|
||||||
|
" security_level: LEVEL_3"
|
||||||
|
" resource_rating_tier: 1"
|
||||||
|
" vulnerability_level: VULNERABILITY_MEDIUM"
|
||||||
|
" }"
|
||||||
|
" owner: \"Widevine\"");
|
||||||
|
|
||||||
|
const char kWidevineProfileMed[] =
|
||||||
|
(" name: \"medium\""
|
||||||
|
" min_output_requirements {"
|
||||||
|
" hdcp_version: HDCP_V1"
|
||||||
|
" analog_output_capabilities: ANALOG_OUTPUT_UNKNOWN"
|
||||||
|
" }"
|
||||||
|
" min_security_requirements {"
|
||||||
|
" oemcrypto_api_version: 12"
|
||||||
|
" security_level: LEVEL_3"
|
||||||
|
" resource_rating_tier: 1"
|
||||||
|
" vulnerability_level: VULNERABILITY_LOW"
|
||||||
|
" }"
|
||||||
|
" owner: \"Widevine\"");
|
||||||
|
|
||||||
|
const char kWidevineProfileHigh[] =
|
||||||
|
(" name: \"high\""
|
||||||
|
" min_output_requirements {"
|
||||||
|
" hdcp_version: HDCP_V1"
|
||||||
|
" analog_output_capabilities: ANALOG_OUTPUT_SUPPORTS_CGMS_A"
|
||||||
|
" }"
|
||||||
|
" min_security_requirements {"
|
||||||
|
" oemcrypto_api_version: 12"
|
||||||
|
" security_level: LEVEL_1"
|
||||||
|
" resource_rating_tier: 2"
|
||||||
|
" vulnerability_level: VULNERABILITY_NONE"
|
||||||
|
" }"
|
||||||
|
" owner: \"Widevine\"");
|
||||||
|
|
||||||
|
const char kWidevineProfileStrict[] =
|
||||||
|
(" name: \"strict\""
|
||||||
|
" min_output_requirements {"
|
||||||
|
" hdcp_version: HDCP_V2_2"
|
||||||
|
" analog_output_capabilities: ANALOG_OUTPUT_SUPPORTS_CGMS_A"
|
||||||
|
" }"
|
||||||
|
" min_security_requirements {"
|
||||||
|
" oemcrypto_api_version: 12"
|
||||||
|
" security_level: LEVEL_1"
|
||||||
|
" resource_rating_tier: 3"
|
||||||
|
" vulnerability_level: VULNERABILITY_NONE"
|
||||||
|
" }"
|
||||||
|
" owner: \"Widevine\"");
|
||||||
|
|
||||||
|
DefaultDeviceSecurityProfileList::DefaultDeviceSecurityProfileList()
|
||||||
|
: SecurityProfileList(kWidevine) {}
|
||||||
|
|
||||||
|
int DefaultDeviceSecurityProfileList::Init() { return AddDefaultProfiles(); }
|
||||||
|
|
||||||
|
int DefaultDeviceSecurityProfileList::AddDefaultProfiles() {
|
||||||
|
std::vector<std::string> default_profile_strings;
|
||||||
|
GetDefaultProfileStrings(&default_profile_strings);
|
||||||
|
for (auto& profile_string : default_profile_strings) {
|
||||||
|
SecurityProfile profile;
|
||||||
|
if (!google::protobuf::TextFormat::ParseFromString(profile_string, &profile)) {
|
||||||
|
LOG(ERROR) << "Unable to load default profile: " << profile.name();
|
||||||
|
ClearAllProfiles();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
InsertProfile(profile);
|
||||||
|
}
|
||||||
|
return NumProfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DefaultDeviceSecurityProfileList::GetDefaultProfileStrings(
|
||||||
|
std::vector<std::string>* default_profile_strings) const {
|
||||||
|
if (default_profile_strings == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default_profile_strings->push_back(kWidevineProfileMin);
|
||||||
|
default_profile_strings->push_back(kWidevineProfileLow);
|
||||||
|
default_profile_strings->push_back(kWidevineProfileMed);
|
||||||
|
default_profile_strings->push_back(kWidevineProfileHigh);
|
||||||
|
default_profile_strings->push_back(kWidevineProfileStrict);
|
||||||
|
return default_profile_strings->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
39
common/default_device_security_profile_list.h
Normal file
39
common/default_device_security_profile_list.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 default security profiless.
|
||||||
|
|
||||||
|
#ifndef COMMON_DEFAULT_DEVICE_SECURITY_PROFILE_LIST_H_
|
||||||
|
#define COMMON_DEFAULT_DEVICE_SECURITY_PROFILE_LIST_H_
|
||||||
|
|
||||||
|
#include "common/security_profile_list.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class DefaultDeviceSecurityProfileList : public SecurityProfileList {
|
||||||
|
public:
|
||||||
|
DefaultDeviceSecurityProfileList();
|
||||||
|
~DefaultDeviceSecurityProfileList() override {}
|
||||||
|
|
||||||
|
// 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() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Initialize the list with Widevine default profiles. The size of the
|
||||||
|
// profile list after the additions is returned.
|
||||||
|
virtual int AddDefaultProfiles();
|
||||||
|
virtual int GetDefaultProfileStrings(
|
||||||
|
std::vector<std::string>* default_profile_strings) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_DEFAULT_DEVICE_SECURITY_PROFILE_LIST_H_
|
||||||
186
common/default_device_security_profile_list_test.cc
Normal file
186
common/default_device_security_profile_list_test.cc
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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/default_device_security_profile_list.h"
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "google/protobuf/util/message_differencer.h"
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "common/client_id_util.h"
|
||||||
|
#include "protos/public/device_common.pb.h"
|
||||||
|
#include "protos/public/security_profile.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace security_profile {
|
||||||
|
|
||||||
|
const uint32_t kResourceTierLow = 1;
|
||||||
|
const uint32_t kResourceTierMed = 2;
|
||||||
|
const uint32_t kResourceTierHigh = 3;
|
||||||
|
const char kMinProfileName[] = "minimum";
|
||||||
|
const char kLowProfileName[] = "low";
|
||||||
|
const char kMedProfileName[] = "medium";
|
||||||
|
const char kHighProfileName[] = "high";
|
||||||
|
const char kStrictProfileName[] = "strict";
|
||||||
|
|
||||||
|
class DefaultDeviceSecurityProfileListTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
DefaultDeviceSecurityProfileListTest() {}
|
||||||
|
~DefaultDeviceSecurityProfileListTest() override {}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
SecurityProfile profile;
|
||||||
|
std::string profile_namespace = "widevine";
|
||||||
|
profile_list_ = absl::make_unique<DefaultDeviceSecurityProfileList>();
|
||||||
|
const int kNumWidevineProfiles = 5;
|
||||||
|
ASSERT_EQ(kNumWidevineProfiles, profile_list_->Init());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure |client_id| and |device_info| with minimum settings.
|
||||||
|
void SetupMinDrmParams(ClientIdentification* client_id,
|
||||||
|
ProvisionedDeviceInfo* device_info) {
|
||||||
|
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(0);
|
||||||
|
client_id->mutable_client_capabilities()->set_resource_rating_tier(
|
||||||
|
kResourceTierLow);
|
||||||
|
device_info->set_security_level(ProvisionedDeviceInfo::LEVEL_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure |client_id| and |device_info| with maximum settings.
|
||||||
|
void SetupMaxDrmParams(ClientIdentification* client_id,
|
||||||
|
ProvisionedDeviceInfo* device_info) {
|
||||||
|
client_id->mutable_client_capabilities()->set_max_hdcp_version(
|
||||||
|
ClientCapabilities::HDCP_V2_3);
|
||||||
|
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(16);
|
||||||
|
client_id->mutable_client_capabilities()->set_resource_rating_tier(
|
||||||
|
kResourceTierHigh);
|
||||||
|
device_info->set_security_level(ProvisionedDeviceInfo::LEVEL_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SecurityProfileList> profile_list_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(DefaultDeviceSecurityProfileListTest, QualifiedProfiles) {
|
||||||
|
ClientIdentification client_id;
|
||||||
|
ProvisionedDeviceInfo device_info;
|
||||||
|
SetupMinDrmParams(&client_id, &device_info);
|
||||||
|
|
||||||
|
std::vector<std::string> qualified_profiles;
|
||||||
|
// Should only return the minimum profile.
|
||||||
|
ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id, device_info,
|
||||||
|
&qualified_profiles));
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kMinProfileName) != qualified_profiles.end());
|
||||||
|
|
||||||
|
// Increase the device capabilities to include the low profile.
|
||||||
|
client_id.mutable_client_capabilities()->set_oem_crypto_api_version(8);
|
||||||
|
ASSERT_EQ(2, profile_list_->GetQualifiedProfiles(client_id, device_info,
|
||||||
|
&qualified_profiles));
|
||||||
|
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kMinProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kLowProfileName) != qualified_profiles.end());
|
||||||
|
|
||||||
|
// Increase the device capabilities to include the med profile.
|
||||||
|
client_id.mutable_client_capabilities()->set_max_hdcp_version(
|
||||||
|
ClientCapabilities::HDCP_V1);
|
||||||
|
client_id.mutable_client_capabilities()->set_oem_crypto_api_version(12);
|
||||||
|
ASSERT_EQ(3, profile_list_->GetQualifiedProfiles(client_id, device_info,
|
||||||
|
&qualified_profiles));
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kMinProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kLowProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kMedProfileName) != qualified_profiles.end());
|
||||||
|
|
||||||
|
// Increase the device capabilities to include the high profile.
|
||||||
|
device_info.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
|
||||||
|
client_id.mutable_client_capabilities()->set_analog_output_capabilities(
|
||||||
|
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
|
||||||
|
client_id.mutable_client_capabilities()->set_resource_rating_tier(
|
||||||
|
kResourceTierMed);
|
||||||
|
ASSERT_EQ(4, profile_list_->GetQualifiedProfiles(client_id, device_info,
|
||||||
|
&qualified_profiles));
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kMinProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kLowProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kMedProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kHighProfileName) != qualified_profiles.end());
|
||||||
|
|
||||||
|
// Increase the device capabilities to include the strict profile.
|
||||||
|
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(5, profile_list_->GetQualifiedProfiles(client_id, device_info,
|
||||||
|
&qualified_profiles));
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kMinProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kLowProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kMedProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kHighProfileName) != qualified_profiles.end());
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kStrictProfileName) != qualified_profiles.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DefaultDeviceSecurityProfileListTest,
|
||||||
|
DeviceQualifiedProfilesForLowEndDevice) {
|
||||||
|
ClientIdentification client_id;
|
||||||
|
ProvisionedDeviceInfo device_info;
|
||||||
|
SetupMinDrmParams(&client_id, &device_info);
|
||||||
|
|
||||||
|
// Only 1 profile should qualify for this device.
|
||||||
|
std::vector<std::string> qualified_profiles;
|
||||||
|
ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id, device_info,
|
||||||
|
&qualified_profiles));
|
||||||
|
EXPECT_TRUE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
||||||
|
kMinProfileName) != qualified_profiles.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DefaultDeviceSecurityProfileListTest,
|
||||||
|
QualifiedProfilesForHighEndDevice) {
|
||||||
|
ClientIdentification client_id;
|
||||||
|
ProvisionedDeviceInfo device_info;
|
||||||
|
SetupMaxDrmParams(&client_id, &device_info);
|
||||||
|
|
||||||
|
// All 5 default profiles should qualify for this device.
|
||||||
|
std::vector<std::string> qualified_profiles;
|
||||||
|
ASSERT_EQ(5, profile_list_->GetQualifiedProfiles(client_id, device_info,
|
||||||
|
&qualified_profiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/160019477): Add test once provisioned device info supports known
|
||||||
|
// vulnerability.
|
||||||
|
TEST_F(DefaultDeviceSecurityProfileListTest,
|
||||||
|
DISABLED_QualifiedProfilesByVunerabilityLevel) {
|
||||||
|
ClientIdentification client_id;
|
||||||
|
ProvisionedDeviceInfo device_info;
|
||||||
|
SetupMaxDrmParams(&client_id, &device_info);
|
||||||
|
|
||||||
|
std::vector<std::string> qualified_profiles;
|
||||||
|
ASSERT_EQ(0, profile_list_->GetQualifiedProfiles(client_id, device_info,
|
||||||
|
&qualified_profiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace security_profile
|
||||||
|
} // namespace widevine
|
||||||
42
common/device_info_util.cc
Normal file
42
common/device_info_util.cc
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.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Implements the device info helper function.
|
||||||
|
#include "common/device_info_util.h"
|
||||||
|
|
||||||
|
#include "absl/strings/ascii.h"
|
||||||
|
#include "protos/public/device_common.pb.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 (const DeviceModel& product_info : device_info.model_info()) {
|
||||||
|
if (IsMatchedMakeModel(product_info.manufacturer(),
|
||||||
|
product_info.model_name(), make_from_client,
|
||||||
|
model_from_client)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace widevine
|
||||||
30
common/device_info_util.h
Normal file
30
common/device_info_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_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,18 @@
|
|||||||
#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/hash_algorithm_util.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 +57,20 @@ 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,
|
||||||
|
HashAlgorithm hash_algorithm, const std::string& signature,
|
||||||
uint32_t expiration_period_seconds) {
|
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 +80,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(),
|
hash_algorithm, 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() +
|
||||||
@@ -116,28 +119,11 @@ Status DeviceStatusList::UpdateStatusList(
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
|
Status DeviceStatusList::GetCertStatus(
|
||||||
ProvisionedDeviceInfo* device_info) {
|
const ClientCert& client_cert, const std::string& make,
|
||||||
CHECK(device_info);
|
const std::string& provider, bool allow_revoked_system_id,
|
||||||
|
DeviceCertificateStatus* device_certificate_status) {
|
||||||
// Keybox checks.
|
CHECK(device_certificate_status);
|
||||||
if (client_cert.type() == ClientIdentification::KEYBOX) {
|
|
||||||
if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) {
|
|
||||||
return Status(error_space, UNSUPPORTED_SYSTEM_ID,
|
|
||||||
"keybox-unsupported-system-id");
|
|
||||||
}
|
|
||||||
// Get device information from certificate status list if available.
|
|
||||||
if (!GetDeviceInfo(client_cert, device_info)) {
|
|
||||||
device_info->Clear();
|
|
||||||
}
|
|
||||||
return OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// DRM certificate checks.
|
|
||||||
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
||||||
"device-certificate-unsupported-token-type");
|
|
||||||
}
|
|
||||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||||
if (expiration_period_seconds_ &&
|
if (expiration_period_seconds_ &&
|
||||||
(GetCurrentTime() >
|
(GetCurrentTime() >
|
||||||
@@ -147,48 +133,68 @@ Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
|
|||||||
}
|
}
|
||||||
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) {
|
|
||||||
*device_info = device_cert_status->device_info();
|
if (device_cert_status == nullptr) {
|
||||||
if (device_cert_status->status() ==
|
if (allow_unknown_devices_ ||
|
||||||
DeviceCertificateStatus::STATUS_REVOKED) {
|
client_cert.type() == ClientIdentification::KEYBOX) {
|
||||||
if (IsRevokedSystemIdAllowed(client_cert.system_id())) {
|
return OkStatus();
|
||||||
LOG(WARNING) << "Allowing REVOKED device: "
|
|
||||||
<< device_info->ShortDebugString();
|
|
||||||
} else {
|
|
||||||
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
|
|
||||||
"device-certificate-revoked");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ((device_cert_status->status() ==
|
return client_cert.SystemIdUnknownError();
|
||||||
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
}
|
||||||
!allow_test_only_devices_) {
|
*device_certificate_status = *device_cert_status;
|
||||||
|
|
||||||
|
if (device_cert_status->status() == DeviceCertificateStatus::STATUS_REVOKED) {
|
||||||
|
if (IsRevokedSystemIdAllowed(client_cert.system_id()) ||
|
||||||
|
allow_revoked_system_id) {
|
||||||
|
LOG(WARNING) << "Allowing REVOKED device: "
|
||||||
|
<< device_cert_status->device_info().ShortDebugString();
|
||||||
|
} else {
|
||||||
|
return client_cert.SystemIdRevokedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The remainder of this function is for DRM certificates.
|
||||||
|
if (client_cert.type() == ClientIdentification::KEYBOX) {
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
// DRM certificate checks.
|
||||||
|
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"device-certificate-unsupported-token-type");
|
||||||
|
}
|
||||||
|
if ((device_cert_status->status() ==
|
||||||
|
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
||||||
|
!allow_test_only_devices_) {
|
||||||
|
if (IsTestOnlyDeviceAllowedByMake(client_cert.system_id(), make) &&
|
||||||
|
IsTestOnlyDeviceAllowedByProvider(client_cert.system_id(), provider)) {
|
||||||
|
LOG(WARNING) << "Allowing TEST_ONLY device with systemId = "
|
||||||
|
<< client_cert.system_id() << ", make = " << make
|
||||||
|
<< ", provider = " << provider << ", device info = "
|
||||||
|
<< device_cert_status->device_info().ShortDebugString();
|
||||||
|
} else {
|
||||||
|
VLOG(2) << "Not allowing TEST ONLY device with systemId = "
|
||||||
|
<< client_cert.system_id() << ", provider = " << provider
|
||||||
|
<< ", device info = "
|
||||||
|
<< device_cert_status->device_info().ShortDebugString();
|
||||||
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||||
"test-only-drm-certificate-not-allowed");
|
"test-only-drm-certificate-not-allowed");
|
||||||
}
|
}
|
||||||
if (!client_cert.signed_by_provisioner() &&
|
|
||||||
(client_cert.signer_serial_number() !=
|
|
||||||
device_cert_status->drm_serial_number())) {
|
|
||||||
// Widevine-provisioned device, and the intermediate certificate serial
|
|
||||||
// number does not match that in the status list. If the status list is
|
|
||||||
// newer than the certificate, indicate an invalid certificate, so that
|
|
||||||
// the device re-provisions. If, on the other hand, the certificate status
|
|
||||||
// list is older than the certificate, the certificate is for all purposes
|
|
||||||
// unknown.
|
|
||||||
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
|
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
|
||||||
"intermediate-certificate-serial-number-mismatch");
|
|
||||||
}
|
|
||||||
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
|
||||||
"device-certificate-status-unknown");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!allow_unknown_devices_) {
|
|
||||||
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
|
||||||
"device-certificate-status-unknown");
|
|
||||||
}
|
|
||||||
device_info->Clear();
|
|
||||||
}
|
}
|
||||||
|
if (!client_cert.signed_by_provisioner() &&
|
||||||
|
(client_cert.signer_serial_number() !=
|
||||||
|
device_cert_status->drm_serial_number())) {
|
||||||
|
// Widevine-provisioned device, and the intermediate certificate serial
|
||||||
|
// number does not match that in the status list. If the status list is
|
||||||
|
// newer than the certificate, indicate an invalid certificate, so that
|
||||||
|
// the device re-provisions. If, on the other hand, the certificate status
|
||||||
|
// list is older than the certificate, the certificate is for all purposes
|
||||||
|
// unknown.
|
||||||
|
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"intermediate-certificate-serial-number-mismatch");
|
||||||
|
}
|
||||||
|
return client_cert.SystemIdUnknownError();
|
||||||
|
}
|
||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,13 +204,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 +261,168 @@ 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::AllowTestOnlyDevicesByMake(
|
||||||
|
const std::string& device_list_by_make) {
|
||||||
|
absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_);
|
||||||
|
if (device_list_by_make.empty()) {
|
||||||
|
allowed_test_only_devices_by_make_.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (absl::string_view device : absl::StrSplit(device_list_by_make, ',')) {
|
||||||
|
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_by_make_.emplace(
|
||||||
|
std::stoi(std::string(device_split.first)), "*");
|
||||||
|
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
|
||||||
|
<< std::stoi(std::string(device_split.first)) << ", make *";
|
||||||
|
} else {
|
||||||
|
allowed_test_only_devices_by_make_.emplace(
|
||||||
|
std::stoi(std::string(device_split.first)),
|
||||||
|
absl::AsciiStrToUpper(device_split.second));
|
||||||
|
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
|
||||||
|
<< std::stoi(std::string(device_split.first))
|
||||||
|
<< ", make = " << absl::AsciiStrToUpper(device_split.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceStatusList::AllowTestOnlyDevicesByProvider(
|
||||||
|
const std::string& device_list_by_provider) {
|
||||||
|
absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_);
|
||||||
|
if (device_list_by_provider.empty()) {
|
||||||
|
allowed_test_only_devices_by_provider_.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (absl::string_view device :
|
||||||
|
absl::StrSplit(device_list_by_provider, ',')) {
|
||||||
|
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_by_provider_.emplace(
|
||||||
|
std::stoi(std::string(device_split.first)), "*");
|
||||||
|
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
|
||||||
|
<< std::stoi(std::string(device_split.first)) << ", provider *";
|
||||||
|
} else {
|
||||||
|
allowed_test_only_devices_by_provider_.emplace(
|
||||||
|
std::stoi(std::string(device_split.first)),
|
||||||
|
absl::AsciiStrToUpper(device_split.second));
|
||||||
|
VLOG(2) << "Allowing TEST_ONLY device: systemId = "
|
||||||
|
<< std::stoi(std::string(device_split.first))
|
||||||
|
<< ", provider = " << 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::IsTestOnlyDeviceAllowedByMake(
|
||||||
const std::string& certificate_provisioning_service_response,
|
uint32_t system_id, 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_makes = allowed_test_only_devices_by_make_.equal_range(system_id);
|
||||||
if (signed_list_start != std::string::npos) {
|
for (auto it = allowed_makes.first; it != allowed_makes.second; ++it) {
|
||||||
size_t signed_list_end = certificate_provisioning_service_response.find(
|
std::string allowed_makes = (*it).second;
|
||||||
kSignedListTerminator, signed_list_start);
|
if (allowed_makes == "*" ||
|
||||||
if (signed_list_end == std::string::npos) {
|
allowed_makes == absl::AsciiStrToUpper(manufacturer)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceStatusList::IsTestOnlyDeviceAllowedByProvider(
|
||||||
|
uint32_t system_id, const std::string& provider) {
|
||||||
|
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
|
||||||
|
std::pair<std::multimap<uint32_t, std::string>::iterator,
|
||||||
|
std::multimap<uint32_t, std::string>::iterator>
|
||||||
|
allowed_providers =
|
||||||
|
allowed_test_only_devices_by_provider_.equal_range(system_id);
|
||||||
|
for (auto it = allowed_providers.first; it != allowed_providers.second;
|
||||||
|
++it) {
|
||||||
|
std::string allowed_provider = (*it).second;
|
||||||
|
if (allowed_provider == "*" ||
|
||||||
|
allowed_provider == absl::AsciiStrToUpper(provider)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||||
|
const std::string& service_response,
|
||||||
|
DeviceCertificateStatusList* certificate_status_list,
|
||||||
|
std::string* serialized_certificate_status_list,
|
||||||
|
HashAlgorithm* hash_algorithm, 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, hash_algorithm,
|
||||||
|
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,
|
||||||
|
hash_algorithm, 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,
|
||||||
|
HashAlgorithm* hash_algorithm, 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 +438,57 @@ 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, hash_algorithm, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DeviceStatusList::ExtractPublishedDevicesInfo(
|
||||||
|
const std::string& serialized_published_devices,
|
||||||
|
std::string* serialized_certificate_status_list,
|
||||||
|
HashAlgorithm* hash_algorithm, 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");
|
*hash_algorithm = HashAlgorithmProtoToEnum(devices_info.hash_algorithm());
|
||||||
}
|
*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,29 +502,99 @@ 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 =
|
||||||
DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
||||||
if (sc == nullptr) {
|
if (sc == nullptr) {
|
||||||
signed_device_certificate_status_list_request->clear();
|
signed_device_certificate_status_list_request->clear();
|
||||||
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
|
return Status(error_space, widevine::SERVICE_CERTIFICATE_NOT_FOUND,
|
||||||
"Drm service certificate is not loaded.");
|
"Drm service certificate is not loaded.");
|
||||||
}
|
}
|
||||||
const RsaPrivateKey* private_key = sc->private_key();
|
const RsaPrivateKey* private_key = sc->private_key();
|
||||||
if (private_key == nullptr) {
|
if (private_key == nullptr) {
|
||||||
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
|
return Status(error_space, widevine::INVALID_SERVICE_PRIVATE_KEY,
|
||||||
"Private key in the service certificate is null.");
|
"Private key in the service certificate is null.");
|
||||||
}
|
}
|
||||||
std::string signature;
|
std::string signature;
|
||||||
private_key->GenerateSignature(device_certificate_status_list_request,
|
private_key->GenerateSignature(
|
||||||
&signature);
|
device_certificate_status_list_request,
|
||||||
|
HashAlgorithmProtoToEnum(signed_request.hash_algorithm()), &signature);
|
||||||
signed_request.set_signature(signature);
|
signed_request.set_signature(signature);
|
||||||
signed_request.SerializeToString(
|
signed_request.SerializeToString(
|
||||||
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,
|
||||||
|
HashAlgorithm* hash_algorithm, 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();
|
||||||
|
*hash_algorithm =
|
||||||
|
HashAlgorithmProtoToEnum(signed_device_list.hash_algorithm());
|
||||||
|
*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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DeviceStatusList::GetDeviceCertificateStatusBySystemId(
|
||||||
|
uint32_t system_id, DeviceCertificateStatus* device_certificate_status) {
|
||||||
|
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||||
|
if (expiration_period_seconds_ &&
|
||||||
|
(GetCurrentTime() >
|
||||||
|
(creation_time_seconds_ + expiration_period_seconds_))) {
|
||||||
|
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||||
|
"certificate-status-list-expired");
|
||||||
|
}
|
||||||
|
DeviceCertificateStatus* device_cert_status =
|
||||||
|
gtl::FindOrNull(device_status_map_, system_id);
|
||||||
|
if (device_cert_status == nullptr) {
|
||||||
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||||
|
"device-certificate-status-unknown");
|
||||||
|
} else {
|
||||||
|
*device_certificate_status = *device_cert_status;
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -12,10 +12,11 @@
|
|||||||
#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/hash_algorithm.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"
|
||||||
#include "protos/public/provisioned_device_info.pb.h"
|
#include "protos/public/provisioned_device_info.pb.h"
|
||||||
@@ -35,14 +36,21 @@ 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,
|
||||||
|
HashAlgorithm hash_algorithm, 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,16 +58,22 @@ 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 status is OK, a copy of the provisioned device info is copied
|
// DEVELOPMENT_CERTIFICATE_NOT_ALLOWED
|
||||||
// into |device_info|. Caller owns |device_info| and it must not be null.
|
// |provider| is the service provider making the license request.
|
||||||
Status GetCertStatus(const ClientCert& client_cert,
|
// If status is OK, a copy of the device certificate status is copied
|
||||||
widevine::ProvisionedDeviceInfo* device_info);
|
// into |device_certificate_status|. Caller owns |device_certificate_status|
|
||||||
|
// and it must not be null.
|
||||||
|
Status GetCertStatus(
|
||||||
|
const ClientCert& client_cert, const std::string& make,
|
||||||
|
const std::string& provider, bool allow_revoked_system_id,
|
||||||
|
widevine::DeviceCertificateStatus* device_certificate_status);
|
||||||
// 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).
|
||||||
bool IsSystemIdActive(uint32_t system_id);
|
bool IsSystemIdActive(uint32_t system_id);
|
||||||
@@ -69,6 +83,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 +98,72 @@ 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:*"
|
||||||
|
// AllowTestOnlyDevicesByMake(device_list_by_make);
|
||||||
|
virtual void AllowTestOnlyDevicesByMake(
|
||||||
|
const std::string& device_list_by_make);
|
||||||
|
|
||||||
|
// Same as above, except by providers instead of by manufacturers.
|
||||||
|
// Example usage:
|
||||||
|
// const std::string device_list = "4121:YouTube,4121:AndroidVideo"
|
||||||
|
// AllowTestOnlyDevicesByProvider(device_list);
|
||||||
|
virtual void AllowTestOnlyDevicesByProvider(
|
||||||
|
const std::string& device_list_by_provider);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Returns OK if |system_id| was found in the device certificate status list
|
||||||
|
// and |device_certificate_status| is populated. If |system_id| is not found,
|
||||||
|
// this call returns an error.
|
||||||
|
virtual Status GetDeviceCertificateStatusBySystemId(
|
||||||
|
uint32_t system_id, DeviceCertificateStatus* device_certificate_status);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// The |hash_algorithm| is the hash algorithm used in signature.
|
||||||
|
// 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,
|
||||||
|
HashAlgorithm* hash_algorithm, std::string* signature);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs signed device certificate status list request string.
|
* Constructs signed device certificate status list request string.
|
||||||
*
|
*
|
||||||
@@ -97,25 +173,106 @@ 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:
|
|
||||||
// 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);
|
||||||
|
|
||||||
absl::Mutex status_map_lock_;
|
// Returns true if the device, which is identified by system_id and
|
||||||
|
// device_manufacturer, is present in |allowed_test_only_devices_by_make_|.
|
||||||
|
bool IsTestOnlyDeviceAllowedByMake(uint32_t system_id,
|
||||||
|
const std::string& device_manufacturer);
|
||||||
|
|
||||||
|
// Returns true if the device, which is identified by system_id and
|
||||||
|
// provider, is present in |allowed_test_only_devices_by_provider_|.
|
||||||
|
bool IsTestOnlyDeviceAllowedByProvider(uint32_t system_id,
|
||||||
|
const std::string& provider);
|
||||||
|
|
||||||
|
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 hash_algorithm
|
||||||
|
* @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,
|
||||||
|
HashAlgorithm* hash_algorithm, 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 hash_algorithm
|
||||||
|
* @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,
|
||||||
|
HashAlgorithm* hash_algorithm, 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
|
||||||
|
* @param hash_algorithm
|
||||||
|
* @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,
|
||||||
|
HashAlgorithm* hash_algorithm, std::string* signature);
|
||||||
|
|
||||||
|
virtual size_t allowed_test_only_devices_by_make_size() {
|
||||||
|
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
|
||||||
|
return allowed_test_only_devices_by_make_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t allowed_test_only_devices_by_provider_size() {
|
||||||
|
absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_);
|
||||||
|
return allowed_test_only_devices_by_provider_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable 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_;
|
ABSL_GUARDED_BY(status_map_lock_);
|
||||||
uint32_t expiration_period_seconds_;
|
uint32_t creation_time_seconds_ = 0;
|
||||||
bool allow_unknown_devices_;
|
uint32_t expiration_period_seconds_ = 0;
|
||||||
bool allow_test_only_devices_;
|
bool allow_unknown_devices_ = false;
|
||||||
|
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_;
|
||||||
|
mutable absl::Mutex allowed_test_only_devices_mutex_;
|
||||||
DISALLOW_COPY_AND_ASSIGN(DeviceStatusList);
|
// Contains a map of 'system_id' to 'manufacturer'. If manufacturer value is
|
||||||
|
// "*", any manufacturer using that system_id is allowed.
|
||||||
|
std::multimap<uint32_t, std::string> allowed_test_only_devices_by_make_
|
||||||
|
ABSL_GUARDED_BY(allowed_test_only_devices_mutex_);
|
||||||
|
// Contains a map of 'system_id' to 'provider'. If provider value is "*", any
|
||||||
|
// provider using that system_id is allowed.
|
||||||
|
std::multimap<uint32_t, std::string> allowed_test_only_devices_by_provider_
|
||||||
|
ABSL_GUARDED_BY(allowed_test_only_devices_mutex_);
|
||||||
|
// Revoked DRM certificate serial numbers.
|
||||||
|
std::set<std::string> revoked_drm_certificate_serial_numbers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -9,28 +9,46 @@
|
|||||||
#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/hash_algorithm.h"
|
||||||
|
#include "common/hash_algorithm_util.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 kTestSystemId_2[] = "8242";
|
||||||
|
const char kTestSystemId_3[] = "6556";
|
||||||
|
const char kTestManufacturer[] = "TestManufacturer";
|
||||||
|
const char kTestProvider[] = "TestProvider";
|
||||||
|
const char kRevokedManufacturer[] = "RevokedManufacturer";
|
||||||
|
const widevine::HashAlgorithm kHashAlgorithm =
|
||||||
|
widevine::HashAlgorithm::kSha256;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
using ::testing::_;
|
|
||||||
using ::testing::Return;
|
using ::testing::Return;
|
||||||
using ::testing::ReturnRef;
|
using ::testing::ReturnRef;
|
||||||
using ::testing::ReturnRefOfCopy;
|
|
||||||
|
|
||||||
const uint32_t kValidCertSystemId = 100;
|
const uint32_t kValidCertSystemId = 100;
|
||||||
const uint32_t kRevokedCertSystemId = 101;
|
const uint32_t kRevokedCertSystemId = 101;
|
||||||
@@ -42,28 +60,47 @@ 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";
|
||||||
|
const char kRevokedDeviceModel[] = "device-model-revoked";
|
||||||
const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
|
const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
|
||||||
const uint32_t kStatusListCreationTime = 17798001;
|
const uint32_t kStatusListCreationTime = 17798001;
|
||||||
const uint32_t kDefaultExpirePeriod = 0;
|
const uint32_t kDefaultExpirePeriod = 0;
|
||||||
|
const bool kDenyRevokedDevice = false;
|
||||||
|
const bool kAllowRevokedDevice = true;
|
||||||
|
|
||||||
class MockCertificateClientCert : public CertificateClientCert {
|
class MockClientCert : public ClientCert {
|
||||||
public:
|
public:
|
||||||
MockCertificateClientCert() {}
|
MockClientCert() {}
|
||||||
MOCK_CONST_METHOD0(system_id, uint32_t());
|
~MockClientCert() override {}
|
||||||
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
|
MOCK_METHOD(uint32_t, system_id, (), (const, override));
|
||||||
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
|
MOCK_METHOD(std::string &, signer_serial_number, (), (const, override));
|
||||||
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
|
MOCK_METHOD(uint32_t, signer_creation_time_seconds, (), (const, override));
|
||||||
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
|
MOCK_METHOD(ClientIdentification::TokenType, type, (), (const, override));
|
||||||
};
|
MOCK_METHOD(const std::string &, encrypted_unique_id, (), (const, override));
|
||||||
|
MOCK_METHOD(const std::string &, unique_id_hash, (), (const, override));
|
||||||
class MockKeyboxClientCert : public KeyboxClientCert {
|
MOCK_METHOD(bool, signed_by_provisioner, (), (const, override));
|
||||||
public:
|
MOCK_METHOD(Status, VerifySignature,
|
||||||
MockKeyboxClientCert() {}
|
(const std::string &message, HashAlgorithm hash_algorithm,
|
||||||
MOCK_CONST_METHOD0(system_id, uint32_t());
|
const std::string &signature, ProtocolVersion protocol_version),
|
||||||
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
|
(const, override));
|
||||||
|
MOCK_METHOD(void, GenerateSigningKey,
|
||||||
|
(const std::string &message, ProtocolVersion protocol_version),
|
||||||
|
(override));
|
||||||
|
MOCK_METHOD(const std::string &, serial_number, (), (const, override));
|
||||||
|
MOCK_METHOD(const std::string &, key, (), (const, override));
|
||||||
|
MOCK_METHOD(SignedMessage::SessionKeyType, key_type, (), (const, override));
|
||||||
|
MOCK_METHOD(bool, using_dual_certificate, (), (const override));
|
||||||
|
MOCK_METHOD(const std::string &, service_id, (), (const, override));
|
||||||
|
MOCK_METHOD(const std::string &, encrypted_key, (), (const, override));
|
||||||
|
MOCK_METHOD(const std::string &, signing_key, (), (const, override));
|
||||||
|
MOCK_METHOD(Status, SystemIdUnknownError, (), (const, override));
|
||||||
|
MOCK_METHOD(Status, SystemIdRevokedError, (), (const, override));
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeviceStatusListTest : public ::testing::Test {
|
class DeviceStatusListTest : public ::testing::Test {
|
||||||
@@ -79,11 +116,16 @@ 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();
|
||||||
cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId);
|
cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId);
|
||||||
cert_status->set_drm_serial_number(kRevokedSerialNumber);
|
cert_status->set_drm_serial_number(kRevokedSerialNumber);
|
||||||
|
cert_status->mutable_device_info()->set_model(kRevokedDeviceModel);
|
||||||
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
|
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
|
||||||
|
|
||||||
// Device cert with status REVOKED ALLOWED DEVICE.
|
// Device cert with status REVOKED ALLOWED DEVICE.
|
||||||
@@ -102,36 +144,89 @@ 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_,
|
||||||
|
kHashAlgorithm,
|
||||||
|
&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(),
|
||||||
ASSERT_TRUE(
|
device_status_list_.UpdateStatusList(
|
||||||
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
test_keys_.public_test_key_1_3072_bits(),
|
||||||
|
serialized_cert_status_list_, kHashAlgorithm,
|
||||||
|
cert_status_list_signature_, kDefaultExpirePeriod));
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT_EQ(OkStatus(), device_status_list_.UpdateStatusList(
|
void GenerateTrivialValidStatusList(std::string *serialized_cert_status_list,
|
||||||
test_keys_.public_test_key_1_3072_bits(),
|
HashAlgorithm hash_algorithm,
|
||||||
serialized_status_list_, kDefaultExpirePeriod));
|
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(root_key->GenerateSignature(*serialized_cert_status_list,
|
||||||
|
hash_algorithm, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
int AllowedTestOnlyDevicesByMakeSize() {
|
||||||
|
return device_status_list_.allowed_test_only_devices_by_make_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AllowedTestOnlyDevicesByProviderSize() {
|
||||||
|
return device_status_list_.allowed_test_only_devices_by_provider_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsTestOnlyDeviceAllowedByMake(uint32_t system_id,
|
||||||
|
const std::string &make) {
|
||||||
|
return device_status_list_.IsTestOnlyDeviceAllowedByMake(system_id, make);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsTestOnlyDeviceAllowedByProvider(uint32_t system_id,
|
||||||
|
const std::string &provider) {
|
||||||
|
return device_status_list_.IsTestOnlyDeviceAllowedByProvider(system_id,
|
||||||
|
provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
DeviceCertificateStatus device_certificate_status;
|
||||||
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));
|
||||||
@@ -139,13 +234,15 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
|||||||
.WillRepeatedly(Return(kValidCertSystemId));
|
.WillRepeatedly(Return(kValidCertSystemId));
|
||||||
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(
|
||||||
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
|
valid_client_cert, kTestManufacturer, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status));
|
||||||
|
ProvisionedDeviceInfo device_info = device_certificate_status.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 +250,26 @@ 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_CALL(revoked_client_cert, SystemIdRevokedError())
|
||||||
device_status_list_.GetCertStatus(revoked_client_cert, &device_info)
|
.WillRepeatedly(
|
||||||
.error_code());
|
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED, "")));
|
||||||
|
EXPECT_EQ(
|
||||||
|
DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||||
|
device_status_list_
|
||||||
|
.GetCertStatus(revoked_client_cert, kTestManufacturer, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status)
|
||||||
|
.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(
|
||||||
device_status_list_.GetCertStatus(revoked_client_cert, &device_info));
|
revoked_client_cert, kTestManufacturer, kTestProvider, kDenyRevokedDevice,
|
||||||
|
&device_certificate_status));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
|
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
||||||
ProvisionedDeviceInfo device_info;
|
DeviceCertificateStatus device_certificate_status;
|
||||||
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));
|
||||||
@@ -173,15 +277,29 @@ TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
|
|||||||
.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(
|
EXPECT_EQ(DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||||
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
device_status_list_
|
||||||
device_status_list_.GetCertStatus(test_only_client_cert, &device_info)
|
.GetCertStatus(test_only_client_cert, kTestManufacturer,
|
||||||
.error_code());
|
kTestProvider, kDenyRevokedDevice,
|
||||||
|
&device_certificate_status)
|
||||||
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
TEST_F(DeviceStatusListTest, GetRevokedIfentifiers) {
|
||||||
ProvisionedDeviceInfo device_info;
|
DeviceCertificateStatus::RevokedIdentifiers revoked_identifiers;
|
||||||
MockCertificateClientCert test_only_client_cert;
|
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) {
|
||||||
|
DeviceCertificateStatus device_certificate_status;
|
||||||
|
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,39 +308,96 @@ 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, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
TEST_F(DeviceStatusListTest, RevokedSystemIdAllowed) {
|
||||||
|
DeviceCertificateStatus device_certificate_status;
|
||||||
|
MockClientCert revoked_client_cert;
|
||||||
|
std::string revoked_drm_serial_number(kRevokedSerialNumber);
|
||||||
|
EXPECT_CALL(revoked_client_cert, type())
|
||||||
|
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||||
|
EXPECT_CALL(revoked_client_cert, system_id())
|
||||||
|
.WillRepeatedly(Return(kRevokedCertSystemId));
|
||||||
|
EXPECT_CALL(revoked_client_cert, signer_serial_number())
|
||||||
|
.WillRepeatedly(ReturnRef(revoked_drm_serial_number));
|
||||||
|
EXPECT_EQ(OkStatus(),
|
||||||
|
device_status_list_.GetCertStatus(
|
||||||
|
revoked_client_cert, kRevokedManufacturer, kTestProvider,
|
||||||
|
kAllowRevokedDevice, &device_certificate_status));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case where the Certificate status is set to Valid.
|
||||||
|
TEST_F(DeviceStatusListTest, ValidKeybox) {
|
||||||
std::multimap<uint32_t, std::string> preprov_keys;
|
std::multimap<uint32_t, std::string> preprov_keys;
|
||||||
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
|
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
|
||||||
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
|
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
|
||||||
|
DeviceCertificateStatus device_certificate_status;
|
||||||
|
MockClientCert valid_client_keybox;
|
||||||
|
|
||||||
// Test case where the Certificate status is set to Valid.
|
|
||||||
ProvisionedDeviceInfo device_info;
|
|
||||||
MockKeyboxClientCert 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(
|
||||||
EXPECT_TRUE(device_info.has_model());
|
valid_client_keybox, kTestManufacturer, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status));
|
||||||
|
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
|
||||||
|
ASSERT_TRUE(device_info.has_model());
|
||||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case where the keybox was not loaded into the pre-prov list.
|
||||||
|
TEST_F(DeviceStatusListTest, UnknownKeybox) {
|
||||||
|
std::multimap<uint32_t, std::string> preprov_keys;
|
||||||
|
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
|
||||||
|
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
|
||||||
|
DeviceCertificateStatus device_certificate_status;
|
||||||
|
MockClientCert unknown_client_keybox;
|
||||||
|
|
||||||
MockKeyboxClientCert 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_CALL(unknown_client_keybox, SystemIdUnknownError()).Times(0);
|
||||||
UNSUPPORTED_SYSTEM_ID,
|
EXPECT_EQ(OkStatus(),
|
||||||
device_status_list_.GetCertStatus(unknown_client_keybox, &device_info)
|
device_status_list_.GetCertStatus(
|
||||||
.error_code());
|
unknown_client_keybox, kTestManufacturer, kTestProvider,
|
||||||
EXPECT_TRUE(device_info.has_model());
|
kDenyRevokedDevice, &device_certificate_status));
|
||||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
|
||||||
|
ASSERT_FALSE(device_info.has_model());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case where the keybox was loaded into the pre-prov list but it's
|
||||||
|
// certificate status is REVOKED.
|
||||||
|
TEST_F(DeviceStatusListTest, RevokedKeybox) {
|
||||||
|
std::multimap<uint32_t, std::string> preprov_keys;
|
||||||
|
preprov_keys.insert(std::make_pair(kRevokedCertSystemId, kTestPreprovKey));
|
||||||
|
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
|
||||||
|
DeviceCertificateStatus device_certificate_status;
|
||||||
|
MockClientCert revoked_client_keybox;
|
||||||
|
|
||||||
|
EXPECT_CALL(revoked_client_keybox, type())
|
||||||
|
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||||
|
EXPECT_CALL(revoked_client_keybox, system_id())
|
||||||
|
.WillRepeatedly(Return(kRevokedCertSystemId));
|
||||||
|
EXPECT_CALL(revoked_client_keybox, SystemIdRevokedError())
|
||||||
|
.WillRepeatedly(
|
||||||
|
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED, "")));
|
||||||
|
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||||
|
device_status_list_
|
||||||
|
.GetCertStatus(revoked_client_keybox, kTestManufacturer,
|
||||||
|
kTestProvider, kDenyRevokedDevice,
|
||||||
|
&device_certificate_status)
|
||||||
|
.error_code());
|
||||||
|
ProvisionedDeviceInfo device_info = device_certificate_status.device_info();
|
||||||
|
ASSERT_TRUE(device_info.has_model());
|
||||||
|
EXPECT_EQ(kRevokedDeviceModel, device_info.model());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
||||||
@@ -230,8 +405,8 @@ 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;
|
DeviceCertificateStatus device_certificate_status;
|
||||||
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())
|
||||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||||
@@ -241,21 +416,27 @@ 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, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status)
|
||||||
|
.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(
|
||||||
device_status_list_.GetCertStatus(older_client_cert, &device_info));
|
older_client_cert, kTestManufacturer, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status));
|
||||||
|
ProvisionedDeviceInfo device_info = device_certificate_status.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;
|
device_certificate_status.Clear();
|
||||||
|
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 +445,21 @@ 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_CALL(newer_client_cert1, SystemIdUnknownError())
|
||||||
device_status_list_.GetCertStatus(newer_client_cert1, &device_info)
|
.WillRepeatedly(
|
||||||
.error_code());
|
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, "")));
|
||||||
|
EXPECT_EQ(
|
||||||
|
DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||||
|
device_status_list_
|
||||||
|
.GetCertStatus(newer_client_cert1, kTestManufacturer, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status)
|
||||||
|
.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_certificate_status.Clear();
|
||||||
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,31 +468,37 @@ 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_CALL(newer_client_cert2, SystemIdUnknownError())
|
||||||
device_status_list_.GetCertStatus(newer_client_cert2, &device_info)
|
.WillRepeatedly(
|
||||||
.error_code());
|
Return(Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, "")));
|
||||||
|
EXPECT_EQ(
|
||||||
|
DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||||
|
device_status_list_
|
||||||
|
.GetCertStatus(newer_client_cert2, kTestManufacturer, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status)
|
||||||
|
.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_, kHashAlgorithm,
|
||||||
|
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_, kHashAlgorithm,
|
||||||
|
cert_status_list_signature_, 0)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockDeviceStatusList : public DeviceStatusList {
|
class MockDeviceStatusList : public DeviceStatusList {
|
||||||
public:
|
public:
|
||||||
MOCK_CONST_METHOD0(GetCurrentTime, uint32_t());
|
MOCK_METHOD(uint32_t, GetCurrentTime, (), (const, override));
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
|
TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
|
||||||
@@ -315,11 +509,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_, kHashAlgorithm,
|
||||||
|
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_, kHashAlgorithm,
|
||||||
|
cert_status_list_signature_, 100)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,10 +528,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_, kHashAlgorithm,
|
||||||
|
cert_status_list_signature_, 100));
|
||||||
|
|
||||||
ProvisionedDeviceInfo device_info;
|
DeviceCertificateStatus device_certificate_status;
|
||||||
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 +542,15 @@ 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(), mock_device_status_list.GetCertStatus(
|
||||||
&device_info));
|
valid_client_cert, kTestManufacturer, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status));
|
||||||
|
|
||||||
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, kTestProvider,
|
||||||
|
kDenyRevokedDevice, &device_certificate_status)
|
||||||
.error_code());
|
.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,4 +573,343 @@ TEST_F(DeviceStatusListTest, IsSystemIdActive) {
|
|||||||
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
|
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowedByMake) {
|
||||||
|
const char kTestManufacturer_AA[] = "AA";
|
||||||
|
const char kTestManufacturer_AAA[] = "AAA";
|
||||||
|
const char kTestManufacturer_BBB[] = "BBB";
|
||||||
|
const char kTestManufacturer_BbB[] = "BbB";
|
||||||
|
const char kTestManufacturer_bbb[] = "bbb";
|
||||||
|
const char kTestManufacturer_CCC[] = "CCC";
|
||||||
|
const char kTestManufacturer_DDD[] = "AAA";
|
||||||
|
std::string allowed_device_list =
|
||||||
|
std::string(kTestSystemId_1) + ":" + std::string(kTestManufacturer_AA);
|
||||||
|
allowed_device_list += "," + std::string(kTestSystemId_2) + ":" +
|
||||||
|
std::string(kTestManufacturer_BBB);
|
||||||
|
allowed_device_list += "," + std::string(kTestSystemId_3) + ":";
|
||||||
|
allowed_device_list += ", " + std::string(kTestSystemId_1) + ":" +
|
||||||
|
std::string(kTestManufacturer_AAA);
|
||||||
|
device_status_list_.AllowTestOnlyDevicesByMake(allowed_device_list);
|
||||||
|
EXPECT_EQ(4, AllowedTestOnlyDevicesByMakeSize());
|
||||||
|
// Verify that device with system_id = kTestSystemId_1 and
|
||||||
|
// manufacturer AA is allowed.
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_1),
|
||||||
|
kTestManufacturer_AA));
|
||||||
|
// Verify that device with system_id = kTestSystemId_1 and
|
||||||
|
// manufacturer AAA is allowed.
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_1),
|
||||||
|
kTestManufacturer_AAA));
|
||||||
|
// Verify that device with system_id = kTestSystemId_2 and
|
||||||
|
// manufacturer AAA is not allowed.
|
||||||
|
// This is because this combination is not in the allowed list.
|
||||||
|
EXPECT_FALSE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
|
||||||
|
kTestManufacturer_AAA));
|
||||||
|
// Verify that device with system_id = kTestSystemId_2 and
|
||||||
|
// manufacturer BBB is allowed.
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
|
||||||
|
kTestManufacturer_BBB));
|
||||||
|
// Verifes that device with mixed case succeeds.
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
|
||||||
|
kTestManufacturer_BbB));
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_2),
|
||||||
|
kTestManufacturer_bbb));
|
||||||
|
// 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(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_3),
|
||||||
|
kTestManufacturer_CCC));
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByMake(std::stoi(kTestSystemId_3),
|
||||||
|
kTestManufacturer_DDD));
|
||||||
|
uint32_t unknown_system_id = 7890;
|
||||||
|
// Verify that device with system_id = unknown_system_id and
|
||||||
|
// manufacturer CCC is not allowed.
|
||||||
|
EXPECT_FALSE(
|
||||||
|
IsTestOnlyDeviceAllowedByMake(unknown_system_id, kTestManufacturer_CCC));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowedByProvider) {
|
||||||
|
const char kTestProvider_AA[] = "AA";
|
||||||
|
const char kTestProvider_AAA[] = "AAA";
|
||||||
|
const char kTestProvider_BBB[] = "BBB";
|
||||||
|
const char kTestProvider_BbB[] = "BbB";
|
||||||
|
const char kTestProvider_bbb[] = "bbb";
|
||||||
|
const char kTestProvider_CCC[] = "CCC";
|
||||||
|
std::string allowed_device_list =
|
||||||
|
std::string(kTestSystemId_1) + ":" + std::string(kTestProvider_AA);
|
||||||
|
allowed_device_list +=
|
||||||
|
"," + std::string(kTestSystemId_2) + ":" + std::string(kTestProvider_BBB);
|
||||||
|
allowed_device_list += "," + std::string(kTestSystemId_3) + ":";
|
||||||
|
allowed_device_list += ", " + std::string(kTestSystemId_1) + ":" +
|
||||||
|
std::string(kTestProvider_AAA);
|
||||||
|
device_status_list_.AllowTestOnlyDevicesByProvider(allowed_device_list);
|
||||||
|
EXPECT_EQ(4, AllowedTestOnlyDevicesByProviderSize());
|
||||||
|
// Verify that device with system_id = kTestSystemId_1 and
|
||||||
|
// provider AA is allowed.
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_1),
|
||||||
|
kTestProvider_AA));
|
||||||
|
// Verify that device with system_id = kTestSystemId_1 and
|
||||||
|
// provider AAA is allowed.
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_1),
|
||||||
|
kTestProvider_AAA));
|
||||||
|
// Verify that device with system_id = kTestSystemId_2 and
|
||||||
|
// provider AAA is not allowed.
|
||||||
|
// This is because this combination is not 'whitelisted'.
|
||||||
|
EXPECT_FALSE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_2),
|
||||||
|
kTestProvider_AAA));
|
||||||
|
// Verify that device with system_id = kTestSystemId_2 and
|
||||||
|
// provider BBB is allowed.
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_2),
|
||||||
|
kTestProvider_BBB));
|
||||||
|
// Verifes that device with mixed case succeeds.
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_2),
|
||||||
|
kTestProvider_BbB));
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_2),
|
||||||
|
kTestProvider_bbb));
|
||||||
|
// Verify that device with system_id = kTestSystemId_3 and
|
||||||
|
// any provider is allowed. This checks that any provider is
|
||||||
|
// allowed for this system_id.
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_3),
|
||||||
|
kTestProvider_CCC));
|
||||||
|
EXPECT_TRUE(IsTestOnlyDeviceAllowedByProvider(std::stoi(kTestSystemId_3),
|
||||||
|
kTestProvider_AAA));
|
||||||
|
uint32_t unknown_system_id = 7890;
|
||||||
|
// Verify that device with system_id = unknown_system_id and
|
||||||
|
// provider CCC is not allowed.
|
||||||
|
EXPECT_FALSE(
|
||||||
|
IsTestOnlyDeviceAllowedByProvider(unknown_system_id, kTestProvider_CCC));
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
HashAlgorithmProtoToEnum(published_devices.hash_algorithm()),
|
||||||
|
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;
|
||||||
|
HashAlgorithm hash_algorithm;
|
||||||
|
ASSERT_EQ(OkStatus(),
|
||||||
|
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||||
|
serialized_published_devices, &actual_cert_status_list,
|
||||||
|
&actual_serialized_cert_status_list, &hash_algorithm,
|
||||||
|
&actual_signature));
|
||||||
|
EXPECT_EQ(published_devices.device_certificate_status_list(),
|
||||||
|
actual_serialized_cert_status_list);
|
||||||
|
EXPECT_EQ(published_devices.signature(), actual_signature);
|
||||||
|
EXPECT_EQ(HashAlgorithmProtoToEnum(published_devices.hash_algorithm()),
|
||||||
|
hash_algorithm);
|
||||||
|
|
||||||
|
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;
|
||||||
|
SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
|
||||||
|
GenerateTrivialValidStatusList(
|
||||||
|
&serialized_cert_status_list,
|
||||||
|
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
|
||||||
|
&signature);
|
||||||
|
*(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;
|
||||||
|
HashAlgorithm hash_algorithm;
|
||||||
|
ASSERT_EQ(OkStatus(),
|
||||||
|
DeviceStatusList::DetermineAndDeserializeServiceResponse(
|
||||||
|
server_response, &actual_cert_status_list,
|
||||||
|
&actual_serialized_cert_status_list, &hash_algorithm,
|
||||||
|
&actual_signature));
|
||||||
|
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
|
||||||
|
EXPECT_EQ(signature, actual_signature);
|
||||||
|
EXPECT_EQ(
|
||||||
|
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
|
||||||
|
hash_algorithm);
|
||||||
|
|
||||||
|
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;
|
||||||
|
SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
|
||||||
|
GenerateTrivialValidStatusList(
|
||||||
|
&serialized_cert_status_list,
|
||||||
|
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
|
||||||
|
&signature);
|
||||||
|
*(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;
|
||||||
|
HashAlgorithm hash_algorithm;
|
||||||
|
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,
|
||||||
|
&hash_algorithm, &actual_signature));
|
||||||
|
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
|
||||||
|
EXPECT_EQ(signature, actual_signature);
|
||||||
|
EXPECT_EQ(
|
||||||
|
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
|
||||||
|
hash_algorithm);
|
||||||
|
|
||||||
|
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;
|
||||||
|
SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
|
||||||
|
GenerateTrivialValidStatusList(
|
||||||
|
&serialized_cert_status_list,
|
||||||
|
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
|
||||||
|
&signature);
|
||||||
|
*(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;
|
||||||
|
HashAlgorithm hash_algorithm;
|
||||||
|
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,
|
||||||
|
&hash_algorithm, &actual_signature));
|
||||||
|
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
|
||||||
|
EXPECT_EQ(signature, actual_signature);
|
||||||
|
EXPECT_EQ(
|
||||||
|
HashAlgorithmProtoToEnum(legacy_signed_cert_status_list.hash_algorithm()),
|
||||||
|
hash_algorithm);
|
||||||
|
|
||||||
|
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,
|
||||||
|
GetDeviceCertificateStatusBySystemIdExpiredDeviceCertificateStatusList) {
|
||||||
|
MockDeviceStatusList mock_device_status_list;
|
||||||
|
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
|
||||||
|
.Times(3)
|
||||||
|
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||||
|
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||||
|
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||||
|
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||||
|
test_keys_.public_test_key_1_3072_bits(),
|
||||||
|
serialized_cert_status_list_, kHashAlgorithm,
|
||||||
|
cert_status_list_signature_, 100));
|
||||||
|
DeviceCertificateStatus device_certificate_status;
|
||||||
|
EXPECT_EQ(OkStatus(),
|
||||||
|
mock_device_status_list.GetDeviceCertificateStatusBySystemId(
|
||||||
|
kValidCertSystemId, &device_certificate_status));
|
||||||
|
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||||
|
mock_device_status_list
|
||||||
|
.GetDeviceCertificateStatusBySystemId(
|
||||||
|
kValidCertSystemId, &device_certificate_status)
|
||||||
|
.error_code());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest,
|
||||||
|
GetDeviceCertificateStatusBySystemIdUnknownDevice) {
|
||||||
|
DeviceCertificateStatus device_certificate_status;
|
||||||
|
uint32_t unknown_system_id = 2000;
|
||||||
|
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||||
|
device_status_list_
|
||||||
|
.GetDeviceCertificateStatusBySystemId(
|
||||||
|
unknown_system_id, &device_certificate_status)
|
||||||
|
.error_code());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeviceStatusListTest, GetDeviceCertificateStatusBySystemIdOk) {
|
||||||
|
DeviceCertificateStatus device_certificate_status;
|
||||||
|
EXPECT_OK(device_status_list_.GetDeviceCertificateStatusBySystemId(
|
||||||
|
kValidCertSystemId, &device_certificate_status));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -16,9 +16,13 @@
|
|||||||
#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/hash_algorithm.h"
|
||||||
|
#include "common/hash_algorithm_util.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 +37,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 +259,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 +281,58 @@ 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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
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, hash_algorithm, 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 +354,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,14 +425,16 @@ 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");
|
||||||
}
|
}
|
||||||
if (!public_key->VerifySignature(signed_root_cert.drm_certificate(),
|
if (!public_key->VerifySignature(
|
||||||
signed_root_cert.signature())) {
|
signed_root_cert.drm_certificate(),
|
||||||
|
HashAlgorithmProtoToEnum(signed_root_cert.hash_algorithm()),
|
||||||
|
signed_root_cert.signature())) {
|
||||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
"invalid-root-certificate-signature");
|
"invalid-root-certificate-signature");
|
||||||
}
|
}
|
||||||
@@ -424,10 +451,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 +500,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,24 +512,34 @@ 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_);
|
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@@ -508,19 +547,22 @@ Status DrmRootCertificate::VerifySignatures(
|
|||||||
if (use_cache) {
|
if (use_cache) {
|
||||||
status = signature_cache_->VerifySignature(
|
status = signature_cache_->VerifySignature(
|
||||||
signed_cert.drm_certificate(), cert_serial_number,
|
signed_cert.drm_certificate(), cert_serial_number,
|
||||||
signed_cert.signature(), signer.public_key(), signer.serial_number());
|
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(),
|
if (!signer_public_key->VerifySignature(
|
||||||
signed_cert.signature())) {
|
signed_cert.drm_certificate(),
|
||||||
|
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
|
||||||
|
signed_cert.signature())) {
|
||||||
return Status(error_space, INVALID_SIGNATURE,
|
return Status(error_space, INVALID_SIGNATURE,
|
||||||
"cache-miss-invalid-signature");
|
"cache-miss-invalid-signature");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,10 +13,16 @@
|
|||||||
|
|
||||||
#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/hash_algorithm.h"
|
||||||
|
#include "common/hash_algorithm_util.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/test_drm_certificates.h"
|
#include "common/test_drm_certificates.h"
|
||||||
@@ -25,6 +31,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 +87,222 @@ 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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
std::string* signature) const override {
|
||||||
|
return private_key_->GenerateSignature(message, hash_algorithm, 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 GenerateSignedDrmCertificate() {
|
void RsaTestSetup() {
|
||||||
SignedDrmCertificate* current_sc(&signed_drm_certificate_);
|
private_keys_.resize(3);
|
||||||
ASSERT_TRUE(drm_certificates_[2].SerializeToString(
|
private_keys_[kDrmRootKey] =
|
||||||
current_sc->mutable_drm_certificate()));
|
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits(),
|
||||||
ASSERT_TRUE(private_keys_[1]->GenerateSignature(
|
widevine::DrmCertificate::RSA);
|
||||||
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
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());
|
||||||
|
|
||||||
current_sc = current_sc->mutable_signer();
|
private_keys_[kInterMediateKey] =
|
||||||
ASSERT_TRUE(drm_certificates_[1].SerializeToString(
|
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_2_2048_bits(),
|
||||||
current_sc->mutable_drm_certificate()));
|
widevine::DrmCertificate::RSA);
|
||||||
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
|
drm_certificates_[kInterMediateKey].set_serial_number("level 1");
|
||||||
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
drm_certificates_[kInterMediateKey].set_creation_time_seconds(1);
|
||||||
|
drm_certificates_[kInterMediateKey].set_public_key(
|
||||||
|
rsa_test_keys_.public_test_key_2_2048_bits());
|
||||||
|
|
||||||
current_sc = current_sc->mutable_signer();
|
private_keys_[kClientKey] =
|
||||||
ASSERT_TRUE(drm_certificates_[0].SerializeToString(
|
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits(),
|
||||||
current_sc->mutable_drm_certificate()));
|
widevine::DrmCertificate::RSA);
|
||||||
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
|
drm_certificates_[kClientKey].set_serial_number("level 2");
|
||||||
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
drm_certificates_[kClientKey].set_creation_time_seconds(2);
|
||||||
|
drm_certificates_[kClientKey].set_public_key(
|
||||||
|
rsa_test_keys_.public_test_key_3_2048_bits());
|
||||||
}
|
}
|
||||||
|
|
||||||
RsaTestKeys test_keys_;
|
void EcTestSetup() {
|
||||||
std::vector<std::unique_ptr<RsaPrivateKey>> private_keys_;
|
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() {
|
||||||
|
SignedDrmCertificate* current_sc(&signed_drm_certificate_);
|
||||||
|
drm_certificates_[kClientKey].set_algorithm(
|
||||||
|
private_keys_[kClientKey]->algorithm());
|
||||||
|
ASSERT_TRUE(drm_certificates_[kClientKey].SerializeToString(
|
||||||
|
current_sc->mutable_drm_certificate()));
|
||||||
|
ASSERT_TRUE(private_keys_[kInterMediateKey]->GenerateSignature(
|
||||||
|
current_sc->drm_certificate(),
|
||||||
|
HashAlgorithmProtoToEnum(current_sc->hash_algorithm()),
|
||||||
|
current_sc->mutable_signature()));
|
||||||
|
|
||||||
|
current_sc = current_sc->mutable_signer();
|
||||||
|
drm_certificates_[kInterMediateKey].set_algorithm(
|
||||||
|
private_keys_[kInterMediateKey]->algorithm());
|
||||||
|
ASSERT_TRUE(drm_certificates_[kInterMediateKey].SerializeToString(
|
||||||
|
current_sc->mutable_drm_certificate()));
|
||||||
|
ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature(
|
||||||
|
current_sc->drm_certificate(),
|
||||||
|
HashAlgorithmProtoToEnum(current_sc->hash_algorithm()),
|
||||||
|
current_sc->mutable_signature()));
|
||||||
|
|
||||||
|
current_sc = current_sc->mutable_signer();
|
||||||
|
drm_certificates_[kDrmRootKey].set_algorithm(
|
||||||
|
private_keys_[kDrmRootKey]->algorithm());
|
||||||
|
ASSERT_TRUE(drm_certificates_[kDrmRootKey].SerializeToString(
|
||||||
|
current_sc->mutable_drm_certificate()));
|
||||||
|
ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature(
|
||||||
|
current_sc->drm_certificate(),
|
||||||
|
HashAlgorithmProtoToEnum(current_sc->hash_algorithm()),
|
||||||
|
current_sc->mutable_signature()));
|
||||||
|
}
|
||||||
|
|
||||||
|
RsaTestKeys rsa_test_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 +314,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 +329,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 +338,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 +347,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 +356,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 +364,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 +373,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 +382,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 +392,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(),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "base/thread_annotations.h"
|
#include "absl/base/thread_annotations.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "util/gtl/map_util.h"
|
#include "util/gtl/map_util.h"
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "common/drm_root_certificate.h"
|
#include "common/drm_root_certificate.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
#include "common/rsa_util.h"
|
#include "common/rsa_util.h"
|
||||||
|
#include "common/status.h"
|
||||||
#include "protos/public/client_identification.pb.h"
|
#include "protos/public/client_identification.pb.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"
|
||||||
@@ -44,16 +45,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 +97,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 +129,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 +188,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();
|
||||||
@@ -185,60 +216,22 @@ Status DrmServiceCertificate::DecryptClientIdentification(
|
|||||||
const EncryptedClientIdentification& encrypted_client_id,
|
const EncryptedClientIdentification& encrypted_client_id,
|
||||||
ClientIdentification* client_id) {
|
ClientIdentification* client_id) {
|
||||||
DCHECK(client_id);
|
DCHECK(client_id);
|
||||||
if (encrypted_client_id.service_certificate_serial_number().empty()) {
|
std::string serialized_client_id;
|
||||||
|
Status status = DecryptEncryptedPayload(
|
||||||
|
encrypted_client_id.service_certificate_serial_number(),
|
||||||
|
encrypted_client_id.provider_id(),
|
||||||
|
encrypted_client_id.encrypted_client_id(),
|
||||||
|
encrypted_client_id.encrypted_client_id_iv(),
|
||||||
|
encrypted_client_id.encrypted_privacy_key(), &serialized_client_id);
|
||||||
|
if (status.error_code() == error::INVALID_ARGUMENT) {
|
||||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"missing-service-certificate-serial-number");
|
status.error_message());
|
||||||
}
|
}
|
||||||
if (encrypted_client_id.provider_id().empty()) {
|
if (status.ok() && !client_id->ParseFromString(serialized_client_id)) {
|
||||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
|
||||||
"missing-service-id");
|
|
||||||
}
|
|
||||||
if (encrypted_client_id.encrypted_client_id().empty()) {
|
|
||||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
|
||||||
"missing-encrypted-client-id");
|
|
||||||
}
|
|
||||||
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
|
|
||||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
|
||||||
"missing-encrypted-client-id-iv");
|
|
||||||
}
|
|
||||||
if (encrypted_client_id.encrypted_privacy_key().empty()) {
|
|
||||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
|
||||||
"missing-encrypted-privacy-key");
|
|
||||||
}
|
|
||||||
std::string privacy_key;
|
|
||||||
std::string provider_id;
|
|
||||||
const DrmServiceCertificate* cert = GetDrmServiceCertificate(
|
|
||||||
encrypted_client_id.service_certificate_serial_number());
|
|
||||||
if (!cert) {
|
|
||||||
return Status(
|
|
||||||
error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
|
||||||
"service-certificate-not-found (SN " +
|
|
||||||
absl::BytesToHexString(
|
|
||||||
encrypted_client_id.service_certificate_serial_number()) +
|
|
||||||
")");
|
|
||||||
}
|
|
||||||
if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(),
|
|
||||||
&privacy_key)) {
|
|
||||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
|
||||||
"privacy-key-decryption-failed");
|
|
||||||
}
|
|
||||||
if (cert->provider_id() != encrypted_client_id.provider_id()) {
|
|
||||||
return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
|
||||||
std::string("provider-id-mismatch (") + cert->provider_id() +
|
|
||||||
" / " + encrypted_client_id.provider_id() + ")");
|
|
||||||
}
|
|
||||||
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
|
||||||
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
|
||||||
encrypted_client_id.encrypted_client_id()));
|
|
||||||
if (serialized_client_id.empty()) {
|
|
||||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
|
||||||
"client-id-decryption-failed");
|
|
||||||
}
|
|
||||||
if (!client_id->ParseFromString(serialized_client_id)) {
|
|
||||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||||
"client-id-parse-failed");
|
"client-id-parse-failed");
|
||||||
}
|
}
|
||||||
return OkStatus();
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmServiceCertificate::ResetServiceCertificates() {
|
void DrmServiceCertificate::ResetServiceCertificates() {
|
||||||
@@ -271,6 +264,67 @@ Status DrmServiceCertificate::ValidateDrmServiceCertificate() {
|
|||||||
return OkStatus();
|
return OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
const EncryptedLicenseRequest& encrypted_license_request,
|
||||||
|
std::string* license_challenge) {
|
||||||
|
Status status = DecryptEncryptedPayload(
|
||||||
|
encrypted_license_request.service_certificate_serial_number(),
|
||||||
|
encrypted_license_request.provider_id(),
|
||||||
|
encrypted_license_request.encrypted_license_request(),
|
||||||
|
encrypted_license_request.encrypted_license_request_iv(),
|
||||||
|
encrypted_license_request.encrypted_privacy_key(), license_challenge);
|
||||||
|
if (status.error_code() == error::INVALID_ARGUMENT) {
|
||||||
|
return Status(error_space, INVALID_ENCRYPTED_LICENSE_CHALLENGE,
|
||||||
|
status.error_message());
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DrmServiceCertificate::DecryptEncryptedPayload(
|
||||||
|
const std::string& service_certificate_serial_number,
|
||||||
|
const std::string& provider_id, const std::string& encrypted_payload,
|
||||||
|
const std::string& iv, const std::string& encrypted_privacy_key,
|
||||||
|
std::string* payload) {
|
||||||
|
if (payload == nullptr) {
|
||||||
|
return Status(error::INVALID_ARGUMENT, "null-payload");
|
||||||
|
}
|
||||||
|
payload->clear();
|
||||||
|
if (service_certificate_serial_number.empty()) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
"missing-service-certificate-serial-number");
|
||||||
|
} else if (provider_id.empty()) {
|
||||||
|
return Status(error::INVALID_ARGUMENT, "missing-service-id");
|
||||||
|
} else if (encrypted_payload.empty()) {
|
||||||
|
return Status(error::INVALID_ARGUMENT, "missing-encrypted-payload");
|
||||||
|
} else if (iv.empty()) {
|
||||||
|
return Status(error::INVALID_ARGUMENT, "missing-iv");
|
||||||
|
} else if (encrypted_privacy_key.empty()) {
|
||||||
|
return Status(error::INVALID_ARGUMENT, "missing-privacy-key");
|
||||||
|
}
|
||||||
|
std::string privacy_key;
|
||||||
|
const DrmServiceCertificate* cert =
|
||||||
|
GetDrmServiceCertificateBySerialNumber(service_certificate_serial_number);
|
||||||
|
if (!cert) {
|
||||||
|
return Status(
|
||||||
|
error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||||
|
"service-certificate-not-found (SN " +
|
||||||
|
absl::BytesToHexString(service_certificate_serial_number) + ")");
|
||||||
|
}
|
||||||
|
if (!cert->private_key()->Decrypt(encrypted_privacy_key, &privacy_key)) {
|
||||||
|
return Status(error::INVALID_ARGUMENT, "privacy-key-decryption-failed");
|
||||||
|
}
|
||||||
|
if (cert->provider_id() != provider_id) {
|
||||||
|
return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||||
|
std::string("provider-id-mismatch (") + cert->provider_id() +
|
||||||
|
" / " + provider_id + ")");
|
||||||
|
}
|
||||||
|
*payload = crypto_util::DecryptAesCbc(privacy_key, iv, encrypted_payload);
|
||||||
|
if (payload->empty()) {
|
||||||
|
return Status(error::INVALID_ARGUMENT, "payload-decryption-failed");
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
DrmServiceCertificate::DrmServiceCertificate(
|
DrmServiceCertificate::DrmServiceCertificate(
|
||||||
const std::string& service_certificate, const std::string& provider_id,
|
const std::string& service_certificate, const std::string& provider_id,
|
||||||
const std::string& serial_number, const uint32_t creation_time_seconds,
|
const std::string& serial_number, const uint32_t creation_time_seconds,
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
#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"
|
||||||
|
#include "protos/public/external_license.pb.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
class RequestInspectorTest;
|
class RequestInspectorTest;
|
||||||
@@ -36,6 +36,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 +53,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 +62,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 +74,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 +97,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(); }
|
||||||
|
|
||||||
@@ -95,22 +107,41 @@ class DrmServiceCertificate {
|
|||||||
// via get deviceCertificate StatusList.
|
// via get deviceCertificate StatusList.
|
||||||
static Status ValidateDrmServiceCertificate();
|
static Status ValidateDrmServiceCertificate();
|
||||||
|
|
||||||
|
// Decrypts the EncryptedLicenseRequest message passed in
|
||||||
|
// |encrypted_license_request|. If successful, the decrypted license request
|
||||||
|
// is placed into |license_challenge|. The decryption is performed using the
|
||||||
|
// private key for the certificate which was used to encrypt the information.
|
||||||
|
// |license_challenge| must not be NULL. Returns status::OK if successful,
|
||||||
|
// or an appropriate error otherwise. This method is thread-safe.
|
||||||
|
static Status DecryptLicenseChallenge(
|
||||||
|
const EncryptedLicenseRequest& encrypted_license_request,
|
||||||
|
std::string* license_challenge);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class DrmServiceCertificateTest;
|
friend class DrmServiceCertificateTest;
|
||||||
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);
|
||||||
|
|
||||||
|
static Status DecryptEncryptedPayload(
|
||||||
|
const std::string& service_certificate_serial_number,
|
||||||
|
const std::string& provider_id, const std::string& encrypted_payload,
|
||||||
|
const std::string& iv, const std::string& privacy_key,
|
||||||
|
std::string* payload);
|
||||||
|
|
||||||
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 +154,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
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "common/aes_cbc_util.h"
|
#include "common/aes_cbc_util.h"
|
||||||
#include "common/drm_root_certificate.h"
|
#include "common/drm_root_certificate.h"
|
||||||
|
#include "common/hash_algorithm_util.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/rsa_util.h"
|
#include "common/rsa_util.h"
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
#include "protos/public/client_identification.pb.h"
|
#include "protos/public/client_identification.pb.h"
|
||||||
#include "protos/public/drm_certificate.pb.h"
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
#include "protos/public/errors.pb.h" // IWYU pragma: keep
|
#include "protos/public/errors.pb.h" // IWYU pragma: keep
|
||||||
|
#include "protos/public/external_license.pb.h"
|
||||||
#include "protos/public/license_server_sdk.pb.h"
|
#include "protos/public/license_server_sdk.pb.h"
|
||||||
#include "protos/public/signed_drm_certificate.pb.h"
|
#include "protos/public/signed_drm_certificate.pb.h"
|
||||||
|
|
||||||
@@ -51,9 +53,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);
|
||||||
@@ -62,8 +64,10 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
|||||||
cert.set_creation_time_seconds(creation_time_seconds);
|
cert.set_creation_time_seconds(creation_time_seconds);
|
||||||
SignedDrmCertificate signed_cert;
|
SignedDrmCertificate signed_cert;
|
||||||
cert.SerializeToString(signed_cert.mutable_drm_certificate());
|
cert.SerializeToString(signed_cert.mutable_drm_certificate());
|
||||||
root_private_key_->GenerateSignature(signed_cert.drm_certificate(),
|
root_private_key_->GenerateSignature(
|
||||||
signed_cert.mutable_signature());
|
signed_cert.drm_certificate(),
|
||||||
|
HashAlgorithmProtoToEnum(signed_cert.hash_algorithm()),
|
||||||
|
signed_cert.mutable_signature());
|
||||||
std::string serialized_cert;
|
std::string serialized_cert;
|
||||||
signed_cert.SerializeToString(&serialized_cert);
|
signed_cert.SerializeToString(&serialized_cert);
|
||||||
return serialized_cert;
|
return serialized_cert;
|
||||||
@@ -119,6 +123,24 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
|||||||
encrypted_client_id->mutable_encrypted_privacy_key());
|
encrypted_client_id->mutable_encrypted_privacy_key());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EncryptLicenseChallenge(
|
||||||
|
const std::string& license_challenge, const std::string& serial_number,
|
||||||
|
const std::string& provider_id, const std::string& public_key,
|
||||||
|
EncryptedLicenseRequest* encrypted_license_request) {
|
||||||
|
CHECK(encrypted_license_request);
|
||||||
|
encrypted_license_request->set_provider_id(provider_id);
|
||||||
|
encrypted_license_request->set_service_certificate_serial_number(
|
||||||
|
serial_number);
|
||||||
|
encrypted_license_request->set_encrypted_license_request(
|
||||||
|
crypto_util::EncryptAesCbc(privacy_key_, iv_, license_challenge));
|
||||||
|
encrypted_license_request->set_encrypted_license_request_iv(iv_);
|
||||||
|
std::unique_ptr<RsaPublicKey> rsa_key(RsaPublicKey::Create(public_key));
|
||||||
|
ASSERT_TRUE(rsa_key.get());
|
||||||
|
rsa_key->Encrypt(
|
||||||
|
privacy_key_,
|
||||||
|
encrypted_license_request->mutable_encrypted_privacy_key());
|
||||||
|
}
|
||||||
|
|
||||||
RsaTestKeys test_keys_;
|
RsaTestKeys test_keys_;
|
||||||
TestDrmCertificates test_certs_;
|
TestDrmCertificates test_certs_;
|
||||||
std::string privacy_key_;
|
std::string privacy_key_;
|
||||||
@@ -160,6 +182,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 +197,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 +227,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");
|
||||||
@@ -298,7 +381,7 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
|||||||
invalid.clear_encrypted_privacy_key();
|
invalid.clear_encrypted_privacy_key();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||||
"missing-encrypted-privacy-key",
|
"missing-privacy-key",
|
||||||
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
||||||
&decrypted_client_id)
|
&decrypted_client_id)
|
||||||
.ToString());
|
.ToString());
|
||||||
@@ -311,7 +394,7 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
|||||||
invalid.clear_encrypted_client_id_iv();
|
invalid.clear_encrypted_client_id_iv();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||||
"missing-encrypted-client-id-iv",
|
"missing-iv",
|
||||||
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
||||||
&decrypted_client_id)
|
&decrypted_client_id)
|
||||||
.ToString());
|
.ToString());
|
||||||
@@ -324,7 +407,7 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
|||||||
invalid.clear_encrypted_client_id();
|
invalid.clear_encrypted_client_id();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||||
"missing-encrypted-client-id",
|
"missing-encrypted-payload",
|
||||||
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
||||||
&decrypted_client_id)
|
&decrypted_client_id)
|
||||||
.ToString());
|
.ToString());
|
||||||
@@ -358,6 +441,142 @@ TEST_F(DrmServiceCertificateTest, PrivateKeyDecryptError) {
|
|||||||
.ToString());
|
.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DrmServiceCertificateTest, DecryptLicenseChallenge) {
|
||||||
|
std::string serial_number("serial_number");
|
||||||
|
std::string provider_id("someservice.com");
|
||||||
|
uint32_t creation_time_seconds(1234);
|
||||||
|
|
||||||
|
EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||||
|
creation_time_seconds));
|
||||||
|
EncryptedLicenseRequest encrypted_license_request;
|
||||||
|
const std::string license_challenge =
|
||||||
|
"<Challenge><Element1>val</Element1></"
|
||||||
|
"Challenge>";
|
||||||
|
EncryptLicenseChallenge(license_challenge, serial_number, provider_id,
|
||||||
|
test_keys_.public_test_key_2_2048_bits(),
|
||||||
|
&encrypted_license_request);
|
||||||
|
std::string decrypted_license_challenge;
|
||||||
|
EXPECT_EQ(OkStatus(),
|
||||||
|
DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
encrypted_license_request, &decrypted_license_challenge));
|
||||||
|
EXPECT_EQ(license_challenge, decrypted_license_challenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DrmServiceCertificateTest, DecryptLicenseChallengeError) {
|
||||||
|
std::string serial_number("serial_number");
|
||||||
|
std::string provider_id("someservice.com");
|
||||||
|
uint32_t creation_time_seconds(1234);
|
||||||
|
|
||||||
|
EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||||
|
creation_time_seconds));
|
||||||
|
EncryptedLicenseRequest encrypted_license_request;
|
||||||
|
const std::string license_challenge =
|
||||||
|
"<Challenge><Element1>val</Element1></"
|
||||||
|
"Challenge>";
|
||||||
|
EncryptLicenseChallenge(license_challenge, serial_number, provider_id,
|
||||||
|
test_keys_.public_test_key_2_2048_bits(),
|
||||||
|
&encrypted_license_request);
|
||||||
|
{
|
||||||
|
EXPECT_EQ(
|
||||||
|
"Errors::INVALID_ENCRYPTED_LICENSE_CHALLENGE: "
|
||||||
|
"null-payload",
|
||||||
|
DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
encrypted_license_request, nullptr)
|
||||||
|
.ToString());
|
||||||
|
}
|
||||||
|
std::string decrypted_license_challenge;
|
||||||
|
{
|
||||||
|
const std::string assigned_serial_number =
|
||||||
|
encrypted_license_request.service_certificate_serial_number();
|
||||||
|
encrypted_license_request.clear_service_certificate_serial_number();
|
||||||
|
EXPECT_EQ(
|
||||||
|
"Errors::INVALID_ENCRYPTED_LICENSE_CHALLENGE: "
|
||||||
|
"missing-service-certificate-serial-number",
|
||||||
|
DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
encrypted_license_request, &decrypted_license_challenge)
|
||||||
|
.ToString());
|
||||||
|
encrypted_license_request.set_service_certificate_serial_number(
|
||||||
|
assigned_serial_number);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string assigned_provider_id =
|
||||||
|
encrypted_license_request.provider_id();
|
||||||
|
encrypted_license_request.clear_provider_id();
|
||||||
|
EXPECT_EQ(
|
||||||
|
"Errors::INVALID_ENCRYPTED_LICENSE_CHALLENGE: "
|
||||||
|
"missing-service-id",
|
||||||
|
DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
encrypted_license_request, &decrypted_license_challenge)
|
||||||
|
.ToString());
|
||||||
|
encrypted_license_request.set_provider_id(assigned_provider_id);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string assigned_encrypted_license_request =
|
||||||
|
encrypted_license_request.encrypted_license_request();
|
||||||
|
encrypted_license_request.clear_encrypted_license_request();
|
||||||
|
EXPECT_EQ(
|
||||||
|
"Errors::INVALID_ENCRYPTED_LICENSE_CHALLENGE: "
|
||||||
|
"missing-encrypted-payload",
|
||||||
|
DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
encrypted_license_request, &decrypted_license_challenge)
|
||||||
|
.ToString());
|
||||||
|
encrypted_license_request.set_encrypted_license_request(
|
||||||
|
assigned_encrypted_license_request);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string assigned_encrypted_license_request_iv =
|
||||||
|
encrypted_license_request.encrypted_license_request_iv();
|
||||||
|
encrypted_license_request.clear_encrypted_license_request_iv();
|
||||||
|
EXPECT_EQ(
|
||||||
|
"Errors::INVALID_ENCRYPTED_LICENSE_CHALLENGE: "
|
||||||
|
"missing-iv",
|
||||||
|
DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
encrypted_license_request, &decrypted_license_challenge)
|
||||||
|
.ToString());
|
||||||
|
encrypted_license_request.set_encrypted_license_request_iv(
|
||||||
|
assigned_encrypted_license_request_iv);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string assigned_encrypted_privacy_key =
|
||||||
|
encrypted_license_request.encrypted_privacy_key();
|
||||||
|
encrypted_license_request.clear_encrypted_privacy_key();
|
||||||
|
EXPECT_EQ(
|
||||||
|
"Errors::INVALID_ENCRYPTED_LICENSE_CHALLENGE: "
|
||||||
|
"missing-privacy-key",
|
||||||
|
DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
encrypted_license_request, &decrypted_license_challenge)
|
||||||
|
.ToString());
|
||||||
|
encrypted_license_request.set_encrypted_privacy_key(
|
||||||
|
assigned_encrypted_privacy_key);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string assigned_encrypted_privacy_key =
|
||||||
|
encrypted_license_request.encrypted_privacy_key();
|
||||||
|
encrypted_license_request.mutable_encrypted_privacy_key()->append(
|
||||||
|
"tampered");
|
||||||
|
EXPECT_EQ(
|
||||||
|
"Errors::INVALID_ENCRYPTED_LICENSE_CHALLENGE: "
|
||||||
|
"privacy-key-decryption-failed",
|
||||||
|
DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
encrypted_license_request, &decrypted_license_challenge)
|
||||||
|
.ToString());
|
||||||
|
encrypted_license_request.set_encrypted_privacy_key(
|
||||||
|
assigned_encrypted_privacy_key);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string assigned_provider_id =
|
||||||
|
encrypted_license_request.provider_id();
|
||||||
|
encrypted_license_request.set_provider_id("tamperedProviderId");
|
||||||
|
EXPECT_EQ(
|
||||||
|
"Errors::SERVICE_CERTIFICATE_NOT_FOUND: "
|
||||||
|
"provider-id-mismatch (" +
|
||||||
|
assigned_provider_id + " / tamperedProviderId)",
|
||||||
|
DrmServiceCertificate::DecryptLicenseChallenge(
|
||||||
|
encrypted_license_request, &decrypted_license_challenge)
|
||||||
|
.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(user): Add more unit tests for various fail cases (bad keys having
|
// TODO(user): Add more unit tests for various fail cases (bad keys having
|
||||||
// to do with bad keys and bad certs).
|
// to do with bad keys and bad certs).
|
||||||
|
|
||||||
|
|||||||
113
common/dual_certificate_client_cert.cc
Normal file
113
common/dual_certificate_client_cert.cc
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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/dual_certificate_client_cert.h"
|
||||||
|
|
||||||
|
#include "common/error_space.h"
|
||||||
|
#include "common/status.h"
|
||||||
|
#include "protos/public/errors.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
Status DualCertificateClientCert::Initialize(
|
||||||
|
const DrmRootCertificate* root_certificate,
|
||||||
|
const std::string& serialized_signing_certificate,
|
||||||
|
const std::string& serialized_encryption_certificate) {
|
||||||
|
Status status = signing_certificate_.Initialize(
|
||||||
|
root_certificate, serialized_signing_certificate);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
status = encryption_certificate_.Initialize(
|
||||||
|
root_certificate, serialized_encryption_certificate);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
if (encryption_certificate_.signer_serial_number() !=
|
||||||
|
signing_certificate_.signer_serial_number()) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"certificate_signer_mismatch");
|
||||||
|
}
|
||||||
|
if ((encryption_certificate_.system_id() !=
|
||||||
|
signing_certificate_.system_id()) ||
|
||||||
|
(encryption_certificate_.service_id() !=
|
||||||
|
signing_certificate_.service_id()) ||
|
||||||
|
(encryption_certificate_.signer_creation_time_seconds() !=
|
||||||
|
signing_certificate_.signer_creation_time_seconds()) ||
|
||||||
|
(encryption_certificate_.signed_by_provisioner() !=
|
||||||
|
signing_certificate_.signed_by_provisioner())) {
|
||||||
|
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||||
|
"invalid_certificate_pair");
|
||||||
|
}
|
||||||
|
return OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DualCertificateClientCert::VerifySignature(
|
||||||
|
const std::string& message, HashAlgorithm hash_algorithm,
|
||||||
|
const std::string& signature, ProtocolVersion protocol_version) const {
|
||||||
|
return signing_certificate_.VerifySignature(message, hash_algorithm,
|
||||||
|
signature, protocol_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DualCertificateClientCert::GenerateSigningKey(
|
||||||
|
const std::string& message, ProtocolVersion protocol_version) {
|
||||||
|
encryption_certificate_.GenerateSigningKey(message, protocol_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& DualCertificateClientCert::encrypted_key() const {
|
||||||
|
return encryption_certificate_.encrypted_key();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& DualCertificateClientCert::key() const {
|
||||||
|
return encryption_certificate_.key();
|
||||||
|
}
|
||||||
|
|
||||||
|
SignedMessage::SessionKeyType DualCertificateClientCert::key_type() const {
|
||||||
|
return encryption_certificate_.key_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/155979840): Support revocation check for the encryption certificate.
|
||||||
|
const std::string& DualCertificateClientCert::serial_number() const {
|
||||||
|
return signing_certificate_.serial_number();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& DualCertificateClientCert::service_id() const {
|
||||||
|
return signing_certificate_.service_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& DualCertificateClientCert::signing_key() const {
|
||||||
|
return encryption_certificate_.signing_key();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& DualCertificateClientCert::signer_serial_number() const {
|
||||||
|
return signing_certificate_.signer_serial_number();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DualCertificateClientCert::signer_creation_time_seconds() const {
|
||||||
|
return signing_certificate_.signer_creation_time_seconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DualCertificateClientCert::signed_by_provisioner() const {
|
||||||
|
return signing_certificate_.signed_by_provisioner();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DualCertificateClientCert::system_id() const {
|
||||||
|
return signing_certificate_.system_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/155979840): Support revocation check for the encryption certificate.
|
||||||
|
const std::string& DualCertificateClientCert::encrypted_unique_id() const {
|
||||||
|
return signing_certificate_.encrypted_unique_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/155979840): Support revocation check for the encryption certificate.
|
||||||
|
const std::string& DualCertificateClientCert::unique_id_hash() const {
|
||||||
|
return signing_certificate_.unique_id_hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
57
common/dual_certificate_client_cert.h
Normal file
57
common/dual_certificate_client_cert.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_DUAL_CERTIFICATE_CLIENT_CERT_H_
|
||||||
|
#define COMMON_DUAL_CERTIFICATE_CLIENT_CERT_H_
|
||||||
|
|
||||||
|
#include "common/certificate_client_cert.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
class DualCertificateClientCert : public ClientCert {
|
||||||
|
public:
|
||||||
|
DualCertificateClientCert() = default;
|
||||||
|
~DualCertificateClientCert() override = default;
|
||||||
|
DualCertificateClientCert(const DualCertificateClientCert&) = delete;
|
||||||
|
DualCertificateClientCert& operator=(const DualCertificateClientCert&) =
|
||||||
|
delete;
|
||||||
|
|
||||||
|
Status Initialize(const DrmRootCertificate* root_certificate,
|
||||||
|
const std::string& serialized_signing_certificate,
|
||||||
|
const std::string& serialized_encryption_certificate);
|
||||||
|
Status VerifySignature(const std::string& message,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
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;
|
||||||
|
const std::string& key() const override;
|
||||||
|
SignedMessage::SessionKeyType key_type() const override;
|
||||||
|
bool using_dual_certificate() const override { return true; }
|
||||||
|
const std::string& serial_number() const override;
|
||||||
|
const std::string& service_id() const override;
|
||||||
|
const std::string& signing_key() const override;
|
||||||
|
const std::string& signer_serial_number() const override;
|
||||||
|
uint32_t signer_creation_time_seconds() const override;
|
||||||
|
bool signed_by_provisioner() const override;
|
||||||
|
uint32_t system_id() const override;
|
||||||
|
widevine::ClientIdentification::TokenType type() const override {
|
||||||
|
return ClientIdentification::DRM_DEVICE_CERTIFICATE;
|
||||||
|
}
|
||||||
|
const std::string& encrypted_unique_id() const override;
|
||||||
|
const std::string& unique_id_hash() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CertificateClientCert signing_certificate_;
|
||||||
|
CertificateClientCert encryption_certificate_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
#endif // COMMON_DUAL_CERTIFICATE_CLIENT_CERT_H_
|
||||||
313
common/ec_key.cc
Normal file
313
common/ec_key.cc
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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/hash_algorithm.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetMessageDigest(const std::string& message,
|
||||||
|
widevine::HashAlgorithm hash_algorithm) {
|
||||||
|
switch (hash_algorithm) {
|
||||||
|
case widevine::HashAlgorithm::kUnspecified:
|
||||||
|
case widevine::HashAlgorithm::kSha256:
|
||||||
|
return widevine::Sha256_Hash(message);
|
||||||
|
case widevine::HashAlgorithm::kSha1:
|
||||||
|
LOG(ERROR) << "Unexpected hash algorithm: "
|
||||||
|
<< static_cast<int>(hash_algorithm);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
LOG(FATAL) << "Unexpected hash algorithm: "
|
||||||
|
<< static_cast<int>(hash_algorithm);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // 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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the message using corresponding hash algorithm.
|
||||||
|
std::string message_digest = GetMessageDigest(message, hash_algorithm);
|
||||||
|
if (message_digest.empty()) {
|
||||||
|
LOG(ERROR) << "Empty message digest";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the message using corresponding hash algorithm.
|
||||||
|
std::string message_digest = GetMessageDigest(message, hash_algorithm);
|
||||||
|
if (message_digest.empty()) {
|
||||||
|
LOG(ERROR) << "Empty message digest";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
150
common/ec_key.h
Normal file
150
common/ec_key.h
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 "absl/base/macros.h"
|
||||||
|
#include "openssl/ec.h"
|
||||||
|
#include "common/hash_algorithm.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.
|
||||||
|
// |hash_algorithm| specifies the hash algorithm.
|
||||||
|
// |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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
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.
|
||||||
|
// |hash_algorithm| specifies the hash algorithm.
|
||||||
|
// |signature| is an ASN.1 DER-encoded signature.
|
||||||
|
// Returns true on success and false on error.
|
||||||
|
virtual bool VerifySignature(const std::string& message,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
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_
|
||||||
320
common/ec_key_test.cc
Normal file
320
common/ec_key_test.cc
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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/hash_algorithm.h"
|
||||||
|
#include "common/random_util.h"
|
||||||
|
|
||||||
|
using ::testing::IsNull;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const widevine::HashAlgorithm kHashAlgorithm =
|
||||||
|
widevine::HashAlgorithm::kSha256;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Death test naming convention. See below link for details:
|
||||||
|
// go/gunitadvanced#death-test-naming
|
||||||
|
using ECKeyTestKeyPairsDeathTest = ECKeyTestKeyPairs;
|
||||||
|
|
||||||
|
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, SignVerifySha1) {
|
||||||
|
std::string signature;
|
||||||
|
EXPECT_FALSE(private_key_->GenerateSignature(
|
||||||
|
plaintext_message_, HashAlgorithm::kSha1, &signature));
|
||||||
|
EXPECT_FALSE(public_key_->VerifySignature(plaintext_message_,
|
||||||
|
HashAlgorithm::kSha1, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, SignVerifySha256) {
|
||||||
|
std::string signature;
|
||||||
|
ASSERT_TRUE(private_key_->GenerateSignature(
|
||||||
|
plaintext_message_, HashAlgorithm::kSha256, &signature));
|
||||||
|
ASSERT_TRUE(public_key_->VerifySignature(plaintext_message_,
|
||||||
|
HashAlgorithm::kSha256, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, SignVerifyUnspecified) {
|
||||||
|
std::string signature;
|
||||||
|
ASSERT_TRUE(private_key_->GenerateSignature(
|
||||||
|
plaintext_message_, HashAlgorithm::kUnspecified, &signature));
|
||||||
|
ASSERT_TRUE(public_key_->VerifySignature(
|
||||||
|
plaintext_message_, HashAlgorithm::kUnspecified, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairsDeathTest, SignVerifyUnexpected) {
|
||||||
|
std::string signature;
|
||||||
|
HashAlgorithm unexpected_hash_algorithm = static_cast<HashAlgorithm>(1234);
|
||||||
|
EXPECT_DEATH(private_key_->GenerateSignature(
|
||||||
|
plaintext_message_, unexpected_hash_algorithm, &signature),
|
||||||
|
"Unexpected hash algorithm: 1234");
|
||||||
|
EXPECT_FALSE(public_key_->VerifySignature(
|
||||||
|
plaintext_message_, unexpected_hash_algorithm, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ECKeyTestKeyPairs, InvalidSignVerifyParameters) {
|
||||||
|
std::string signature;
|
||||||
|
EXPECT_FALSE(private_key_->GenerateSignature("", kHashAlgorithm, &signature));
|
||||||
|
EXPECT_FALSE(public_key_->VerifySignature("", kHashAlgorithm, "signature"));
|
||||||
|
EXPECT_FALSE(private_key_->GenerateSignature(plaintext_message_,
|
||||||
|
kHashAlgorithm, nullptr));
|
||||||
|
EXPECT_FALSE(
|
||||||
|
public_key_->VerifySignature(plaintext_message_, kHashAlgorithm, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
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_,
|
||||||
|
kHashAlgorithm, &signature));
|
||||||
|
EXPECT_FALSE(ephemeral_public_key_->VerifySignature(
|
||||||
|
plaintext_message_, kHashAlgorithm, 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()));
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(ECKeyTestKeyPairsDeathTest, ECKeyTestKeyPairsDeathTest,
|
||||||
|
::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_,
|
||||||
|
kHashAlgorithm, &signature));
|
||||||
|
EXPECT_FALSE(wrong_curve_public_key_->VerifySignature(
|
||||||
|
plaintext_message_, kHashAlgorithm, 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_
|
||||||
259
common/ecies_crypto_test.cc
Normal file
259
common/ecies_crypto_test.cc
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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_METHOD(bool, GetECKey,
|
||||||
|
(ECPrivateKey::EllipticCurve curve, std::string* private_key,
|
||||||
|
std::string* public_key),
|
||||||
|
(override));
|
||||||
|
};
|
||||||
|
|
||||||
|
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_
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
|
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
|
|
||||||
@@ -19,7 +20,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));
|
||||||
|
|||||||
18
common/hash_algorithm.h
Normal file
18
common/hash_algorithm.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_HASH_ALGORITHM_H_
|
||||||
|
#define COMMON_HASH_ALGORITHM_H_
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
enum class HashAlgorithm { kUnspecified, kSha1, kSha256 };
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_HASH_ALGORITHM_H_
|
||||||
55
common/hash_algorithm_util.cc
Normal file
55
common/hash_algorithm_util.cc
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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/hash_algorithm_util.h"
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "protos/public/hash_algorithm.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
HashAlgorithm HashAlgorithmProtoToEnum(
|
||||||
|
HashAlgorithmProto hash_algorithm_proto) {
|
||||||
|
switch (hash_algorithm_proto) {
|
||||||
|
case HASH_ALGORITHM_UNSPECIFIED:
|
||||||
|
return HashAlgorithm::kUnspecified;
|
||||||
|
case HASH_ALGORITHM_SHA_1:
|
||||||
|
return HashAlgorithm::kSha1;
|
||||||
|
case HASH_ALGORITHM_SHA_256:
|
||||||
|
return HashAlgorithm::kSha256;
|
||||||
|
default:
|
||||||
|
// See below link for using proto3 enum in switch statement:
|
||||||
|
// http://shortn/_ma9MY7V9wh
|
||||||
|
if (HashAlgorithmProto_IsValid(hash_algorithm_proto)) {
|
||||||
|
LOG(ERROR) << "Unsupported value " << hash_algorithm_proto;
|
||||||
|
} else {
|
||||||
|
LOG(WARNING) << "Unexpected value " << hash_algorithm_proto;
|
||||||
|
}
|
||||||
|
return HashAlgorithm::kUnspecified;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashAlgorithmProto HashAlgorithmEnumToProto(HashAlgorithm hash_algorithm) {
|
||||||
|
switch (hash_algorithm) {
|
||||||
|
case HashAlgorithm::kUnspecified:
|
||||||
|
return HASH_ALGORITHM_UNSPECIFIED;
|
||||||
|
case HashAlgorithm::kSha1:
|
||||||
|
return HASH_ALGORITHM_SHA_1;
|
||||||
|
case HashAlgorithm::kSha256:
|
||||||
|
return HASH_ALGORITHM_SHA_256;
|
||||||
|
}
|
||||||
|
LOG(WARNING) << "Unexpected hash algorithm "
|
||||||
|
<< static_cast<int>(hash_algorithm);
|
||||||
|
return HASH_ALGORITHM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HashAlgorithmEnumToString(HashAlgorithm hash_algorithm) {
|
||||||
|
return HashAlgorithmProto_Name(HashAlgorithmEnumToProto(hash_algorithm));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
25
common/hash_algorithm_util.h
Normal file
25
common/hash_algorithm_util.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_HASH_ALGORITHM_UTIL_H_
|
||||||
|
#define COMMON_HASH_ALGORITHM_UTIL_H_
|
||||||
|
|
||||||
|
#include "common/hash_algorithm.h"
|
||||||
|
#include "protos/public/hash_algorithm.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
HashAlgorithm HashAlgorithmProtoToEnum(HashAlgorithmProto hash_algorithm_proto);
|
||||||
|
|
||||||
|
HashAlgorithmProto HashAlgorithmEnumToProto(HashAlgorithm hash_algorithm);
|
||||||
|
|
||||||
|
std::string HashAlgorithmEnumToString(HashAlgorithm hash_algorithm);
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_HASH_ALGORITHM_UTIL_H_
|
||||||
58
common/hash_algorithm_util_test.cc
Normal file
58
common/hash_algorithm_util_test.cc
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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/hash_algorithm_util.h"
|
||||||
|
|
||||||
|
#include "testing/gmock.h"
|
||||||
|
#include "testing/gunit.h"
|
||||||
|
#include "common/hash_algorithm.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
TEST(HashAlgorithmTest, ProtoToEnumUnspecified) {
|
||||||
|
EXPECT_EQ(HashAlgorithm::kUnspecified,
|
||||||
|
HashAlgorithmProtoToEnum(HASH_ALGORITHM_UNSPECIFIED));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HashAlgorithmTest, ProtoToEnumSha1) {
|
||||||
|
EXPECT_EQ(HashAlgorithm::kSha1,
|
||||||
|
HashAlgorithmProtoToEnum(HASH_ALGORITHM_SHA_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HashAlgorithmTest, ProtoToEnumSha256) {
|
||||||
|
EXPECT_EQ(HashAlgorithm::kSha256,
|
||||||
|
HashAlgorithmProtoToEnum(HASH_ALGORITHM_SHA_256));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HashAlgorithmTest, ProtoToEnumUnsupported) {
|
||||||
|
EXPECT_EQ(HashAlgorithm::kUnspecified,
|
||||||
|
HashAlgorithmProtoToEnum(static_cast<HashAlgorithmProto>(1234)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HashAlgorithmTest, EnumToProtoUnspecified) {
|
||||||
|
EXPECT_EQ(HASH_ALGORITHM_UNSPECIFIED,
|
||||||
|
HashAlgorithmEnumToProto(HashAlgorithm::kUnspecified));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HashAlgorithmTest, EnumToProtoSha1) {
|
||||||
|
EXPECT_EQ(HASH_ALGORITHM_SHA_1,
|
||||||
|
HashAlgorithmEnumToProto(HashAlgorithm::kSha1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HashAlgorithmTest, EnumToProtoSha256) {
|
||||||
|
EXPECT_EQ(HASH_ALGORITHM_SHA_256,
|
||||||
|
HashAlgorithmEnumToProto(HashAlgorithm::kSha256));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HashAlgorithmTest, EnumToProtoUnexpected) {
|
||||||
|
int some_value = 1234;
|
||||||
|
EXPECT_EQ(HASH_ALGORITHM_UNSPECIFIED,
|
||||||
|
HashAlgorithmEnumToProto(static_cast<HashAlgorithm>(some_value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
58
common/keybox_client_cert.cc
Normal file
58
common/keybox_client_cert.cc
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// |hash_algorithm| is needed for function inheritance.
|
||||||
|
// For KeyBoxClientCert, we always use HMAC-SHA256 in signature verification.
|
||||||
|
Status KeyboxClientCert::VerifySignature(
|
||||||
|
const std::string& message, HashAlgorithm hash_algorithm,
|
||||||
|
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
|
||||||
86
common/keybox_client_cert.h
Normal file
86
common/keybox_client_cert.h
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2019 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef COMMON_KEYBOX_CLIENT_CERT_H_
|
||||||
|
#define COMMON_KEYBOX_CLIENT_CERT_H_
|
||||||
|
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "common/client_cert.h"
|
||||||
|
#include "common/error_space.h"
|
||||||
|
#include "common/hash_algorithm.h"
|
||||||
|
#include "protos/public/errors.pb.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,
|
||||||
|
HashAlgorithm hash_algorithm,
|
||||||
|
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_; }
|
||||||
|
SignedMessage::SessionKeyType key_type() const override {
|
||||||
|
return SignedMessage::WRAPPED_AES_KEY;
|
||||||
|
}
|
||||||
|
bool using_dual_certificate() const override { return false; }
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
const std::string& encrypted_unique_id() const override {
|
||||||
|
return unimplemented_;
|
||||||
|
}
|
||||||
|
const std::string& unique_id_hash() const override { return unimplemented_; }
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
Status SystemIdUnknownError() const override {
|
||||||
|
return Status(error_space, UNSUPPORTED_SYSTEM_ID,
|
||||||
|
absl::StrCat("keybox-unsupported-system-id: ", system_id()));
|
||||||
|
}
|
||||||
|
Status SystemIdRevokedError() const override {
|
||||||
|
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||||
|
absl::StrCat("keybox-system-id-revoked: ", system_id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
@@ -10,7 +10,9 @@
|
|||||||
#define COMMON_MOCK_RSA_KEY_H_
|
#define COMMON_MOCK_RSA_KEY_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
|
#include "common/hash_algorithm.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
@@ -20,12 +22,18 @@ class MockRsaPrivateKey : public RsaPrivateKey {
|
|||||||
MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {}
|
MockRsaPrivateKey() : RsaPrivateKey(RSA_new()) {}
|
||||||
~MockRsaPrivateKey() override {}
|
~MockRsaPrivateKey() override {}
|
||||||
|
|
||||||
MOCK_CONST_METHOD2(Decrypt, bool(const std::string& encrypted_message,
|
MOCK_METHOD(bool, Decrypt,
|
||||||
std::string* decrypted_message));
|
(const std::string& encrypted_message,
|
||||||
MOCK_CONST_METHOD2(GenerateSignature,
|
std::string* decrypted_message),
|
||||||
bool(const std::string& message, std::string* signature));
|
(const, override));
|
||||||
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
MOCK_METHOD(bool, GenerateSignature,
|
||||||
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
(const std::string& message, HashAlgorithm hash_algorithm,
|
||||||
|
std::string* signature),
|
||||||
|
(const, override));
|
||||||
|
MOCK_METHOD(bool, MatchesPrivateKey, (const RsaPrivateKey& private_key),
|
||||||
|
(const, override));
|
||||||
|
MOCK_METHOD(bool, MatchesPublicKey, (const RsaPublicKey& public_key),
|
||||||
|
(const, override));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MockRsaPrivateKey(const MockRsaPrivateKey&) = delete;
|
MockRsaPrivateKey(const MockRsaPrivateKey&) = delete;
|
||||||
@@ -37,12 +45,18 @@ class MockRsaPublicKey : public RsaPublicKey {
|
|||||||
MockRsaPublicKey() : RsaPublicKey(RSA_new()) {}
|
MockRsaPublicKey() : RsaPublicKey(RSA_new()) {}
|
||||||
~MockRsaPublicKey() override {}
|
~MockRsaPublicKey() override {}
|
||||||
|
|
||||||
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
|
MOCK_METHOD(bool, Encrypt,
|
||||||
std::string* encrypted_message));
|
(const std::string& clear_message,
|
||||||
MOCK_CONST_METHOD2(VerifySignature,
|
std::string* encrypted_message),
|
||||||
bool(const std::string& message, const std::string& signature));
|
(const, override));
|
||||||
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
MOCK_METHOD(bool, VerifySignature,
|
||||||
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
(const std::string& message, HashAlgorithm hash_algorithm,
|
||||||
|
const std::string& signature),
|
||||||
|
(const, override));
|
||||||
|
MOCK_METHOD(bool, MatchesPrivateKey, (const RsaPrivateKey& private_key),
|
||||||
|
(const, override));
|
||||||
|
MOCK_METHOD(bool, MatchesPublicKey, (const RsaPublicKey& public_key),
|
||||||
|
(const, override));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MockRsaPublicKey(const MockRsaPublicKey&) = delete;
|
MockRsaPublicKey(const MockRsaPublicKey&) = delete;
|
||||||
@@ -54,14 +68,14 @@ class MockRsaKeyFactory : public RsaKeyFactory {
|
|||||||
MockRsaKeyFactory() {}
|
MockRsaKeyFactory() {}
|
||||||
~MockRsaKeyFactory() override {}
|
~MockRsaKeyFactory() override {}
|
||||||
|
|
||||||
MOCK_CONST_METHOD1(CreateFromPkcs1PrivateKey,
|
MOCK_METHOD(std::unique_ptr<RsaPrivateKey>, CreateFromPkcs1PrivateKey,
|
||||||
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
|
(const std::string& private_key), (const, override));
|
||||||
MOCK_CONST_METHOD2(
|
MOCK_METHOD(std::unique_ptr<RsaPrivateKey>, CreateFromPkcs8PrivateKey,
|
||||||
CreateFromPkcs8PrivateKey,
|
(const std::string& private_key,
|
||||||
std::unique_ptr<RsaPrivateKey>(const std::string& private_key,
|
const std::string& private_key_passphrase),
|
||||||
const std::string& private_key_passphrase));
|
(const, override));
|
||||||
MOCK_CONST_METHOD1(CreateFromPkcs1PublicKey,
|
MOCK_METHOD(std::unique_ptr<RsaPublicKey>, CreateFromPkcs1PublicKey,
|
||||||
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
|
(const std::string& public_key), (const, override));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;
|
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;
|
||||||
|
|||||||
94
common/oemcrypto_core_message/odk/Android.bp
Normal file
94
common/oemcrypto_core_message/odk/Android.bp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// Builds libwv_odk.a, The ODK Library (libwv_odk) is used by
|
||||||
|
// the CDM and by oemcrypto implementations.
|
||||||
|
cc_library_static {
|
||||||
|
name: "libwv_odk",
|
||||||
|
include_dirs: [
|
||||||
|
"vendor/widevine/libwvdrmengine/oemcrypto/include",
|
||||||
|
"vendor/widevine/libwvdrmengine/oemcrypto/odk/include",
|
||||||
|
"vendor/widevine/libwvdrmengine/oemcrypto/odk/src",
|
||||||
|
],
|
||||||
|
|
||||||
|
srcs: [
|
||||||
|
"src/odk.c",
|
||||||
|
"src/odk_overflow.c",
|
||||||
|
"src/odk_serialize.c",
|
||||||
|
"src/odk_timer.c",
|
||||||
|
"src/odk_util.c",
|
||||||
|
"src/serialization_base.c",
|
||||||
|
],
|
||||||
|
proprietary: true,
|
||||||
|
|
||||||
|
owner: "widevine",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// Builds libwv_kdo.a, The ODK Library companion (libwv_kdo) is used by
|
||||||
|
// the CDM and by oemcrypto tests, but not by oemcrypto implementations.
|
||||||
|
cc_library_static {
|
||||||
|
name: "libwv_kdo",
|
||||||
|
include_dirs: [
|
||||||
|
"vendor/widevine/libwvdrmengine/oemcrypto/include",
|
||||||
|
"vendor/widevine/libwvdrmengine/oemcrypto/odk/include",
|
||||||
|
"vendor/widevine/libwvdrmengine/oemcrypto/odk/src",
|
||||||
|
],
|
||||||
|
|
||||||
|
srcs: [
|
||||||
|
"src/core_message_deserialize.cpp",
|
||||||
|
"src/core_message_serialize.cpp",
|
||||||
|
"src/core_message_serialize_proto.cpp",
|
||||||
|
],
|
||||||
|
|
||||||
|
static_libs: [
|
||||||
|
"libcdm_protos",
|
||||||
|
"libwv_odk",
|
||||||
|
],
|
||||||
|
|
||||||
|
proprietary: true,
|
||||||
|
|
||||||
|
owner: "widevine",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// Builds odk_test executable, which tests the ODK library.
|
||||||
|
cc_test {
|
||||||
|
name: "odk_test",
|
||||||
|
include_dirs: [
|
||||||
|
"vendor/widevine/libwvdrmengine/oemcrypto/include",
|
||||||
|
"vendor/widevine/libwvdrmengine/oemcrypto/odk/include",
|
||||||
|
"vendor/widevine/libwvdrmengine/oemcrypto/odk/src",
|
||||||
|
],
|
||||||
|
|
||||||
|
// WARNING: Module tags are not supported in Soong.
|
||||||
|
// For native test binaries, use the "cc_test" module type. Some differences:
|
||||||
|
// - If you don't use gtest, set "gtest: false"
|
||||||
|
// - Binaries will be installed into /data/nativetest[64]/<name>/<name>
|
||||||
|
// - Both 32 & 64 bit versions will be built (as appropriate)
|
||||||
|
|
||||||
|
owner: "widevine",
|
||||||
|
proprietary: true,
|
||||||
|
|
||||||
|
static_libs: [
|
||||||
|
"libcdm_protos",
|
||||||
|
"libcdm",
|
||||||
|
"libwv_odk",
|
||||||
|
"libwv_kdo",
|
||||||
|
],
|
||||||
|
|
||||||
|
srcs: [
|
||||||
|
"test/odk_test.cpp",
|
||||||
|
"test/odk_test_helper.cpp",
|
||||||
|
"test/odk_timer_test.cpp",
|
||||||
|
],
|
||||||
|
|
||||||
|
}
|
||||||
66
common/oemcrypto_core_message/odk/BUILD
Normal file
66
common/oemcrypto_core_message/odk/BUILD
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.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "odk_common_hdrs",
|
||||||
|
srcs = [
|
||||||
|
"src/odk_serialize.h",
|
||||||
|
"src/odk_structs_priv.h",
|
||||||
|
"src/serialization_base.h",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "odk",
|
||||||
|
srcs = [
|
||||||
|
"src/odk.c",
|
||||||
|
"src/odk_assert.h",
|
||||||
|
"src/odk_endian.h",
|
||||||
|
"src/odk_overflow.c",
|
||||||
|
"src/odk_overflow.h",
|
||||||
|
"src/odk_serialize.c",
|
||||||
|
"src/odk_timer.c",
|
||||||
|
"src/odk_util.c",
|
||||||
|
"src/odk_util.h",
|
||||||
|
"src/serialization_base.c",
|
||||||
|
":odk_common_hdrs",
|
||||||
|
],
|
||||||
|
hdrs = [
|
||||||
|
"include/OEMCryptoCENCCommon.h",
|
||||||
|
"include/odk.h",
|
||||||
|
"include/odk_attributes.h",
|
||||||
|
"include/odk_structs.h",
|
||||||
|
"include/odk_target.h",
|
||||||
|
],
|
||||||
|
copts = ["-std=c99"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "kdo",
|
||||||
|
srcs = [
|
||||||
|
"src/core_message_deserialize.cpp",
|
||||||
|
"src/core_message_serialize.cpp",
|
||||||
|
"src/core_message_serialize_proto.cpp",
|
||||||
|
":odk_common_hdrs",
|
||||||
|
],
|
||||||
|
hdrs = [
|
||||||
|
"include/core_message_deserialize.h",
|
||||||
|
"include/core_message_serialize.h",
|
||||||
|
"include/core_message_serialize_proto.h",
|
||||||
|
"include/core_message_types.h",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":odk",
|
||||||
|
"//protos/public:certificate_provisioning_cc_proto",
|
||||||
|
"//protos/public:license_protocol_cc_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
10
common/oemcrypto_core_message/odk/README
Normal file
10
common/oemcrypto_core_message/odk/README
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
This ODK Library is used to generate and parse core OEMCrypto messages for
|
||||||
|
OEMCrypto v16 and above.
|
||||||
|
|
||||||
|
This library is used by both OEMCrypto on a device, and by Widevine license and
|
||||||
|
provisioning servers.
|
||||||
|
|
||||||
|
The source of truth for these files is in the server code base on piper. Do not
|
||||||
|
edit these files in the Android directory tree or in the Widevine Git
|
||||||
|
repository. If you need to edit these files and are not sure how to procede,
|
||||||
|
please ask for help from an engineer on the Widevine server or device teams.
|
||||||
179
common/oemcrypto_core_message/odk/include/OEMCryptoCENCCommon.h
Normal file
179
common/oemcrypto_core_message/odk/include/OEMCryptoCENCCommon.h
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* OEMCryptoCENCCommon.h
|
||||||
|
*
|
||||||
|
* Common structures and error codes between WV servers and OEMCrypto.
|
||||||
|
*
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @addtogroup common_types
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
/** Error and result codes returned by OEMCrypto functions. */
|
||||||
|
typedef enum OEMCryptoResult {
|
||||||
|
OEMCrypto_SUCCESS = 0,
|
||||||
|
OEMCrypto_ERROR_INIT_FAILED = 1,
|
||||||
|
OEMCrypto_ERROR_TERMINATE_FAILED = 2,
|
||||||
|
OEMCrypto_ERROR_OPEN_FAILURE = 3,
|
||||||
|
OEMCrypto_ERROR_CLOSE_FAILURE = 4,
|
||||||
|
OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED = 5, /* deprecated */
|
||||||
|
OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED = 6, /* deprecated */
|
||||||
|
OEMCrypto_ERROR_SHORT_BUFFER = 7,
|
||||||
|
OEMCrypto_ERROR_NO_DEVICE_KEY = 8, /* no keybox device key. */
|
||||||
|
OEMCrypto_ERROR_NO_ASSET_KEY = 9,
|
||||||
|
OEMCrypto_ERROR_KEYBOX_INVALID = 10,
|
||||||
|
OEMCrypto_ERROR_NO_KEYDATA = 11,
|
||||||
|
OEMCrypto_ERROR_NO_CW = 12,
|
||||||
|
OEMCrypto_ERROR_DECRYPT_FAILED = 13,
|
||||||
|
OEMCrypto_ERROR_WRITE_KEYBOX = 14,
|
||||||
|
OEMCrypto_ERROR_WRAP_KEYBOX = 15,
|
||||||
|
OEMCrypto_ERROR_BAD_MAGIC = 16,
|
||||||
|
OEMCrypto_ERROR_BAD_CRC = 17,
|
||||||
|
OEMCrypto_ERROR_NO_DEVICEID = 18,
|
||||||
|
OEMCrypto_ERROR_RNG_FAILED = 19,
|
||||||
|
OEMCrypto_ERROR_RNG_NOT_SUPPORTED = 20,
|
||||||
|
OEMCrypto_ERROR_SETUP = 21,
|
||||||
|
OEMCrypto_ERROR_OPEN_SESSION_FAILED = 22,
|
||||||
|
OEMCrypto_ERROR_CLOSE_SESSION_FAILED = 23,
|
||||||
|
OEMCrypto_ERROR_INVALID_SESSION = 24,
|
||||||
|
OEMCrypto_ERROR_NOT_IMPLEMENTED = 25,
|
||||||
|
OEMCrypto_ERROR_NO_CONTENT_KEY = 26,
|
||||||
|
OEMCrypto_ERROR_CONTROL_INVALID = 27,
|
||||||
|
OEMCrypto_ERROR_UNKNOWN_FAILURE = 28,
|
||||||
|
OEMCrypto_ERROR_INVALID_CONTEXT = 29,
|
||||||
|
OEMCrypto_ERROR_SIGNATURE_FAILURE = 30,
|
||||||
|
OEMCrypto_ERROR_TOO_MANY_SESSIONS = 31,
|
||||||
|
OEMCrypto_ERROR_INVALID_NONCE = 32,
|
||||||
|
OEMCrypto_ERROR_TOO_MANY_KEYS = 33,
|
||||||
|
OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED = 34,
|
||||||
|
OEMCrypto_ERROR_INVALID_RSA_KEY = 35,
|
||||||
|
OEMCrypto_ERROR_KEY_EXPIRED = 36,
|
||||||
|
OEMCrypto_ERROR_INSUFFICIENT_RESOURCES = 37,
|
||||||
|
OEMCrypto_ERROR_INSUFFICIENT_HDCP = 38,
|
||||||
|
OEMCrypto_ERROR_BUFFER_TOO_LARGE = 39,
|
||||||
|
OEMCrypto_WARNING_GENERATION_SKEW = 40, /* Warning, not error. */
|
||||||
|
OEMCrypto_ERROR_GENERATION_SKEW = 41,
|
||||||
|
OEMCrypto_LOCAL_DISPLAY_ONLY = 42, /* Info, not an error. */
|
||||||
|
OEMCrypto_ERROR_ANALOG_OUTPUT = 43,
|
||||||
|
OEMCrypto_ERROR_WRONG_PST = 44,
|
||||||
|
OEMCrypto_ERROR_WRONG_KEYS = 45,
|
||||||
|
OEMCrypto_ERROR_MISSING_MASTER = 46,
|
||||||
|
OEMCrypto_ERROR_LICENSE_INACTIVE = 47,
|
||||||
|
OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE = 48,
|
||||||
|
OEMCrypto_ERROR_ENTRY_IN_USE = 49,
|
||||||
|
OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE = 50, /* Obsolete. Don't use. */
|
||||||
|
/* Use OEMCrypto_ERROR_NO_CONTENT_KEY instead of KEY_NOT_LOADED. */
|
||||||
|
OEMCrypto_KEY_NOT_LOADED = 51, /* Obsolete. */
|
||||||
|
OEMCrypto_KEY_NOT_ENTITLED = 52,
|
||||||
|
OEMCrypto_ERROR_BAD_HASH = 53,
|
||||||
|
OEMCrypto_ERROR_OUTPUT_TOO_LARGE = 54,
|
||||||
|
OEMCrypto_ERROR_SESSION_LOST_STATE = 55,
|
||||||
|
OEMCrypto_ERROR_SYSTEM_INVALIDATED = 56,
|
||||||
|
OEMCrypto_ERROR_LICENSE_RELOAD = 57,
|
||||||
|
OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES = 58,
|
||||||
|
OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION = 59,
|
||||||
|
/* ODK return values */
|
||||||
|
ODK_ERROR_BASE = 1000,
|
||||||
|
ODK_ERROR_CORE_MESSAGE = ODK_ERROR_BASE,
|
||||||
|
ODK_SET_TIMER = ODK_ERROR_BASE + 1,
|
||||||
|
ODK_DISABLE_TIMER = ODK_ERROR_BASE + 2,
|
||||||
|
ODK_TIMER_EXPIRED = ODK_ERROR_BASE + 3,
|
||||||
|
ODK_UNSUPPORTED_API = ODK_ERROR_BASE + 4,
|
||||||
|
ODK_STALE_RENEWAL = ODK_ERROR_BASE + 5,
|
||||||
|
} OEMCryptoResult;
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid values for status in the usage table.
|
||||||
|
*/
|
||||||
|
typedef enum OEMCrypto_Usage_Entry_Status {
|
||||||
|
kUnused = 0,
|
||||||
|
kActive = 1,
|
||||||
|
kInactive = 2, /* Deprecated. Use kInactiveUsed or kInactiveUnused. */
|
||||||
|
kInactiveUsed = 3,
|
||||||
|
kInactiveUnused = 4,
|
||||||
|
} OEMCrypto_Usage_Entry_Status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OEMCrypto_LicenseType is used in the license message to indicate if the key
|
||||||
|
* objects are for content keys, or for entitlement keys.
|
||||||
|
*/
|
||||||
|
typedef enum OEMCrypto_LicenseType {
|
||||||
|
OEMCrypto_ContentLicense = 0,
|
||||||
|
OEMCrypto_EntitlementLicense = 1,
|
||||||
|
OEMCrypto_LicenstType_MaxValue = OEMCrypto_EntitlementLicense,
|
||||||
|
} OEMCrypto_LicenseType;
|
||||||
|
|
||||||
|
/* Private key type used in the provisioning response. */
|
||||||
|
typedef enum OEMCrypto_PrivateKeyType {
|
||||||
|
OEMCrypto_RSA_Private_Key = 0,
|
||||||
|
OEMCrypto_ECC_Private_Key = 1,
|
||||||
|
} OEMCrypto_PrivateKeyType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to indicate a substring of a signed message in OEMCrypto_LoadKeys and
|
||||||
|
* other functions which must verify that a parameter is contained within a
|
||||||
|
* signed message.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
size_t offset;
|
||||||
|
size_t length;
|
||||||
|
} OEMCrypto_Substring;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Points to the relevant fields for a content key. The fields are extracted
|
||||||
|
* from the License Response message offered to OEMCrypto_LoadKeys(). Each
|
||||||
|
* field points to one of the components of the key. Key data, key control,
|
||||||
|
* and both IV fields are 128 bits (16 bytes):
|
||||||
|
* @param key_id: the unique id of this key.
|
||||||
|
* @param key_id_length: the size of key_id. OEMCrypto may assume this is at
|
||||||
|
* most 16. However, OEMCrypto shall correctly handle key id lengths
|
||||||
|
* from 1 to 16 bytes.
|
||||||
|
* @param key_data_iv: the IV for performing AES-128-CBC decryption of the
|
||||||
|
* key_data field.
|
||||||
|
* @param key_data - the key data. It is encrypted (AES-128-CBC) with the
|
||||||
|
* session's derived encrypt key and the key_data_iv.
|
||||||
|
* @param key_control_iv: the IV for performing AES-128-CBC decryption of the
|
||||||
|
* key_control field.
|
||||||
|
* @param key_control: the key control block. It is encrypted (AES-128-CBC) with
|
||||||
|
* the content key from the key_data field.
|
||||||
|
*
|
||||||
|
* The memory for the OEMCrypto_KeyObject fields is allocated and freed
|
||||||
|
* by the caller of OEMCrypto_LoadKeys().
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
OEMCrypto_Substring key_id;
|
||||||
|
OEMCrypto_Substring key_data_iv;
|
||||||
|
OEMCrypto_Substring key_data;
|
||||||
|
OEMCrypto_Substring key_control_iv;
|
||||||
|
OEMCrypto_Substring key_control;
|
||||||
|
} OEMCrypto_KeyObject;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* core_message_deserialize.h
|
||||||
|
*
|
||||||
|
* OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO)
|
||||||
|
*
|
||||||
|
* This file declares functions to deserialize request messages prepared by
|
||||||
|
* Widevine clients (OEMCrypto/ODK).
|
||||||
|
*
|
||||||
|
* Please refer to core_message_types.h for details.
|
||||||
|
*
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_types.h"
|
||||||
|
|
||||||
|
namespace oemcrypto_core_message {
|
||||||
|
namespace deserialize {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counterpart (deserializer) of ODK_PrepareCoreLicenseRequest (serializer)
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* [in] oemcrypto_core_message
|
||||||
|
* [out] core_license_request
|
||||||
|
*/
|
||||||
|
bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||||
|
ODK_LicenseRequest* core_license_request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counterpart (deserializer) of ODK_PrepareCoreRenewalRequest (serializer)
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* [in] oemcrypto_core_message
|
||||||
|
* [out] core_renewal_request
|
||||||
|
*/
|
||||||
|
bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||||
|
ODK_RenewalRequest* core_renewal_request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counterpart (deserializer) of ODK_PrepareCoreProvisioningRequest (serializer)
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* [in] oemcrypto_core_message
|
||||||
|
* [out] core_provisioning_request
|
||||||
|
*/
|
||||||
|
bool CoreProvisioningRequestFromMessage(
|
||||||
|
const std::string& oemcrypto_core_message,
|
||||||
|
ODK_ProvisioningRequest* core_provisioning_request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializer counterpart is not used and is therefore not implemented.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* [in] oemcrypto_core_message
|
||||||
|
* [out] core_common_request
|
||||||
|
*/
|
||||||
|
bool CoreCommonRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||||
|
ODK_CommonRequest* core_common_request);
|
||||||
|
|
||||||
|
} // namespace deserialize
|
||||||
|
} // namespace oemcrypto_core_message
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* core_message_serialize.h
|
||||||
|
*
|
||||||
|
* OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO)
|
||||||
|
*
|
||||||
|
* This file declares functions to serialize response messages that will be
|
||||||
|
* parsed by Widevine clients (OEMCrypto/ODK).
|
||||||
|
*
|
||||||
|
* Please refer to core_message_types.h for details.
|
||||||
|
*
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_types.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_structs.h"
|
||||||
|
|
||||||
|
namespace oemcrypto_core_message {
|
||||||
|
namespace serialize {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
|
||||||
|
* struct-input variant
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* [in] parsed_lic
|
||||||
|
* [in] core_request
|
||||||
|
* [in] core_request_sha256
|
||||||
|
* [out] oemcrypto_core_message
|
||||||
|
*/
|
||||||
|
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
|
||||||
|
const ODK_LicenseRequest& core_request,
|
||||||
|
const std::string& core_request_sha256,
|
||||||
|
std::string* oemcrypto_core_message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counterpart (serializer) of ODK_ParseRenewal (deserializer)
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* [in] core_request
|
||||||
|
* [in] renewal_duration_seconds
|
||||||
|
* [out] oemcrypto_core_message
|
||||||
|
*/
|
||||||
|
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
|
||||||
|
uint64_t renewal_duration_seconds,
|
||||||
|
std::string* oemcrypto_core_message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
|
||||||
|
* struct-input variant
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* [in] parsed_prov
|
||||||
|
* [in] core_request
|
||||||
|
* [out] oemcrypto_core_message
|
||||||
|
*/
|
||||||
|
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
|
||||||
|
const ODK_ProvisioningRequest& core_request,
|
||||||
|
std::string* oemcrypto_core_message);
|
||||||
|
} // namespace serialize
|
||||||
|
} // namespace oemcrypto_core_message
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_
|
||||||
@@ -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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* core_message_serialize_proto.h
|
||||||
|
*
|
||||||
|
* These functions are an extension of those found in
|
||||||
|
* core_message_serialize.h. The difference is that these use the
|
||||||
|
* license and provisioning messages in protobuf format to create the core
|
||||||
|
* message.
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_types.h"
|
||||||
|
#include "protos/public/certificate_provisioning.pb.h"
|
||||||
|
#include "protos/public/license_protocol.pb.h"
|
||||||
|
|
||||||
|
namespace oemcrypto_core_message {
|
||||||
|
namespace serialize {
|
||||||
|
|
||||||
|
// @ public create response (serializer) functions accepting proto input
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* [in] serialized_license
|
||||||
|
serialized widevine::License
|
||||||
|
* [in] core_request oemcrypto core message from request.
|
||||||
|
* [in] core_request_sha256 - hash of serialized core request.
|
||||||
|
* [in] nonce_required - if the device should require a nonce match.
|
||||||
|
* [out] oemcrypto_core_message - the serialized oemcrypto core response.
|
||||||
|
*/
|
||||||
|
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||||
|
const ODK_LicenseRequest& core_request,
|
||||||
|
const std::string& core_request_sha256,
|
||||||
|
const bool nonce_required,
|
||||||
|
std::string* oemcrypto_core_message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* [in] serialized_provisioning_response
|
||||||
|
* serialized widevine::ProvisioningResponse
|
||||||
|
* [in] core_request
|
||||||
|
* [out] oemcrypto_core_message
|
||||||
|
*/
|
||||||
|
bool CreateCoreProvisioningResponseFromProto(
|
||||||
|
const std::string& serialized_provisioning_response,
|
||||||
|
const ODK_ProvisioningRequest& core_request,
|
||||||
|
std::string* oemcrypto_core_message);
|
||||||
|
|
||||||
|
} // namespace serialize
|
||||||
|
} // namespace oemcrypto_core_message
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_
|
||||||
118
common/oemcrypto_core_message/odk/include/core_message_types.h
Normal file
118
common/oemcrypto_core_message/odk/include/core_message_types.h
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
/*********************************************************************
|
||||||
|
* core_message_types.h
|
||||||
|
*
|
||||||
|
* OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO)
|
||||||
|
*
|
||||||
|
* For Widevine Modular DRM, there are six message types between a server and
|
||||||
|
* a client device: license request and response, provisioning request and
|
||||||
|
* response, and renewal request and response.
|
||||||
|
*
|
||||||
|
* In OEMCrypto v15 and earlier, messages from the server were parsed by the
|
||||||
|
* CDM layer above OEMCrypto; the CDM in turn gave OEMCrypto a collection of
|
||||||
|
* pointers to protected data within the message. However, the pointers
|
||||||
|
* themselves were not signed by the server.
|
||||||
|
*
|
||||||
|
* Starting from OEMCrypto v16, all fields used by OEMCrypto in each of these
|
||||||
|
* messages have been identified in the document "Widevine Core Message
|
||||||
|
* Serialization". These fields are called the core of the message. Core
|
||||||
|
* message fields are (de)serialized using the ODK, a C library provided by
|
||||||
|
* Widevine. OEMCrypto will parse and verify the core of the message with
|
||||||
|
* help from the ODK.
|
||||||
|
*
|
||||||
|
* The KDO library is the counterpart of ODK used in the CDM & Widevine
|
||||||
|
* servers. For each message type generated by the ODK, KDO provides a
|
||||||
|
* corresponding parser. For each message type to be parsed by the ODK,
|
||||||
|
* KDO provides a corresponding writer.
|
||||||
|
*
|
||||||
|
* Table: ODK vs KDO (s: serialize; d: deserialize)
|
||||||
|
* +----------------------------------------+---------------------------------------+
|
||||||
|
* | ODK | KDO |
|
||||||
|
* +---+------------------------------------+---+-----------------------------------+
|
||||||
|
* | s | ODK_PrepareCoreLicenseRequest | d | CoreLicenseRequestFromMessage |
|
||||||
|
* | +------------------------------------+ +-----------------------------------+
|
||||||
|
* | | ODK_PrepareCoreRenewalRequest | | CoreRenewalRequestFromMessage |
|
||||||
|
* | +------------------------------------+ +-----------------------------------+
|
||||||
|
* | | ODK_PrepareCoreProvisioningRequest | | CoreProvisioningRequestFromMessage|
|
||||||
|
* | +------------------------------------+ +-----------------------------------+
|
||||||
|
* | | ODK_PrepareCommonRequest | | CoreCommonRequestFromMessage |
|
||||||
|
* +---+------------------------------------+---+-----------------------------------+
|
||||||
|
* | d | ODK_ParseLicense | s | CreateCoreLicenseResponse |
|
||||||
|
* | +------------------------------------+ +-----------------------------------+
|
||||||
|
* | | ODK_ParseRenewal | | CreateCoreRenewalResponse |
|
||||||
|
* | +------------------------------------+ +-----------------------------------+
|
||||||
|
* | | ODK_ParseProvisioning | | CreateCoreProvisioningResponse |
|
||||||
|
* +---+------------------------------------+---+-----------------------------------+
|
||||||
|
*
|
||||||
|
*********************************************************************/
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace oemcrypto_core_message {
|
||||||
|
|
||||||
|
// @ input/output structs
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output structure for CommonRequestFromMessage
|
||||||
|
* Input structure for CreateCommonResponse
|
||||||
|
*/
|
||||||
|
struct ODK_CommonRequest {
|
||||||
|
uint16_t api_minor_version;
|
||||||
|
uint16_t api_major_version;
|
||||||
|
uint32_t nonce;
|
||||||
|
uint32_t session_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output structure for CoreLicenseRequestFromMessage
|
||||||
|
* Input structure for CreateCoreLicenseResponse
|
||||||
|
*/
|
||||||
|
struct ODK_LicenseRequest {
|
||||||
|
uint16_t api_minor_version;
|
||||||
|
uint16_t api_major_version;
|
||||||
|
uint32_t nonce;
|
||||||
|
uint32_t session_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output structure for CoreRenewalRequestFromMessage
|
||||||
|
* Input structure for CreateCoreRenewalResponse
|
||||||
|
*/
|
||||||
|
struct ODK_RenewalRequest {
|
||||||
|
uint16_t api_minor_version;
|
||||||
|
uint16_t api_major_version;
|
||||||
|
uint32_t nonce;
|
||||||
|
uint32_t session_id;
|
||||||
|
uint64_t playback_time_seconds;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output structure for CoreProvisioningRequestFromMessage
|
||||||
|
* Input structure for CreateCoreProvisioningResponse
|
||||||
|
*/
|
||||||
|
struct ODK_ProvisioningRequest {
|
||||||
|
uint16_t api_minor_version;
|
||||||
|
uint16_t api_major_version;
|
||||||
|
uint32_t nonce;
|
||||||
|
uint32_t session_id;
|
||||||
|
std::string device_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace oemcrypto_core_message
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_
|
||||||
613
common/oemcrypto_core_message/odk/include/odk.h
Normal file
613
common/oemcrypto_core_message/odk/include/odk.h
Normal file
@@ -0,0 +1,613 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mainpage OEMCrypto v16 Core Message Serialization library
|
||||||
|
*
|
||||||
|
* For Widevine Modular DRM, there are six message types between a server and
|
||||||
|
* a client device: license request and response, provisioning request and
|
||||||
|
* response, and renewal request and response.
|
||||||
|
*
|
||||||
|
* In OEMCrypto v15 and earlier, messages from the server were parsed by the
|
||||||
|
* CDM layer above OEMCrypto; the CDM in turn gave OEMCrypto a collection of
|
||||||
|
* pointers to protected data within the message. However, the pointers
|
||||||
|
* themselves were not signed by the server.
|
||||||
|
*
|
||||||
|
* Starting from OEMCrypto v16, all fields used by OEMCrypto in each of these
|
||||||
|
* messages have been identified in the document "Widevine Core Message
|
||||||
|
* Serialization". These fields are called the core of the message. Core
|
||||||
|
* message fields are (de)serialized using the ODK, a C library provided by
|
||||||
|
* Widevine. OEMCrypto will parse and verify the core of the message with
|
||||||
|
* help from the ODK.
|
||||||
|
*
|
||||||
|
* The ODK functions that parse code will fill out structs that have similar
|
||||||
|
* formats to the function parameters of the OEMCrypto v15 functions being
|
||||||
|
* replaced. The ODK will be provided in source code and it is Widevine's
|
||||||
|
* intention that partners can build and link ODK with their implementation
|
||||||
|
* of OEMCrypto with no or few code changes.
|
||||||
|
*
|
||||||
|
* OEMCrypto implementers shall build the ODK library as part of the Trusted
|
||||||
|
* Application (TA) running in the TEE. All memory and buffers used by the
|
||||||
|
* ODK library shall be sanitized by the OEMCrypto implementer to prevent
|
||||||
|
* modification by any process running the REE.
|
||||||
|
*
|
||||||
|
* See the documents
|
||||||
|
* <a href="../odk">Widevine Core Message Serialization</a>
|
||||||
|
* and
|
||||||
|
* <a href="../../lic_duration_and_renewal">License Duration and Renewal</a>
|
||||||
|
* for a detailed description of the ODK API. You can
|
||||||
|
* find these documents in the widevine repository as
|
||||||
|
* docs/Widevine_Core_Message_Serialization.pdf and
|
||||||
|
* docs/License_Duration_and_Renewal.pdf
|
||||||
|
*
|
||||||
|
* @defgroup odk_parser Core Message Parsing and Verification
|
||||||
|
* Functions that parse core messages and verify they are valid.
|
||||||
|
* TODO(user): add documentation for parsing functions.
|
||||||
|
*
|
||||||
|
* @defgroup odk_packer Core Message Creation
|
||||||
|
* Functions that create core messages.
|
||||||
|
* TODO(user): add documentation for packing functions.
|
||||||
|
*
|
||||||
|
* @defgroup odk_timer Timer and Clock Functions
|
||||||
|
* Functions related to enforcing timer and duration restrictions.
|
||||||
|
* TODO(user): add documentation for timers and clocks.
|
||||||
|
*
|
||||||
|
* @defgroup common_types Common Types
|
||||||
|
* Enumerations and structures that are used by several OEMCrypto and ODK
|
||||||
|
* functions.
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/OEMCryptoCENCCommon.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_structs.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @addtogroup odk_timer
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function initializes the session's data structures. It shall be
|
||||||
|
* called from OEMCrypto_OpenSession.
|
||||||
|
*
|
||||||
|
* @param[out] timer_limits: the session's timer limits.
|
||||||
|
* @param[out] clock_values: the session's clock values.
|
||||||
|
* @param[out] nonce_values: the session's ODK nonce values.
|
||||||
|
* @param[in] api_major_version: the API version of OEMCrypto.
|
||||||
|
* @param[in] session_id: the session id of the newly created session.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
ODK_NonceValues* nonce_values,
|
||||||
|
uint32_t api_major_version,
|
||||||
|
uint32_t session_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sets the nonce value in the session's nonce structure. It
|
||||||
|
* shall be called from OEMCrypto_GenerateNonce.
|
||||||
|
*
|
||||||
|
* @param[in,out] nonce_values: the session's nonce data.
|
||||||
|
* @param[in] nonce: the new nonce that was just generated.
|
||||||
|
*
|
||||||
|
* @retval true on success
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
|
||||||
|
uint32_t nonce);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function initializes the clock values in the session clock_values
|
||||||
|
* structure. It shall be called from OEMCrypto_PrepAndSignLicenseRequest.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* @param[in,out] clock_values: the session's clock data.
|
||||||
|
* @param[in] system_time_seconds: the current time on OEMCrypto's monotonic
|
||||||
|
* clock.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
|
||||||
|
uint64_t system_time_seconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sets the values in the clock_values structure. It shall be
|
||||||
|
* called from OEMCrypto_LoadUsageEntry. When a usage entry from a v15 or
|
||||||
|
* earlier license is loaded, the value time_of_license_loaded shall be used
|
||||||
|
* in place of time_of_license_signed.
|
||||||
|
*
|
||||||
|
* @param[in,out] clock_values: the session's clock data.
|
||||||
|
* @param[in] time_of_license_signed: the value time_license_received from the
|
||||||
|
* loaded usage entry.
|
||||||
|
* @param[in] time_of_first_decrypt: the value time_of_first_decrypt from the
|
||||||
|
* loaded usage entry.
|
||||||
|
* @param[in] time_of_last_decrypt: the value time_of_last_decrypt from the
|
||||||
|
* loaded usage entry.
|
||||||
|
* @param[in] status: the value status from the loaded usage entry.
|
||||||
|
* @param[in] system_time_seconds: the current time on OEMCrypto's monotonic
|
||||||
|
* clock.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
|
||||||
|
uint64_t time_of_license_signed,
|
||||||
|
uint64_t time_of_first_decrypt,
|
||||||
|
uint64_t time_of_last_decrypt,
|
||||||
|
enum OEMCrypto_Usage_Entry_Status status,
|
||||||
|
uint64_t system_time_seconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This updates the clock values, and determines if playback may start based
|
||||||
|
* on the given system time. It uses the values in clock_values to determine
|
||||||
|
* if this is the first playback for the license or the first playback for
|
||||||
|
* just this session.
|
||||||
|
*
|
||||||
|
* This shall be called from the first call in a session to any of
|
||||||
|
* OEMCrypto_DecryptCENC or any of the OEMCrypto_Generic* functions.
|
||||||
|
*
|
||||||
|
* If OEMCrypto uses a hardware timer, and this function returns
|
||||||
|
* ODK_SET_TIMER, then the timer should be set to the value pointed to by
|
||||||
|
* timer_value.
|
||||||
|
*
|
||||||
|
* @param[in] system_time_seconds: the current time on OEMCrypto's monotonic
|
||||||
|
* clock, in seconds.
|
||||||
|
* @param[in] timer_limits: timer limits specified in the license.
|
||||||
|
* @param[in,out] clock_values: the sessions clock values.
|
||||||
|
* @param[out] timer_value: set to the new timer value. Only used if the return
|
||||||
|
* value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a
|
||||||
|
* hardware timer.
|
||||||
|
*
|
||||||
|
* @retval ODK_SET_TIMER: Success. The timer should be reset to the specified
|
||||||
|
* value and playback is allowed.
|
||||||
|
* @retval ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
|
||||||
|
* allowed.
|
||||||
|
* @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
||||||
|
const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
uint64_t* timer_value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vendors that do not implement their own timer should call
|
||||||
|
* ODK_UpdateLastPlaybackTime regularly during playback. This updates the
|
||||||
|
* clock values, and determines if playback may continue based on the given
|
||||||
|
* system time. This shall be called from any of OEMCrypto_DecryptCENC or any
|
||||||
|
* of the OEMCrypto_Generic* functions.
|
||||||
|
*
|
||||||
|
* All Vendors (i.e. those that do or do not implement their own timer) shall
|
||||||
|
* call ODK_UpdateLastPlaybackTime from the function
|
||||||
|
* OEMCrypto_UpdateUsageEntry before updating the usage entry so that the
|
||||||
|
* clock values are accurate.
|
||||||
|
*
|
||||||
|
* @param[in] system_time_seconds: the current time on OEMCrypto's monotonic
|
||||||
|
* clock, in seconds.
|
||||||
|
* @param[in] timer_limits: timer limits specified in the license.
|
||||||
|
* @param[in,out] clock_values: the sessions clock values.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS: Success. Playback is allowed.
|
||||||
|
* @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
|
||||||
|
const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function modifies the session's clock values to indicate that the
|
||||||
|
* license has been deactivated. It shall be called from
|
||||||
|
* OEMCrypto_DeactivateUsageEntry
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* @param[in,out] clock_values: the sessions clock values.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values);
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @addtogroup odk_packer
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the message to include a core license request at the beginning of
|
||||||
|
* the message buffer. The values in nonce_values are used to populate the
|
||||||
|
* message.
|
||||||
|
*
|
||||||
|
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignLicenseRequest.
|
||||||
|
*
|
||||||
|
* NOTE: if the message pointer is null and/or input core_message_size is
|
||||||
|
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
|
||||||
|
* core_message_size to the size needed.
|
||||||
|
*
|
||||||
|
* @param[in,out] message: Pointer to memory for the entire message. Modified by
|
||||||
|
* the ODK library.
|
||||||
|
* @param[in] message_length: length of the entire message buffer.
|
||||||
|
* @param[in,out] core_message_size: length of the core message at the beginning
|
||||||
|
* of the message. (in) size of buffer reserved for the core message, in
|
||||||
|
* bytes. (out) actual length of the core message, in bytes.
|
||||||
|
* @param[in] nonce_values: pointer to the session's nonce data.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||||
|
uint8_t* message, size_t message_length, size_t* core_message_size,
|
||||||
|
const ODK_NonceValues* nonce_values);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the message to include a core renewal request at the beginning of
|
||||||
|
* the message buffer. The values in nonce_values, clock_values and
|
||||||
|
* system_time_seconds are used to populate the message. The nonce_values
|
||||||
|
* should match those from the license.
|
||||||
|
*
|
||||||
|
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignRenewalRequest.
|
||||||
|
*
|
||||||
|
* If status in clock_values indicates that a license has not been loaded,
|
||||||
|
* then this is a license release. The ODK library will change the value of
|
||||||
|
* nonce_values.api_major_version to 15. This will make
|
||||||
|
* OEMCrypto_PrepAndSignRenewalRequest sign just the message body, as it does
|
||||||
|
* for all legacy licenses.
|
||||||
|
*
|
||||||
|
* NOTE: if the message pointer is null and/or input core_message_size is
|
||||||
|
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
|
||||||
|
* core_message_size to the size needed.
|
||||||
|
*
|
||||||
|
* @param[in,out] message: Pointer to memory for the entire message. Modified by
|
||||||
|
* the ODK library.
|
||||||
|
* @param[in] message_length: length of the entire message buffer.
|
||||||
|
* @param[in,out] core_message_size: length of the core message at the beginning
|
||||||
|
* of the message. (in) size of buffer reserved for the core message, in
|
||||||
|
* bytes. (out) actual length of the core message, in bytes.
|
||||||
|
* @param[in,out] nonce_values: pointer to the session's nonce data.
|
||||||
|
* @param[in,out] clock_values: the session's clock values.
|
||||||
|
* @param[in] system_time_seconds: the current time on OEMCrypto's clock, in
|
||||||
|
* seconds.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
||||||
|
size_t message_length,
|
||||||
|
size_t* core_message_size,
|
||||||
|
ODK_NonceValues* nonce_values,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
uint64_t system_time_seconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the message to include a core provisioning request at the
|
||||||
|
* beginning of the message buffer. The values in nonce_values are used to
|
||||||
|
* populate the message.
|
||||||
|
*
|
||||||
|
* This shall be called by OEMCrypto from
|
||||||
|
* OEMCrypto_PrepAndSignProvisioningRequest.
|
||||||
|
*
|
||||||
|
* The buffer device_id shall be the same std::string returned by
|
||||||
|
* OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and
|
||||||
|
* stable across reboots and factory resets for an L1 device.
|
||||||
|
*
|
||||||
|
* NOTE: if the message pointer is null and/or input core_message_size is
|
||||||
|
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
|
||||||
|
* core_message_size to the size needed.
|
||||||
|
*
|
||||||
|
* @param[in,out] message: Pointer to memory for the entire message. Modified by
|
||||||
|
* the ODK library.
|
||||||
|
* @param[in] message_length: length of the entire message buffer.
|
||||||
|
* @param[in,out] core_message_size: length of the core message at the beginning
|
||||||
|
* of the message. (in) size of buffer reserved for the core message, in
|
||||||
|
* bytes. (out) actual length of the core message, in bytes.
|
||||||
|
* @param[in] nonce_values: pointer to the session's nonce data.
|
||||||
|
* @param[in] device_id: For devices with a keybox, this is the device ID from
|
||||||
|
* the keybox. For devices with an OEM Certificate, this is a device
|
||||||
|
* unique id string.
|
||||||
|
* @param[in] device_id_length: length of device_id. The device ID can be at
|
||||||
|
* most 64 bytes.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||||
|
uint8_t* message, size_t message_length, size_t* core_message_size,
|
||||||
|
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||||
|
size_t device_id_length);
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @addtogroup odk_timer
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sets all limits in the timer_limits struct to the
|
||||||
|
* key_duration and initializes the other values. The field
|
||||||
|
* nonce_values.api_major_version will be set to 15. It shall be called from
|
||||||
|
* OEMCrypto_LoadKeys when loading a legacy license.
|
||||||
|
*
|
||||||
|
* @param[out] timer_limits: The session's timer limits.
|
||||||
|
* @param[in,out] clock_values: The session's clock values.
|
||||||
|
* @param[in,out] nonce_values: The session's ODK nonce values.
|
||||||
|
* @param[in] key_duration: The duration from the first key's key control
|
||||||
|
* block. In practice, the key duration is the same for all keys and is
|
||||||
|
* the same as the license duration.
|
||||||
|
* @param[in] system_time_seconds: The current time on the system clock, as
|
||||||
|
* described in the document "License Duration and Renewal".
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
ODK_NonceValues* nonce_values,
|
||||||
|
uint32_t key_duration,
|
||||||
|
uint64_t system_time_seconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function updates the clock_values as needed if a v15 renewal is
|
||||||
|
* accepted. The field nonce_values.api_major_version is verified to be 15.
|
||||||
|
*
|
||||||
|
* This is called from OEMCrypto_RefreshKeys for a valid license renewal.
|
||||||
|
* OEMCrypto shall pass in the current system time, and the key duration from
|
||||||
|
* the first object in the OEMCrypto_KeyRefreshObject.
|
||||||
|
*
|
||||||
|
* @param[in] timer_limits: The session's timer limits.
|
||||||
|
* @param[in,out] clock_values: The session's clock values.
|
||||||
|
* @param[in] nonce_values: The session's ODK nonce values.
|
||||||
|
* @param[in] system_time_seconds: The current time on the system clock, as
|
||||||
|
* described in the document "License Duration and Renewal".
|
||||||
|
* @param[in] new_key_duration: The duration from the first
|
||||||
|
* OEMCrypto_KeyRefreshObject in key_array.
|
||||||
|
* @param[out] timer_value: set to the new timer value. Only used if the return
|
||||||
|
* value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a
|
||||||
|
* hardware timer.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||||
|
* @retval ODK_SET_TIMER: Success. The timer should be reset to the specified
|
||||||
|
* value and playback is allowed.
|
||||||
|
* @retval ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
|
||||||
|
* allowed.
|
||||||
|
* @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
const ODK_NonceValues* nonce_values,
|
||||||
|
uint64_t system_time_seconds,
|
||||||
|
uint32_t new_key_duration,
|
||||||
|
uint64_t* timer_value);
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @addtogroup odk_parser
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function ODK_ParseLicense will parse the message and verify fields in
|
||||||
|
* the message.
|
||||||
|
*
|
||||||
|
* If the message does not parse correctly, ODK_VerifyAndParseLicense will
|
||||||
|
* return ODK_ERROR_CORE_MESSAGE that OEMCrypto should return to the CDM
|
||||||
|
* layer above.
|
||||||
|
*
|
||||||
|
* If the API in the message is not 16, then ODK_UNSUPPORTED_API is returned.
|
||||||
|
*
|
||||||
|
* If initial_license_load is true, and nonce_required in the license is
|
||||||
|
* true, then the ODK library shall verify that nonce_values->nonce and
|
||||||
|
* nonce_values->session_id are the same as those in the message. If
|
||||||
|
* verification fails, then it shall return OEMCrypto_ERROR_INVALID_NONCE.
|
||||||
|
*
|
||||||
|
* If initial_license_load is false, and nonce_required is true, then
|
||||||
|
* ODK_ParseLicense will set the values in nonce_values from those in the
|
||||||
|
* message.
|
||||||
|
*
|
||||||
|
* The function ODK_ParseLicense will verify that each substring points to a
|
||||||
|
* location in the message body. The message body is the buffer starting at
|
||||||
|
* message + core_message_length with size message_length -
|
||||||
|
* core_message_length.
|
||||||
|
*
|
||||||
|
* If initial_license_load is true, then ODK_ParseLicense shall verify that
|
||||||
|
* the parameter request_hash matches request_hash in the parsed license. If
|
||||||
|
* verification fails, then it shall return ODK_ERROR_CORE_MESSAGE. This was
|
||||||
|
* computed by OEMCrypto when the license was requested.
|
||||||
|
*
|
||||||
|
* If usage_entry_present is true, then ODK_ParseLicense shall verify that
|
||||||
|
* the pst in the license has a nonzero length.
|
||||||
|
*
|
||||||
|
* @param[in] message: pointer to the message buffer.
|
||||||
|
* @param[in] message_length: length of the entire message buffer.
|
||||||
|
* @param[in] core_message_size: length of the core message, at the beginning of
|
||||||
|
* the message buffer.
|
||||||
|
* @param[in] initial_license_load: true when called for OEMCrypto_LoadLicense
|
||||||
|
* and false when called for OEMCrypto_ReloadLicense.
|
||||||
|
* @param[in] usage_entry_present: true if the session has a new usage entry
|
||||||
|
* associated with it created via OEMCrypto_CreateNewUsageEntry.
|
||||||
|
* @param[in] request_hash: the hash of the license request core message. This
|
||||||
|
* was computed by OEMCrypto when the license request was signed.
|
||||||
|
* @param[in,out] timer_limits: The session's timer limits. These will be
|
||||||
|
* updated.
|
||||||
|
* @param[in,out] clock_values: The session's clock values. These will be
|
||||||
|
* updated.
|
||||||
|
* @param[in,out] nonce_values: The session's nonce values. These will be
|
||||||
|
* updated.
|
||||||
|
* @param[out] parsed_license: the destination for the data.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval ODK_ERROR_CORE_MESSAGE: if the message did not parse correctly, or
|
||||||
|
* there were other incorrect values. An error should be returned to the
|
||||||
|
* CDM layer.
|
||||||
|
* @retval ODK_UNSUPPORTED_API
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_NONCE
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_ParseLicense(
|
||||||
|
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||||
|
bool initial_license_load, bool usage_entry_present,
|
||||||
|
const uint8_t request_hash[ODK_SHA256_HASH_SIZE],
|
||||||
|
ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
|
||||||
|
ODK_NonceValues* nonce_values, ODK_ParsedLicense* parsed_license);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function ODK_ParseRenewal will parse the message and verify its
|
||||||
|
* contents. If the message does not parse correctly, an error of
|
||||||
|
* ODK_ERROR_CORE_MESSAGE is returned.
|
||||||
|
*
|
||||||
|
* ODK_ParseRenewal shall verify that all fields in nonce_values match those
|
||||||
|
* in the license. Otherwise it shall return OEMCrypto_ERROR_INVALID_NONCE.
|
||||||
|
*
|
||||||
|
* After parsing the message, this function updates the clock_values based on
|
||||||
|
* the timer_limits and the current system time. If playback may not
|
||||||
|
* continue, then ODK_TIMER_EXPIRED is returned.
|
||||||
|
*
|
||||||
|
* If playback may continue, a return value of ODK_SET_TIMER or
|
||||||
|
* ODK_TIMER_EXPIRED is returned. If the return value is ODK_SET_TIMER, then
|
||||||
|
* playback may continue until the timer expires. If the return value is
|
||||||
|
* ODK_DISABLE_TIMER, then playback time is not limited.
|
||||||
|
*
|
||||||
|
* If OEMCrypto uses a hardware timer, and this function returns
|
||||||
|
* ODK_SET_TIMER, then OEMCrypto shall set the timer to the value pointed to
|
||||||
|
* by timer_value.
|
||||||
|
*
|
||||||
|
* @param[in] message: pointer to the message buffer.
|
||||||
|
* @param[in] message_length: length of the entire message buffer.
|
||||||
|
* @param[in] core_message_size: length of the core message, at the beginning of
|
||||||
|
* the message buffer.
|
||||||
|
* @param[in] nonce_values: pointer to the session's nonce data.
|
||||||
|
* @param[in] system_time_seconds: the current time on OEMCrypto's clock, in
|
||||||
|
* seconds.
|
||||||
|
* @param[in] timer_limits: timer limits specified in the license.
|
||||||
|
* @param[in,out] clock_values: the sessions clock values.
|
||||||
|
* @param[out] timer_value: set to the new timer value. Only used if the return
|
||||||
|
* value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a
|
||||||
|
* hardware timer.
|
||||||
|
*
|
||||||
|
* @retval ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there
|
||||||
|
* were other incorrect values. An error should be returned to the CDM
|
||||||
|
* layer.
|
||||||
|
* @retval ODK_SET_TIMER: Success. The timer should be reset to the specified
|
||||||
|
* timer value.
|
||||||
|
* @retval ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
|
||||||
|
* allowed.
|
||||||
|
* @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
|
||||||
|
* @retval ODK_UNSUPPORTED_API
|
||||||
|
* @retval ODK_STALE_RENEWAL: This renewal is not the most recently signed. It
|
||||||
|
* is rejected.
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_NONCE
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
||||||
|
size_t core_message_length,
|
||||||
|
const ODK_NonceValues* nonce_values,
|
||||||
|
uint64_t system_time_seconds,
|
||||||
|
const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
uint64_t* timer_value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function ODK_ParseProvisioning will parse the message and verify the
|
||||||
|
* nonce values match those in the license.
|
||||||
|
*
|
||||||
|
* If the message does not parse correctly, ODK_ParseProvisioning will return
|
||||||
|
* an error that OEMCrypto should return to the CDM layer above.
|
||||||
|
*
|
||||||
|
* If the API in the message is larger than 16, then ODK_UNSUPPORTED_API is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* ODK_ParseProvisioning shall verify that nonce_values->nonce and
|
||||||
|
* nonce_values->session_id are the same as those in the message. Otherwise
|
||||||
|
* it shall return OEMCrypto_ERROR_INVALID_NONCE.
|
||||||
|
*
|
||||||
|
* The function ODK_ParseProvisioning will verify that each substring points
|
||||||
|
* to a location in the message body. The message body is the buffer starting
|
||||||
|
* at message + core_message_length with size message_length -
|
||||||
|
* core_message_length.
|
||||||
|
*
|
||||||
|
* @param[in] message: pointer to the message buffer.
|
||||||
|
* @param[in] message_length: length of the entire message buffer.
|
||||||
|
* @param[in] core_message_size: length of the core message, at the beginning of
|
||||||
|
* the message buffer.
|
||||||
|
* @param[in] nonce_values: pointer to the session's nonce data.
|
||||||
|
* @param[in] device_id: a pointer to a buffer containing the device ID of the
|
||||||
|
* device. The ODK function will verify it matches that in the message.
|
||||||
|
* @param[in] device_id_length: the length of the device ID.
|
||||||
|
* @param[out] parsed_response: destination for the parse data.
|
||||||
|
*
|
||||||
|
* @retval OEMCrypto_SUCCESS
|
||||||
|
* @retval ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there
|
||||||
|
* were other incorrect values. An error should be returned to the CDM
|
||||||
|
* layer.
|
||||||
|
* @retval ODK_UNSUPPORTED_API
|
||||||
|
* @retval OEMCrypto_ERROR_INVALID_NONCE
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This method is new in version 16 of the API.
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_ParseProvisioning(
|
||||||
|
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||||
|
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||||
|
size_t device_id_length, ODK_ParsedProvisioning* parsed_response);
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_H_
|
||||||
20
common/oemcrypto_core_message/odk/include/odk_attributes.h
Normal file
20
common/oemcrypto_core_message/odk/include/odk_attributes.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_ATTRIBUTES_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_ATTRIBUTES_H_
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define UNUSED __attribute__((__unused__))
|
||||||
|
#else
|
||||||
|
#define UNUSED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_ATTRIBUTES_H_
|
||||||
221
common/oemcrypto_core_message/odk/include/odk_structs.h
Normal file
221
common/oemcrypto_core_message/odk/include/odk_structs.h
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_STRUCTS_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_STRUCTS_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/OEMCryptoCENCCommon.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_target.h"
|
||||||
|
|
||||||
|
/* The version of this library. */
|
||||||
|
#define ODK_MAJOR_VERSION 16
|
||||||
|
#define ODK_MINOR_VERSION 3
|
||||||
|
|
||||||
|
/* ODK Version string. Date changed automatically on each release. */
|
||||||
|
#define ODK_RELEASE_DATE "ODK v16.3 AUTOGEN_RELEASE_DATE"
|
||||||
|
|
||||||
|
/* The lowest version number for an ODK message. */
|
||||||
|
#define ODK_FIRST_VERSION 16
|
||||||
|
|
||||||
|
/* Some useful constants. */
|
||||||
|
#define ODK_DEVICE_ID_LEN_MAX 64
|
||||||
|
#define ODK_SHA256_HASH_SIZE 32
|
||||||
|
|
||||||
|
/// @addtogroup odk_timer
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer limits are specified in a license and are used to determine when
|
||||||
|
* playback is allowed. See the document "License Duration and Renewal" for a
|
||||||
|
* discussion on the time restrictions that may be placed on a license. The
|
||||||
|
* fields in this structure are directly related to the fields in the core
|
||||||
|
* license message. The fields are set when OEMCrypto calls the function
|
||||||
|
* ODK_ParseLicense or ODK_InitializeV15Values.
|
||||||
|
*
|
||||||
|
* @param soft_enforce_rental_duration: A boolean controlling the soft or hard
|
||||||
|
* enforcement of rental duration.
|
||||||
|
* @param soft_enforce_playback_duration: A boolean controlling the soft or hard
|
||||||
|
* enforcement of playback duration.
|
||||||
|
* @param earliest_playback_start_seconds: The earliest time that the first
|
||||||
|
* playback is allowed. Measured in seconds since the license request was
|
||||||
|
* signed. For most use cases, this is zero.
|
||||||
|
* @param rental_duration_seconds: Window of time for the allowed first
|
||||||
|
* playback. Measured in seconds since the earliest playback start. If
|
||||||
|
* soft_enforce_rental_duration is true, this applies only to the first
|
||||||
|
* playback. If soft_enforce_rental_duration is false, then this
|
||||||
|
* restricts any playback. A value of zero means no limit.
|
||||||
|
* @param total_playback_duration_seconds: Window of time for allowed playback.
|
||||||
|
* Measured in seconds since the first playback start. If
|
||||||
|
* soft_enforce_playback_duration is true, this applies only to the start
|
||||||
|
* of playback for any session. If soft_enforce_playback_duration is
|
||||||
|
* false, then this restricts any playback. A value of zero means no
|
||||||
|
* limit.
|
||||||
|
* @param initial_renewal_duration_seconds: Window of time for allowed playback.
|
||||||
|
* Measured in seconds since the first playback start. This value is only
|
||||||
|
* used to start the renewal timer. After a renewal message is loaded,
|
||||||
|
* the timer will be reset. A value of zero means no limit.
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This struct changed in API version 16.2.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
bool soft_enforce_rental_duration;
|
||||||
|
bool soft_enforce_playback_duration;
|
||||||
|
uint64_t earliest_playback_start_seconds;
|
||||||
|
uint64_t rental_duration_seconds;
|
||||||
|
uint64_t total_playback_duration_seconds;
|
||||||
|
uint64_t initial_renewal_duration_seconds;
|
||||||
|
} ODK_TimerLimits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clock values are modified when decryption occurs or when a renewal is
|
||||||
|
* processed. They are used to track the current status of the license --
|
||||||
|
* i.e. has playback started? When does the timer expire? See the section
|
||||||
|
* "Complete ODK API" of the document "Widevine Core Message Serialization"
|
||||||
|
* for a complete list of all fields in this structure. Most of these values
|
||||||
|
* shall be saved with the usage entry.
|
||||||
|
*
|
||||||
|
* All times are in seconds. Most of the fields in this structure are saved
|
||||||
|
* in the usage entry. This structure should be initialized when a usage
|
||||||
|
* entry is created or loaded, and should be used to save a usage entry. It
|
||||||
|
* is updated using the ODK functions listed below. The time values are based
|
||||||
|
* on OEMCrypto's system clock, as described in the document "License
|
||||||
|
* Duration and Renewal".
|
||||||
|
*
|
||||||
|
* @param time_of_license_signed: Time that the license request was signed,
|
||||||
|
* based on OEMCrypto's system clock. This value shall be stored and
|
||||||
|
* reloaded with usage entry as time_of_license_received.
|
||||||
|
* @param time_of_first_decrypt: Time of the first decrypt or call select key,
|
||||||
|
* based on OEMCrypto's system clock. This is 0 if the license has not
|
||||||
|
* been used to decrypt any data. This value shall be stored and reloaded
|
||||||
|
* with usage entry.
|
||||||
|
* @param time_of_last_decrypt: Time of the most recent decrypt call, based on
|
||||||
|
* OEMCrypto's system clock. This value shall be stored and reloaded with
|
||||||
|
* usage entry.
|
||||||
|
* @param time_of_renewal_request: Time of the most recent renewal request,
|
||||||
|
* based on OEMCrypto's system clock. This is used to verify that a
|
||||||
|
* renewal is not stale.
|
||||||
|
* @param time_when_timer_expires: Time that the current timer expires, based on
|
||||||
|
* OEMCrypto's system clock. If the timer is active, this is used by the
|
||||||
|
* ODK library to determine if it has expired.
|
||||||
|
* @param timer_status: Used internally by the ODK library to indicate the
|
||||||
|
* current timer status.
|
||||||
|
* @param status: The license or usage entry status. This value shall be stored
|
||||||
|
* and reloaded with usage entry.
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This struct changed in API version 16.2.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint64_t time_of_license_signed;
|
||||||
|
uint64_t time_of_first_decrypt;
|
||||||
|
uint64_t time_of_last_decrypt;
|
||||||
|
uint64_t time_of_renewal_request;
|
||||||
|
uint64_t time_when_timer_expires;
|
||||||
|
uint32_t timer_status;
|
||||||
|
enum OEMCrypto_Usage_Entry_Status status;
|
||||||
|
} ODK_ClockValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nonce values are used to match a license or provisioning request to a
|
||||||
|
* license or provisioning response. They are also used to match a renewal
|
||||||
|
* request and response to a license. For this reason, the api_version might
|
||||||
|
* be lower than that supported by OEMCrypto. The api_version matches the
|
||||||
|
* version of the license. Similarly the nonce and session_id match the
|
||||||
|
* session that generated the license request. For an offline license, these
|
||||||
|
* might not match the session that is loading the license. We use the nonce
|
||||||
|
* to prevent a license from being replayed. By also including a session_id
|
||||||
|
* in the license request and license response, we prevent an attack using
|
||||||
|
* the birthday paradox to generate nonce collisions on a single device.
|
||||||
|
*
|
||||||
|
* @param api_major_version: the API version of the license. This is initialized
|
||||||
|
* to the API version of the ODK library, but may be lower.
|
||||||
|
* @param api_minor_version: the minor version of the ODK library. This is used
|
||||||
|
* by the server to verify that device is not using an obsolete version
|
||||||
|
* of the ODK library.
|
||||||
|
* @param nonce: a randomly generated number used to prevent replay attacks.
|
||||||
|
* @param session_id: the session id of the session which signed the license or
|
||||||
|
* provisioning request. It is used to prevent replay attacks from one
|
||||||
|
* session to another.
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This struct changed in API version 16.2.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint16_t api_minor_version;
|
||||||
|
uint16_t api_major_version;
|
||||||
|
uint32_t nonce;
|
||||||
|
uint32_t session_id;
|
||||||
|
} ODK_NonceValues;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @addtogroup odk_parser
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parsed license structure contains information from the license
|
||||||
|
* message. The function ODK_ParseLicense will fill in the fields of this
|
||||||
|
* message. All substrings are contained within the message body.
|
||||||
|
*
|
||||||
|
* @param enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits.
|
||||||
|
* @param enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is
|
||||||
|
* 512 bits.
|
||||||
|
* @param pst: the Provider Session Token.
|
||||||
|
* @param srm_restriction_data: optional data specifying the minimum SRM
|
||||||
|
* version.
|
||||||
|
* @param license_type: specifies if the license contains content keys or
|
||||||
|
* entitlement keys.
|
||||||
|
* @param nonce_required: indicates if the license requires a nonce.
|
||||||
|
* @param timer_limits: time limits of the for the license.
|
||||||
|
* @param key_array_length: number of keys present.
|
||||||
|
* @param key_array: set of keys to be installed.
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This struct changed in API version 16.2.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
OEMCrypto_Substring enc_mac_keys_iv;
|
||||||
|
OEMCrypto_Substring enc_mac_keys;
|
||||||
|
OEMCrypto_Substring pst;
|
||||||
|
OEMCrypto_Substring srm_restriction_data;
|
||||||
|
OEMCrypto_LicenseType license_type;
|
||||||
|
bool nonce_required;
|
||||||
|
ODK_TimerLimits timer_limits;
|
||||||
|
uint32_t key_array_length;
|
||||||
|
OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS];
|
||||||
|
} ODK_ParsedLicense;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parsed provisioning structure contains information from the license
|
||||||
|
* message. The function ODK_ParseProvisioning will fill in the fields of
|
||||||
|
* this message. All substrings are contained within the message body.
|
||||||
|
*
|
||||||
|
* @param key_type: indicates if this key is an RSA or ECC private key.
|
||||||
|
* @param enc_private_key: encrypted private key for the DRM certificate.
|
||||||
|
* @param enc_private_key_iv: IV for decrypting new private key. Size is 128
|
||||||
|
* bits.
|
||||||
|
* @param encrypted_message_key: used for provisioning 3.0 to derive keys.
|
||||||
|
*
|
||||||
|
* @version
|
||||||
|
* This struct changed in API version 16.2.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
OEMCrypto_PrivateKeyType key_type;
|
||||||
|
OEMCrypto_Substring enc_private_key;
|
||||||
|
OEMCrypto_Substring enc_private_key_iv;
|
||||||
|
OEMCrypto_Substring encrypted_message_key; /* Used for Prov 3.0 */
|
||||||
|
} ODK_ParsedProvisioning;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_STRUCTS_H_
|
||||||
19
common/oemcrypto_core_message/odk/include/odk_target.h
Normal file
19
common/oemcrypto_core_message/odk/include/odk_target.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file is distributed
|
||||||
|
// under the Widevine Master License Agreement.
|
||||||
|
|
||||||
|
// Partners are expected to edit this file to support target specific code
|
||||||
|
// and limits.
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_TARGET_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_TARGET_H_
|
||||||
|
|
||||||
|
// Maximum number of keys can be modified to suit target's resource tier.
|
||||||
|
#define ODK_MAX_NUM_KEYS 32
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_TARGET_H_
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_deserialize.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_structs.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_serialize.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_structs_priv.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/serialization_base.h"
|
||||||
|
|
||||||
|
namespace oemcrypto_core_message {
|
||||||
|
namespace deserialize {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template for parsing requests
|
||||||
|
*
|
||||||
|
* Template arguments:
|
||||||
|
* S: kdo output struct
|
||||||
|
* T: struct serialized by odk
|
||||||
|
* U: auto-generated deserializing function for |T|
|
||||||
|
*/
|
||||||
|
template <typename S, typename T, typename U>
|
||||||
|
bool ParseRequest(uint32_t message_type,
|
||||||
|
const std::string& oemcrypto_core_message, S* core_request,
|
||||||
|
T* prepared, const U unpacker) {
|
||||||
|
if (core_request == nullptr || prepared == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* buf =
|
||||||
|
reinterpret_cast<const uint8_t*>(oemcrypto_core_message.c_str());
|
||||||
|
const size_t buf_length = oemcrypto_core_message.size();
|
||||||
|
|
||||||
|
uint8_t blk[SIZE_OF_MESSAGE_STRUCT];
|
||||||
|
Message* msg = reinterpret_cast<Message*>(blk);
|
||||||
|
InitMessage(msg, const_cast<uint8_t*>(buf), buf_length);
|
||||||
|
SetSize(msg, buf_length);
|
||||||
|
|
||||||
|
unpacker(msg, prepared);
|
||||||
|
if (!ValidMessage(msg)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& core_message = prepared->core_message;
|
||||||
|
core_request->api_major_version = core_message.nonce_values.api_major_version;
|
||||||
|
core_request->api_minor_version = core_message.nonce_values.api_minor_version;
|
||||||
|
core_request->nonce = core_message.nonce_values.nonce;
|
||||||
|
core_request->session_id = core_message.nonce_values.session_id;
|
||||||
|
// Verify that the minor version matches the released version for the given
|
||||||
|
// major version.
|
||||||
|
if (core_request->api_major_version < ODK_FIRST_VERSION) {
|
||||||
|
// Non existing versions are not supported.
|
||||||
|
return false;
|
||||||
|
} else if (core_request->api_major_version == 16) {
|
||||||
|
// For version 16, we demand a minor version of at least 2.
|
||||||
|
// We accept 16.2, 16.3, or higher.
|
||||||
|
if (core_request->api_major_version < 2) return false;
|
||||||
|
} else {
|
||||||
|
// Other versions do not (yet) have a restriction on minor number.
|
||||||
|
// In particular, future versions are accepted for forward compatibility.
|
||||||
|
}
|
||||||
|
// For v16, a release and a renewal use the same message structure.
|
||||||
|
// However, for future API versions, the release might be a separate
|
||||||
|
// message. Otherwise, we expect an exact match of message types.
|
||||||
|
if (message_type != ODK_Common_Request_Type &&
|
||||||
|
core_message.message_type != message_type &&
|
||||||
|
!(message_type == ODK_Renewal_Request_Type &&
|
||||||
|
core_message.message_type == ODK_Release_Request_Type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Verify that the amount of buffer we read, which is GetOffset, is not more
|
||||||
|
// than the total message size. We allow the total message size to be larger
|
||||||
|
// for forward compatibility because future messages might have extra fields
|
||||||
|
// that we can ignore.
|
||||||
|
if (core_message.message_length < GetOffset(msg)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||||
|
ODK_LicenseRequest* core_license_request) {
|
||||||
|
const auto unpacker = Unpack_ODK_PreparedLicenseRequest;
|
||||||
|
ODK_PreparedLicenseRequest prepared_license = {};
|
||||||
|
return ParseRequest(ODK_License_Request_Type, oemcrypto_core_message,
|
||||||
|
core_license_request, &prepared_license, unpacker);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||||
|
ODK_RenewalRequest* core_renewal_request) {
|
||||||
|
const auto unpacker = Unpack_ODK_PreparedRenewalRequest;
|
||||||
|
ODK_PreparedRenewalRequest prepared_renewal = {};
|
||||||
|
if (!ParseRequest(ODK_Renewal_Request_Type, oemcrypto_core_message,
|
||||||
|
core_renewal_request, &prepared_renewal, unpacker)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
core_renewal_request->playback_time_seconds = prepared_renewal.playback_time;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreProvisioningRequestFromMessage(
|
||||||
|
const std::string& oemcrypto_core_message,
|
||||||
|
ODK_ProvisioningRequest* core_provisioning_request) {
|
||||||
|
const auto unpacker = Unpack_ODK_PreparedProvisioningRequest;
|
||||||
|
ODK_PreparedProvisioningRequest prepared_provision = {};
|
||||||
|
if (!ParseRequest(ODK_Provisioning_Request_Type, oemcrypto_core_message,
|
||||||
|
core_provisioning_request, &prepared_provision, unpacker)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const uint8_t* device_id = prepared_provision.device_id;
|
||||||
|
const uint32_t device_id_length = prepared_provision.device_id_length;
|
||||||
|
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {};
|
||||||
|
if (memcmp(zero, device_id + device_id_length,
|
||||||
|
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
core_provisioning_request->device_id.assign(
|
||||||
|
reinterpret_cast<const char*>(device_id), device_id_length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreCommonRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||||
|
ODK_CommonRequest* common_request) {
|
||||||
|
const auto unpacker = Unpack_ODK_PreparedCommonRequest;
|
||||||
|
ODK_PreparedCommonRequest prepared_common = {};
|
||||||
|
return ParseRequest(ODK_Common_Request_Type, oemcrypto_core_message,
|
||||||
|
common_request, &prepared_common, unpacker);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace deserialize
|
||||||
|
} // namespace oemcrypto_core_message
|
||||||
131
common/oemcrypto_core_message/odk/src/core_message_serialize.cpp
Normal file
131
common/oemcrypto_core_message/odk/src/core_message_serialize.cpp
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_serialize.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_structs.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_serialize.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_structs_priv.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/serialization_base.h"
|
||||||
|
|
||||||
|
namespace oemcrypto_core_message {
|
||||||
|
namespace serialize {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template for parsing requests
|
||||||
|
*
|
||||||
|
* Template arguments:
|
||||||
|
* T: struct to be deserialized by odk
|
||||||
|
* S: kdo input struct
|
||||||
|
* P: auto-generated serializing function for |T|
|
||||||
|
*/
|
||||||
|
template <typename T, typename S, typename P>
|
||||||
|
bool CreateResponse(uint32_t message_type, const S& core_request,
|
||||||
|
std::string* oemcrypto_core_message, T& response,
|
||||||
|
const P& packer) {
|
||||||
|
if (!oemcrypto_core_message) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* header = &response.request.core_message;
|
||||||
|
header->message_type = message_type;
|
||||||
|
header->nonce_values.api_major_version = core_request.api_major_version;
|
||||||
|
header->nonce_values.api_minor_version = core_request.api_minor_version;
|
||||||
|
header->nonce_values.nonce = core_request.nonce;
|
||||||
|
header->nonce_values.session_id = core_request.session_id;
|
||||||
|
// The message API version for the response is the minimum of our version and
|
||||||
|
// the request's version.
|
||||||
|
if (core_request.api_major_version > ODK_MAJOR_VERSION) {
|
||||||
|
header->nonce_values.api_major_version = ODK_MAJOR_VERSION;
|
||||||
|
header->nonce_values.api_minor_version = ODK_MINOR_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr size_t BUF_CAPACITY = 2048;
|
||||||
|
std::vector<uint8_t> buf(BUF_CAPACITY, 0);
|
||||||
|
uint8_t blk[SIZE_OF_MESSAGE_STRUCT];
|
||||||
|
Message* msg = reinterpret_cast<Message*>(blk);
|
||||||
|
InitMessage(msg, buf.data(), buf.capacity());
|
||||||
|
packer(msg, &response);
|
||||||
|
if (!ValidMessage(msg)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t message_length = GetSize(msg);
|
||||||
|
InitMessage(msg, buf.data() + sizeof(header->message_type),
|
||||||
|
sizeof(header->message_length));
|
||||||
|
Pack_uint32_t(msg, &message_length);
|
||||||
|
oemcrypto_core_message->assign(reinterpret_cast<const char*>(buf.data()),
|
||||||
|
message_length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CopyDeviceId(const ODK_ProvisioningRequest& src,
|
||||||
|
ODK_ProvisioningResponse* dest) {
|
||||||
|
auto& request = dest->request;
|
||||||
|
const std::string& device_id = src.device_id;
|
||||||
|
if (request.device_id_length > sizeof(request.device_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
request.device_id_length = device_id.size();
|
||||||
|
memset(request.device_id, 0, sizeof(request.device_id));
|
||||||
|
memcpy(request.device_id, device_id.data(), request.device_id_length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
|
||||||
|
const ODK_LicenseRequest& core_request,
|
||||||
|
const std::string& core_request_sha256,
|
||||||
|
std::string* oemcrypto_core_message) {
|
||||||
|
ODK_LicenseResponse license_response{
|
||||||
|
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic), {0}};
|
||||||
|
if (core_request_sha256.size() != sizeof(license_response.request_hash))
|
||||||
|
return false;
|
||||||
|
memcpy(license_response.request_hash, core_request_sha256.data(),
|
||||||
|
sizeof(license_response.request_hash));
|
||||||
|
return CreateResponse(ODK_License_Response_Type, core_request,
|
||||||
|
oemcrypto_core_message, license_response,
|
||||||
|
Pack_ODK_LicenseResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
|
||||||
|
uint64_t renewal_duration_seconds,
|
||||||
|
std::string* oemcrypto_core_message) {
|
||||||
|
ODK_RenewalResponse renewal_response{{}, core_request.playback_time_seconds};
|
||||||
|
renewal_response.request.playback_time = core_request.playback_time_seconds;
|
||||||
|
renewal_response.renewal_duration_seconds = renewal_duration_seconds;
|
||||||
|
return CreateResponse(ODK_Renewal_Response_Type, core_request,
|
||||||
|
oemcrypto_core_message, renewal_response,
|
||||||
|
Pack_ODK_RenewalResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
|
||||||
|
const ODK_ProvisioningRequest& core_request,
|
||||||
|
std::string* oemcrypto_core_message) {
|
||||||
|
ODK_ProvisioningResponse prov_response{
|
||||||
|
{}, const_cast<ODK_ParsedProvisioning*>(&parsed_prov)};
|
||||||
|
if (!CopyDeviceId(core_request, &prov_response)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return CreateResponse(ODK_Provisioning_Response_Type, core_request,
|
||||||
|
oemcrypto_core_message, prov_response,
|
||||||
|
Pack_ODK_ProvisioningResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace serialize
|
||||||
|
} // namespace oemcrypto_core_message
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_serialize_proto.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/core_message_serialize.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_structs.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_serialize.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_structs_priv.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/serialization_base.h"
|
||||||
|
#include "protos/public/certificate_provisioning.pb.h"
|
||||||
|
#include "protos/public/license_protocol.pb.h"
|
||||||
|
|
||||||
|
namespace oemcrypto_core_message {
|
||||||
|
namespace serialize {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* @ private functions */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract OEMCrypto_Substring (offset, length) from serialized protobuf
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* message: serialized license protobuf
|
||||||
|
* field: substring value
|
||||||
|
*/
|
||||||
|
OEMCrypto_Substring GetOecSubstring(const std::string& message,
|
||||||
|
const std::string& field) {
|
||||||
|
OEMCrypto_Substring substring = {};
|
||||||
|
size_t pos = message.find(field);
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
substring = OEMCrypto_Substring{pos, field.length()};
|
||||||
|
}
|
||||||
|
return substring;
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCrypto_KeyObject KeyContainerToOecKey(
|
||||||
|
const std::string& proto, const widevine::License::KeyContainer& k) {
|
||||||
|
OEMCrypto_KeyObject obj = {};
|
||||||
|
obj.key_id = GetOecSubstring(proto, k.id());
|
||||||
|
obj.key_data_iv = GetOecSubstring(proto, k.iv());
|
||||||
|
// Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes,
|
||||||
|
// the padding will always be 16 bytes.
|
||||||
|
const std::string& key_data = k.key();
|
||||||
|
const size_t PKCS5_PADDING_SIZE = 16;
|
||||||
|
obj.key_data = GetOecSubstring(
|
||||||
|
proto, key_data.substr(0, std::max(PKCS5_PADDING_SIZE, key_data.size()) -
|
||||||
|
PKCS5_PADDING_SIZE));
|
||||||
|
if (k.has_key_control()) {
|
||||||
|
const auto& key_control = k.key_control();
|
||||||
|
obj.key_control_iv = GetOecSubstring(proto, key_control.iv());
|
||||||
|
obj.key_control = GetOecSubstring(proto, key_control.key_control_block());
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// @ public create response functions
|
||||||
|
|
||||||
|
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||||
|
const ODK_LicenseRequest& core_request,
|
||||||
|
const std::string& core_request_sha256,
|
||||||
|
const bool nonce_required,
|
||||||
|
std::string* oemcrypto_core_message) {
|
||||||
|
widevine::License lic;
|
||||||
|
if (!lic.ParseFromString(serialized_license)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ODK_ParsedLicense parsed_lic{};
|
||||||
|
bool any_content = false;
|
||||||
|
bool any_entitlement = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < lic.key_size(); ++i) {
|
||||||
|
const auto& k = lic.key(i);
|
||||||
|
switch (k.type()) {
|
||||||
|
case widevine::License_KeyContainer::SIGNING: {
|
||||||
|
if (!k.has_key()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
parsed_lic.enc_mac_keys_iv =
|
||||||
|
GetOecSubstring(serialized_license, k.iv());
|
||||||
|
parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, k.key());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case widevine::License_KeyContainer::CONTENT:
|
||||||
|
case widevine::License_KeyContainer::OPERATOR_SESSION:
|
||||||
|
case widevine::License_KeyContainer::ENTITLEMENT: {
|
||||||
|
if (k.type() == widevine::License_KeyContainer::ENTITLEMENT) {
|
||||||
|
any_entitlement = true;
|
||||||
|
} else {
|
||||||
|
any_content = true;
|
||||||
|
}
|
||||||
|
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t& n = parsed_lic.key_array_length;
|
||||||
|
parsed_lic.key_array[n++] = KeyContainerToOecKey(serialized_license, k);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (any_content && any_entitlement) {
|
||||||
|
// TODO(b/147513335): this should be logged -- both type of keys.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!any_content && !any_entitlement) {
|
||||||
|
// TODO(b/147513335): this should be logged -- no keys?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parsed_lic.license_type =
|
||||||
|
any_content ? OEMCrypto_ContentLicense : OEMCrypto_EntitlementLicense;
|
||||||
|
const auto& lid = lic.id();
|
||||||
|
if (lid.has_provider_session_token()) {
|
||||||
|
parsed_lic.pst =
|
||||||
|
GetOecSubstring(serialized_license, lid.provider_session_token());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lic.has_srm_requirement()) {
|
||||||
|
parsed_lic.srm_restriction_data =
|
||||||
|
GetOecSubstring(serialized_license, lic.srm_requirement());
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed_lic.nonce_required = nonce_required;
|
||||||
|
const auto& policy = lic.policy();
|
||||||
|
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
|
||||||
|
timer_limits.soft_enforce_rental_duration =
|
||||||
|
policy.soft_enforce_rental_duration();
|
||||||
|
timer_limits.soft_enforce_playback_duration =
|
||||||
|
policy.soft_enforce_playback_duration();
|
||||||
|
timer_limits.earliest_playback_start_seconds = 0;
|
||||||
|
timer_limits.rental_duration_seconds = policy.rental_duration_seconds();
|
||||||
|
timer_limits.total_playback_duration_seconds =
|
||||||
|
policy.playback_duration_seconds();
|
||||||
|
timer_limits.initial_renewal_duration_seconds =
|
||||||
|
policy.renewal_delay_seconds() +
|
||||||
|
policy.renewal_recovery_duration_seconds();
|
||||||
|
|
||||||
|
return CreateCoreLicenseResponse(parsed_lic, core_request,
|
||||||
|
core_request_sha256, oemcrypto_core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateCoreProvisioningResponseFromProto(
|
||||||
|
const std::string& serialized_provisioning_resp,
|
||||||
|
const ODK_ProvisioningRequest& core_request,
|
||||||
|
std::string* oemcrypto_core_message) {
|
||||||
|
ODK_ParsedProvisioning parsed_prov{};
|
||||||
|
widevine::ProvisioningResponse prov;
|
||||||
|
if (!prov.ParseFromString(serialized_provisioning_resp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed_prov.key_type =
|
||||||
|
OEMCrypto_RSA_Private_Key; // TODO(b/148404408): ECC or RSA
|
||||||
|
if (prov.has_device_rsa_key()) {
|
||||||
|
parsed_prov.enc_private_key =
|
||||||
|
GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key());
|
||||||
|
}
|
||||||
|
if (prov.has_device_rsa_key_iv()) {
|
||||||
|
parsed_prov.enc_private_key_iv =
|
||||||
|
GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key_iv());
|
||||||
|
}
|
||||||
|
if (prov.has_wrapping_key()) {
|
||||||
|
parsed_prov.encrypted_message_key =
|
||||||
|
GetOecSubstring(serialized_provisioning_resp, prov.wrapping_key());
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateCoreProvisioningResponse(parsed_prov, core_request,
|
||||||
|
oemcrypto_core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace serialize
|
||||||
|
} // namespace oemcrypto_core_message
|
||||||
24
common/oemcrypto_core_message/odk/src/kdo.gypi
Normal file
24
common/oemcrypto_core_message/odk/src/kdo.gypi
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
################################################################################
|
||||||
|
# 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.
|
||||||
|
################################################################################ This file and proprietary
|
||||||
|
# source code may only be used and distributed under the Widevine Master License
|
||||||
|
# Agreement.
|
||||||
|
|
||||||
|
# These files are used by the server and by some ODK test code. These files are
|
||||||
|
# not built into the ODK library on the device.
|
||||||
|
{
|
||||||
|
'sources': [
|
||||||
|
'core_message_deserialize.cpp',
|
||||||
|
'core_message_serialize.cpp',
|
||||||
|
'core_message_serialize_proto.cpp',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'src',
|
||||||
|
'../include',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
425
common/oemcrypto_core_message/odk/src/odk.c
Normal file
425
common/oemcrypto_core_message/odk/src/odk.c
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_structs.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_overflow.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_serialize.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_structs_priv.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_util.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/serialization_base.h"
|
||||||
|
|
||||||
|
/* @ private odk functions */
|
||||||
|
|
||||||
|
static OEMCryptoResult ODK_PrepareRequest(
|
||||||
|
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||||
|
ODK_MessageType message_type, const ODK_NonceValues* nonce_values,
|
||||||
|
void* prepared_request_buffer, size_t prepared_request_buffer_length) {
|
||||||
|
if (nonce_values == NULL || core_message_length == NULL ||
|
||||||
|
prepared_request_buffer == NULL ||
|
||||||
|
*core_message_length > message_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t blk[SIZE_OF_MESSAGE_STRUCT];
|
||||||
|
Message* msg = (Message*)blk;
|
||||||
|
InitMessage(msg, message, *core_message_length);
|
||||||
|
|
||||||
|
/* The core message should be at the beginning of the buffer, and with a
|
||||||
|
* shorter length. */
|
||||||
|
if (sizeof(ODK_CoreMessage) > prepared_request_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
ODK_CoreMessage* core_message = (ODK_CoreMessage*)prepared_request_buffer;
|
||||||
|
*core_message = (ODK_CoreMessage){
|
||||||
|
message_type,
|
||||||
|
0,
|
||||||
|
*nonce_values,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Set core message length, and pack prepared request into message if the
|
||||||
|
* message buffer has been correctly initialized by the caller. */
|
||||||
|
switch (message_type) {
|
||||||
|
case ODK_License_Request_Type: {
|
||||||
|
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
|
||||||
|
if (sizeof(ODK_PreparedLicenseRequest) > prepared_request_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
Pack_ODK_PreparedLicenseRequest(
|
||||||
|
msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_Renewal_Request_Type: {
|
||||||
|
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
|
||||||
|
if (sizeof(ODK_PreparedRenewalRequest) > prepared_request_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
Pack_ODK_PreparedRenewalRequest(
|
||||||
|
msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_Provisioning_Request_Type: {
|
||||||
|
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
|
||||||
|
if (sizeof(ODK_PreparedProvisioningRequest) >
|
||||||
|
prepared_request_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
Pack_ODK_PreparedProvisioningRequest(
|
||||||
|
msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*core_message_length = core_message->message_length;
|
||||||
|
if (GetStatus(msg) != MESSAGE_STATUS_OK) {
|
||||||
|
/* This is to indicate the caller that the core_message_length has been
|
||||||
|
* appropriately set, but the message buffer is either empty or too small,
|
||||||
|
* which needs to be initialized and filled in the subsequent call. */
|
||||||
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||||
|
}
|
||||||
|
if (GetSize(msg) != *core_message_length) {
|
||||||
|
/* This should not happen. Something is wrong. */
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OEMCryptoResult ODK_ParseResponse(
|
||||||
|
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||||
|
ODK_MessageType message_type, const ODK_NonceValues* nonce_values,
|
||||||
|
void* response_buffer, uint32_t response_buffer_length) {
|
||||||
|
if (message == NULL || response_buffer == NULL ||
|
||||||
|
core_message_length > message_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t blk[SIZE_OF_MESSAGE_STRUCT];
|
||||||
|
Message* msg = (Message*)blk;
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
/* We initialize the message buffer with a size of the entire message
|
||||||
|
* length. */
|
||||||
|
/* TODO(b/164486737): Fix the cast-qual warning */
|
||||||
|
InitMessage(msg, (uint8_t*)message, message_length);
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
/* The core message should be at the beginning of the buffer, and with a
|
||||||
|
* shorter length. The core message is the part we are parsing. */
|
||||||
|
SetSize(msg, core_message_length);
|
||||||
|
|
||||||
|
/* Parse message and unpack it into response buffer. */
|
||||||
|
switch (message_type) {
|
||||||
|
case ODK_License_Response_Type: {
|
||||||
|
if (sizeof(ODK_LicenseResponse) > response_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)response_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_Renewal_Response_Type: {
|
||||||
|
if (sizeof(ODK_RenewalResponse) > response_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)response_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_Provisioning_Response_Type: {
|
||||||
|
if (sizeof(ODK_ProvisioningResponse) > response_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
Unpack_ODK_ProvisioningResponse(
|
||||||
|
msg, (ODK_ProvisioningResponse*)response_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ODK_CoreMessage* core_message = (ODK_CoreMessage*)response_buffer;
|
||||||
|
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
|
||||||
|
message_type != core_message->message_type ||
|
||||||
|
GetOffset(msg) != core_message->message_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nonce_values) {
|
||||||
|
/* always verify nonce_values for Renewal and Provisioning responses */
|
||||||
|
if (!ODK_NonceValuesEqual(nonce_values, &(core_message->nonce_values))) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @ public odk functions */
|
||||||
|
|
||||||
|
/* @@ prepare request functions */
|
||||||
|
|
||||||
|
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||||
|
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||||
|
const ODK_NonceValues* nonce_values) {
|
||||||
|
if (core_message_length == NULL || nonce_values == NULL) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
ODK_PreparedLicenseRequest license_request = {
|
||||||
|
{0, 0, {}},
|
||||||
|
};
|
||||||
|
return ODK_PrepareRequest(
|
||||||
|
message, message_length, core_message_length, ODK_License_Request_Type,
|
||||||
|
nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
||||||
|
size_t message_length,
|
||||||
|
size_t* core_message_size,
|
||||||
|
ODK_NonceValues* nonce_values,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
uint64_t system_time_seconds) {
|
||||||
|
if (core_message_size == NULL || nonce_values == NULL ||
|
||||||
|
clock_values == NULL) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the license has not been loaded, then this is release instead of a
|
||||||
|
* renewal. All releases use v15. */
|
||||||
|
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED ||
|
||||||
|
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE) {
|
||||||
|
nonce_values->api_major_version = ODK_FIRST_VERSION - 1;
|
||||||
|
}
|
||||||
|
if (nonce_values->api_major_version < ODK_FIRST_VERSION) {
|
||||||
|
*core_message_size = 0;
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ODK_PreparedRenewalRequest renewal_request = {{0, 0, {}}, 0};
|
||||||
|
/* First, we compute the time this request was made relative to the playback
|
||||||
|
* clock. */
|
||||||
|
if (clock_values->time_of_first_decrypt == 0) {
|
||||||
|
/* It is OK to preemptively request a renewal before playback starts.
|
||||||
|
* We'll treat this as asking for a renewal at playback time 0. */
|
||||||
|
renewal_request.playback_time = 0;
|
||||||
|
} else {
|
||||||
|
/* Otherwise, playback_time is relative to the first decrypt. */
|
||||||
|
if (odk_sub_overflow_u64(system_time_seconds,
|
||||||
|
clock_values->time_of_first_decrypt,
|
||||||
|
&renewal_request.playback_time)) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save time for this request so that we can verify the response. This makes
|
||||||
|
* all earlier requests invalid. If preparing this request fails, then all
|
||||||
|
* requests will be bad. */
|
||||||
|
clock_values->time_of_renewal_request = renewal_request.playback_time;
|
||||||
|
|
||||||
|
return ODK_PrepareRequest(
|
||||||
|
message, message_length, core_message_size, ODK_Renewal_Request_Type,
|
||||||
|
nonce_values, &renewal_request, sizeof(ODK_PreparedRenewalRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||||
|
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||||
|
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||||
|
size_t device_id_length) {
|
||||||
|
if (core_message_length == NULL || nonce_values == NULL) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
ODK_PreparedProvisioningRequest provisioning_request = {
|
||||||
|
{0, 0, {}},
|
||||||
|
0,
|
||||||
|
{0},
|
||||||
|
};
|
||||||
|
if (device_id_length > sizeof(provisioning_request.device_id)) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
provisioning_request.device_id_length = (uint32_t)device_id_length;
|
||||||
|
if (device_id) {
|
||||||
|
memcpy(provisioning_request.device_id, device_id, device_id_length);
|
||||||
|
}
|
||||||
|
return ODK_PrepareRequest(message, message_length, core_message_length,
|
||||||
|
ODK_Provisioning_Request_Type, nonce_values,
|
||||||
|
&provisioning_request,
|
||||||
|
sizeof(ODK_PreparedProvisioningRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @@ parse response functions */
|
||||||
|
|
||||||
|
OEMCryptoResult ODK_ParseLicense(
|
||||||
|
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||||
|
bool initial_license_load, bool usage_entry_present,
|
||||||
|
const uint8_t* request_hash, ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values,
|
||||||
|
ODK_ParsedLicense* parsed_license) {
|
||||||
|
if (message == NULL || request_hash == NULL || timer_limits == NULL ||
|
||||||
|
clock_values == NULL || nonce_values == NULL || parsed_license == NULL) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ODK_LicenseResponse license_response = {{{0, 0, {}}}, NULL, {0}};
|
||||||
|
license_response.parsed_license = parsed_license;
|
||||||
|
|
||||||
|
const OEMCryptoResult err = ODK_ParseResponse(
|
||||||
|
message, message_length, core_message_length, ODK_License_Response_Type,
|
||||||
|
NULL, &license_response, sizeof(ODK_LicenseResponse));
|
||||||
|
|
||||||
|
if (err != OEMCrypto_SUCCESS) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We do not support future API version. Also, this function should not be
|
||||||
|
* used for legacy licenses. */
|
||||||
|
if (license_response.request.core_message.nonce_values.api_major_version >
|
||||||
|
ODK_MAJOR_VERSION ||
|
||||||
|
license_response.request.core_message.nonce_values.api_major_version <
|
||||||
|
ODK_FIRST_VERSION) {
|
||||||
|
return ODK_UNSUPPORTED_API;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the server sent us an older format, record the license's API version. */
|
||||||
|
if (nonce_values->api_major_version >
|
||||||
|
license_response.request.core_message.nonce_values.api_major_version) {
|
||||||
|
nonce_values->api_major_version =
|
||||||
|
license_response.request.core_message.nonce_values.api_major_version;
|
||||||
|
nonce_values->api_minor_version =
|
||||||
|
license_response.request.core_message.nonce_values.api_minor_version;
|
||||||
|
} else if (nonce_values->api_minor_version >
|
||||||
|
license_response.request.core_message.nonce_values
|
||||||
|
.api_minor_version) {
|
||||||
|
nonce_values->api_minor_version =
|
||||||
|
license_response.request.core_message.nonce_values.api_minor_version;
|
||||||
|
}
|
||||||
|
/* If the license has a provider session token (pst), then OEMCrypto should
|
||||||
|
* have a usage entry loaded. The opposite is also an error. */
|
||||||
|
if ((usage_entry_present && parsed_license->pst.length == 0) ||
|
||||||
|
(!usage_entry_present && parsed_license->pst.length > 0)) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed_license->nonce_required) {
|
||||||
|
if (initial_license_load) {
|
||||||
|
if (nonce_values->nonce !=
|
||||||
|
license_response.request.core_message.nonce_values.nonce ||
|
||||||
|
nonce_values->session_id !=
|
||||||
|
license_response.request.core_message.nonce_values.session_id) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||||
|
}
|
||||||
|
} else { /* !initial_license_load */
|
||||||
|
nonce_values->nonce =
|
||||||
|
license_response.request.core_message.nonce_values.nonce;
|
||||||
|
nonce_values->session_id =
|
||||||
|
license_response.request.core_message.nonce_values.session_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* For v16, in order to be backwards compatible with a v15 license server,
|
||||||
|
* OEMCrypto stores a hash of the core license request and only signs the
|
||||||
|
* message body. Here, when we process the license response, we verify that
|
||||||
|
* the server has the same hash of the core request. */
|
||||||
|
if (initial_license_load && parsed_license->nonce_required &&
|
||||||
|
crypto_memcmp(request_hash, license_response.request_hash,
|
||||||
|
ODK_SHA256_HASH_SIZE)) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
*timer_limits = parsed_license->timer_limits;
|
||||||
|
/* And update the clock values state. */
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
||||||
|
size_t core_message_length,
|
||||||
|
const ODK_NonceValues* nonce_values,
|
||||||
|
uint64_t system_time,
|
||||||
|
const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
uint64_t* timer_value) {
|
||||||
|
if (message == NULL || nonce_values == NULL || timer_limits == NULL ||
|
||||||
|
clock_values == NULL) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ODK_RenewalResponse renewal_response = {
|
||||||
|
{{0, 0, {}}, 0},
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
const OEMCryptoResult err = ODK_ParseResponse(
|
||||||
|
message, message_length, core_message_length, ODK_Renewal_Response_Type,
|
||||||
|
nonce_values, &renewal_response, sizeof(ODK_RenewalResponse));
|
||||||
|
|
||||||
|
if (err != OEMCrypto_SUCCESS) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reference:
|
||||||
|
* Doc: License Duration and Renewal (Changes for OEMCrypto v16)
|
||||||
|
* Section: Renewal Message
|
||||||
|
*/
|
||||||
|
/* If a renewal request is lost in transit, we should throw it out and create
|
||||||
|
* a new one. We use the timestamp to make sure we have the latest request.
|
||||||
|
*/
|
||||||
|
if (clock_values->time_of_renewal_request <
|
||||||
|
renewal_response.request.playback_time) {
|
||||||
|
return ODK_STALE_RENEWAL;
|
||||||
|
}
|
||||||
|
return ODK_ComputeRenewalDuration(timer_limits, clock_values, system_time,
|
||||||
|
renewal_response.renewal_duration_seconds,
|
||||||
|
timer_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult ODK_ParseProvisioning(
|
||||||
|
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||||
|
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||||
|
size_t device_id_length, ODK_ParsedProvisioning* parsed_response) {
|
||||||
|
if (message == NULL || nonce_values == NULL || device_id == NULL ||
|
||||||
|
parsed_response == NULL) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ODK_ProvisioningResponse provisioning_response = {{{0, 0, {}}, 0, {0}}, NULL};
|
||||||
|
provisioning_response.parsed_provisioning = parsed_response;
|
||||||
|
|
||||||
|
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OEMCryptoResult err = ODK_ParseResponse(
|
||||||
|
message, message_length, core_message_length,
|
||||||
|
ODK_Provisioning_Response_Type, nonce_values, &provisioning_response,
|
||||||
|
sizeof(ODK_ProvisioningResponse));
|
||||||
|
|
||||||
|
if (err != OEMCrypto_SUCCESS) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crypto_memcmp(device_id, provisioning_response.request.device_id,
|
||||||
|
device_id_length) != 0) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||||
|
/* check bytes beyond device_id_length are 0 */
|
||||||
|
if (crypto_memcmp(zero,
|
||||||
|
provisioning_response.request.device_id + device_id_length,
|
||||||
|
ODK_DEVICE_ID_LEN_MAX - device_id_length) != 0) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
30
common/oemcrypto_core_message/odk/src/odk.gyp
Normal file
30
common/oemcrypto_core_message/odk/src/odk.gyp
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.
|
||||||
|
################################################################################ This file and proprietary
|
||||||
|
# source code may only be used and distributed under the Widevine Master License
|
||||||
|
# Agreement.
|
||||||
|
|
||||||
|
{
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'odk',
|
||||||
|
'type': 'static_library',
|
||||||
|
'include_dirs': [
|
||||||
|
'../include',
|
||||||
|
'../../include',
|
||||||
|
],
|
||||||
|
'includes' : [
|
||||||
|
'odk.gypi',
|
||||||
|
],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'include_dirs': [
|
||||||
|
'../include',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
23
common/oemcrypto_core_message/odk/src/odk.gypi
Normal file
23
common/oemcrypto_core_message/odk/src/odk.gypi
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
################################################################################
|
||||||
|
# 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.
|
||||||
|
################################################################################ This file and proprietary
|
||||||
|
# source code may only be used and distributed under the Widevine Master License
|
||||||
|
# Agreement.
|
||||||
|
|
||||||
|
# These files are built into the ODK library on the device. They are also used
|
||||||
|
# by the server and by test cocde. These files should compile on C99 compilers.
|
||||||
|
{
|
||||||
|
'sources': [
|
||||||
|
'odk.c',
|
||||||
|
'odk_overflow.c',
|
||||||
|
'odk_serialize.c',
|
||||||
|
'odk_timer.c',
|
||||||
|
'odk_util.c',
|
||||||
|
'serialization_base.c',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
30
common/oemcrypto_core_message/odk/src/odk_assert.h
Normal file
30
common/oemcrypto_core_message/odk/src/odk_assert.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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ASSERT_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ASSERT_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (__STDC_VERSION__ >= 201112L)
|
||||||
|
#include <assert.h>
|
||||||
|
#define odk_static_assert static_assert
|
||||||
|
#else
|
||||||
|
#define odk_static_assert(msg, e) \
|
||||||
|
enum { odk_static_assert = 1 / (!!((msg) && (e))) };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ASSERT_H_
|
||||||
35
common/oemcrypto_core_message/odk/src/odk_endian.h
Normal file
35
common/oemcrypto_core_message/odk/src/odk_endian.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ENDIAN_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ENDIAN_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__ANDROID__)
|
||||||
|
#include <endian.h>
|
||||||
|
#define oemcrypto_htobe32 htobe32
|
||||||
|
#define oemcrypto_be32toh be32toh
|
||||||
|
#define oemcrypto_htobe64 htobe64
|
||||||
|
#define oemcrypto_be64toh be64toh
|
||||||
|
#else /* defined(__linux__) || defined(__ANDROID__) */
|
||||||
|
uint32_t oemcrypto_htobe32(uint32_t u32);
|
||||||
|
uint32_t oemcrypto_be32toh(uint32_t u32);
|
||||||
|
uint64_t oemcrypto_htobe64(uint64_t u64);
|
||||||
|
uint64_t oemcrypto_be64toh(uint64_t u64);
|
||||||
|
#endif /* defined(__linux__) || defined(__ANDROID__) */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ENDIAN_H_
|
||||||
42
common/oemcrypto_core_message/odk/src/odk_overflow.c
Normal file
42
common/oemcrypto_core_message/odk/src/odk_overflow.c
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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
|
||||||
|
if (a >= b) {
|
||||||
|
if (c) {
|
||||||
|
*c = a - b;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
|
||||||
|
if (UINT64_MAX - a >= b) {
|
||||||
|
if (c) {
|
||||||
|
*c = a + b;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int odk_add_overflow_ux(size_t a, size_t b, size_t* c) {
|
||||||
|
if (SIZE_MAX - a >= b) {
|
||||||
|
if (c) {
|
||||||
|
*c = a + b;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
29
common/oemcrypto_core_message/odk/src/odk_overflow.h
Normal file
29
common/oemcrypto_core_message/odk/src/odk_overflow.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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_OVERFLOW_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_OVERFLOW_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
|
||||||
|
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
|
||||||
|
int odk_add_overflow_ux(size_t a, size_t b, size_t* c);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_OVERFLOW_H_
|
||||||
223
common/oemcrypto_core_message/odk/src/odk_serialize.c
Normal file
223
common/oemcrypto_core_message/odk/src/odk_serialize.c
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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code is auto-generated, do not edit
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_structs_priv.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/serialization_base.h"
|
||||||
|
|
||||||
|
/* @ serialize */
|
||||||
|
|
||||||
|
/* @@ private serialize */
|
||||||
|
|
||||||
|
static void Pack_ODK_NonceValues(Message* msg, ODK_NonceValues const* obj) {
|
||||||
|
Pack_uint16_t(msg, &obj->api_minor_version);
|
||||||
|
Pack_uint16_t(msg, &obj->api_major_version);
|
||||||
|
Pack_uint32_t(msg, &obj->nonce);
|
||||||
|
Pack_uint32_t(msg, &obj->session_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Pack_ODK_CoreMessage(Message* msg, ODK_CoreMessage const* obj) {
|
||||||
|
Pack_uint32_t(msg, &obj->message_type);
|
||||||
|
Pack_uint32_t(msg, &obj->message_length);
|
||||||
|
Pack_ODK_NonceValues(msg, &obj->nonce_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Pack_OEMCrypto_KeyObject(Message* msg,
|
||||||
|
OEMCrypto_KeyObject const* obj) {
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->key_id);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->key_data_iv);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->key_data);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->key_control_iv);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->key_control);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) {
|
||||||
|
Pack_bool(msg, &obj->soft_enforce_rental_duration);
|
||||||
|
Pack_bool(msg, &obj->soft_enforce_playback_duration);
|
||||||
|
Pack_uint64_t(msg, &obj->earliest_playback_start_seconds);
|
||||||
|
Pack_uint64_t(msg, &obj->rental_duration_seconds);
|
||||||
|
Pack_uint64_t(msg, &obj->total_playback_duration_seconds);
|
||||||
|
Pack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
|
||||||
|
/* hand-coded */
|
||||||
|
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
|
||||||
|
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->pst);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
|
||||||
|
Pack_enum(msg, obj->license_type);
|
||||||
|
Pack_bool(msg, &obj->nonce_required);
|
||||||
|
Pack_ODK_TimerLimits(msg, &obj->timer_limits);
|
||||||
|
Pack_uint32_t(msg, &obj->key_array_length);
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < (size_t)obj->key_array_length; i++) {
|
||||||
|
Pack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Pack_ODK_ParsedProvisioning(Message* msg,
|
||||||
|
ODK_ParsedProvisioning const* obj) {
|
||||||
|
Pack_enum(msg, obj->key_type);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
|
||||||
|
Pack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @@ odk serialize */
|
||||||
|
|
||||||
|
void Pack_ODK_PreparedLicenseRequest(Message* msg,
|
||||||
|
ODK_PreparedLicenseRequest const* obj) {
|
||||||
|
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_ODK_PreparedRenewalRequest(Message* msg,
|
||||||
|
ODK_PreparedRenewalRequest const* obj) {
|
||||||
|
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||||
|
Pack_uint64_t(msg, &obj->playback_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_ODK_PreparedProvisioningRequest(
|
||||||
|
Message* msg, ODK_PreparedProvisioningRequest const* obj) {
|
||||||
|
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||||
|
Pack_uint32_t(msg, &obj->device_id_length);
|
||||||
|
PackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @@ kdo serialize */
|
||||||
|
|
||||||
|
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj) {
|
||||||
|
Pack_ODK_PreparedLicenseRequest(msg, &obj->request);
|
||||||
|
Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license);
|
||||||
|
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse const* obj) {
|
||||||
|
Pack_ODK_PreparedRenewalRequest(msg, &obj->request);
|
||||||
|
Pack_uint64_t(msg, &obj->renewal_duration_seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_ODK_ProvisioningResponse(Message* msg,
|
||||||
|
ODK_ProvisioningResponse const* obj) {
|
||||||
|
Pack_ODK_PreparedProvisioningRequest(msg, &obj->request);
|
||||||
|
Pack_ODK_ParsedProvisioning(
|
||||||
|
msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @ deserialize */
|
||||||
|
|
||||||
|
/* @@ private deserialize */
|
||||||
|
|
||||||
|
static void Unpack_ODK_NonceValues(Message* msg, ODK_NonceValues* obj) {
|
||||||
|
Unpack_uint16_t(msg, &obj->api_minor_version);
|
||||||
|
Unpack_uint16_t(msg, &obj->api_major_version);
|
||||||
|
Unpack_uint32_t(msg, &obj->nonce);
|
||||||
|
Unpack_uint32_t(msg, &obj->session_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Unpack_ODK_CoreMessage(Message* msg, ODK_CoreMessage* obj) {
|
||||||
|
Unpack_uint32_t(msg, &obj->message_type);
|
||||||
|
Unpack_uint32_t(msg, &obj->message_length);
|
||||||
|
Unpack_ODK_NonceValues(msg, &obj->nonce_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) {
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->key_id);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->key_data_iv);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->key_data);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->key_control_iv);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->key_control);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) {
|
||||||
|
Unpack_bool(msg, &obj->soft_enforce_rental_duration);
|
||||||
|
Unpack_bool(msg, &obj->soft_enforce_playback_duration);
|
||||||
|
Unpack_uint64_t(msg, &obj->earliest_playback_start_seconds);
|
||||||
|
Unpack_uint64_t(msg, &obj->rental_duration_seconds);
|
||||||
|
Unpack_uint64_t(msg, &obj->total_playback_duration_seconds);
|
||||||
|
Unpack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->pst);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
|
||||||
|
obj->license_type = (OEMCrypto_LicenseType)Unpack_enum(msg);
|
||||||
|
Unpack_bool(msg, &obj->nonce_required);
|
||||||
|
Unpack_ODK_TimerLimits(msg, &obj->timer_limits);
|
||||||
|
Unpack_uint32_t(msg, &obj->key_array_length);
|
||||||
|
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
|
||||||
|
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < obj->key_array_length; i++) {
|
||||||
|
Unpack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Unpack_ODK_ParsedProvisioning(Message* msg,
|
||||||
|
ODK_ParsedProvisioning* obj) {
|
||||||
|
obj->key_type = (OEMCrypto_PrivateKeyType)Unpack_enum(msg);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
|
||||||
|
Unpack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @ kdo deserialize */
|
||||||
|
|
||||||
|
void Unpack_ODK_PreparedLicenseRequest(Message* msg,
|
||||||
|
ODK_PreparedLicenseRequest* obj) {
|
||||||
|
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_ODK_PreparedRenewalRequest(Message* msg,
|
||||||
|
ODK_PreparedRenewalRequest* obj) {
|
||||||
|
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||||
|
Unpack_uint64_t(msg, &obj->playback_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_ODK_PreparedProvisioningRequest(
|
||||||
|
Message* msg, ODK_PreparedProvisioningRequest* obj) {
|
||||||
|
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||||
|
Unpack_uint32_t(msg, &obj->device_id_length);
|
||||||
|
UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_ODK_PreparedCommonRequest(Message* msg,
|
||||||
|
ODK_PreparedCommonRequest* obj) {
|
||||||
|
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||||
|
}
|
||||||
|
/* @@ odk deserialize */
|
||||||
|
|
||||||
|
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) {
|
||||||
|
Unpack_ODK_PreparedLicenseRequest(msg, &obj->request);
|
||||||
|
Unpack_ODK_ParsedLicense(msg, obj->parsed_license);
|
||||||
|
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj) {
|
||||||
|
Unpack_ODK_PreparedRenewalRequest(msg, &obj->request);
|
||||||
|
Unpack_uint64_t(msg, &obj->renewal_duration_seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_ODK_ProvisioningResponse(Message* msg,
|
||||||
|
ODK_ProvisioningResponse* obj) {
|
||||||
|
Unpack_ODK_PreparedProvisioningRequest(msg, &obj->request);
|
||||||
|
Unpack_ODK_ParsedProvisioning(msg, obj->parsed_provisioning);
|
||||||
|
}
|
||||||
58
common/oemcrypto_core_message/odk/src/odk_serialize.h
Normal file
58
common/oemcrypto_core_message/odk/src/odk_serialize.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code is auto-generated, do not edit
|
||||||
|
*/
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_SERIALIZE_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_SERIALIZE_H_
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_structs_priv.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/serialization_base.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* odk pack */
|
||||||
|
void Pack_ODK_PreparedLicenseRequest(Message* msg,
|
||||||
|
const ODK_PreparedLicenseRequest* obj);
|
||||||
|
void Pack_ODK_PreparedRenewalRequest(Message* msg,
|
||||||
|
const ODK_PreparedRenewalRequest* obj);
|
||||||
|
void Pack_ODK_PreparedProvisioningRequest(
|
||||||
|
Message* msg, const ODK_PreparedProvisioningRequest* obj);
|
||||||
|
|
||||||
|
/* odk unpack */
|
||||||
|
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj);
|
||||||
|
void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj);
|
||||||
|
void Unpack_ODK_ProvisioningResponse(Message* msg,
|
||||||
|
ODK_ProvisioningResponse* obj);
|
||||||
|
|
||||||
|
/* kdo pack */
|
||||||
|
void Pack_ODK_LicenseResponse(Message* msg, const ODK_LicenseResponse* obj);
|
||||||
|
void Pack_ODK_RenewalResponse(Message* msg, const ODK_RenewalResponse* obj);
|
||||||
|
void Pack_ODK_ProvisioningResponse(Message* msg,
|
||||||
|
const ODK_ProvisioningResponse* obj);
|
||||||
|
|
||||||
|
/* kdo unpack */
|
||||||
|
void Unpack_ODK_PreparedLicenseRequest(Message* msg,
|
||||||
|
ODK_PreparedLicenseRequest* obj);
|
||||||
|
void Unpack_ODK_PreparedRenewalRequest(Message* msg,
|
||||||
|
ODK_PreparedRenewalRequest* obj);
|
||||||
|
void Unpack_ODK_PreparedProvisioningRequest(
|
||||||
|
Message* msg, ODK_PreparedProvisioningRequest* obj);
|
||||||
|
|
||||||
|
void Unpack_ODK_PreparedCommonRequest(Message* msg,
|
||||||
|
ODK_PreparedCommonRequest* obj);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_SERIALIZE_H_
|
||||||
115
common/oemcrypto_core_message/odk/src/odk_structs_priv.h
Normal file
115
common/oemcrypto_core_message/odk/src/odk_structs_priv.h
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_STRUCTS_PRIV_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_STRUCTS_PRIV_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/OEMCryptoCENCCommon.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_structs.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ODK_License_Request_Type = 1,
|
||||||
|
ODK_License_Response_Type = 2,
|
||||||
|
ODK_Renewal_Request_Type = 3,
|
||||||
|
ODK_Renewal_Response_Type = 4,
|
||||||
|
ODK_Provisioning_Request_Type = 5,
|
||||||
|
ODK_Provisioning_Response_Type = 6,
|
||||||
|
|
||||||
|
// Reserve future message types to support forward compatibility.
|
||||||
|
ODK_Release_Request_Type = 7,
|
||||||
|
ODK_Release_Response_Type = 8,
|
||||||
|
ODK_Common_Request_Type = 9,
|
||||||
|
ODK_Common_Response_Type = 10,
|
||||||
|
} ODK_MessageType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t message_type;
|
||||||
|
uint32_t message_length;
|
||||||
|
ODK_NonceValues nonce_values;
|
||||||
|
} ODK_CoreMessage;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ODK_CoreMessage core_message;
|
||||||
|
} ODK_PreparedLicenseRequest;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ODK_CoreMessage core_message;
|
||||||
|
uint64_t playback_time;
|
||||||
|
} ODK_PreparedRenewalRequest;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ODK_CoreMessage core_message;
|
||||||
|
uint32_t device_id_length;
|
||||||
|
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
|
||||||
|
} ODK_PreparedProvisioningRequest;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ODK_CoreMessage core_message;
|
||||||
|
} ODK_PreparedCommonRequest;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ODK_PreparedLicenseRequest request;
|
||||||
|
ODK_ParsedLicense* parsed_license;
|
||||||
|
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
|
||||||
|
} ODK_LicenseResponse;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ODK_PreparedRenewalRequest request;
|
||||||
|
uint64_t renewal_duration_seconds;
|
||||||
|
} ODK_RenewalResponse;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ODK_PreparedProvisioningRequest request;
|
||||||
|
ODK_ParsedProvisioning* parsed_provisioning;
|
||||||
|
} ODK_ProvisioningResponse;
|
||||||
|
|
||||||
|
// These are the sum of sizeof of each individual member of the request structs
|
||||||
|
// without any padding added by the compiler. Make sure they get updated when
|
||||||
|
// request structs change. Refer to test suite OdkSizeTest in
|
||||||
|
// ../test/odk_test.cpp for validations of each of the defined request sizes.
|
||||||
|
#define ODK_LICENSE_REQUEST_SIZE 20
|
||||||
|
#define ODK_RENEWAL_REQUEST_SIZE 28
|
||||||
|
#define ODK_PROVISIONING_REQUEST_SIZE 88
|
||||||
|
|
||||||
|
// These are the possible timer status values.
|
||||||
|
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 // Should not happen.
|
||||||
|
// When the structure has been initialized, but no license is loaded.
|
||||||
|
#define ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED 1
|
||||||
|
// After the license is loaded, before a successful decrypt.
|
||||||
|
#define ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED 2
|
||||||
|
// After the license is loaded, if a renewal has also been loaded.
|
||||||
|
#define ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED 3
|
||||||
|
// The first decrypt has occurred and the timer is active.
|
||||||
|
#define ODK_CLOCK_TIMER_STATUS_ACTIVE 4
|
||||||
|
// The first decrypt has occurred and the timer is unlimited.
|
||||||
|
#define ODK_CLOCK_TIMER_STATUS_UNLIMITED 5
|
||||||
|
// The timer has transitioned from active to expired.
|
||||||
|
#define ODK_CLOCK_TIMER_STATUS_EXPIRED 6
|
||||||
|
// The license has been marked as inactive.
|
||||||
|
#define ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE 7
|
||||||
|
|
||||||
|
// A helper function for computing timer limits when a renewal is loaded.
|
||||||
|
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
uint64_t system_time_seconds,
|
||||||
|
uint64_t new_renewal_duration,
|
||||||
|
uint64_t* timer_value);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_STRUCTS_PRIV_H_
|
||||||
509
common/oemcrypto_core_message/odk/src/odk_timer.c
Normal file
509
common/oemcrypto_core_message/odk/src/odk_timer.c
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_attributes.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_overflow.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_structs_priv.h"
|
||||||
|
|
||||||
|
/* Private function. Checks to see if the license is active. Returns
|
||||||
|
* ODK_TIMER_EXPIRED if the license is valid but inactive. Returns
|
||||||
|
* OEMCrypto_SUCCESS if the license is active. Returns
|
||||||
|
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. */
|
||||||
|
static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values) {
|
||||||
|
/* Check some basic errors. */
|
||||||
|
if (clock_values == NULL || timer_limits == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
/* Check if the license has not been loaded yet. */
|
||||||
|
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_UNDEFINED ||
|
||||||
|
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED) {
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
if (clock_values->status > kActive) {
|
||||||
|
return ODK_TIMER_EXPIRED;
|
||||||
|
}
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private function. Sets the timer_value to be the min(timer_value, new_value),
|
||||||
|
* with the convention that 0 means infinite. The convention that 0 means
|
||||||
|
* infinite is used for all Widevine license and duration values. */
|
||||||
|
static void ComputeMinimum(uint64_t* timer_value, uint64_t new_value) {
|
||||||
|
if (timer_value == NULL) return;
|
||||||
|
if (new_value > 0) {
|
||||||
|
if (*timer_value == 0 || *timer_value > new_value) {
|
||||||
|
*timer_value = new_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private function. Check to see if the rental window restricts playback. If
|
||||||
|
* the rental enforcement is hard, or if this is the first playback, then we
|
||||||
|
* verify that system_time_seconds is within the rental window. If the
|
||||||
|
* enforcement is soft and we have already started playback, then there is no
|
||||||
|
* restriction.
|
||||||
|
* Return ODK_TIMER_EXPIRED if out of the window.
|
||||||
|
* Return ODK_TIMER_ACTIVE if within the window, and there is a hard limit.
|
||||||
|
* Return ODK_DISABLE_TIMER if no there should be no limit.
|
||||||
|
* Return other error on error.
|
||||||
|
* Also, if this function does compute a limit, the timer_value is reduced to
|
||||||
|
* obey that limit. If the limit is less restrictive than the current
|
||||||
|
* timer_value, then timer_value is not changed. */
|
||||||
|
static OEMCryptoResult ODK_CheckRentalWindow(
|
||||||
|
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
|
||||||
|
uint64_t system_time_seconds, uint64_t* timer_value) {
|
||||||
|
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
/* If playback has already started, and rental duration enforcement is soft,
|
||||||
|
* then there is no restriction. */
|
||||||
|
if (clock_values->time_of_first_decrypt > 0 &&
|
||||||
|
timer_limits->soft_enforce_rental_duration) {
|
||||||
|
return ODK_DISABLE_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rental_clock = time since license signed. */
|
||||||
|
uint64_t rental_clock = 0;
|
||||||
|
if (odk_sub_overflow_u64(system_time_seconds,
|
||||||
|
clock_values->time_of_license_signed,
|
||||||
|
&rental_clock)) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
/* Check if it is before license is valid. This is an unusual case. First
|
||||||
|
* playback may still work if it occurs after the rental window opens. */
|
||||||
|
if (rental_clock < timer_limits->earliest_playback_start_seconds) {
|
||||||
|
return ODK_TIMER_EXPIRED;
|
||||||
|
}
|
||||||
|
/* If the rental duration is 0, there is no limit. */
|
||||||
|
if (timer_limits->rental_duration_seconds == 0) {
|
||||||
|
return ODK_DISABLE_TIMER;
|
||||||
|
}
|
||||||
|
/* End of rental window, based on rental clock (not system time). */
|
||||||
|
uint64_t end_of_rental_window = 0;
|
||||||
|
if (odk_add_overflow_u64(timer_limits->earliest_playback_start_seconds,
|
||||||
|
timer_limits->rental_duration_seconds,
|
||||||
|
&end_of_rental_window)) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
if (end_of_rental_window <= rental_clock) {
|
||||||
|
return ODK_TIMER_EXPIRED;
|
||||||
|
}
|
||||||
|
/* At this point system_time is within the rental window. */
|
||||||
|
if (timer_limits->soft_enforce_rental_duration) {
|
||||||
|
/* For soft enforcement, we allow playback, and do not adjust the timer. */
|
||||||
|
return ODK_DISABLE_TIMER;
|
||||||
|
}
|
||||||
|
uint64_t time_left = 0;
|
||||||
|
if (odk_sub_overflow_u64(end_of_rental_window, rental_clock, &time_left)) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
ComputeMinimum(timer_value, time_left);
|
||||||
|
return ODK_SET_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private function. Check to see if the playback window restricts
|
||||||
|
* playback. This should only be called if playback has started, so that
|
||||||
|
* clock_values->time_of_first_decrypt is nonzero.
|
||||||
|
* Return ODK_TIMER_EXPIRED if out of the window.
|
||||||
|
* Return ODK_SET_TIMER if within the window, and there is a hard limit.
|
||||||
|
* Return ODK_DISABLE_TIMER if no limit.
|
||||||
|
* Return other error on error.
|
||||||
|
* Also, if this function does compute a limit, the timer_value is reduced to
|
||||||
|
* obey that limit. If the limit is less restrictive than the current
|
||||||
|
* timer_value, then timer_value is not changed. */
|
||||||
|
static OEMCryptoResult ODK_CheckPlaybackWindow(
|
||||||
|
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
|
||||||
|
uint64_t system_time_seconds, uint64_t* timer_value) {
|
||||||
|
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
/* if the playback duration is 0, there is no limit. */
|
||||||
|
if (timer_limits->total_playback_duration_seconds == 0) {
|
||||||
|
return ODK_DISABLE_TIMER;
|
||||||
|
}
|
||||||
|
uint64_t end_of_playback_window = 0;
|
||||||
|
if (odk_add_overflow_u64(timer_limits->total_playback_duration_seconds,
|
||||||
|
clock_values->time_of_first_decrypt,
|
||||||
|
&end_of_playback_window)) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
if (end_of_playback_window <= system_time_seconds) {
|
||||||
|
return ODK_TIMER_EXPIRED;
|
||||||
|
}
|
||||||
|
/* At this point, system_time is within the total playback window. */
|
||||||
|
if (timer_limits->soft_enforce_playback_duration) {
|
||||||
|
/* For soft enforcement, we allow playback, and do not adjust the timer. */
|
||||||
|
return ODK_DISABLE_TIMER;
|
||||||
|
}
|
||||||
|
uint64_t time_left = 0;
|
||||||
|
if (odk_sub_overflow_u64(end_of_playback_window, system_time_seconds,
|
||||||
|
&time_left)) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
ComputeMinimum(timer_value, time_left);
|
||||||
|
return ODK_SET_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the timer status. If playback has already started, we use the given
|
||||||
|
* status. However, if playback has not yet started, then we expect a call to
|
||||||
|
* ODK_AttemptFirstPlayback in the future, and we need to signal to it that we
|
||||||
|
* have already computed the timer limit. */
|
||||||
|
static void ODK_UpdateTimerStatusForRenewal(ODK_ClockValues* clock_values,
|
||||||
|
uint32_t new_status) {
|
||||||
|
if (clock_values == NULL) {
|
||||||
|
return; /* should not happen. */
|
||||||
|
}
|
||||||
|
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED) {
|
||||||
|
/* Signal that the timer is already set. */
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED;
|
||||||
|
} else {
|
||||||
|
clock_values->timer_status = new_status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private function, but accessed from odk.c so cannot be static. This checks to
|
||||||
|
* see if a renewal message should restart the playback timer and sets the value
|
||||||
|
* appropriately. */
|
||||||
|
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
uint64_t system_time_seconds,
|
||||||
|
uint64_t new_renewal_duration,
|
||||||
|
uint64_t* timer_value) {
|
||||||
|
if (timer_limits == NULL || clock_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT; /* should not happen. */
|
||||||
|
}
|
||||||
|
/* If this is before the license was signed, something is odd. Return an
|
||||||
|
* error. */
|
||||||
|
if (system_time_seconds < clock_values->time_of_license_signed) {
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OEMCryptoResult license_status =
|
||||||
|
ODK_LicenseActive(timer_limits, clock_values);
|
||||||
|
/* If the license is not active, then we cannot renew the license. */
|
||||||
|
if (license_status != OEMCrypto_SUCCESS) {
|
||||||
|
return license_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We start with the new renewal duration as the new timer limit. */
|
||||||
|
uint64_t new_timer_value = new_renewal_duration;
|
||||||
|
|
||||||
|
/* Then we factor in the rental window restrictions. This might decrease
|
||||||
|
* new_timer_value. */
|
||||||
|
const OEMCryptoResult rental_status = ODK_CheckRentalWindow(
|
||||||
|
timer_limits, clock_values, system_time_seconds, &new_timer_value);
|
||||||
|
|
||||||
|
/* If the rental status forbids playback, then we're done. */
|
||||||
|
if ((rental_status != ODK_DISABLE_TIMER) &&
|
||||||
|
(rental_status != ODK_SET_TIMER)) {
|
||||||
|
return rental_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If playback has already started and it has hard enforcement, then check
|
||||||
|
* total playback window. */
|
||||||
|
if (clock_values->time_of_first_decrypt > 0 &&
|
||||||
|
!timer_limits->soft_enforce_playback_duration) {
|
||||||
|
/* This might decrease new_timer_value. */
|
||||||
|
const OEMCryptoResult playback_status = ODK_CheckPlaybackWindow(
|
||||||
|
timer_limits, clock_values, system_time_seconds, &new_timer_value);
|
||||||
|
/* If the timer limits forbid playback in the playback window, then we're
|
||||||
|
* done. */
|
||||||
|
if ((playback_status != ODK_DISABLE_TIMER) &&
|
||||||
|
(playback_status != ODK_SET_TIMER)) {
|
||||||
|
return playback_status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If new_timer_value is infinite (represented by 0), then there are no
|
||||||
|
* limits, so we can return now. */
|
||||||
|
if (new_timer_value == 0) {
|
||||||
|
clock_values->time_when_timer_expires = 0;
|
||||||
|
ODK_UpdateTimerStatusForRenewal(clock_values,
|
||||||
|
ODK_CLOCK_TIMER_STATUS_UNLIMITED);
|
||||||
|
return ODK_DISABLE_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the caller gave us a pointer to store the new timer value. Fill it. */
|
||||||
|
if (timer_value != NULL) {
|
||||||
|
*timer_value = new_timer_value;
|
||||||
|
}
|
||||||
|
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
|
||||||
|
&clock_values->time_when_timer_expires)) {
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
ODK_UpdateTimerStatusForRenewal(clock_values, ODK_CLOCK_TIMER_STATUS_ACTIVE);
|
||||||
|
return ODK_SET_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/************************************************************************/
|
||||||
|
/* Public functions, declared in odk.h. */
|
||||||
|
|
||||||
|
/* This is called when OEMCrypto opens a new session. */
|
||||||
|
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
ODK_NonceValues* nonce_values,
|
||||||
|
uint32_t api_major_version,
|
||||||
|
uint32_t session_id) {
|
||||||
|
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
/* Check that the API version passed in from OEMCrypto matches the version of
|
||||||
|
* this ODK library. */
|
||||||
|
if (api_major_version != ODK_MAJOR_VERSION) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
timer_limits->soft_enforce_rental_duration = false;
|
||||||
|
timer_limits->soft_enforce_playback_duration = false;
|
||||||
|
timer_limits->earliest_playback_start_seconds = 0;
|
||||||
|
timer_limits->rental_duration_seconds = 0;
|
||||||
|
timer_limits->total_playback_duration_seconds = 0;
|
||||||
|
timer_limits->initial_renewal_duration_seconds = 0;
|
||||||
|
|
||||||
|
ODK_InitializeClockValues(clock_values, 0);
|
||||||
|
|
||||||
|
nonce_values->api_major_version = ODK_MAJOR_VERSION;
|
||||||
|
nonce_values->api_minor_version = ODK_MINOR_VERSION;
|
||||||
|
nonce_values->nonce = 0;
|
||||||
|
nonce_values->session_id = session_id;
|
||||||
|
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called when OEMCrypto generates a new nonce in
|
||||||
|
* OEMCrypto_GenerateNonce. */
|
||||||
|
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
|
||||||
|
uint32_t nonce) {
|
||||||
|
if (nonce_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
/* Setting the nonce should only happen once per session. */
|
||||||
|
if (nonce_values->nonce != 0) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
nonce_values->nonce = nonce;
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called when OEMCrypto signs a license. */
|
||||||
|
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
|
||||||
|
uint64_t system_time_seconds) {
|
||||||
|
if (clock_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
clock_values->time_of_license_signed = system_time_seconds;
|
||||||
|
clock_values->time_of_first_decrypt = 0;
|
||||||
|
clock_values->time_of_last_decrypt = 0;
|
||||||
|
clock_values->time_when_timer_expires = 0;
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
|
||||||
|
clock_values->status = kUnused;
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called when OEMCrypto reloads a usage entry. */
|
||||||
|
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
|
||||||
|
uint64_t time_of_license_signed,
|
||||||
|
uint64_t time_of_first_decrypt,
|
||||||
|
uint64_t time_of_last_decrypt,
|
||||||
|
enum OEMCrypto_Usage_Entry_Status status,
|
||||||
|
uint64_t system_time_seconds UNUSED) {
|
||||||
|
if (clock_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
clock_values->time_of_license_signed = time_of_license_signed;
|
||||||
|
clock_values->time_of_first_decrypt = time_of_first_decrypt;
|
||||||
|
clock_values->time_of_last_decrypt = time_of_last_decrypt;
|
||||||
|
clock_values->time_when_timer_expires = 0;
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
|
||||||
|
clock_values->status = status;
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called on the first playback for a session. */
|
||||||
|
OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
||||||
|
const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
uint64_t* timer_value) {
|
||||||
|
if (clock_values == NULL || timer_limits == NULL) {
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
/* All times are relative to when the license was signed. */
|
||||||
|
uint64_t rental_time = 0;
|
||||||
|
if (odk_sub_overflow_u64(system_time_seconds,
|
||||||
|
clock_values->time_of_license_signed,
|
||||||
|
&rental_time)) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
if (rental_time < timer_limits->earliest_playback_start_seconds) {
|
||||||
|
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||||
|
return ODK_TIMER_EXPIRED;
|
||||||
|
}
|
||||||
|
/* If the license is inactive or not loaded, then playback is not allowed. */
|
||||||
|
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
|
||||||
|
if (status != OEMCrypto_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We start with the initial renewal duration as the timer limit. */
|
||||||
|
uint64_t new_timer_value = timer_limits->initial_renewal_duration_seconds;
|
||||||
|
/* However, if a renewal was loaded before this first playback, use the
|
||||||
|
* previously computed limit. */
|
||||||
|
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED) {
|
||||||
|
if (clock_values->time_when_timer_expires <= system_time_seconds) {
|
||||||
|
return ODK_TIMER_EXPIRED;
|
||||||
|
}
|
||||||
|
if (odk_sub_overflow_u64(clock_values->time_when_timer_expires,
|
||||||
|
system_time_seconds, &new_timer_value)) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then we factor in the rental window restrictions. This might decrease
|
||||||
|
* new_timer_value. */
|
||||||
|
status = ODK_CheckRentalWindow(timer_limits, clock_values,
|
||||||
|
system_time_seconds, &new_timer_value);
|
||||||
|
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If playback has not already started, then this is the first playback. */
|
||||||
|
if (clock_values->time_of_first_decrypt == 0) {
|
||||||
|
clock_values->time_of_first_decrypt = system_time_seconds;
|
||||||
|
clock_values->status = kActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Similar to the rental window, we check the playback window
|
||||||
|
* restrictions. This might decrease new_timer_value. */
|
||||||
|
status = ODK_CheckPlaybackWindow(timer_limits, clock_values,
|
||||||
|
system_time_seconds, &new_timer_value);
|
||||||
|
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We know we are allowed to decrypt. The rest computes the timer duration. */
|
||||||
|
clock_values->time_of_last_decrypt = system_time_seconds;
|
||||||
|
|
||||||
|
/* If new_timer_value is infinite (represented by 0), then there are no
|
||||||
|
* limits, so we can return now. */
|
||||||
|
if (new_timer_value == 0) {
|
||||||
|
clock_values->time_when_timer_expires = 0;
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_UNLIMITED;
|
||||||
|
return ODK_DISABLE_TIMER;
|
||||||
|
}
|
||||||
|
/* If the caller gave us a pointer to store the new timer value. Fill it. */
|
||||||
|
if (timer_value) {
|
||||||
|
*timer_value = new_timer_value;
|
||||||
|
}
|
||||||
|
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
|
||||||
|
&clock_values->time_when_timer_expires)) {
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE;
|
||||||
|
return ODK_SET_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called regularly during playback if OEMCrypto does not implement its
|
||||||
|
* own timer. */
|
||||||
|
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
|
||||||
|
const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values) {
|
||||||
|
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
|
||||||
|
if (status != OEMCrypto_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
switch (clock_values->timer_status) {
|
||||||
|
case ODK_CLOCK_TIMER_STATUS_UNLIMITED:
|
||||||
|
break;
|
||||||
|
case ODK_CLOCK_TIMER_STATUS_ACTIVE:
|
||||||
|
/* Note: we allow playback at the time when the timer expires, but not
|
||||||
|
* after. This is not important for business cases, but it makes it
|
||||||
|
* easier to write tests. */
|
||||||
|
if (clock_values->time_when_timer_expires > 0 &&
|
||||||
|
system_time_seconds > clock_values->time_when_timer_expires) {
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_EXPIRED;
|
||||||
|
return ODK_TIMER_EXPIRED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: /* Expired, error state, or never started. */
|
||||||
|
return ODK_TIMER_EXPIRED;
|
||||||
|
}
|
||||||
|
clock_values->time_of_last_decrypt = system_time_seconds;
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called from OEMCrypto_DeactivateUsageEntry. */
|
||||||
|
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
|
||||||
|
if (clock_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
if (clock_values->status == kUnused) {
|
||||||
|
clock_values->status = kInactiveUnused;
|
||||||
|
} else if (clock_values->status == kActive) {
|
||||||
|
clock_values->status = kInactiveUsed;
|
||||||
|
}
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called when OEMCrypto loads a legacy v15 license, from
|
||||||
|
* OEMCrypto_LoadKeys. */
|
||||||
|
OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
ODK_NonceValues* nonce_values,
|
||||||
|
uint32_t key_duration,
|
||||||
|
uint64_t system_time_seconds) {
|
||||||
|
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
timer_limits->soft_enforce_playback_duration = false;
|
||||||
|
timer_limits->soft_enforce_rental_duration = false;
|
||||||
|
timer_limits->earliest_playback_start_seconds = 0;
|
||||||
|
timer_limits->rental_duration_seconds = 0;
|
||||||
|
timer_limits->total_playback_duration_seconds = 0;
|
||||||
|
timer_limits->initial_renewal_duration_seconds = key_duration;
|
||||||
|
|
||||||
|
nonce_values->api_major_version = 15;
|
||||||
|
nonce_values->api_minor_version = 0;
|
||||||
|
if (key_duration > 0) {
|
||||||
|
clock_values->time_when_timer_expires = system_time_seconds + key_duration;
|
||||||
|
} else {
|
||||||
|
clock_values->time_when_timer_expires = 0;
|
||||||
|
}
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called when OEMCrypto loads a legacy license renewal in
|
||||||
|
* OEMCrypto_RefreshKeys. */
|
||||||
|
OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
|
||||||
|
ODK_ClockValues* clock_values,
|
||||||
|
const ODK_NonceValues* nonce_values,
|
||||||
|
uint64_t system_time_seconds,
|
||||||
|
uint32_t new_key_duration,
|
||||||
|
uint64_t* timer_value) {
|
||||||
|
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
if (nonce_values->api_major_version != 15) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||||
|
}
|
||||||
|
if (clock_values->status > kActive) {
|
||||||
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
|
||||||
|
return ODK_TIMER_EXPIRED;
|
||||||
|
}
|
||||||
|
return ODK_ComputeRenewalDuration(timer_limits, clock_values,
|
||||||
|
system_time_seconds, new_key_duration,
|
||||||
|
timer_value);
|
||||||
|
}
|
||||||
40
common/oemcrypto_core_message/odk/src/odk_util.c
Normal file
40
common/oemcrypto_core_message/odk/src/odk_util.c
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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_util.h"
|
||||||
|
|
||||||
|
int crypto_memcmp(const void* in_a, const void* in_b, size_t len) {
|
||||||
|
if (len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only valid pointers are allowed. */
|
||||||
|
if (in_a == NULL || in_b == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* a = (const uint8_t*)in_a;
|
||||||
|
const uint8_t* b = (const uint8_t*)in_b;
|
||||||
|
uint8_t x = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
x |= a[i] ^ b[i];
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b) {
|
||||||
|
if (a == NULL || b == NULL) {
|
||||||
|
return (a == b);
|
||||||
|
}
|
||||||
|
return (a->api_major_version == b->api_major_version &&
|
||||||
|
a->api_minor_version == b->api_minor_version &&
|
||||||
|
a->nonce == b->nonce && a->session_id == b->session_id);
|
||||||
|
}
|
||||||
34
common/oemcrypto_core_message/odk/src/odk_util.h
Normal file
34
common/oemcrypto_core_message/odk/src/odk_util.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_UTIL_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_UTIL_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/odk_structs.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* crypto_memcmp returns zero iff the |len| bytes at |a| and |b| are equal. It
|
||||||
|
* takes an amount of time dependent on |len|, but independent of the contents
|
||||||
|
* of |a| and |b|. Unlike memcmp, it cannot be used to order elements as the
|
||||||
|
* return value when a != b is undefined, other than being non-zero. */
|
||||||
|
int crypto_memcmp(const void* a, const void* b, size_t len);
|
||||||
|
|
||||||
|
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_UTIL_H_
|
||||||
242
common/oemcrypto_core_message/odk/src/serialization_base.c
Normal file
242
common/oemcrypto_core_message/odk/src/serialization_base.c
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/serialization_base.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/OEMCryptoCENCCommon.h"
|
||||||
|
#include "common/oemcrypto_core_message/odk/src/odk_overflow.h"
|
||||||
|
|
||||||
|
struct _Message {
|
||||||
|
uint8_t* base;
|
||||||
|
size_t capacity;
|
||||||
|
size_t size; /* bytes written */
|
||||||
|
size_t read_offset; /* bytes read */
|
||||||
|
MessageStatus status;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ValidMessage(Message* message) {
|
||||||
|
if (message == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (message->status != MESSAGE_STATUS_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (message->base == NULL) {
|
||||||
|
message->status = MESSAGE_STATUS_NULL_POINTER_ERROR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (message->size > message->capacity ||
|
||||||
|
message->read_offset > message->size) {
|
||||||
|
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PackBytes(Message* message, const uint8_t* ptr, size_t count) {
|
||||||
|
if (count <= message->capacity - message->size) {
|
||||||
|
memcpy((void*)(message->base + message->size), (void*)ptr, count);
|
||||||
|
message->size += count;
|
||||||
|
} else {
|
||||||
|
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_enum(Message* message, int value) {
|
||||||
|
uint32_t v32 = value;
|
||||||
|
Pack_uint32_t(message, &v32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_bool(Message* message, const bool* value) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
uint8_t data[4] = {0};
|
||||||
|
data[3] = *value ? 1 : 0;
|
||||||
|
PackBytes(message, data, sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_uint16_t(Message* message, const uint16_t* value) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
uint8_t data[2] = {0};
|
||||||
|
data[0] = *value >> 8;
|
||||||
|
data[1] = *value >> 0;
|
||||||
|
PackBytes(message, data, sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_uint32_t(Message* message, const uint32_t* value) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
uint8_t data[4] = {0};
|
||||||
|
data[0] = *value >> 24;
|
||||||
|
data[1] = *value >> 16;
|
||||||
|
data[2] = *value >> 8;
|
||||||
|
data[3] = *value >> 0;
|
||||||
|
PackBytes(message, data, sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_uint64_t(Message* message, const uint64_t* value) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
uint32_t hi = *value >> 32;
|
||||||
|
uint32_t lo = *value;
|
||||||
|
Pack_uint32_t(message, &hi);
|
||||||
|
Pack_uint32_t(message, &lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackArray(Message* message, const uint8_t* base, size_t size) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
PackBytes(message, base, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj) {
|
||||||
|
uint32_t offset = (uint32_t)obj->offset;
|
||||||
|
uint32_t length = (uint32_t)obj->length;
|
||||||
|
Pack_uint32_t(msg, &offset);
|
||||||
|
Pack_uint32_t(msg, &length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UnpackBytes(Message* message, uint8_t* ptr, size_t count) {
|
||||||
|
if (count <= message->size - message->read_offset) {
|
||||||
|
memcpy((void*)ptr, (void*)(message->base + message->read_offset), count);
|
||||||
|
message->read_offset += count;
|
||||||
|
} else {
|
||||||
|
message->status = MESSAGE_STATUS_UNDERFLOW_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Unpack_enum(Message* message) {
|
||||||
|
uint32_t v32;
|
||||||
|
Unpack_uint32_t(message, &v32);
|
||||||
|
return v32;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_bool(Message* message, bool* value) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
uint8_t data[4] = {0};
|
||||||
|
UnpackBytes(message, data, sizeof(data));
|
||||||
|
*value = (0 != data[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_uint16_t(Message* message, uint16_t* value) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
uint8_t data[2] = {0};
|
||||||
|
UnpackBytes(message, data, sizeof(data));
|
||||||
|
*value = data[0];
|
||||||
|
*value = *value << 8 | data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_uint32_t(Message* message, uint32_t* value) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
uint8_t data[4] = {0};
|
||||||
|
UnpackBytes(message, data, sizeof(data));
|
||||||
|
*value = data[0];
|
||||||
|
*value = *value << 8 | data[1];
|
||||||
|
*value = *value << 8 | data[2];
|
||||||
|
*value = *value << 8 | data[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_uint64_t(Message* message, uint64_t* value) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
uint32_t hi = 0;
|
||||||
|
uint32_t lo = 0;
|
||||||
|
Unpack_uint32_t(message, &hi);
|
||||||
|
Unpack_uint32_t(message, &lo);
|
||||||
|
*value = hi;
|
||||||
|
*value = *value << 32 | lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj) {
|
||||||
|
uint32_t offset = 0, length = 0;
|
||||||
|
Unpack_uint32_t(msg, &offset);
|
||||||
|
Unpack_uint32_t(msg, &length);
|
||||||
|
if (!ValidMessage(msg)) return;
|
||||||
|
/* Each substring should be contained within the message body, which is in the
|
||||||
|
* total message, just after the core message. The offset of a substring is
|
||||||
|
* relative to the message body. So we need to verify:
|
||||||
|
* 0 < offset and offset + length < message->capacity - message->size
|
||||||
|
* or offset + length + message->size < message->capacity
|
||||||
|
*/
|
||||||
|
size_t substring_end = 0; /* = offset + length; */
|
||||||
|
size_t end = 0; /* = substring_end + message->size; */
|
||||||
|
if (odk_add_overflow_ux(offset, length, &substring_end) ||
|
||||||
|
odk_add_overflow_ux(substring_end, msg->size, &end) ||
|
||||||
|
end > msg->capacity) {
|
||||||
|
msg->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
obj->offset = offset;
|
||||||
|
obj->length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy out */
|
||||||
|
void UnpackArray(Message* message, uint8_t* address, size_t size) {
|
||||||
|
if (!ValidMessage(message)) return;
|
||||||
|
UnpackBytes(message, address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The message structure, which is separate from the buffer,
|
||||||
|
* is initialized to reference the buffer
|
||||||
|
*/
|
||||||
|
void InitMessage(Message* message, uint8_t* buffer, size_t capacity) {
|
||||||
|
if (message == NULL) return;
|
||||||
|
memset(message, 0, sizeof(Message));
|
||||||
|
message->base = buffer;
|
||||||
|
message->capacity = capacity;
|
||||||
|
message->size = 0;
|
||||||
|
message->read_offset = 0;
|
||||||
|
message->status = MESSAGE_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the message to an empty state
|
||||||
|
*/
|
||||||
|
void ResetMessage(Message* message) {
|
||||||
|
message->size = 0;
|
||||||
|
message->read_offset = 0;
|
||||||
|
message->status = MESSAGE_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* GetBase(Message* message) {
|
||||||
|
if (message == NULL) return NULL;
|
||||||
|
return message->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetCapacity(Message* message) {
|
||||||
|
if (message == NULL) return 0;
|
||||||
|
return message->capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetSize(Message* message) {
|
||||||
|
if (message == NULL) return 0;
|
||||||
|
return message->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSize(Message* message, size_t size) {
|
||||||
|
if (message == NULL) return;
|
||||||
|
if (size > message->capacity)
|
||||||
|
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||||
|
else
|
||||||
|
message->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageStatus GetStatus(Message* message) { return message->status; }
|
||||||
|
|
||||||
|
void SetStatus(Message* message, MessageStatus status) {
|
||||||
|
message->status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetOffset(Message* message) {
|
||||||
|
if (message == NULL) return 0;
|
||||||
|
return message->read_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SizeOfMessageStruct() { return sizeof(Message); }
|
||||||
95
common/oemcrypto_core_message/odk/src/serialization_base.h
Normal file
95
common/oemcrypto_core_message/odk/src/serialization_base.h
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////// This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#ifndef COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_SERIALIZATION_BASE_H_
|
||||||
|
#define COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_SERIALIZATION_BASE_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "common/oemcrypto_core_message/odk/include/OEMCryptoCENCCommon.h"
|
||||||
|
|
||||||
|
#define SIZE_OF_MESSAGE_STRUCT 64
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Description:
|
||||||
|
* Point |msg| to stack-array |blk|.
|
||||||
|
* |blk| is guaranteed large enough to hold a |Message| struct.
|
||||||
|
* |blk| cannot be used in the same scope as a variable name.
|
||||||
|
* |msg| points to valid memory in the same scope |AllocateMessage| is used.
|
||||||
|
* Parameters:
|
||||||
|
* msg: pointer to pointer to |Message| struct
|
||||||
|
* blk: variable name for stack-array
|
||||||
|
*/
|
||||||
|
#define AllocateMessage(msg, blk) \
|
||||||
|
uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \
|
||||||
|
*(msg) = (Message*)(blk)
|
||||||
|
|
||||||
|
typedef struct _Message Message;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MESSAGE_STATUS_OK,
|
||||||
|
MESSAGE_STATUS_UNKNOWN_ERROR,
|
||||||
|
MESSAGE_STATUS_OVERFLOW_ERROR,
|
||||||
|
MESSAGE_STATUS_UNDERFLOW_ERROR,
|
||||||
|
MESSAGE_STATUS_PARSE_ERROR,
|
||||||
|
MESSAGE_STATUS_NULL_POINTER_ERROR,
|
||||||
|
MESSAGE_STATUS_API_VALUE_ERROR
|
||||||
|
} MessageStatus;
|
||||||
|
|
||||||
|
bool ValidMessage(Message* message);
|
||||||
|
|
||||||
|
void Pack_enum(Message* message, int value);
|
||||||
|
void Pack_bool(Message* message, const bool* value);
|
||||||
|
void Pack_uint16_t(Message* message, const uint16_t* value);
|
||||||
|
void Pack_uint32_t(Message* message, const uint32_t* value);
|
||||||
|
void Pack_uint64_t(Message* message, const uint64_t* value);
|
||||||
|
void PackArray(Message* message, const uint8_t* base, size_t size);
|
||||||
|
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj);
|
||||||
|
|
||||||
|
int Unpack_enum(Message* message);
|
||||||
|
void Unpack_bool(Message* message, bool* value);
|
||||||
|
void Unpack_uint16_t(Message* message, uint16_t* value);
|
||||||
|
void Unpack_uint32_t(Message* message, uint32_t* value);
|
||||||
|
void Unpack_uint64_t(Message* message, uint64_t* value);
|
||||||
|
void UnpackArray(Message* message, uint8_t* address,
|
||||||
|
size_t size); /* copy out */
|
||||||
|
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize a message structure to reference a separate buffer. The caller
|
||||||
|
* is responsible for ensuring that the buffer remains allocated for the
|
||||||
|
* lifetime of the message.
|
||||||
|
*/
|
||||||
|
void InitMessage(Message* message, uint8_t* buffer, size_t capacity);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset an existing the message to an empty state
|
||||||
|
*/
|
||||||
|
void ResetMessage(Message* message);
|
||||||
|
uint8_t* GetBase(Message* message);
|
||||||
|
size_t GetCapacity(Message* message);
|
||||||
|
size_t GetSize(Message* message);
|
||||||
|
void SetSize(Message* message, size_t size);
|
||||||
|
MessageStatus GetStatus(Message* message);
|
||||||
|
void SetStatus(Message* message, MessageStatus status);
|
||||||
|
size_t GetOffset(Message* message);
|
||||||
|
|
||||||
|
size_t SizeOfMessageStruct();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_SERIALIZATION_BASE_H_
|
||||||
@@ -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
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user