Update to support OEMCrypto v16 with ODK

This commit is contained in:
KongQun Yang
2020-09-21 15:54:04 -07:00
parent 93265ab9d1
commit 69d813f0f1
203 changed files with 16337 additions and 2290 deletions

2
BUILD
View File

@@ -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",

View File

@@ -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(

View File

@@ -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",
], ],
) )

View File

@@ -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');

View File

@@ -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

View File

@@ -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.

View 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

View 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_

View File

@@ -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

View File

@@ -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__

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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
View 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_

View 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

View 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

View 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_

View 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

View File

@@ -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);
} }

View File

@@ -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.

View File

@@ -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);

View 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

View 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_

View 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

View 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
View 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_

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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");
} }

View File

@@ -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

View File

@@ -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(),

View File

@@ -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,

View File

@@ -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

View File

@@ -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).

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@@ -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) {

View File

@@ -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
View 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
View 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
View 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

View 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

View 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_

View File

@@ -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
View 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_

View 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

View 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_

View 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

View 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

View File

@@ -0,0 +1,86 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#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_

View 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

View 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_

View 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

View File

@@ -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;

View 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",
],
}

View 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",
],
)

View 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.

View 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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -0,0 +1,69 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
//////////////////////////////////////////////////////////////////////////////// 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View File

@@ -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

View 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

View File

@@ -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

View 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',
],
}

View 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;
}

View 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',
],
}
},
],
}

View 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',
],
}

View 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_

View 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_

View 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;
}

View 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_

View 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);
}

View 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_

View 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_

View 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);
}

View 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);
}

View 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_

View 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); }

View 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_

View File

@@ -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>;

View 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

View 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_

View 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