Replace hardcoded parameters

This commit is contained in:
Lu Chen
2020-01-27 16:05:15 -08:00
parent cdd4d97e0f
commit 5c42bf9b7f
134 changed files with 9510 additions and 1938 deletions

View File

@@ -1,6 +1,6 @@
workspace(name = "media_cas_packager_sdk")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository", "git_repository")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# CCTZ (Time-zone framework), needed by abseil.
git_repository(
@@ -11,14 +11,33 @@ git_repository(
git_repository(
name = "abseil_repo",
commit = "475d64f2de7403a01b1b36c487328ed41d29c20c", #2018-04-10
commit = "aa844899c937bde5d2b24f276b59997e5b668bde", #2019-08-08
remote = "https://github.com/abseil/abseil-cpp.git",
)
# Name com_google_protobuf instead of protobuf_repo because Bazel's proto rules
# implicitly depend on @com_google_protobuf. See
# https://bazel.build/blog/2017/02/27/protocol-buffers.html.
git_repository(
name = "protobuf_repo",
name = "com_google_protobuf",
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(
@@ -62,7 +81,7 @@ new_git_repository(
bind(
name = "protobuf",
actual = "@protobuf_repo//:protobuf",
actual = "@com_google_protobuf//:protobuf",
)
bind(

View File

@@ -20,6 +20,35 @@ filegroup(
],
)
cc_library(
name = "content_id_util",
srcs = ["content_id_util.cc"],
hdrs = ["content_id_util.h"],
deps = [
":error_space",
":status",
"//license_server_sdk/internal:sdk",
"//protos/public:errors_cc_proto",
"//protos/public:external_license_cc_proto",
"//protos/public:license_protocol_cc_proto",
"//protos/public:license_server_sdk_cc_proto",
"//protos/public:widevine_pssh_cc_proto",
],
)
cc_test(
name = "content_id_util_test",
srcs = ["content_id_util_test.cc"],
deps = [
":content_id_util",
"//testing:gunit_main",
"//protos/public:errors_cc_proto",
"//protos/public:external_license_cc_proto",
"//protos/public:license_protocol_cc_proto",
"//protos/public:widevine_pssh_cc_proto",
],
)
cc_library(
name = "widevine_system_id",
srcs = ["widevine_system_id.cc"],
@@ -32,12 +61,37 @@ cc_library(
hdrs = ["certificate_type.h"],
)
cc_library(
name = "security_profile_list",
srcs = ["security_profile_list.cc"],
hdrs = ["security_profile_list.h"],
deps = [
":client_id_util",
"@abseil_repo//absl/synchronization",
"//protos/public:client_identification_cc_proto",
"//protos/public:provisioned_device_info_cc_proto",
"//protos/public:security_profile_cc_proto",
],
)
cc_test(
name = "security_profile_list_test",
timeout = "short",
srcs = ["security_profile_list_test.cc"],
deps = [
":security_profile_list",
"//base",
"//external:protobuf",
"//testing:gunit_main",
"//protos/public:security_profile_cc_proto",
],
)
cc_library(
name = "status",
srcs = ["status.cc"],
hdrs = ["status.h"],
deps = [
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"//util:error_space",
@@ -55,12 +109,23 @@ cc_test(
cc_library(
name = "client_cert",
srcs = ["client_cert.cc"],
hdrs = ["client_cert.h"],
srcs = [
"certificate_client_cert.cc",
"certificate_client_cert.h",
"client_cert.cc",
"keybox_client_cert.cc",
],
hdrs = [
"client_cert.h",
"keybox_client_cert.h",
],
deps = [
":crypto_util",
":drm_root_certificate",
":ec_key",
":ec_util",
":error_space",
":openssl_util",
":random_util",
":rsa_key",
":sha_util",
@@ -68,16 +133,13 @@ cc_library(
":status",
":wvm_token_handler",
"//base",
"//strings",
"@abseil_repo//absl/memory",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"@abseil_repo//absl/time",
"//util/gtl:map_util",
"//protos/public:client_identification_proto",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:license_protocol_proto",
"//protos/public:signed_drm_certificate_proto",
"//protos/public:client_identification_cc_proto",
"//protos/public:drm_certificate_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:license_protocol_cc_proto",
"//protos/public:signed_drm_certificate_cc_proto",
],
)
@@ -86,22 +148,19 @@ cc_test(
srcs = ["client_cert_test.cc"],
deps = [
":client_cert",
":drm_root_certificate",
":ec_test_keys",
":error_space",
":rsa_key",
":rsa_test_keys",
":sha_util",
":status",
":test_drm_certificates",
":wvm_test_keys",
"//base",
"//strings",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"@abseil_repo//absl/time",
"//common:rsa_key",
"//common:rsa_test_keys",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto",
"//protos/public:drm_certificate_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:signed_drm_certificate_cc_proto",
],
)
@@ -111,22 +170,29 @@ cc_library(
hdrs = ["device_status_list.h"],
deps = [
":client_cert",
":crypto_util",
":drm_root_certificate",
":drm_service_certificate",
":error_space",
":random_util",
":rsa_key",
":signing_key_util",
":status",
"//base",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//util/gtl:map_util",
"//protos/public:client_identification_proto",
"//protos/public:device_certificate_status_proto",
"//protos/public:errors_proto",
"//protos/public:provisioned_device_info_proto",
"//protos/public:client_identification_cc_proto",
"//protos/public:device_certificate_status_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:provisioned_device_info_cc_proto",
"//protos/public:signed_device_info_cc_proto",
],
)
cc_library(
name = "device_info_util",
srcs = ["device_info_util.cc"],
hdrs = ["device_info_util.h"],
deps = [
"@abseil_repo//absl/strings",
"//protos/public:provisioned_device_info_cc_proto",
],
)
@@ -137,15 +203,19 @@ cc_test(
deps = [
":client_cert",
":device_status_list",
":rsa_key",
":rsa_test_keys",
":status",
"//base",
"//external:protobuf",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//common:rsa_key",
"//common:rsa_test_keys",
"//protos/public:client_identification_proto",
"//protos/public:errors_proto",
"//protos/public:provisioned_device_info_proto",
"//protos/public:signed_drm_certificate_proto",
"//protos/public:client_identification_cc_proto",
"//protos/public:device_certificate_status_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:provisioned_device_info_cc_proto",
"//protos/public:signed_device_info_cc_proto",
"//protos/public:signed_drm_certificate_cc_proto",
],
)
@@ -155,18 +225,19 @@ cc_library(
hdrs = ["drm_root_certificate.h"],
deps = [
":certificate_type",
":ec_key",
":error_space",
":rsa_key",
":sha_util",
":signer_public_key",
":status",
"//base",
"@abseil_repo//absl/memory",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//external:openssl",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto",
"//protos/public:drm_certificate_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:signed_drm_certificate_cc_proto",
],
)
@@ -176,6 +247,8 @@ cc_test(
srcs = ["drm_root_certificate_test.cc"],
deps = [
":drm_root_certificate",
":ec_key",
":ec_test_keys",
":error_space",
":rsa_key",
":rsa_test_keys",
@@ -183,9 +256,10 @@ cc_test(
"//base",
"//external:protobuf",
"//testing:gunit_main",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto",
"@abseil_repo//absl/memory",
"//protos/public:drm_certificate_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:signed_drm_certificate_cc_proto",
],
)
@@ -195,13 +269,25 @@ cc_library(
hdrs = ["client_id_util.h"],
deps = [
":aes_cbc_util",
":client_cert",
":drm_service_certificate",
":error_space",
":status",
"//base",
"@abseil_repo//absl/strings",
"//protos/public:client_identification_proto",
"//protos/public:errors_proto",
"//protos/public:client_identification_cc_proto",
"//protos/public:drm_certificate_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:signed_drm_certificate_cc_proto",
],
)
cc_library(
name = "private_key_util",
hdrs = ["private_key_util.h"],
deps = [
"//base",
"//external:openssl",
],
)
@@ -210,6 +296,7 @@ cc_library(
srcs = ["rsa_util.cc"],
hdrs = ["rsa_util.h"],
deps = [
":private_key_util",
"//base",
"//external:openssl",
],
@@ -269,9 +356,6 @@ cc_library(
testonly = 1,
srcs = ["rsa_test_keys.cc"],
hdrs = ["rsa_test_keys.h"],
deps = [
"//base",
],
)
cc_library(
@@ -284,6 +368,168 @@ cc_library(
],
)
cc_library(
name = "ec_util",
srcs = ["ec_util.cc"],
hdrs = [
"ec_key.h",
"ec_util.h",
],
deps = [
":openssl_util",
":private_key_util",
"//base",
"@abseil_repo//absl/memory",
"//external:openssl",
],
)
cc_test(
name = "ec_util_test",
size = "medium",
timeout = "short",
srcs = ["ec_util_test.cc"],
deps = [
":ec_test_keys",
":ec_util",
":openssl_util",
"//base",
"//testing:gunit",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "ec_key",
srcs = ["ec_key.cc"],
hdrs = ["ec_key.h"],
deps = [
":aes_cbc_util",
":ec_util",
":openssl_util",
":sha_util",
"//base",
"@abseil_repo//absl/memory",
"//external:openssl",
],
)
cc_test(
name = "ec_key_test",
size = "medium",
timeout = "short",
srcs = ["ec_key_test.cc"],
deps = [
":ec_key",
":ec_test_keys",
":ec_util",
":random_util",
"//testing:gunit",
"//testing:gunit_main",
"//external:openssl",
],
)
cc_library(
name = "ec_key_source",
hdrs = ["ec_key_source.h"],
deps = [
":ec_key",
"//base",
],
)
cc_library(
name = "local_ec_key_source",
srcs = ["local_ec_key_source.cc"],
hdrs = [
"local_ec_key_source.h",
],
deps = [
":ec_key",
":ec_key_source",
":ec_util",
"//base",
"//external:openssl",
],
)
cc_test(
name = "local_ec_key_source_test",
size = "medium",
timeout = "short",
srcs = ["local_ec_key_source_test.cc"],
deps = [
":ec_key",
":ec_test_keys",
":ec_util",
":local_ec_key_source",
":random_util",
"//testing:gunit",
"//testing:gunit_main",
"//external:openssl",
],
)
cc_library(
name = "fake_ec_key_source",
testonly = 1,
srcs = ["fake_ec_key_source.cc"],
hdrs = ["fake_ec_key_source.h"],
deps = [
":ec_key",
":ec_key_source",
":ec_test_keys",
"//base",
"//external:openssl",
],
)
cc_library(
name = "ecies_crypto",
srcs = ["ecies_crypto.cc"],
hdrs = ["ecies_crypto.h"],
deps = [
":aes_cbc_util",
":crypto_util",
":ec_key",
":ec_key_source",
":ec_util",
":openssl_util",
":status",
"//base",
"@abseil_repo//absl/memory",
"@abseil_repo//absl/strings",
"//external:openssl",
],
)
cc_test(
name = "ecies_crypto_test",
size = "medium",
timeout = "short",
srcs = ["ecies_crypto_test.cc"],
deps = [
":ec_key",
":ec_key_source",
":ec_test_keys",
":ec_util",
":ecies_crypto",
":fake_ec_key_source",
"//testing:gunit",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
],
)
cc_library(
name = "ec_test_keys",
testonly = 1,
srcs = ["ec_test_keys.cc"],
hdrs = ["ec_test_keys.h"],
)
cc_library(
name = "aes_cbc_util",
srcs = ["aes_cbc_util.cc"],
@@ -313,7 +559,6 @@ cc_library(
"//base",
"@abseil_repo//absl/strings",
"//external:openssl",
"//util/endian",
],
)
@@ -326,6 +571,7 @@ cc_test(
"//testing:gunit",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//external:openssl",
],
)
@@ -420,7 +666,6 @@ cc_library(
":rsa_key",
":sha_util",
":status",
"//base",
],
)
@@ -431,7 +676,7 @@ cc_library(
deps = [
":crypto_util",
"//base",
"//protos/public:license_protocol_proto",
"//protos/public:license_protocol_cc_proto",
],
)
@@ -445,7 +690,7 @@ cc_test(
"//testing:gunit",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//protos/public:license_protocol_proto",
"//protos/public:license_protocol_cc_proto",
],
)
@@ -454,10 +699,6 @@ cc_library(
testonly = 1,
srcs = ["test_drm_certificates.cc"],
hdrs = ["test_drm_certificates.h"],
deps = [
"//base",
"@abseil_repo//absl/strings",
],
)
cc_library(
@@ -509,7 +750,7 @@ cc_library(
deps = [
"//util:error_space",
"//util:proto_status",
"//protos/public:errors_proto",
"//protos/public:errors_cc_proto",
],
)
@@ -527,9 +768,9 @@ cc_library(
"//base",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//protos/public:client_identification_proto",
"//protos/public:errors_proto",
"//protos/public:remote_attestation_proto",
"//protos/public:client_identification_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:remote_attestation_cc_proto",
],
)
@@ -549,10 +790,10 @@ cc_library(
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//util/gtl:map_util",
"//protos/public:client_identification_proto",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto",
"//protos/public:client_identification_cc_proto",
"//protos/public:drm_certificate_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:signed_drm_certificate_cc_proto",
],
)
@@ -572,11 +813,11 @@ cc_test(
"//external:protobuf",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//protos/public:client_identification_proto",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:license_server_sdk_proto",
"//protos/public:signed_drm_certificate_proto",
"//protos/public:client_identification_cc_proto",
"//protos/public:drm_certificate_cc_proto",
"//protos/public:errors_cc_proto",
"//protos/public:license_server_sdk_cc_proto",
"//protos/public:signed_drm_certificate_cc_proto",
],
)
@@ -587,9 +828,7 @@ cc_library(
deps = [
":status",
":vmp_checker",
"//base",
"@abseil_repo//absl/strings",
"//protos/public:license_protocol_proto",
"//protos/public:license_protocol_cc_proto",
],
)
@@ -598,7 +837,6 @@ cc_library(
srcs = ["x509_cert.cc"],
hdrs = ["x509_cert.h"],
deps = [
":error_space",
":openssl_util",
":rsa_key",
":status",
@@ -629,7 +867,6 @@ cc_test(
":rsa_key",
":test_utils",
":x509_cert",
"//base",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
],
@@ -646,8 +883,8 @@ cc_library(
":status",
":x509_cert",
"//base",
"//protos/public:errors_proto",
"//protos/public:verified_media_pipeline_proto",
"//protos/public:errors_cc_proto",
"//protos/public:verified_media_pipeline_cc_proto",
],
)
@@ -661,8 +898,8 @@ cc_test(
"//base",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//protos/public:errors_proto",
"//protos/public:verified_media_pipeline_proto",
"//protos/public:errors_cc_proto",
"//protos/public:verified_media_pipeline_cc_proto",
],
)
@@ -670,10 +907,7 @@ cc_library(
name = "string_util",
srcs = ["string_util.cc"],
hdrs = ["string_util.h"],
deps = [
":status",
"//base",
],
deps = [":status"],
)
cc_test(
@@ -681,7 +915,121 @@ cc_test(
srcs = ["string_util_test.cc"],
deps = [
":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",
"//external:protobuf",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//protos/public:drm_certificate_cc_proto",
],
)
cc_library(
name = "signer_public_key",
srcs = ["signer_public_key.cc"],
hdrs = ["signer_public_key.h"],
deps = [
":ec_key",
":rsa_key",
"@abseil_repo//absl/memory",
"//protos/public:drm_certificate_cc_proto",
],
)
cc_test(
name = "signer_public_key_test",
srcs = ["signer_public_key_test.cc"],
deps = [
":ec_key",
":ec_test_keys",
":rsa_key",
":rsa_test_keys",
":signer_public_key",
"//testing:gunit_main",
"//protos/public:drm_certificate_cc_proto",
],
)
cc_library(
name = "core_message_util",
srcs = ["core_message_util.cc"],
hdrs = ["core_message_util.h"],
deps = [
":sha_util",
"//common/oemcrypto_core_message/odk:kdo",
],
)

View File

@@ -29,7 +29,8 @@ namespace crypto_util {
TEST(CryptoUtilTest, EncryptAndDecryptAesCbc) {
std::string plain_text("Foo");
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
std::string ciphertext =
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
std::string(kIv, kIv + sizeof(kIv)), plain_text);
std::string expected_ciphertext(
"\xCF\x1A\x3\x1C\x9C\x8C\xB9Z\xEC\xC0\x17\xDCRxX\xD7");
@@ -37,7 +38,8 @@ TEST(CryptoUtilTest, EncryptAndDecryptAesCbc) {
ASSERT_GT(ciphertext.size(), plain_text.size());
ASSERT_EQ(expected_ciphertext, ciphertext);
std::string decrypted = DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
std::string decrypted =
DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
std::string(kIv, kIv + sizeof(kIv)), ciphertext);
ASSERT_EQ(plain_text, decrypted);
}
@@ -65,7 +67,8 @@ TEST(CryptoUtilTest, 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)));
ASSERT_EQ(std::string(kExpectedPlaintext,
kExpectedPlaintext + sizeof(kExpectedPlaintext)),
@@ -75,8 +78,8 @@ TEST(CryptoUtilTest, DecryptAesCbcNoPad) {
decrypted = DecryptAesCbcNoPad(
std::string(kKey, kKey + sizeof(kKey)), dummy_iv,
std::string(kCiphertext, kCiphertext + sizeof(kCiphertext)));
ASSERT_EQ(
std::string(kExpectedPlaintextEmptyIv,
ASSERT_EQ(std::string(
kExpectedPlaintextEmptyIv,
kExpectedPlaintextEmptyIv + sizeof(kExpectedPlaintextEmptyIv)),
decrypted);
}
@@ -85,8 +88,8 @@ TEST(CryptoUtilTest, TestFailedEncrypt) {
// Test with bogus initialization vector.
std::string plain_text("Foo");
std::string bogus_iv("bogus");
std::string ciphertext =
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, plain_text);
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
bogus_iv, plain_text);
ASSERT_EQ(ciphertext.size(), 0);
// Test with bogus key.
@@ -124,14 +127,15 @@ TEST(CryptoUtilTest, TestFailedEncryptNoPad) {
TEST(CryptoUtilTest, TestFailedDecrypt) {
// First, encrypt the data.
std::string plain_text("Foo");
std::string ciphertext = EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
std::string ciphertext =
EncryptAesCbc(std::string(kKey, kKey + sizeof(kKey)),
std::string(kIv, kIv + sizeof(kIv)), plain_text);
ASSERT_NE(ciphertext.size(), 0);
// Test Decrypt with bogus iv.
std::string bogus_iv("bogus");
plain_text =
DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv, ciphertext);
plain_text = DecryptAesCbc(std::string(kKey, kKey + sizeof(kKey)), bogus_iv,
ciphertext);
ASSERT_EQ(plain_text.size(), 0);
// Test Decrypt with bogus key.

View File

@@ -0,0 +1,268 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/certificate_client_cert.h"
#include "glog/logging.h"
#include "absl/memory/memory.h"
#include "common/crypto_util.h"
#include "common/ec_key.h"
#include "common/ec_util.h"
#include "common/error_space.h"
#include "common/openssl_util.h"
#include "common/random_util.h"
#include "common/rsa_key.h"
#include "common/sha_util.h"
#include "common/signing_key_util.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
namespace widevine {
using EllipticCurve = ECPrivateKey::EllipticCurve;
ECPrivateKey::EllipticCurve CertificateAlgorithmToCurve(
DrmCertificate::Algorithm algorithm) {
switch (algorithm) {
case DrmCertificate::ECC_SECP256R1:
return ECPrivateKey::SECP256R1;
case DrmCertificate::ECC_SECP384R1:
return ECPrivateKey::SECP384R1;
case DrmCertificate::ECC_SECP521R1:
return ECPrivateKey::SECP521R1;
default:
return ECPrivateKey::UNDEFINED_CURVE;
}
}
class ClientCertAlgorithmRSA : public ClientCertAlgorithm {
public:
ClientCertAlgorithmRSA() {}
~ClientCertAlgorithmRSA() override {}
ClientCertAlgorithmRSA(const ClientCertAlgorithmRSA&) = delete;
ClientCertAlgorithmRSA& operator=(const ClientCertAlgorithmRSA&) = delete;
Status Initialize(const std::string& public_key,
DrmCertificate::Algorithm /*not_used*/) override {
rsa_public_key_ =
std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key));
if (!rsa_public_key_) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-public-key-failed");
}
session_key_ = Random16Bytes();
if (!rsa_public_key_->Encrypt(session_key_, &wrapped_session_key_)) {
return Status(error_space, ENCRYPT_ERROR,
"drm-certificate-failed-encrypt-session-key");
}
return OkStatus();
}
Status VerifySignature(const std::string& message,
const std::string& signature) const override {
CHECK(rsa_public_key_);
if (!rsa_public_key_->VerifySignature(message, signature)) {
return Status(error_space, INVALID_SIGNATURE, "");
}
return OkStatus();
}
const std::string& session_key() const override { return session_key_; }
const std::string& wrapped_session_key() const override {
return wrapped_session_key_;
}
private:
std::unique_ptr<RsaPublicKey> rsa_public_key_;
std::string session_key_;
std::string wrapped_session_key_;
};
// ClientCertAlgorithmECC implements the Widevine protocol using ECC. It
// verifies an ECC based request and generates keys for use in building a
// license. The curve type value is contained in |algorithm|.
class ClientCertAlgorithmECC : public ClientCertAlgorithm {
public:
ClientCertAlgorithmECC() = default;
~ClientCertAlgorithmECC() override = default;
ClientCertAlgorithmECC(const ClientCertAlgorithmECC&) = delete;
ClientCertAlgorithmECC& operator=(const ClientCertAlgorithmECC&) = delete;
Status Initialize(const std::string& public_key,
DrmCertificate::Algorithm algorithm) override {
ECPrivateKey::EllipticCurve curve_id =
CertificateAlgorithmToCurve(algorithm);
if (curve_id == ECPrivateKey::UNDEFINED_CURVE) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-unknown-curve");
}
// Parse the certifcate ECC public key.
client_ecc_public_key_ = ECPublicKey::Create(public_key);
if (client_ecc_public_key_ == nullptr) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-public-key-failed");
}
// Generate an ephemeral ecc key pair with the same curve as used by the
// certificate public key.
ScopedECKEY key = ec_util::GenerateKeyWithCurve(curve_id);
auto new_private_key = absl::make_unique<ECPrivateKey>(std::move(key));
if (new_private_key == nullptr) {
return Status(error_space, DRM_DEVICE_CERTIFICATE_ECC_KEYGEN_FAILED,
"drm-certificate-ephemeral-private-key-failed");
}
// Serialize the ephemeral public key for inclusion in a license response.
std::unique_ptr<ECPublicKey> new_public_key = new_private_key->PublicKey();
if (new_public_key == nullptr ||
!new_public_key->SerializedKey(&ephemeral_public_key_)) {
return Status(error_space, DRM_DEVICE_CERTIFICATE_ECC_KEYGEN_FAILED,
"drm-certificate-ephemeral-public-key-failed");
}
// Generate the session key from the ephemeral private key and the
// certificate public key.
if (!new_private_key->DeriveSharedSessionKey(*client_ecc_public_key_,
&derived_session_key_)) {
return Status(error_space, DRM_DEVICE_CERTIFICATE_ECC_KEYGEN_FAILED,
"drm-certificate-shared-key-gen-failed");
}
return OkStatus();
}
Status VerifySignature(const std::string& message,
const std::string& signature) const override {
CHECK(client_ecc_public_key_);
if (!client_ecc_public_key_->VerifySignature(message, signature)) {
return Status(error_space, INVALID_SIGNATURE, "");
}
return OkStatus();
}
// Returns an aes key generated from the sha256 hash of the shared ecc secret.
// This key is used for key derivation.
const std::string& session_key() const override {
return derived_session_key_;
}
// Returns an ephemeral serialized ecc public key. This key is added to a
// license response in the SignedMessage::session_key field. The client will
// use this key to generate the shared secret and derived session key.
const std::string& wrapped_session_key() const override {
return ephemeral_public_key_;
}
private:
std::unique_ptr<ECPublicKey> client_ecc_public_key_;
std::string ephemeral_public_key_;
std::string derived_session_key_;
};
Status CertificateClientCert::Initialize(
const DrmRootCertificate* root_certificate,
const std::string& serialized_certificate) {
CHECK(root_certificate);
if (is_initialized_) {
return Status(error_space, INVALID_PARAMETER,
"certificate-is-already-initialized");
}
SignedDrmCertificate signed_device_cert;
Status status = root_certificate->VerifyCertificate(
serialized_certificate, &signed_device_cert, &device_cert_);
if (!status.ok()) {
return status;
}
if (device_cert_.type() != DrmCertificate::DEVICE ||
device_cert_.public_key().empty()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"expected-device-certificate-type");
}
const SignedDrmCertificate& device_cert_signer = signed_device_cert.signer();
if (!model_certificate_.ParseFromString(
device_cert_signer.drm_certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-invalid-signer");
}
if (model_certificate_.type() != DrmCertificate::DEVICE_MODEL) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"expected-device-model-certificate-type");
}
if (!model_certificate_.has_serial_number()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-signer-serial-number");
}
// Check to see if this model certificate is signed by a
// provisioner (entity using Widevine Provisioning Server SDK).
if (device_cert_signer.has_signer()) {
if (!provisioner_certificate_.ParseFromString(
device_cert_signer.signer().drm_certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"model-certificate-invalid-signer");
}
if (provisioner_certificate_.type() != DrmCertificate::PROVISIONER) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"expected-provisioning-provider-certificate-type");
}
if (!provisioner_certificate_.has_provider_id() ||
provisioner_certificate_.provider_id().empty()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-provisioning-service-id");
}
signed_by_provisioner_ = true;
}
if (!model_certificate_.has_system_id()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"model-certificate-missing-system-id");
}
switch (device_cert_.algorithm()) {
case DrmCertificate::RSA:
algorithm_ = absl::make_unique<ClientCertAlgorithmRSA>();
break;
case DrmCertificate::ECC_SECP256R1:
case DrmCertificate::ECC_SECP384R1:
case DrmCertificate::ECC_SECP521R1:
algorithm_ = absl::make_unique<ClientCertAlgorithmECC>();
break;
default:
return Status(error_space, INVALID_DRM_CERTIFICATE,
"unsupported-certificate-algorithm");
}
status = algorithm_->Initialize(device_cert_.public_key(),
device_cert_.algorithm());
if (!status.ok()) {
return status;
}
is_initialized_ = true;
return OkStatus();
}
Status CertificateClientCert::VerifySignature(
const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) const {
return algorithm_->VerifySignature(
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
signature);
}
void CertificateClientCert::GenerateSigningKey(
const std::string& message, ProtocolVersion protocol_version) {
signing_key_ = crypto_util::DeriveKey(
key(), crypto_util::kSigningKeyLabel,
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
SigningKeyMaterialSizeBits(protocol_version));
}
} // namespace widevine

View File

@@ -0,0 +1,96 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef COMMON_CERTIFICATE_CLIENT_CERT_H_
#define COMMON_CERTIFICATE_CLIENT_CERT_H_
#include "common/client_cert.h"
#include "protos/public/drm_certificate.pb.h"
namespace widevine {
class ClientCertAlgorithm {
public:
ClientCertAlgorithm() = default;
virtual ~ClientCertAlgorithm() = default;
ClientCertAlgorithm(const ClientCertAlgorithm&) = delete;
ClientCertAlgorithm& operator=(const ClientCertAlgorithm&) = delete;
// Initialize the algorithm module using the |public_key| from the drm
// certificate. The |algorithm| value provides additional information needed
// by the ECC implementation of this interface.
virtual Status Initialize(const std::string& public_key,
DrmCertificate::Algorithm algorithm) = 0;
// Verify the |signature| of an incoming request |message| using the public
// key from the drm certificate.
virtual Status VerifySignature(const std::string& message,
const std::string& signature) const = 0;
// Returns the key to be used in key derivation of the license
// protocol.
virtual const std::string& session_key() const = 0;
// Returns a byte std::string to be included in the SignedMessage::session_key
// field of a license response. This key may be either an encrypted aes key,
// or the bytes of an ephemeral public key.
virtual const std::string& wrapped_session_key() const = 0;
};
class CertificateClientCert : public ClientCert {
public:
CertificateClientCert() {}
~CertificateClientCert() override {}
CertificateClientCert(const CertificateClientCert&) = delete;
CertificateClientCert& operator=(const CertificateClientCert&) = delete;
Status Initialize(const DrmRootCertificate* root_certificate,
const std::string& serialized_certificate);
Status VerifySignature(const std::string& message,
const std::string& signature,
ProtocolVersion protocol_version) const override;
void GenerateSigningKey(const std::string& message,
ProtocolVersion protocol_version) override;
const std::string& encrypted_key() const override {
return algorithm_->wrapped_session_key();
}
const std::string& key() const override { return algorithm_->session_key(); }
const std::string& serial_number() const override {
return device_cert_.serial_number();
}
const std::string& service_id() const override {
return provisioner_certificate_.provider_id();
}
const std::string& signing_key() const override { return signing_key_; }
const std::string& signer_serial_number() const override {
return model_certificate_.serial_number();
}
uint32_t signer_creation_time_seconds() const override {
return model_certificate_.creation_time_seconds();
}
bool signed_by_provisioner() const override { return signed_by_provisioner_; }
uint32_t system_id() const override { return model_certificate_.system_id(); }
widevine::ClientIdentification::TokenType type() const override {
return ClientIdentification::DRM_DEVICE_CERTIFICATE;
}
private:
std::unique_ptr<ClientCertAlgorithm> algorithm_;
bool signed_by_provisioner_ = false;
std::string signing_key_;
DrmCertificate model_certificate_;
DrmCertificate device_cert_;
DrmCertificate provisioner_certificate_;
bool is_initialized_ = false;
};
} // namespace widevine
#endif // COMMON_CERTIFICATE_CLIENT_CERT_H_

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
// License Agreement. For a copy of this agreement, please contact
@@ -8,19 +8,19 @@
#include "common/client_cert.h"
#include <cstddef>
#include <memory>
#include <utility>
#include <vector>
#include <string>
#include "glog/logging.h"
#include "strings/serialize.h"
#include "absl/memory/memory.h"
#include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h"
#include "util/gtl/map_util.h"
#include "common/certificate_client_cert.h"
#include "common/crypto_util.h"
#include "common/drm_root_certificate.h"
#include "common/error_space.h"
#include "common/keybox_client_cert.h"
#include "common/random_util.h"
#include "common/rsa_key.h"
#include "common/sha_util.h"
#include "common/signing_key_util.h"
#include "common/status.h"
@@ -30,93 +30,6 @@
#include "protos/public/signed_drm_certificate.pb.h"
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(
const std::multimap<uint32_t, std::string>& keymap) {
@@ -139,124 +52,50 @@ uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
return WvmTokenHandler::GetSystemId(keybox_bytes);
}
Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) {
if (keybox_bytes.size() < kKeyboxSizeBytes) {
return Status(error_space, INVALID_KEYBOX_TOKEN,
"keybox-token-is-too-short");
}
Status ClientCert::Create(
const DrmRootCertificate* root_certificate,
widevine::ClientIdentification::TokenType token_type,
const std::string& token, std::unique_ptr<ClientCert>* client_cert) {
CHECK(client_cert);
Status status;
switch (token_type) {
case ClientIdentification::KEYBOX:
return CreateWithKeybox(token, client_cert);
set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes));
set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes));
bool insecure_keybox = false;
Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &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());
case ClientIdentification::DRM_DEVICE_CERTIFICATE:
return CreateWithDrmCertificate(root_certificate, token, client_cert);
default:
return Status(error_space, error::UNIMPLEMENTED,
"client-type-not-implemented");
}
return OkStatus();
}
Status KeyboxClientCert::VerifySignature(const std::string& message,
const std::string& signature,
ProtocolVersion protocol_version) {
DCHECK(!signing_key().empty());
using crypto_util::VerifySignatureHmacSha256;
if (!VerifySignatureHmacSha256(
GetClientSigningKey(signing_key(), protocol_version), signature,
message)) {
return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
// Creates a Device Certificate based ClientCert. The |client_cert| is a
// caller supplied unique_ptr to receive the new ClientCert.
Status ClientCert::CreateWithDrmCertificate(
const DrmRootCertificate* root_certificate,
const std::string& drm_certificate,
std::unique_ptr<ClientCert>* client_cert) {
CHECK(client_cert);
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();
}
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");
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);
}
if (!model_certificate.has_serial_number()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-signer-serial-number");
}
// Check to see if this model certificate is signed by a
// provisioner (entity using Widevine Provisioning Server SDK).
if (signer.has_signer()) {
DrmCertificate provisioner_certificate;
if (!provisioner_certificate.ParseFromString(
signer.signer().drm_certificate())) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"model-certificate-invalid-signer");
}
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
set_signed_by_provisioner(true);
} else {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"expected-provisioning-provider-certificate-type");
}
if (!provisioner_certificate.has_provider_id() ||
provisioner_certificate.provider_id().empty()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-provisioning-service-id");
}
set_service_id(provisioner_certificate.provider_id());
}
set_signer_serial_number(model_certificate.serial_number());
set_signer_creation_time_seconds(model_certificate.creation_time_seconds());
if (!model_certificate.has_system_id()) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"model-certificate-missing-system-id");
}
set_system_id(model_certificate.system_id());
set_serial_number(device_cert.serial_number());
set_public_key(device_cert.public_key());
rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
if (rsa_public_key_ == nullptr) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-public-key-failed");
}
// TODO(user): Move this somewhere else. It is license protocol.
set_key(Random16Bytes());
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
return Status(error_space, ENCRYPT_ERROR,
"drm-certificate-failed-encrypt-session-key");
}
return OkStatus();
}
Status CertificateClientCert::VerifySignature(
const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) {
CHECK(rsa_public_key_);
if (!rsa_public_key_->VerifySignature(
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
signature)) {
return Status(error_space, INVALID_SIGNATURE, "");
}
return OkStatus();
return status;
}
} // 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
// License Agreement. For a copy of this agreement, please contact
@@ -9,179 +9,67 @@
#ifndef COMMON_CLIENT_CERT_H__
#define COMMON_CLIENT_CERT_H__
#include <map>
#include <memory>
#include <string>
#include "common/rsa_key.h"
#include "common/drm_root_certificate.h"
#include "common/status.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
class DrmRootCertificate;
class SignedDrmCertificate;
// Handler class for LicenseRequests; validates requests and encrypts licenses.
// TODO(user): Remove extra accessors after Keybox parsing is moved
// to a separate class in KeyboxClientCert class.
class ClientCert {
protected:
ClientCert() = default;
public:
virtual ~ClientCert() {}
// Creates a ClientCert from the |token|. The type of ClientCert created is
// determined by the |token_type|.
static Status Create(
const DrmRootCertificate* root_certificate,
widevine::ClientIdentification::TokenType token_type,
const std::string& token, ClientCert** client_cert);
// Creates a Keybox based ClientCert.
const std::string& token, 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,
ClientCert** client_cert);
std::unique_ptr<ClientCert>* client_cert);
// Creates a Device Certificate based ClientCert.
static Status CreateWithDrmCertificate(
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
ClientCert** client_cert);
// Creates a HMAC SHA256 signature based on the message and the key().
// signature is owned by the caller and can not be NULL.
virtual void CreateSignature(const std::string& message, std::string* signature);
const DrmRootCertificate* root_certificate,
const std::string& drm_certificate,
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
// classes information and the passed in message. Returns OK if signature
// is valid.
virtual Status VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) = 0;
virtual Status VerifySignature(const std::string& message,
const std::string& signature,
ProtocolVersion protocol_version) const = 0;
// Creates a signing_key that is accessible using signing_key(). Signing_key
// is constructed by doing a key derivation using the key() and message.
virtual void GenerateSigningKey(const std::string& message,
ProtocolVersion protocol_version);
// Used to create signing keys. For Keybox token types this is the device key.
// For Device Certificate token types this the session key.
virtual const std::string& key() const = 0;
virtual void set_key(const std::string& key) = 0;
ProtocolVersion protocol_version) = 0;
virtual const std::string& encrypted_key() const = 0;
virtual uint32_t system_id() const { return system_id_; }
virtual const std::string& signing_key() const { return signing_key_; }
virtual const std::string& public_key() const { return public_key_; }
virtual const std::string& serial_number() const { return serial_number_; }
virtual void set_serial_number(const std::string& serial_number) {
serial_number_ = serial_number;
}
virtual const std::string& signer_serial_number() const {
return signer_serial_number_;
}
virtual uint32_t signer_creation_time_seconds() const {
return signer_creation_time_seconds_;
}
virtual const std::string& key() const = 0;
virtual const std::string& serial_number() const = 0;
virtual const std::string& service_id() const = 0;
virtual const std::string& signing_key() const = 0;
virtual const std::string& signer_serial_number() const = 0;
virtual uint32_t signer_creation_time_seconds() const = 0;
virtual bool signed_by_provisioner() const = 0;
virtual uint32_t system_id() const = 0;
virtual widevine::ClientIdentification::TokenType type() const = 0;
virtual std::string service_id() const { return service_id_; }
virtual bool signed_by_provisioner() const { return signed_by_provisioner_; }
protected:
ClientCert() {}
virtual void set_system_id(uint32_t system_id) { system_id_ = system_id; }
virtual void set_signing_key(const std::string& signing_key) {
signing_key_ = signing_key;
}
virtual void set_service_id(const std::string& service_id) {
service_id_ = service_id;
}
virtual void set_signed_by_provisioner(bool provisioner_signed_flag) {
signed_by_provisioner_ = provisioner_signed_flag;
}
std::string public_key_;
std::string serial_number_;
std::string signer_serial_number_;
uint32_t signer_creation_time_seconds_ = 0;
bool signed_by_provisioner_ = false;
private:
uint32_t system_id_ = 0;
std::string signing_key_;
std::string service_id_;
DISALLOW_COPY_AND_ASSIGN(ClientCert);
};
// This class implements the crypto operations based on the Widevine keybox.
// It will unpack token and perform all the crypto operations for securing
// the key material in the license response.
class KeyboxClientCert : public ClientCert {
public:
~KeyboxClientCert() override;
// Set the system-wide pre-provisioning keys; argument must be human-readable
// hex digits.
// Must be called before any other method of this class is called, unless
// created by ClientCert::CreateWithPreProvisioningKey(...).
static void SetPreProvisioningKeys(const std::multimap<uint32_t, std::string>& keys);
static bool IsSystemIdKnown(const uint32_t system_id);
static uint32_t GetSystemId(const std::string& keybox_bytes);
Status Initialize(const std::string& keybox_bytes);
Status VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) override;
const std::string& key() const override { return device_key_; }
void set_key(const std::string& key) override { device_key_ = key; }
const std::string& encrypted_key() const override { return encrypted_device_key_; }
widevine::ClientIdentification::TokenType type() const override {
return widevine::ClientIdentification::KEYBOX;
}
private:
KeyboxClientCert();
friend class ClientCert;
friend class MockKeyboxClientCert;
std::string device_key_;
std::string encrypted_device_key_;
DISALLOW_COPY_AND_ASSIGN(KeyboxClientCert);
};
// This class implements the device certificate operations based on RSA keys.
// It will unpack token and perform all the crypto operations for securing
// the key material in the license response.
using widevine::RsaPublicKey;
class CertificateClientCert : public ClientCert {
public:
~CertificateClientCert() override;
Status VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) override;
const std::string& key() const override { return session_key_; }
void set_key(const std::string& key) override { session_key_ = key; }
const std::string& encrypted_key() const override {
return encrypted_session_key_;
}
widevine::ClientIdentification::TokenType type() const override {
return widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE;
}
protected:
friend class ClientCert;
friend class MockCertificateClientCert;
Status Initialize(const DrmRootCertificate* drm_root_certificate,
const std::string& serialized_certificate);
virtual void set_public_key(const std::string& public_key) {
public_key_ = public_key;
}
virtual void set_signer_serial_number(const std::string& signer_serial_number) {
signer_serial_number_ = signer_serial_number;
}
virtual void set_signer_creation_time_seconds(uint32_t creation_time_seconds) {
signer_creation_time_seconds_ = creation_time_seconds;
}
std::string session_key_;
std::string encrypted_session_key_;
std::unique_ptr<RsaPublicKey> rsa_public_key_;
private:
CertificateClientCert();
DISALLOW_COPY_AND_ASSIGN(CertificateClientCert);
};
} // namespace widevine
#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
// License Agreement. For a copy of this agreement, please contact
@@ -8,41 +8,42 @@
#include "common/client_cert.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include "glog/logging.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "common/drm_root_certificate.h"
#include "common/ec_test_keys.h"
#include "common/error_space.h"
#include "common/keybox_client_cert.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h"
#include "common/sha_util.h"
#include "common/status.h"
#include "common/test_drm_certificates.h"
#include "common/wvm_test_keys.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
using widevine::ClientCert;
using widevine::ClientIdentification;
using widevine::Status;
namespace widevine {
const DrmCertificate::Type kNoSigner = DrmCertificate::ROOT;
const DrmCertificate::Type kDeviceModelSigner = DrmCertificate::DEVICE_MODEL;
const DrmCertificate::Type kProvisionerSigner = DrmCertificate::PROVISIONER;
// TODO(user): Change these tests to use on-the-fly generated intermediate
// and device certificates based on RsaTestKeys.
// TODO(user): Add testcase(s) CreateSignature,
// and GenerateSigningKey.
namespace widevine {
using ::testing::_;
using ::testing::Return;
class ClientCertTest : public ::testing::Test {
class ClientCertTest
: public ::testing::TestWithParam<DrmCertificate::Algorithm> {
public:
~ClientCertTest() override = default;
void SetUp() override {
if (!setup_preprov_keys_) {
KeyboxClientCert::SetPreProvisioningKeys(
@@ -82,7 +83,7 @@ class ClientCertTest : public ::testing::Test {
: certificate_(certificate),
expected_serial_number_(expected_serial_number),
expected_system_id_(expected_system_id),
expected_status_(std::move(expected_status)) {}
expected_status_(expected_status) {}
};
void TestBasicValidation(const TestTokenAndKeys& expectation,
@@ -91,32 +92,41 @@ class ClientCertTest : public ::testing::Test {
void TestBasicValidationDrmCertificate(
const TestCertificateAndData& expectation, const bool compare_data);
void GenerateSignature(const std::string& message, const std::string& private_key,
void GenerateSignature(const std::string& message,
const std::string& private_key,
std::string* signature);
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate,
SignedDrmCertificate* signer,
const std::string& private_key);
DrmCertificate* GenerateProvisionerCertificate(uint32_t system_id,
const std::string& serial_number,
DrmCertificate* GenerateProvisionerCertificate(
uint32_t system_id, const std::string& serial_number,
const std::string& provider_id);
SignedDrmCertificate* GenerateSignedProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, const std::string& service_id);
DrmCertificate* GenerateIntermediateCertificate(uint32_t system_id,
const std::string& serial_number);
uint32_t system_id, const std::string& serial_number,
const std::string& service_id);
DrmCertificate* GenerateIntermediateCertificate(
uint32_t system_id, const std::string& serial_number);
SignedDrmCertificate* GenerateSignedIntermediateCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number);
DrmCertificate* GenerateDrmCertificate(uint32_t system_id,
const std::string& serial_number);
const std::string& serial_number, DrmCertificate::Type signer_cert_type);
DrmCertificate* GenerateDrmCertificate(
uint32_t system_id, const std::string& serial_number,
DrmCertificate::Algorithm = DrmCertificate::RSA);
SignedDrmCertificate* GenerateSignedDrmCertificate(
SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number);
const std::string& serial_number,
DrmCertificate::Algorithm = DrmCertificate::RSA);
std::string GetPublicKeyByCertType(DrmCertificate::Type cert_type);
std::string GetPrivateKeyByCertType(DrmCertificate::Type cert_type);
std::string GetECCPublicKey(DrmCertificate::Algorithm algorithm);
RsaTestKeys test_rsa_keys_;
TestDrmCertificates test_drm_certs_;
std::unique_ptr<DrmRootCertificate> root_cert_;
static bool setup_preprov_keys_;
};
bool ClientCertTest::setup_preprov_keys_(false);
void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
@@ -124,19 +134,10 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
const bool compare_device_key) {
// Test validation of a valid request.
Status status;
ClientCert* client_cert_ptr = nullptr;
std::unique_ptr<ClientCert> keybox_cert;
// Two ways to create a client cert object, test both.
for (int i = 0; i < 2; i++) {
if (i == 0) {
status =
ClientCert::Create(root_cert_.get(), ClientIdentification::KEYBOX,
expectation.token_, &client_cert_ptr);
} else {
status =
ClientCert::CreateWithKeybox(expectation.token_, &client_cert_ptr);
}
std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
status = ClientCert::Create(root_cert_.get(), ClientIdentification::KEYBOX,
expectation.token_, &keybox_cert);
if (expect_success) {
ASSERT_EQ(OkStatus(), status);
ASSERT_TRUE(keybox_cert.get());
@@ -151,7 +152,6 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
EXPECT_FALSE(keybox_cert);
}
}
}
void ClientCertTest::TestBasicValidationDrmCertificate(
const TestCertificateAndData& expectation, const bool compare_data) {
@@ -162,11 +162,10 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
// Test validation of a valid request.
Status status;
ClientCert* client_cert_ptr = nullptr;
std::unique_ptr<ClientCert> drm_certificate_cert;
status = ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
expectation.certificate_, &client_cert_ptr);
std::unique_ptr<ClientCert> drm_certificate_cert(client_cert_ptr);
expectation.certificate_, &drm_certificate_cert);
ASSERT_EQ(expectation.expected_status_, status);
if (expectation.expected_status_.ok()) {
ASSERT_TRUE(drm_certificate_cert.get());
@@ -205,13 +204,48 @@ SignedDrmCertificate* ClientCertTest::SignCertificate(
return signed_certificate.release();
}
std::string ClientCertTest::GetPublicKeyByCertType(
DrmCertificate::Type cert_type) {
if (cert_type == DrmCertificate::DEVICE) {
return test_rsa_keys_.public_test_key_3_2048_bits();
} else if (cert_type == DrmCertificate::DEVICE_MODEL) {
return test_rsa_keys_.public_test_key_2_2048_bits();
}
return test_rsa_keys_.public_test_key_1_3072_bits();
}
std::string ClientCertTest::GetPrivateKeyByCertType(
DrmCertificate::Type cert_type) {
if (cert_type == DrmCertificate::DEVICE) {
return test_rsa_keys_.private_test_key_3_2048_bits();
} else if (cert_type == DrmCertificate::DEVICE_MODEL) {
return test_rsa_keys_.private_test_key_2_2048_bits();
}
return test_rsa_keys_.private_test_key_1_3072_bits();
}
std::string ClientCertTest::GetECCPublicKey(
DrmCertificate::Algorithm algorithm) {
ECTestKeys keys;
switch (algorithm) {
case DrmCertificate::ECC_SECP256R1:
return keys.public_test_key_1_secp256r1();
case DrmCertificate::ECC_SECP384R1:
return keys.public_test_key_1_secp384r1();
case DrmCertificate::ECC_SECP521R1:
return keys.public_test_key_1_secp521r1();
default:
return "";
}
}
DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
uint32_t system_id, const std::string& serial_number) {
std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate);
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
intermediate_certificate->set_serial_number(serial_number);
intermediate_certificate->set_public_key(
test_rsa_keys_.public_test_key_2_2048_bits());
GetPublicKeyByCertType(DrmCertificate::DEVICE_MODEL));
intermediate_certificate->set_system_id(system_id);
intermediate_certificate->set_creation_time_seconds(1234);
return intermediate_certificate.release();
@@ -219,42 +253,49 @@ DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
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(
GenerateIntermediateCertificate(system_id, serial_number));
// Must use the same key pair used by GenerateIntermediateCertificate().
return SignCertificate(*intermediate_certificate, signer,
test_rsa_keys_.private_test_key_1_3072_bits());
GetPrivateKeyByCertType(signer_cert_type));
}
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);
drm_certificate->set_type(DrmCertificate::DEVICE);
drm_certificate->set_serial_number(serial_number);
drm_certificate->set_system_id(system_id);
drm_certificate->set_public_key(test_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_algorithm(algorithm);
return drm_certificate.release();
}
SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
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(
GenerateDrmCertificate(system_id, serial_number));
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate(
*drm_certificate, signer, test_rsa_keys_.private_test_key_2_2048_bits()));
GenerateDrmCertificate(system_id, serial_number, algorithm));
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(
SignCertificate(*drm_certificate, signer,
GetPrivateKeyByCertType(DrmCertificate::DEVICE_MODEL)));
return signed_drm_certificate.release();
}
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);
provisioner_certificate->set_type(DrmCertificate::PROVISIONER);
provisioner_certificate->set_serial_number(serial_number);
// TODO(user): Need to generate 3072 bit test for provisioner certificates.
provisioner_certificate->set_public_key(
test_rsa_keys_.public_test_key_1_3072_bits());
GetPublicKeyByCertType(DrmCertificate::DrmCertificate::PROVISIONER));
provisioner_certificate->set_system_id(system_id);
provisioner_certificate->set_provider_id(provider_id);
provisioner_certificate->set_creation_time_seconds(1234);
@@ -262,11 +303,12 @@ DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
}
SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
uint32_t system_id, const std::string& serial_number, const std::string& service_id) {
uint32_t system_id, const std::string& serial_number,
const std::string& service_id) {
std::unique_ptr<DrmCertificate> provisioner_certificate(
GenerateProvisionerCertificate(system_id, serial_number, service_id));
return SignCertificate(*provisioner_certificate, nullptr,
test_rsa_keys_.private_test_key_1_3072_bits());
GetPrivateKeyByCertType(DrmCertificate::ROOT));
}
TEST_F(ClientCertTest, BasicValidation) {
@@ -296,19 +338,25 @@ TEST_F(ClientCertTest, BasicValidation) {
KeyboxClientCert::GetSystemId(kValidTokenAndExpectedKeys[0].token_));
}
TEST_F(ClientCertTest, BasicCertValidation) {
TEST_P(ClientCertTest, BasicCertValidation) {
const uint32_t system_id = 1234;
const std::string serial_number("serial_number");
std::unique_ptr<SignedDrmCertificate> signed_cert(
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
nullptr, system_id, serial_number),
system_id, serial_number + "-device"));
GenerateSignedDrmCertificate(
GenerateSignedIntermediateCertificate(nullptr, system_id,
serial_number, kNoSigner),
system_id, serial_number + "-device", GetParam()));
const TestCertificateAndData kValidCertificateAndExpectedData(
signed_cert->SerializeAsString(), serial_number, system_id, OkStatus());
const bool compare_data = true;
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
compare_data);
}
INSTANTIATE_TEST_SUITE_P(BasicCertValidation, ClientCertTest,
testing::Values(DrmCertificate::RSA,
DrmCertificate::ECC_SECP256R1,
DrmCertificate::ECC_SECP384R1,
DrmCertificate::ECC_SECP521R1));
TEST_F(ClientCertTest, InvalidKeybox) {
const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = {
@@ -350,18 +398,19 @@ TEST_F(ClientCertTest, InvalidCertificate) {
GenerateSignature(invalid_drm_cert->drm_certificate(),
test_rsa_keys_.private_test_key_2_2048_bits(),
invalid_drm_cert->mutable_signature());
invalid_drm_cert->set_allocated_signer(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
invalid_drm_cert->set_allocated_signer(GenerateSignedIntermediateCertificate(
nullptr, system_id, signer_sn, kNoSigner));
// Invalid device public key.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
dev_cert->set_public_key("bad-device-public-key");
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
*dev_cert,
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(
SignCertificate(*dev_cert,
GenerateSignedIntermediateCertificate(
nullptr, system_id, signer_sn, kNoSigner),
test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid serialized intermediate certificate.
signed_signer.reset(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
signed_signer.reset(GenerateSignedIntermediateCertificate(
nullptr, system_id, signer_sn, kNoSigner));
signed_signer->set_drm_certificate("bad-serialized-cert");
GenerateSignature(signed_signer->drm_certificate(),
test_rsa_keys_.private_test_key_1_3072_bits(),
@@ -382,7 +431,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
// Invalid device certificate signature.
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
GenerateSignedDrmCertificate(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn,
kNoSigner),
system_id, device_sn));
bad_device_signature->set_signature("bad-signature");
// Missing model system ID.
@@ -405,8 +455,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid serialized intermediate certificate.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signed_signer.reset(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
signed_signer.reset(GenerateSignedIntermediateCertificate(
nullptr, system_id, signer_sn, kNoSigner));
signed_signer->set_signature("bad-signature");
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
SignCertificate(*dev_cert, signed_signer.release(),
@@ -453,8 +503,9 @@ TEST_F(ClientCertTest, MissingPreProvKey) {
"00000002012345678e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e"));
ClientCert* client_cert_ptr = nullptr;
Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr);
std::unique_ptr<ClientCert> client_cert_ptr;
Status status = ClientCert::Create(
root_cert_.get(), ClientIdentification::KEYBOX, token, &client_cert_ptr);
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
}
@@ -470,9 +521,9 @@ TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
service_id));
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
system_id,
intermediate_serial_number));
GenerateSignedIntermediateCertificate(
signed_provisioner_cert.release(), system_id,
intermediate_serial_number, kProvisionerSigner));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
@@ -480,13 +531,12 @@ TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
ClientCert* client_cert_ptr = nullptr;
std::unique_ptr<ClientCert> drm_cert;
EXPECT_OK(ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr));
ASSERT_TRUE(client_cert_ptr != nullptr);
std::unique_ptr<ClientCert> drm_cert(client_cert_ptr);
serialized_cert, &drm_cert));
ASSERT_TRUE(drm_cert);
EXPECT_EQ(service_id, drm_cert->service_id());
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
@@ -506,9 +556,9 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertEmptyServiceId) {
service_id));
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
system_id,
intermediate_serial_number));
GenerateSignedIntermediateCertificate(
signed_provisioner_cert.release(), system_id,
intermediate_serial_number, kProvisionerSigner));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
@@ -516,7 +566,7 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertEmptyServiceId) {
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
ClientCert* client_cert_ptr = nullptr;
std::unique_ptr<ClientCert> client_cert_ptr;
EXPECT_EQ("missing-provisioning-service-id",
ClientCert::Create(root_cert_.get(),
@@ -535,27 +585,119 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
const std::string intermediate_serial_number2("intermediate-serial-number-2");
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
GenerateSignedIntermediateCertificate(nullptr, system_id2,
intermediate_serial_number2));
GenerateSignedIntermediateCertificate(
nullptr, system_id2, intermediate_serial_number2, kNoSigner));
// Instead of using a provisioner certificate to sign this intermediate
// certificate, use another intermediate certificate. This is an invalid
// chain and should generate an error when trying to create a client
// certificate.
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(signed_intermediate_cert2.release(),
system_id,
intermediate_serial_number));
GenerateSignedIntermediateCertificate(
signed_intermediate_cert2.release(), system_id,
intermediate_serial_number, kDeviceModelSigner));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
system_id, device_serial_number));
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
ClientCert* client_cert_ptr = nullptr;
std::unique_ptr<ClientCert> client_cert_ptr;
// TODO(user): Fix this test. It is failing for the right reasons, but the
// certificate chain is broken (intermediate signature does not match signer).
ASSERT_EQ("cache-miss-invalid-signature",
ASSERT_EQ("expected-provisioning-provider-certificate-type",
ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message());
EXPECT_FALSE(client_cert_ptr);
}
TEST_F(ClientCertTest, InvalidDeviceCertChainSize_TooLong) {
const uint32_t system_id = 5000;
const std::string service_id("widevine_test.com");
const std::string device_serial_number("device-serial-number");
const std::string intermediate_serial_number1("intermediate-serial-number-1");
const std::string intermediate_serial_number2("intermediate-serial-number-2");
const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id));
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert1(
GenerateSignedIntermediateCertificate(
signed_provisioner_cert.release(), system_id,
intermediate_serial_number1, kProvisionerSigner));
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
GenerateSignedIntermediateCertificate(
signed_intermediate_cert1.release(), system_id,
intermediate_serial_number2, kDeviceModelSigner));
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert2.release(),
system_id, device_serial_number));
std::string serialized_cert;
signed_device_cert->SerializeToString(&serialized_cert);
std::unique_ptr<ClientCert> client_cert_ptr = nullptr;
ASSERT_EQ("certificate-chain-size-exceeded",
ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message());
EXPECT_FALSE(client_cert_ptr);
}
TEST_F(ClientCertTest, DeviceCertTypeNotLeaf) {
const uint32_t system_id = 5000;
const std::string service_id("widevine_test.com");
const std::string intermediate_serial_number("intermediate-serial-number");
const std::string provisioner_serial_number("provisioner-serial-number");
const std::string drm_serial_number("drm-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id));
// Use a DEVICE certificate as the intermediate certificate.
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedDrmCertificate(signed_provisioner_cert.release(), system_id,
intermediate_serial_number));
std::unique_ptr<SignedDrmCertificate> signed_drm_cert(
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
system_id, drm_serial_number));
std::string serialized_cert;
signed_drm_cert->SerializeToString(&serialized_cert);
std::unique_ptr<ClientCert> client_cert_ptr;
EXPECT_EQ("device-cert-must-be-leaf",
ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
.error_message());
EXPECT_FALSE(client_cert_ptr);
}
TEST_F(ClientCertTest, InvalidLeafCertificateType) {
const uint32_t system_id = 5000;
const std::string service_id("widevine_test.com");
const std::string intermediate_serial_number("intermediate-serial-number");
const std::string provisioner_serial_number("provisioner-serial-number");
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
service_id));
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
GenerateSignedIntermediateCertificate(
signed_provisioner_cert.release(), system_id,
intermediate_serial_number, kProvisionerSigner));
std::string serialized_cert;
signed_intermediate_cert->SerializeToString(&serialized_cert);
std::unique_ptr<ClientCert> client_cert_ptr;
// Leaf certificate must be a device certificate.
EXPECT_EQ("expected-device-certificate-type",
ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE,
serialized_cert, &client_cert_ptr)
@@ -566,11 +708,10 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
TEST_F(ClientCertTest, Protocol21WithDrmCert) {
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(
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
test_drm_certs_.test_user_device_certificate(), &client_cert));
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
@@ -592,11 +733,10 @@ TEST_F(ClientCertTest, Protocol22WithDrmCert) {
const char message[] = "There is nothing permanent except change.";
const std::string message_hash(Sha512_Hash(message));
ClientCert* client_cert_ptr = nullptr;
std::unique_ptr<ClientCert> client_cert;
ASSERT_OK(ClientCert::Create(
root_cert_.get(), ClientIdentification::DRM_DEVICE_CERTIFICATE,
test_drm_certs_.test_user_device_certificate(), &client_cert_ptr));
std::unique_ptr<ClientCert> client_cert(client_cert_ptr);
test_drm_certs_.test_user_device_certificate(), &client_cert));
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));

View File

@@ -10,9 +10,13 @@
#include "glog/logging.h"
#include "common/aes_cbc_util.h"
#include "common/client_cert.h"
#include "common/drm_service_certificate.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/signed_drm_certificate.pb.h"
namespace widevine {
@@ -42,7 +46,8 @@ 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()) {
if (nv.name() == name) {
return nv.value();
@@ -86,4 +91,28 @@ Status DecryptEncryptedClientIdentification(
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

View File

@@ -37,7 +37,8 @@ std::string GetClientInfo(const ClientIdentification& client_id,
// Return the value from client_id.client_info() matching the given name,
// or the given default value if not found.
std::string GetClientInfo(const ClientIdentification& client_id,
absl::string_view name, const std::string& default_value);
absl::string_view name,
const std::string& default_value);
// Decrypts the encrypted client identification in |encrypted_client_id| into
// |client_id| using the private key for the service certificate which was
@@ -56,6 +57,8 @@ Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
const std::string& privacy_key, ClientIdentification* client_id);
uint32_t GetSystemId(const ClientIdentification& client_id);
} // namespace widevine
#endif // COMMON_CLIENT_ID_UTIL_H_

64
common/content_id_util.cc Normal file
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.
////////////////////////////////////////////////////////////////////////////////
#include "common/content_id_util.h"
#include "common/error_space.h"
#include "common/status.h"
#include "license_server_sdk/internal/parse_content_id.h"
#include "protos/public/errors.pb.h"
#include "protos/public/external_license.pb.h"
#include "protos/public/license_protocol.pb.h"
#include "protos/public/license_server_sdk.pb.h"
#include "protos/public/widevine_pssh.pb.h"
namespace widevine {
// TODO(user): Move the util methods from
// //license_server_sdk/internal/parse_content_id.h
// into this file.
Status GetContentIdFromExternalLicenseRequest(
const ExternalLicenseRequest& external_license_request,
std::string* content_id) {
LicenseRequest::ContentIdentification content_identification =
external_license_request.content_id();
WidevinePsshData widevine_pssh_data;
if (content_identification.has_widevine_pssh_data()) {
widevine_pssh_data.ParseFromString(
content_identification.widevine_pssh_data().pssh_data(0));
} else if (content_identification.has_webm_key_id()) {
widevine_pssh_data.ParseFromString(
content_identification.webm_key_id().header());
} else if (content_identification.has_init_data()) {
ContentInfo content_info;
if (ParseContentId(content_identification, &content_info).ok()) {
widevine_pssh_data =
content_info.content_info_entry(0).pssh().widevine_data();
}
}
*content_id = widevine_pssh_data.content_id();
return OkStatus();
}
Status GetContentIdFromSignedExternalLicenseRequest(
const SignedMessage& signed_message, std::string* content_id) {
if (signed_message.type() != SignedMessage::EXTERNAL_LICENSE_REQUEST) {
return Status(
error_space, error::INVALID_ARGUMENT,
"Unexpected SignedMessage Type. EXTERNAL_LICENSE_REQUEST expected");
}
ExternalLicenseRequest external_license_request;
if (!external_license_request.ParseFromString(signed_message.msg())) {
return Status(error_space, EXTERNAL_LICENSE_REQUEST_PARSE_ERROR,
"Unable to parse into External License Request");
}
return GetContentIdFromExternalLicenseRequest(external_license_request,
content_id);
}
} // namespace widevine

30
common/content_id_util.h Normal file
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_CONTENT_ID_UTIL_H_
#define COMMON_CONTENT_ID_UTIL_H_
#include "common/status.h"
#include "protos/public/external_license.pb.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {
// Get content identifier as a std::string from a SignedMessage that includes a
// serialized ExternalLicenseRequest.
Status GetContentIdFromSignedExternalLicenseRequest(
const SignedMessage& signed_message, std::string* content_id);
// Get content identifier as a std::string from an ExternalLicenseRequest.
Status GetContentIdFromExternalLicenseRequest(
const ExternalLicenseRequest& external_license_request,
std::string* content_id);
} // namespace widevine
#endif // COMMON_CONTENT_ID_UTIL_H_

View File

@@ -0,0 +1,81 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/content_id_util.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "protos/public/errors.pb.h"
#include "protos/public/external_license.pb.h"
#include "protos/public/license_protocol.pb.h"
#include "protos/public/widevine_pssh.pb.h"
namespace {
const char kContentId[] = "TestContentId";
const char kPlayReadyChallenge[] = "<TestPRChallenge></TestPRChallenge>";
} // namespace
namespace widevine {
// Builds a SignedMessage that includes an ExternalLicenseRequest.
SignedMessage BuildSignedExternalLicenseRequest(
const ExternalLicenseRequest::RequestType type, const std::string& request,
const std::string& content_id) {
ExternalLicenseRequest external_license_request;
external_license_request.set_request_type(type);
external_license_request.set_request(request);
LicenseRequest::ContentIdentification::WidevinePsshData* cenc_id =
external_license_request.mutable_content_id()
->mutable_widevine_pssh_data();
WidevinePsshData widevine_pssh_data;
widevine_pssh_data.set_content_id(content_id);
std::string widevine_pssh_string;
widevine_pssh_data.SerializeToString(&widevine_pssh_string);
cenc_id->add_pssh_data(widevine_pssh_string);
SignedMessage signed_message;
signed_message.set_type(SignedMessage::EXTERNAL_LICENSE_REQUEST);
EXPECT_TRUE(
external_license_request.SerializeToString(signed_message.mutable_msg()));
return signed_message;
}
TEST(ContentIdUtil, GetContentId) {
std::string content_id;
EXPECT_OK(GetContentIdFromSignedExternalLicenseRequest(
BuildSignedExternalLicenseRequest(
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST,
kPlayReadyChallenge, kContentId),
&content_id));
EXPECT_EQ(kContentId, content_id);
}
TEST(ContentIdUtil, GetContentIdFailureWithIncorrectType) {
std::string content_id;
SignedMessage signed_message = BuildSignedExternalLicenseRequest(
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST, kPlayReadyChallenge,
kContentId);
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
Status status =
GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id);
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
EXPECT_TRUE(content_id.empty());
}
TEST(ContentIdUtil, GetContentIdFailureWithInvalidExternalLicenseRequest) {
std::string content_id;
SignedMessage signed_message = BuildSignedExternalLicenseRequest(
ExternalLicenseRequest::PLAYREADY_LICENSE_REQUEST, kPlayReadyChallenge,
kContentId);
signed_message.set_msg("Invalid payload");
Status status =
GetContentIdFromSignedExternalLicenseRequest(signed_message, &content_id);
EXPECT_EQ(EXTERNAL_LICENSE_REQUEST_PARSE_ERROR, status.error_code());
EXPECT_TRUE(content_id.empty());
}
} // namespace widevine

View File

@@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/core_message_util.h"
#include "common/oemcrypto_core_message/odk/include/core_message_deserialize.h"
#include "common/oemcrypto_core_message/odk/include/core_message_serialize.h"
#include "common/oemcrypto_core_message/odk/include/core_message_serialize_proto.h"
#include "common/sha_util.h"
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
using oemcrypto_core_message::serialize::CreateCoreLicenseResponseFromProto;
using oemcrypto_core_message::serialize::
CreateCoreProvisioningResponseFromProto;
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
using widevine::Sha256_Hash;
namespace widevine {
namespace core_message_util {
void GetCoreProvisioningResponse(const std::string& provisioning_response,
std::string* core_message) {
oemcrypto_core_message::ODK_ProvisioningRequest odk_provisioning_request;
CoreProvisioningRequestFromMessage(*core_message, &odk_provisioning_request);
CreateCoreProvisioningResponseFromProto(
provisioning_response, odk_provisioning_request, core_message);
}
void GetCoreRenewalOrReleaseLicenseResponse(std::string* core_message) {
oemcrypto_core_message::ODK_RenewalRequest odk_renewal_request;
CoreRenewalRequestFromMessage(*core_message, &odk_renewal_request);
CreateCoreRenewalResponse(odk_renewal_request, core_message);
}
void GetCoreNewLicenseResponse(const std::string& license,
std::string* core_message) {
oemcrypto_core_message::ODK_LicenseRequest odk_license_request;
CoreLicenseRequestFromMessage(*core_message, &odk_license_request);
std::string core_request_sha256 = Sha256_Hash(*core_message);
CreateCoreLicenseResponseFromProto(license, odk_license_request,
core_request_sha256, core_message);
}
} // namespace core_message_util
} // namespace widevine

View File

@@ -0,0 +1,33 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef COMMON_CORE_MESSAGE_UTIL_H_
#define COMMON_CORE_MESSAGE_UTIL_H_
#include <string>
namespace widevine {
namespace core_message_util {
// Gets the core message from |provisioning_response|. The output is held in
// |core_message|. The provisioning response to be sent will be updated with
// this |core_message|.
void GetCoreProvisioningResponse(const std::string& provisioning_response,
std::string* core_message);
// Gets the core message for renewal or release license response. The output
// is held in |core_message|.
void GetCoreRenewalOrReleaseLicenseResponse(std::string* core_message);
// Gets the core message from |license|. The output is held in |core_message|.
// The license to be sent will be updated with this |core_message|.
void GetCoreNewLicenseResponse(const std::string& license,
std::string* core_message);
} // namespace core_message_util
} // namespace widevine
#endif // COMMON_CORE_MESSAGE_UTIL_H_

View File

@@ -17,7 +17,6 @@
#include "openssl/evp.h"
#include "openssl/hmac.h"
#include "openssl/sha.h"
#include "util/endian/endian.h"
namespace widevine {
namespace crypto_util {
@@ -41,6 +40,11 @@ const char kGroupKeyLabel[] = "GROUP_ENCRYPTION";
const char kPhonyGroupMasterKey[] = "fedcba9876543210";
const int kAes128KeySizeBits = 128;
const int kAes128KeySizeBytes = 16;
const int kAes256KeySizeBytes = 32;
const char kKeyboxV3Label[] = "Keyboxv3";
const int kAesBlockSizeBits = AES_BLOCK_SIZE * 8;
const int kAesMaxDerivedBlocks = 255;
const uint32_t kCENCSchemeID = 0x63656E63; // 'cenc' (AES-CTR): 0x63656E63
const uint32_t kCBC1SchemeID = 0x63626331; // 'cbc1' (AES-CBC): 0x63626331
@@ -94,31 +98,45 @@ bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view 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,
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.
const uint32_t output_block_count = size_bits / kAesBlockSizeBits;
if (size_bits % kAesBlockSizeBits ||
output_block_count > kAesMaxDerivedBlocks) {
return "";
}
// We only handle even multiples of 16 bytes (128 bits) right now.
if ((size_bits % 128) || (size_bits > (128 * 255))) {
const EVP_CIPHER* cipher = nullptr;
const size_t key_size_bytes = key.size();
switch (key_size_bytes) {
case kAes128KeySizeBytes:
cipher = EVP_aes_128_cbc();
break;
case kAes256KeySizeBytes:
cipher = EVP_aes_256_cbc();
break;
default:
return "";
}
std::string result;
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
for (unsigned char counter = 1; counter <= (size_bits / 128); counter++) {
if (CMAC_Init(cmac_ctx, key.data(), key.size(), cipher, 0)) {
for (unsigned char counter = 0; counter < output_block_count; counter++) {
if (CMAC_Init(cmac_ctx, key.data(), key_size_bytes, cipher, nullptr)) {
std::string message;
message.append(1, counter);
message.append(1, counter + 1);
message.append(label.data(), label.size());
message.append(1, '\0');
message.append(context.data(), context.size());
char size_string[4];
BigEndian::Store32(&size_string, size_bits);
message.append(&size_string[0], &size_string[0] + 4);
message.append(1, (size_bits >> 24) & 0xFF);
message.append(1, (size_bits >> 16) & 0xFF);
message.append(1, (size_bits >> 8) & 0xFF);
message.append(1, size_bits & 0xFF);
if (CMAC_Update(cmac_ctx, reinterpret_cast<const uint8_t*>(message.data()),
message.size())) {
size_t reslen;

View File

@@ -14,7 +14,6 @@
#include <string>
#include "base/macros.h"
#include "absl/strings/string_view.h"
namespace widevine {
@@ -32,6 +31,7 @@ extern const char kIvLabel[];
extern const int kIvSizeBits;
extern const int kAes128KeySizeBits;
extern const int kAes128KeySizeBytes;
extern const char kKeyboxV3Label[];
extern const uint32_t kCENCSchemeID; // 'cenc' (AES-CTR): 0x63656E63
extern const uint32_t kCBC1SchemeID; // 'cbc1' (AES-CBC): 0x63626331
@@ -53,7 +53,8 @@ std::string DeriveIv(absl::string_view context);
std::string DeriveKeyId(absl::string_view context);
// Helper function to derive a key using the group master key and context.
std::string DeriveGroupSessionKey(absl::string_view context, const uint32_t size_bits);
std::string DeriveGroupSessionKey(absl::string_view context,
const uint32_t size_bits);
// Helper function to derive a signing key for from the signing context.
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,

View File

@@ -8,6 +8,8 @@
// Unit tests for the crypto_util helper functions.
#include "common/crypto_util.h"
#include <string>
#include "testing/gmock.h"
@@ -15,7 +17,7 @@
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "common/crypto_util.h"
#include "openssl/aes.h"
namespace widevine {
namespace crypto_util {
@@ -25,19 +27,33 @@ const char kCBC1Str[] = "cbc1";
const char kCENSStr[] = "cens";
const char kCBCSStr[] = "cbcs";
static unsigned char key_data[] =
{ 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e,
0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b };
static unsigned char kAes128KeyData[] = {0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82,
0x32, 0x9e, 0x6b, 0x3b, 0x4e, 0x29,
0xfa, 0x3b, 0x00, 0x4b};
static std::string key_str(key_data, key_data + sizeof(key_data));
static unsigned char 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[] =
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
static unsigned char kAes128IvData[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f};
static std::string iv_str(iv_data, iv_data + sizeof(iv_data));
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,
0x92, 0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08,
0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60,
@@ -60,22 +76,88 @@ TEST(CryptoUtilTest, DeriveAes128KeyTest) {
0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22 };
std::string label_str(label, label + sizeof(label));
std::string key_str(key_data, key_data + sizeof(key_data));
std::string context_str(context, context + sizeof(context));
std::string result = DeriveKey(key_str, label_str, context_str, 128);
std::string result = DeriveKey(aes_128_key_, label_str, context_str, 128);
std::string output_128(output0, output0 + sizeof(output0));
ASSERT_EQ(result, output_128);
result = DeriveKey(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));
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,
0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1 };
std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128);
@@ -84,7 +166,7 @@ TEST(CryptoUtilTest, DeriveGroupSesionKey) {
ASSERT_EQ(output_128, group_session_key);
}
TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
TEST_F(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
unsigned char message_data[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
@@ -96,14 +178,14 @@ TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
std::string message(message_data, message_data + sizeof(message_data));
std::string signature(CreateSignatureHmacSha256(key_str, message));
std::string signature(CreateSignatureHmacSha256(aes_128_key_, message));
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[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
@@ -123,20 +205,20 @@ TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) {
ASSERT_EQ(signature.size(), 32);
// Create valid signature to compare.
signature = CreateSignatureHmacSha256(key_str, message);
signature = CreateSignatureHmacSha256(aes_128_key_, message);
// Test with bogus key.
ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message));
// Test with munged signature.
signature[0] = 0xFF;
ASSERT_FALSE(VerifySignatureHmacSha256(key_str, signature, message));
ASSERT_FALSE(VerifySignatureHmacSha256(aes_128_key_, signature, message));
// 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[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
@@ -148,13 +230,13 @@ TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) {
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
std::string message(message_data, message_data + sizeof(message_data));
std::string signature(CreateSignatureHmacSha1(key_str, message));
std::string signature(CreateSignatureHmacSha1(aes_128_key_, message));
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[] = {
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
@@ -173,17 +255,17 @@ TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) {
// This should still produce an hmac signature.
ASSERT_EQ(20, signature.size());
// Create valid signature to compare.
signature = CreateSignatureHmacSha1(key_str, message);
signature = CreateSignatureHmacSha1(aes_128_key_, message);
// Test with bogus key.
ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message));
// Test with munged signature.
signature[0] = 0xFF;
ASSERT_FALSE(VerifySignatureHmacSha1(key_str, signature, message));
ASSERT_FALSE(VerifySignatureHmacSha1(aes_128_key_, signature, message));
// 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.
std::pair<std::string, std::string> id_iv_pairs[] = {
{"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.
std::pair<std::string, std::string> context_id_pairs[] = {
{"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"},
@@ -213,14 +295,14 @@ TEST(CryptoUtilTest, DeriveKeyId) {
}
}
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) {
TEST_F(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) {
uint32_t cc_code;
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code));
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code));
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code));
}
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromString) {
TEST_F(CryptoUtilTest, Verify4CCEncryptionIDFromString) {
uint32_t cc_code = 0;
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code));
ASSERT_EQ(kCENCSchemeID, cc_code);

View File

@@ -0,0 +1,41 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Implements the device info helper function.
#include "common/device_info_util.h"
#include "absl/strings/ascii.h"
namespace widevine {
bool IsMatchedMakeModel(const std::string& expected_make,
const std::string& expected_model,
const std::string& make_from_client,
const std::string& model_from_client) {
return absl::AsciiStrToLower(expected_make) ==
absl::AsciiStrToLower(make_from_client) &&
absl::AsciiStrToLower(expected_model) ==
absl::AsciiStrToLower(model_from_client);
}
bool VerifyMakeModel(const ProvisionedDeviceInfo& device_info,
const std::string& make_from_client,
const std::string& model_from_client) {
if (IsMatchedMakeModel(device_info.manufacturer(), device_info.model(),
make_from_client, model_from_client)) {
return true;
}
for (ProvisionedDeviceInfo::ModelInfo product_info :
device_info.model_info()) {
if (IsMatchedMakeModel(product_info.manufacturer(), product_info.model(),
make_from_client, model_from_client)) {
return true;
}
}
return false;
}
} // namespace widevine

29
common/device_info_util.h Normal file
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.
////////////////////////////////////////////////////////////////////////////////
#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 <time.h>
#include <map>
#include <memory>
#include <cstdint>
#include "glog/logging.h"
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
@@ -23,9 +27,17 @@
#include "common/client_cert.h"
#include "common/drm_service_certificate.h"
#include "common/error_space.h"
#include "common/keybox_client_cert.h"
#include "common/rsa_key.h"
#include "common/status.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/signed_device_info.pb.h"
using ::widevine::DeviceCertificateStatusListRequest;
using ::widevine::SignedDeviceInfo;
using ::widevine::SignedDeviceInfoRequest;
namespace widevine {
@@ -44,29 +56,19 @@ DeviceStatusList* DeviceStatusList::Instance() {
return device_status_list;
}
DeviceStatusList::DeviceStatusList()
: creation_time_seconds_(0),
expiration_period_seconds_(0),
allow_unknown_devices_(true),
allow_test_only_devices_(false) {}
DeviceStatusList::DeviceStatusList() {}
DeviceStatusList::~DeviceStatusList() {}
Status DeviceStatusList::UpdateStatusList(
const std::string& root_certificate_public_key,
const std::string& serialized_certificate_status_list,
uint32_t expiration_period_seconds) {
SignedDeviceCertificateStatusList signed_certificate_status_list;
if (!signed_certificate_status_list.ParseFromString(
serialized_certificate_status_list)) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"signed-certificate-status-list-parse-error");
}
if (!signed_certificate_status_list.has_certificate_status_list()) {
const std::string& serialized_device_certificate_status_list,
const std::string& signature, uint32_t expiration_period_seconds) {
if (serialized_device_certificate_status_list.empty()) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list");
}
if (!signed_certificate_status_list.has_signature()) {
if (signature.empty()) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list-signature");
}
@@ -76,17 +78,16 @@ Status DeviceStatusList::UpdateStatusList(
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-public-key");
}
if (!root_key->VerifySignature(
signed_certificate_status_list.certificate_status_list(),
signed_certificate_status_list.signature())) {
if (!root_key->VerifySignature(serialized_device_certificate_status_list,
signature)) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"invalid-status-list-signature");
}
DeviceCertificateStatusList certificate_status_list;
if (!certificate_status_list.ParseFromString(
signed_certificate_status_list.certificate_status_list())) {
serialized_device_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 &&
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
@@ -117,6 +118,7 @@ Status DeviceStatusList::UpdateStatusList(
}
Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
const std::string& device_manufacturer,
ProvisionedDeviceInfo* device_info) {
CHECK(device_info);
@@ -162,9 +164,20 @@ Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
if ((device_cert_status->status() ==
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
!allow_test_only_devices_) {
if (IsTestOnlyDeviceAllowed(client_cert.system_id(),
device_manufacturer)) {
LOG(WARNING) << "Allowing TEST_ONLY device with systemId = "
<< client_cert.system_id()
<< "make = " << device_manufacturer
<< ", device info = " << device_info->ShortDebugString();
} else {
VLOG(2) << "Not allowing TEST ONLY device with systemId = "
<< client_cert.system_id() << "make = " << device_manufacturer
<< ", device info = " << device_info->ShortDebugString();
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
"test-only-drm-certificate-not-allowed");
}
}
if (!client_cert.signed_by_provisioner() &&
(client_cert.signer_serial_number() !=
device_cert_status->drm_serial_number())) {
@@ -198,13 +211,27 @@ bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
absl::ReaderMutexLock lock(&status_map_lock_);
DeviceCertificateStatus* device_cert_status =
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();
return true;
}
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) {
absl::ReaderMutexLock lock(&status_map_lock_);
DeviceCertificateStatus* device_cert_status =
@@ -241,31 +268,121 @@ void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) {
std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end());
}
void DeviceStatusList::AllowTestOnlyDevices(const std::string& device_list) {
absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_);
if (device_list.empty()) {
allowed_test_only_devices_.clear();
return;
}
for (absl::string_view device : absl::StrSplit(device_list, ',')) {
const std::pair<absl::string_view, absl::string_view> device_split =
absl::StrSplit(device, ':');
if (device_split.second.empty() || device_split.second == "*") {
allowed_test_only_devices_.emplace(
std::stoi(std::string(device_split.first)), "*");
VLOG(2) << "Whitelisting TEST_ONLY device: systemId = "
<< std::stoi(std::string(device_split.first))
<< ", manufacturer = *";
} else {
allowed_test_only_devices_.emplace(
std::stoi(std::string(device_split.first)),
absl::AsciiStrToUpper(device_split.second));
VLOG(2) << "Whitelisting TEST_ONLY device: systemId = "
<< std::stoi(std::string(device_split.first))
<< ", manufacturer = "
<< absl::AsciiStrToUpper(device_split.second);
}
}
}
bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
auto it = std::binary_search(allowed_revoked_devices_.begin(),
allowed_revoked_devices_.end(), system_id);
return it;
}
Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
const std::string& certificate_provisioning_service_response,
std::string* signed_certificate_status_list, std::string* certificate_status_list) {
Status status = OkStatus();
size_t signed_list_start =
certificate_provisioning_service_response.find(kSignedList);
if (signed_list_start != std::string::npos) {
size_t signed_list_end = certificate_provisioning_service_response.find(
kSignedListTerminator, signed_list_start);
if (signed_list_end == std::string::npos) {
bool DeviceStatusList::IsTestOnlyDeviceAllowed(uint32_t system_id,
const std::string manufacturer) {
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_manufacturers = allowed_test_only_devices_.equal_range(system_id);
for (auto it = allowed_manufacturers.first;
it != allowed_manufacturers.second; ++it) {
std::string allowed_manufacturer = (*it).second;
if (allowed_manufacturer == "*" ||
allowed_manufacturer == absl::AsciiStrToUpper(manufacturer)) {
return true;
}
}
return false;
}
Status DeviceStatusList::DetermineAndDeserializeServiceResponse(
const std::string& service_response,
DeviceCertificateStatusList* certificate_status_list,
std::string* serialized_certificate_status_list, std::string* signature) {
if (certificate_status_list == nullptr) {
return Status(error_space, error::INVALID_ARGUMENT,
"certificate_status_list is empty");
} else if (serialized_certificate_status_list == nullptr) {
return Status(error_space, error::INVALID_ARGUMENT,
"serialized_certificate_status_list is empty");
} else if (signature == nullptr) {
return Status(error_space, error::INVALID_ARGUMENT, "signature is empty");
}
// We support three types of payload parsing. The legacy path checks for a
// JSON encoded payload as well as just a plain base64 (web safe or normal)
// payload. If that doesn't match, then the method will try to parse the
// serialized PublishedDeviceInfo proto.
Status status = ExtractPublishedDevicesInfo(
service_response, serialized_certificate_status_list, signature);
// If the payload was not correctly parsed as a PublishedDevices proto.
// then attempt to parse it as a legacy payload.
if (!status.ok()) {
status = ExtractLegacyDeviceList(
service_response, serialized_certificate_status_list, signature);
// The payload could not be parsed in either format, return the failure
// information.
if (!status.ok()) {
return status;
}
}
if (!certificate_status_list->ParseFromString(
*serialized_certificate_status_list)) {
return Status(error_space, widevine::INVALID_CERTIFICATE_STATUS_LIST,
"certificate-status-list-parse-error");
}
return OkStatus();
}
Status DeviceStatusList::ExtractLegacyDeviceList(
const std::string& raw_certificate_provisioning_service_response,
std::string* serialized_certificate_status_list, std::string* signature) {
// First, attempt to extract the legacy JSON response. Example legacy format.
// "signedList":"<b64 encoded data>"
// where the b64 encoded data is a DeviceCertificateStatusListResponse.
size_t b64_list_response_start =
raw_certificate_provisioning_service_response.find(kSignedList);
std::string serialized_signed_certificate_status_list;
if (b64_list_response_start != std::string::npos) {
size_t b64_list_response_end =
raw_certificate_provisioning_service_response.find(
kSignedListTerminator, b64_list_response_start);
if (b64_list_response_end == std::string::npos) {
return Status(
error_space, error::INVALID_ARGUMENT,
"Unable to parse the certificate_provisioning_service_response. "
"SignedList not terminated.");
}
std::string signed_list(
certificate_provisioning_service_response.begin() + signed_list_start +
kSignedListLen,
certificate_provisioning_service_response.begin() + signed_list_end);
raw_certificate_provisioning_service_response.begin() +
b64_list_response_start + kSignedListLen,
raw_certificate_provisioning_service_response.begin() +
b64_list_response_end);
// Strip off quotes.
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\"'),
@@ -281,46 +398,55 @@ Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
// Strip off carriage return (the control-M character)
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\r'),
signed_list.end());
if (!absl::WebSafeBase64Unescape(signed_list,
signed_certificate_status_list)) {
if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) {
if (!absl::WebSafeBase64Unescape(
signed_list, &serialized_signed_certificate_status_list)) {
if (!absl::Base64Unescape(signed_list,
&serialized_signed_certificate_status_list)) {
return Status(error_space, error::INVALID_ARGUMENT,
"Base64 decode of signedlist failed.");
}
}
} else {
// certificate_provisioning_service_response is the signed list and not a
// JSON message.
if (!absl::WebSafeBase64Unescape(certificate_provisioning_service_response,
signed_certificate_status_list)) {
if (!absl::Base64Unescape(certificate_provisioning_service_response,
signed_certificate_status_list)) {
// If this was not a legacy JSON response, attempt to deserialize the base64
// response.
if (!absl::WebSafeBase64Unescape(
raw_certificate_provisioning_service_response,
&serialized_signed_certificate_status_list)) {
if (!absl::Base64Unescape(raw_certificate_provisioning_service_response,
&serialized_signed_certificate_status_list)) {
return Status(error_space, error::INVALID_ARGUMENT,
"Base64 decode of certList failed.");
}
}
}
SignedDeviceCertificateStatusList signed_status_list;
if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"signed-certificate-status-list-parse-error");
// Attempt to parse the legacy serialized signed status list into the proto
// and extract the serialized status list and signature.
return ParseLegacySignedDeviceCertificateStatusList(
serialized_signed_certificate_status_list,
serialized_certificate_status_list, signature);
}
if (!signed_status_list.has_certificate_status_list()) {
Status DeviceStatusList::ExtractPublishedDevicesInfo(
const std::string& serialized_published_devices,
std::string* serialized_certificate_status_list, std::string* signature) {
// TODO(b/139067045): Change from using the SignedDeviceInfo proto
// to using the correct proto from the API. This duplicate, wire-compatible
// proto was a temporary way to workaround Proto2/Proto3 compatibility issues.
SignedDeviceInfo devices_info;
if (!devices_info.ParseFromString(serialized_published_devices)) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list");
"published-devices-info-parse-error");
}
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();
*serialized_certificate_status_list =
devices_info.device_certificate_status_list();
*signature = devices_info.signature();
return OkStatus();
}
Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
const std::string& version,
const std::string& serialized_service_certificate,
std::string* signed_device_certificate_status_list_request) {
if (version.empty()) {
return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty");
@@ -334,9 +460,10 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
DeviceCertificateStatusListRequest request;
request.set_sdk_version(version);
request.set_sdk_time_seconds(DeviceStatusList::Instance()->GetCurrentTime());
request.set_service_certificate(serialized_service_certificate);
std::string 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(
device_certificate_status_list_request);
const DrmServiceCertificate* sc =
@@ -359,4 +486,50 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
signed_device_certificate_status_list_request);
return OkStatus();
}
Status DeviceStatusList::ParseLegacySignedDeviceCertificateStatusList(
const std::string& serialized_signed_device_certificate_status_list,
std::string* serialized_device_certificate_status_list,
std::string* signature) {
// Parse the serialized_signed_device_certificate_status_list to extract the
// serialized_device_certificate_status_list
SignedDeviceCertificateStatusList signed_device_list;
if (!signed_device_list.ParseFromString(
serialized_signed_device_certificate_status_list)) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"signed-certificate-status-list-parse-error");
}
if (signed_device_list.certificate_status_list().empty()) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list");
}
if (signed_device_list.signature().empty()) {
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list-signature");
}
*serialized_device_certificate_status_list =
signed_device_list.certificate_status_list();
*signature = signed_device_list.signature();
return OkStatus();
}
void DeviceStatusList::RevokedDrmCertificateSerialNumbers(
const std::string& drm_certificate_serial_numbers) {
for (absl::string_view drm_certificate_serial_number :
absl::StrSplit(drm_certificate_serial_numbers, ',')) {
revoked_drm_certificate_serial_numbers_.insert(
std::string(drm_certificate_serial_number));
}
}
bool DeviceStatusList::IsDrmCertificateRevoked(
const std::string& device_certificate_serial_number) {
if (revoked_drm_certificate_serial_numbers_.find(
device_certificate_serial_number) !=
revoked_drm_certificate_serial_numbers_.end()) {
return true;
}
return false;
}
} // namespace widevine

View File

@@ -12,9 +12,9 @@
#define COMMON_DEVICE_STATUS_LIST_H__
#include <map>
#include <set>
#include <string>
#include "base/macros.h"
#include "absl/synchronization/mutex.h"
#include "common/status.h"
#include "protos/public/device_certificate_status.pb.h"
@@ -35,14 +35,20 @@ class DeviceStatusList {
static DeviceStatusList* Instance();
DeviceStatusList();
DeviceStatusList(const DeviceStatusList&) = delete;
DeviceStatusList& operator=(const DeviceStatusList&) = delete;
virtual ~DeviceStatusList();
// Takes |serialized_certificate_status_list| and copies to an internal map of
// device certifcate status list. The internal map is used to verify
// a device was not revoked. Returns true is the list was successfully parsed.
Status UpdateStatusList(const std::string& root_certificate_public_key,
const std::string& serialized_certificate_status_list,
uint32_t expiration_period_seconds);
// Takes |serialized_device_certificate_status_list| and copies to an
// internal map of device certificate status list. The internal map is used
// to verify a device was not revoked. Returns true is the list was
// successfully parsed.
Status UpdateStatusList(
const std::string& root_certificate_public_key,
const std::string& serialized_device_certificate_status_list,
const std::string& signature, uint32_t expiration_period_seconds);
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
bool allow_unknown_devices() const { return allow_unknown_devices_; }
void set_allow_test_only_devices(bool allow) {
@@ -50,15 +56,20 @@ class DeviceStatusList {
}
bool allow_test_only_devices() const { return allow_test_only_devices_; }
// 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
// UNSUPPORTED_SYSTEM_ID
// INVALID_DRM_CERTIFICATE
// DRM_DEVICE_CERTIFICATE_REVOKED
// DRM_DEVICE_CERTIFICATE_UNKNOWN
// If a TEST_ONLY device using "make" as identified by |device_manufacturer|,
// was not whitelisted, then will return
// DEVELOPMENT_CERTIFICATE_NOT_ALLOWED
// If status is OK, a copy of the provisioned device info is copied
// into |device_info|. Caller owns |device_info| and it must not be null.
Status GetCertStatus(const ClientCert& client_cert,
const std::string& device_manufacturer,
widevine::ProvisionedDeviceInfo* device_info);
// Returns true if the pre-provisioning key or certificate for the specified
// system ID are active (not disallowed or revoked).
@@ -69,6 +80,14 @@ class DeviceStatusList {
// Caller owns <device_info> and it must not be null.
bool GetDeviceInfo(const ClientCert& client_cert,
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.
virtual uint32_t GetCurrentTime() const;
@@ -76,18 +95,56 @@ class DeviceStatusList {
// a comma separated list of systems Ids to allow even if revoked.
virtual void AllowRevokedDevices(const std::string& system_id_list);
/**
* Parses signed device certificate status list and certificate status list
* from certificateProvisoningServer response.
*
* @param certificate_provisioning_service_response
* @param signed_certificate_status_list
* @param certificate_status_list
* @return WvPLStatus - Status::OK if success, else error.
*/
static Status ExtractFromProvisioningServiceResponse(
const std::string& certificate_provisioning_service_response,
std::string* signed_certificate_status_list, std::string* certificate_status_list);
// Enable delivery of licenses to TEST_ONLY client devices. |device_list| is
// a comma separated list of devices to allow even if the device state is
// TEST_ONLY. Each device is specified by a colon separated system_id and
// manufacturer. If the manufacturer is not specified, all manufacturers for
// that system_id are allowed.
// 'device_list' is expected to be of the format <device>,<device>..., and
// each 'device' will contain a 'system_id' and 'manufacturer' OR will contain
// only a 'system_id'.
// 'device' is expected to be of the format <system_id>:<manufacturer> OR
// of the format <system_id>:
// Example usage:
// const std::string device_list = "4121:LG,7912:*"
// AllowTestOnlyDevices(device_list);
virtual void AllowTestOnlyDevices(const std::string& device_list);
// A comma separated list of DRM Certificate Serial Numbers that are revoked.
virtual void RevokedDrmCertificateSerialNumbers(
const std::string& drm_certificate_serial_numbers);
// Return true, if the specified |device_certificate_serial_number| was
// revoked ... else, false.
bool IsDrmCertificateRevoked(
const std::string& device_certificate_serial_number);
// Parses the serialized certificate status list and the signature from the
// service_response. The service_response is the JSON payload that comes
// in the response to a certificate status list request. Both the legacy
// format and the newer SignedDeviceInfo format are supported.
//
// |service_response| is the response provided from the Widevine API that
// produces the certificate list. The response can be in one of a few
// formats:
// 1) The JSON response from the legacy API.
// 2) The Base 64 encoded payload within the JSON response that contains
// the serialized certificate list (Web safe or regular base64).
// 3) The raw bytes of the serialized PublishedDevices proto returned from
// the new Widevine API that generates the serialized certificate list.
// The |certificate_status_list| is the deserialized list from the
// service_response.
// The |serialized_certificate_status_list| is the binary serialized status
// list. This is an out parameter which allows the caller to verify the
// serialized proto against the |signature|.
// The |signature| is the signature of the serialized_certificate_status_list
// using RSASSA-PSS signed with the root certificate private key.
// Returns WvPLStatus - Status::OK if success, else error.
static Status DetermineAndDeserializeServiceResponse(
const std::string& service_response,
DeviceCertificateStatusList* certificate_status_list,
std::string* serialized_certificate_status_list, std::string* signature);
/**
* Constructs signed device certificate status list request string.
*
@@ -97,25 +154,80 @@ class DeviceStatusList {
*/
static Status GenerateSignedDeviceCertificateStatusListRequest(
const std::string& version,
const std::string& serialized_service_certificate,
std::string* signed_device_certificate_status_list_request);
private:
friend class DeviceStatusListTest;
/**
* Parses the serialized legacy device certificate status list and signature.
* The certificate_provisioning_service_response is the JSON payload that
* comes in the response to a certificate status list request.
*
* @param legacy_certificate_provisioning_service_response
* @param serialized_certificate_status_list
* @param signature
* @return WvPLStatus - Status::OK if success, else error.
*/
static Status ExtractLegacyDeviceList(
const std::string& raw_certificate_provisioning_service_response,
std::string* serialized_certificate_status_list, std::string* signature);
/**
* Parses the serialized published devices response.
* The published_devices_info_response is the JSON payload that comes in the
* response to a PublishedDevices request.
*
* @param published_devices_response the serialized PublishedDevices proto
* containing the certificate status list.
* @param serialized_certificate_status_list
* @param signature
* @return WvPLStatus - Status::OK if success, else error.
*/
static Status ExtractPublishedDevicesInfo(
const std::string& serialized_published_devices,
std::string* serialized_certificate_status_list, std::string* signature);
/**
* Returns a |serialized_device_certificate_status_list| in its output
* parameter by parsing |serialized_signed_device_certificate_status_list|
* returned from Widevine Certificate Provisioning Server.
*
* @param serialized_signed_device_certificate_status_list
* @param serialized_device_certificate_status_list
*
* @return Status - Status::OK if success, else error.
*/
static Status ParseLegacySignedDeviceCertificateStatusList(
const std::string& serialized_signed_device_certificate_status_list,
std::string* serialized_device_certificate_status_list,
std::string* signature);
// Returns true if the system ID is allowed to be revoked.
// Caller owns |system_id|. They must not be null.
bool IsRevokedSystemIdAllowed(uint32_t system_id);
// Returns true if the device, which is identified by system_id and
// device_manufacturer, is present in |allowed_test_only_devices_|.
bool IsTestOnlyDeviceAllowed(uint32_t system_id,
const std::string device_manufacturer);
absl::Mutex status_map_lock_;
// Key is the system id for the device.
std::map<uint32_t, widevine::DeviceCertificateStatus> device_status_map_;
uint32_t creation_time_seconds_;
uint32_t expiration_period_seconds_;
bool allow_unknown_devices_;
bool allow_test_only_devices_;
uint32_t creation_time_seconds_ = 0;
uint32_t expiration_period_seconds_ = 0;
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
// revoked.
std::vector<uint32_t> allowed_revoked_devices_;
DISALLOW_COPY_AND_ASSIGN(DeviceStatusList);
absl::Mutex allowed_test_only_devices_mutex_;
// Contains a map of 'system_id' to 'make'. If 'make' value is "*", any
// 'make' for that 'system_id' is allowed.
std::multimap<uint32_t, std::string> allowed_test_only_devices_;
// Revoked DRM certificate serial numbers.
std::set<std::string> revoked_drm_certificate_serial_numbers_;
};
} // namespace widevine

View File

@@ -9,22 +9,39 @@
#include "common/device_status_list.h"
#include <stddef.h>
#include <memory>
#include <type_traits>
#include <utility>
#include "glog/logging.h"
#include "google/protobuf/util/message_differencer.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "common/client_cert.h"
#include "common/keybox_client_cert.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h"
#include "common/status.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/provisioned_device_info.pb.h"
#include "protos/public/signed_device_info.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
namespace {
const char kTestSystemId_1[] = "4121";
const char kTestManufacturer_LG[] = "LG";
const char kTestManufacturer_LGE[] = "LGE";
const char kTestSystemId_2[] = "8242";
const char kTestManufacturer_Samsung[] = "Samsung";
const char kTestSystemId_3[] = "6556";
const char kTestManufacturer[] = "TestManufacturer";
} // namespace
namespace widevine {
using ::testing::_;
@@ -42,6 +59,10 @@ const char kValidSerialNumber[] = "valid-serial-number";
const char kRevokedSerialNumber[] = "revoked-serial-number";
const char kRevokedAllowDeviceSerialNumber[] =
"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 kMismatchSerialNumber[] = "mismatch-serial-number";
const char kDeviceModel[] = "device-model-x";
@@ -49,21 +70,25 @@ const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
const uint32_t kStatusListCreationTime = 17798001;
const uint32_t kDefaultExpirePeriod = 0;
class MockCertificateClientCert : public CertificateClientCert {
class MockClientCert : public ClientCert {
public:
MockCertificateClientCert() {}
MockClientCert() {}
~MockClientCert() override {}
MOCK_CONST_METHOD0(system_id, uint32_t());
MOCK_CONST_METHOD0(signer_serial_number, std::string &());
MOCK_CONST_METHOD0(signer_creation_time_seconds, uint32_t());
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
MOCK_CONST_METHOD0(signed_by_provisioner, bool());
};
class MockKeyboxClientCert : public KeyboxClientCert {
public:
MockKeyboxClientCert() {}
MOCK_CONST_METHOD0(system_id, uint32_t());
MOCK_CONST_METHOD0(type, ClientIdentification::TokenType());
MOCK_CONST_METHOD3(VerifySignature, Status(const std::string &message,
const std::string &signature,
ProtocolVersion protocol_version));
MOCK_METHOD2(GenerateSigningKey, void(const std::string &message,
ProtocolVersion protocol_version));
MOCK_CONST_METHOD0(serial_number, const std::string &());
MOCK_CONST_METHOD0(key, const std::string &());
MOCK_CONST_METHOD0(service_id, const std::string &());
MOCK_CONST_METHOD0(encrypted_key, const std::string &());
MOCK_CONST_METHOD0(signing_key, const std::string &());
};
class DeviceStatusListTest : public ::testing::Test {
@@ -79,6 +104,10 @@ class DeviceStatusListTest : public ::testing::Test {
cert_status->set_drm_serial_number(kValidSerialNumber);
cert_status->mutable_device_info()->set_model(kDeviceModel);
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.
cert_status = cert_status_list_.add_certificate_status();
@@ -102,36 +131,77 @@ class DeviceStatusListTest : public ::testing::Test {
cert_status->set_status(DeviceCertificateStatus::STATUS_TEST_ONLY);
cert_status_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(
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_,
&cert_status_list_signature_));
ASSERT_TRUE(root_key->GenerateSignature(
signed_cert_status_list_.certificate_status_list(),
signed_cert_status_list_.mutable_signature()));
ASSERT_TRUE(
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
ASSERT_EQ(OkStatus(), device_status_list_.UpdateStatusList(
// Update the device_status_list_ with the serialized status list
// and signature.
ASSERT_EQ(OkStatus(),
device_status_list_.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, kDefaultExpirePeriod));
serialized_cert_status_list_, cert_status_list_signature_,
kDefaultExpirePeriod));
}
void GenerateTrivialValidStatusList(std::string *serialized_cert_status_list,
std::string *signature) {
DeviceCertificateStatusList cert_status_list;
DeviceCertificateStatus *cert_status;
// Device cert with status RELEASED.
cert_status = cert_status_list.add_certificate_status();
cert_status->mutable_device_info()->set_system_id(kValidCertSystemId);
cert_status->set_drm_serial_number(kValidSerialNumber);
cert_status->mutable_device_info()->set_model(kDeviceModel);
cert_status->set_status(DeviceCertificateStatus::STATUS_RELEASED);
cert_status_list.set_creation_time_seconds(kStatusListCreationTime);
// Generate the serialized list and signature.
std::unique_ptr<RsaPrivateKey> root_key(
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
ASSERT_TRUE(root_key);
cert_status_list.SerializeToString(serialized_cert_status_list);
ASSERT_TRUE(
root_key->GenerateSignature(*serialized_cert_status_list, signature));
}
int VerifyAllowedTestOnlyDevicesAdded() {
return device_status_list_.allowed_test_only_devices_.size();
}
bool VerifyIsTestOnlyDeviceAllowed(uint32_t system_id,
std::string manufacturer) {
return device_status_list_.IsTestOnlyDeviceAllowed(system_id, manufacturer);
}
int VerifyRevokedDeviceCertificatesCount() {
return device_status_list_.revoked_drm_certificate_serial_numbers_.size();
}
bool VerifyIsDeviceCertificateRevoked(
std::string device_certificate_serial_number) {
return device_status_list_.IsDrmCertificateRevoked(
device_certificate_serial_number);
}
DeviceStatusList device_status_list_;
RsaTestKeys test_keys_;
DeviceCertificateStatusList cert_status_list_;
SignedDeviceCertificateStatusList signed_cert_status_list_;
std::string serialized_status_list_;
std::string serialized_cert_status_list_;
std::string cert_status_list_signature_;
};
// Returns the number of DevcieCertificateStatus messages in the list.
TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
// Test case where the Certificate status is set to Valid.
ProvisionedDeviceInfo device_info;
MockCertificateClientCert valid_client_cert;
MockClientCert valid_client_cert;
std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
@@ -140,12 +210,13 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
EXPECT_CALL(valid_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
device_status_list_.GetCertStatus(valid_client_cert,
kTestManufacturer, &device_info));
EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model());
// Test case where the Certificate status is Revoked.
MockCertificateClientCert revoked_client_cert;
MockClientCert revoked_client_cert;
std::string revoked_drm_serial_number(kRevokedSerialNumber);
EXPECT_CALL(revoked_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
@@ -153,19 +224,21 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
.WillRepeatedly(Return(kRevokedCertSystemId));
EXPECT_CALL(revoked_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(revoked_drm_serial_number));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED,
device_status_list_.GetCertStatus(revoked_client_cert, &device_info)
EXPECT_EQ(
DRM_DEVICE_CERTIFICATE_REVOKED,
device_status_list_
.GetCertStatus(revoked_client_cert, kTestManufacturer, &device_info)
.error_code());
// Test case where the revoked cert is allowed.
device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId));
EXPECT_OK(
device_status_list_.GetCertStatus(revoked_client_cert, &device_info));
EXPECT_OK(device_status_list_.GetCertStatus(revoked_client_cert,
kTestManufacturer, &device_info));
}
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
ProvisionedDeviceInfo device_info;
MockCertificateClientCert test_only_client_cert;
MockClientCert test_only_client_cert;
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
EXPECT_CALL(test_only_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
@@ -175,13 +248,26 @@ TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
EXPECT_EQ(
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
device_status_list_.GetCertStatus(test_only_client_cert, &device_info)
device_status_list_
.GetCertStatus(test_only_client_cert, kTestManufacturer, &device_info)
.error_code());
}
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
TEST_F(DeviceStatusListTest, GetRevokedIfentifiers) {
DeviceCertificateStatus::RevokedIdentifiers revoked_identifiers;
ASSERT_TRUE(device_status_list_.GetRevokedIdentifiers(kValidCertSystemId,
&revoked_identifiers));
EXPECT_EQ(kRevokedSerialNumber,
revoked_identifiers.revoked_certificate_serial_numbers(0));
EXPECT_EQ(kRevokedUniqueIdentifiers,
revoked_identifiers.revoked_unique_id_hashes(0));
ASSERT_FALSE(device_status_list_.GetRevokedIdentifiers(kUnknownSystemId,
&revoked_identifiers));
}
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
ProvisionedDeviceInfo device_info;
MockCertificateClientCert test_only_client_cert;
MockClientCert test_only_client_cert;
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
device_status_list_.set_allow_test_only_devices(true);
EXPECT_CALL(test_only_client_cert, type())
@@ -190,8 +276,9 @@ TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
.WillRepeatedly(Return(kTestOnlyCertSystemId));
EXPECT_CALL(test_only_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(test_only_client_cert,
&device_info));
EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(test_only_client_cert,
kTestManufacturer, &device_info));
}
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
@@ -201,25 +288,27 @@ TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
// Test case where the Certificate status is set to Valid.
ProvisionedDeviceInfo device_info;
MockKeyboxClientCert valid_client_keybox;
MockClientCert valid_client_keybox;
std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_keybox, type())
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(valid_client_keybox, system_id())
.WillRepeatedly(Return(kValidCertSystemId));
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(valid_client_keybox,
&device_info));
EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(valid_client_keybox,
kTestManufacturer, &device_info));
EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model());
MockKeyboxClientCert unknown_client_keybox;
MockClientCert unknown_client_keybox;
EXPECT_CALL(unknown_client_keybox, type())
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(unknown_client_keybox, system_id())
.WillRepeatedly(Return(kUnknownSystemId));
EXPECT_EQ(
UNSUPPORTED_SYSTEM_ID,
device_status_list_.GetCertStatus(unknown_client_keybox, &device_info)
device_status_list_
.GetCertStatus(unknown_client_keybox, kTestManufacturer, &device_info)
.error_code());
EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model());
@@ -230,7 +319,7 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
// Test case where the signer certificate is older than the current status
// list.
MockCertificateClientCert older_client_cert;
MockClientCert older_client_cert;
ProvisionedDeviceInfo device_info;
std::string mismatch_drm_serial_number(kMismatchSerialNumber);
EXPECT_CALL(older_client_cert, type())
@@ -241,21 +330,24 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(older_client_cert, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime - 1));
EXPECT_EQ(INVALID_DRM_CERTIFICATE,
device_status_list_.GetCertStatus(older_client_cert, &device_info)
EXPECT_EQ(
INVALID_DRM_CERTIFICATE,
device_status_list_
.GetCertStatus(older_client_cert, kTestManufacturer, &device_info)
.error_code());
// We allow this case only for certs signed by a provisioner cert.
EXPECT_CALL(older_client_cert, signed_by_provisioner())
.WillOnce(Return(true));
EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(older_client_cert, &device_info));
device_status_list_.GetCertStatus(older_client_cert,
kTestManufacturer, &device_info));
EXPECT_TRUE(device_info.has_system_id());
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
// Test case where the signer certificate is newer than the current status
// list, and unknown devices are allowed.
MockCertificateClientCert newer_client_cert1;
MockClientCert newer_client_cert1;
EXPECT_CALL(newer_client_cert1, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(newer_client_cert1, system_id())
@@ -264,14 +356,16 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_.GetCertStatus(newer_client_cert1, &device_info)
EXPECT_EQ(
DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_
.GetCertStatus(newer_client_cert1, kTestManufacturer, &device_info)
.error_code());
// Test case where the signer certificate is newer than the current status
// list, and unknown devices are not allowed.
device_status_list_.set_allow_unknown_devices(false);
MockCertificateClientCert newer_client_cert2;
MockClientCert newer_client_cert2;
EXPECT_CALL(newer_client_cert2, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
EXPECT_CALL(newer_client_cert2, system_id())
@@ -280,8 +374,10 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime + 1));
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_.GetCertStatus(newer_client_cert2, &device_info)
EXPECT_EQ(
DRM_DEVICE_CERTIFICATE_UNKNOWN,
device_status_list_
.GetCertStatus(newer_client_cert2, kTestManufacturer, &device_info)
.error_code());
}
@@ -289,16 +385,16 @@ TEST_F(DeviceStatusListTest, InvalidStatusList) {
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
device_status_list_
.UpdateStatusList(test_keys_.public_test_key_2_2048_bits(),
serialized_status_list_, 0)
serialized_cert_status_list_,
cert_status_list_signature_, 0)
.error_code());
++(*signed_cert_status_list_.mutable_certificate_status_list())[4];
ASSERT_TRUE(
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
++(serialized_cert_status_list_)[4];
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
device_status_list_
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 0)
serialized_cert_status_list_,
cert_status_list_signature_, 0)
.error_code());
}
@@ -315,11 +411,13 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
.WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100));
serialized_cert_status_list_,
cert_status_list_signature_, 100));
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100)
serialized_cert_status_list_,
cert_status_list_signature_, 100)
.error_code());
}
@@ -332,10 +430,11 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
.WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100));
serialized_cert_status_list_,
cert_status_list_signature_, 100));
ProvisionedDeviceInfo device_info;
MockCertificateClientCert valid_client_cert;
MockClientCert valid_client_cert;
std::string valid_drm_serial_number(kValidSerialNumber);
EXPECT_CALL(valid_client_cert, type())
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
@@ -345,12 +444,14 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime - 1));
EXPECT_EQ(OkStatus(), mock_device_status_list.GetCertStatus(valid_client_cert,
&device_info));
EXPECT_EQ(OkStatus(),
mock_device_status_list.GetCertStatus(
valid_client_cert, kTestManufacturer, &device_info));
EXPECT_EQ(
EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list.GetCertStatus(valid_client_cert, &device_info)
mock_device_status_list
.GetCertStatus(valid_client_cert, kTestManufacturer, &device_info)
.error_code());
}
@@ -373,4 +474,219 @@ TEST_F(DeviceStatusListTest, IsSystemIdActive) {
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
}
TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowed) {
std::string whitelisted_device_list =
std::string(kTestSystemId_1) + ":" + std::string(kTestManufacturer_LG);
whitelisted_device_list += "," + std::string(kTestSystemId_2) + ":" +
std::string(kTestManufacturer_Samsung);
whitelisted_device_list += "," + std::string(kTestSystemId_3) + ":";
whitelisted_device_list += ", " + std::string(kTestSystemId_1) + ":" +
std::string(kTestManufacturer_LGE);
device_status_list_.AllowTestOnlyDevices(whitelisted_device_list);
EXPECT_EQ(4, VerifyAllowedTestOnlyDevicesAdded());
// Verify that device with system_id = kTestSystemId_1 and
// manufacturer = kTestManufacturer_LG is allowed.
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_1),
kTestManufacturer_LG));
// Verify that device with system_id = kTestSystemId_1 and
// manufacturer = kTestManufacturer_LGE is allowed.
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_1),
kTestManufacturer_LGE));
// Verify that device with system_id = kTestSystemId_2 and
// manufacturer = kTestManufacturer_LGE is not allowed.
// This is because this combination is not 'whitelisted'.
EXPECT_FALSE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2),
kTestManufacturer_LGE));
// Verify that device with system_id = kTestSystemId_2 and
// manufacturer = kTestManufacturer_Samsung is allowed.
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2),
kTestManufacturer_Samsung));
// Verifes that device with mixed case succeeds.
EXPECT_TRUE(
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), "samSung"));
EXPECT_TRUE(
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), "SAMsung"));
// Verify that device with system_id = kTestSystemId_3 and
// any manufacturer is allowed. This checks that any manufacturer is
// allowed for this system_id.
EXPECT_TRUE(
VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3), "Cisco"));
EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3),
"ScientificAtlanta"));
uint32_t unknown_system_id = 7890;
// Verify that device with system_id = unknown_system_id and
// manufacturer = "Cisco" is not allowed.
EXPECT_FALSE(VerifyIsTestOnlyDeviceAllowed(unknown_system_id, "Cisco"));
}
TEST_F(DeviceStatusListTest, IsDrmDeviceCertificateRevoked) {
std::string revoked_device_certificate_serial_numbers =
std::string(kRevokedDeviceCertificateSerialNumber1);
revoked_device_certificate_serial_numbers +=
"," + std::string(kRevokedDeviceCertificateSerialNumber2);
revoked_device_certificate_serial_numbers +=
"," + std::string(kRevokedDeviceCertificateSerialNumber3);
device_status_list_.RevokedDrmCertificateSerialNumbers(
revoked_device_certificate_serial_numbers);
EXPECT_EQ(3, VerifyRevokedDeviceCertificatesCount());
// Verify that device certificates with serial number,
// 'kRevokedDeviceCertificateSerialNumber1',
// 'kRevokedDeviceCertificateSerialNumber2',
// 'kRevokedDeviceCertificateSerialNumber3' are revoked.
EXPECT_TRUE(
VerifyIsDeviceCertificateRevoked(kRevokedDeviceCertificateSerialNumber1));
EXPECT_TRUE(
VerifyIsDeviceCertificateRevoked(kRevokedDeviceCertificateSerialNumber2));
EXPECT_TRUE(
VerifyIsDeviceCertificateRevoked(kRevokedDeviceCertificateSerialNumber3));
const std::string unrevoked_device_certificate_serial_number =
"unrevoked_device_certificate_serial_number";
EXPECT_FALSE(VerifyIsDeviceCertificateRevoked(
unrevoked_device_certificate_serial_number));
}
TEST_F(DeviceStatusListTest, DetermineAndDeserializeServiceResponseSuccess) {
SignedDeviceInfo published_devices;
GenerateTrivialValidStatusList(
published_devices.mutable_device_certificate_status_list(),
published_devices.mutable_signature());
std::string serialized_published_devices;
ASSERT_TRUE(
published_devices.SerializeToString(&serialized_published_devices));
DeviceCertificateStatusList actual_cert_status_list;
std::string actual_serialized_cert_status_list;
std::string actual_signature;
ASSERT_EQ(OkStatus(),
DeviceStatusList::DetermineAndDeserializeServiceResponse(
serialized_published_devices, &actual_cert_status_list,
&actual_serialized_cert_status_list, &actual_signature));
EXPECT_EQ(published_devices.device_certificate_status_list(),
actual_serialized_cert_status_list);
EXPECT_EQ(published_devices.signature(), actual_signature);
DeviceCertificateStatusList expected_cert_status_list;
ASSERT_TRUE(expected_cert_status_list.ParseFromString(
published_devices.device_certificate_status_list()));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_cert_status_list, actual_cert_status_list));
}
TEST_F(DeviceStatusListTest,
DetermineAndDeserializeServiceResponseLegacySuccess) {
std::string serialized_cert_status_list;
std::string signature;
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
*(legacy_signed_cert_status_list.mutable_certificate_status_list()) =
serialized_cert_status_list;
*(legacy_signed_cert_status_list.mutable_signature()) = signature;
std::string serialized_signed_cert_status_list;
ASSERT_TRUE(legacy_signed_cert_status_list.SerializeToString(
&serialized_signed_cert_status_list));
std::string websafe_b64_serialized_signed_cert_status_list;
absl::WebSafeBase64Escape(serialized_signed_cert_status_list,
&websafe_b64_serialized_signed_cert_status_list);
std::string server_response = absl::StrCat(
"{\n"
" \"kind\": \"certificateprovisioning#certificateStatusListResponse\"\n"
" \"signedList\": \"",
websafe_b64_serialized_signed_cert_status_list, "\"\n}\n");
std::string actual_serialized_cert_status_list;
std::string actual_signature;
DeviceCertificateStatusList actual_cert_status_list;
ASSERT_EQ(OkStatus(),
DeviceStatusList::DetermineAndDeserializeServiceResponse(
server_response, &actual_cert_status_list,
&actual_serialized_cert_status_list, &actual_signature));
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
EXPECT_EQ(signature, actual_signature);
DeviceCertificateStatusList expected_cert_status_list;
ASSERT_TRUE(
expected_cert_status_list.ParseFromString(serialized_cert_status_list));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_cert_status_list, actual_cert_status_list));
}
TEST_F(DeviceStatusListTest,
DetermineAndDeserializeServiceResponseLegacyWebSafeBase64Success) {
std::string serialized_cert_status_list;
std::string signature;
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
*(legacy_signed_cert_status_list.mutable_certificate_status_list()) =
serialized_cert_status_list;
*(legacy_signed_cert_status_list.mutable_signature()) = signature;
std::string serialized_signed_cert_status_list;
ASSERT_TRUE(legacy_signed_cert_status_list.SerializeToString(
&serialized_signed_cert_status_list));
std::string websafe_b64_serialized_signed_cert_status_list;
absl::WebSafeBase64Escape(serialized_signed_cert_status_list,
&websafe_b64_serialized_signed_cert_status_list);
std::string actual_serialized_cert_status_list;
std::string actual_signature;
DeviceCertificateStatusList actual_cert_status_list;
ASSERT_EQ(OkStatus(),
DeviceStatusList::DetermineAndDeserializeServiceResponse(
websafe_b64_serialized_signed_cert_status_list,
&actual_cert_status_list, &actual_serialized_cert_status_list,
&actual_signature));
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
EXPECT_EQ(signature, actual_signature);
DeviceCertificateStatusList expected_cert_status_list;
ASSERT_TRUE(
expected_cert_status_list.ParseFromString(serialized_cert_status_list));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_cert_status_list, actual_cert_status_list));
}
TEST_F(DeviceStatusListTest,
DetermineAndDeserializeServiceResponseLegacyBase64Success) {
std::string serialized_cert_status_list;
std::string signature;
GenerateTrivialValidStatusList(&serialized_cert_status_list, &signature);
SignedDeviceCertificateStatusList legacy_signed_cert_status_list;
*(legacy_signed_cert_status_list.mutable_certificate_status_list()) =
serialized_cert_status_list;
*(legacy_signed_cert_status_list.mutable_signature()) = signature;
std::string serialized_signed_cert_status_list;
ASSERT_TRUE(legacy_signed_cert_status_list.SerializeToString(
&serialized_signed_cert_status_list));
std::string websafe_b64_serialized_signed_cert_status_list;
absl::WebSafeBase64Escape(serialized_signed_cert_status_list,
&websafe_b64_serialized_signed_cert_status_list);
std::string actual_serialized_cert_status_list;
std::string actual_signature;
DeviceCertificateStatusList actual_cert_status_list;
ASSERT_EQ(OkStatus(),
DeviceStatusList::DetermineAndDeserializeServiceResponse(
websafe_b64_serialized_signed_cert_status_list,
&actual_cert_status_list, &actual_serialized_cert_status_list,
&actual_signature));
EXPECT_EQ(serialized_cert_status_list, actual_serialized_cert_status_list);
EXPECT_EQ(signature, actual_signature);
DeviceCertificateStatusList expected_cert_status_list;
ASSERT_TRUE(
expected_cert_status_list.ParseFromString(serialized_cert_status_list));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_cert_status_list, actual_cert_status_list));
}
} // namespace widevine

View File

@@ -16,9 +16,11 @@
#include "absl/memory/memory.h"
#include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h"
#include "common/ec_key.h"
#include "common/error_space.h"
#include "common/rsa_key.h"
#include "common/sha_util.h"
#include "common/signer_public_key.h"
#include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h"
@@ -33,6 +35,13 @@ const char kTestingString[] = "test"; // Code development / unit tests.
const bool kUseCache = true;
// 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.
// TODO(user): common::test_certificates is a testonly target, consider
// how to use instead of dupliciating the test cert here.
@@ -248,12 +257,17 @@ static const unsigned char kProdRootCertificate[] = {
// number (signer).
struct VerifiedCertSignature {
VerifiedCertSignature(const std::string& cert, const std::string& sig,
const std::string& signer_sn)
: signed_cert(cert), signature(sig), signer_serial(signer_sn) {}
const std::string& 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 signature;
std::string signer_serial;
std::string signer_public_key;
};
// Map of certificate serial number to its signature.
@@ -265,50 +279,57 @@ class VerifiedCertSignatureCache {
// Checks cache, on miss, uses public key. If successful, adds to
// cache.
Status VerifySignature(const std::string& cert, const std::string& serial_number,
Status VerifySignature(const std::string& cert,
const std::string& serial_number,
const std::string& signature,
const std::string& signer_public_key,
const std::string& signer_serial_number) {
const DrmCertificate& signer) {
{
VerifiedCertSignatures::iterator cached_signature;
absl::ReaderMutexLock read_lock(&signature_cache_mutex_);
cached_signature = signature_cache_.find(serial_number);
if (cached_signature != signature_cache_.end()) {
// TODO(user): Log which of the following three conditions occurs.
if ((cert != cached_signature->second.signed_cert) ||
(signature != cached_signature->second.signature) ||
(signer_serial_number != cached_signature->second.signer_serial)) {
// Cached signature mismatch.
if (cert != cached_signature->second.signed_cert) {
return Status(error_space, INVALID_SIGNATURE, "cached-cert-mismatch");
}
if (signature != cached_signature->second.signature) {
return Status(error_space, INVALID_SIGNATURE,
"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();
}
}
// Cache miss. Verify signature.
std::unique_ptr<RsaPublicKey> signer_key(
key_factory_->CreateFromPkcs1PublicKey(signer_public_key));
if (!signer_key) {
std::unique_ptr<SignerPublicKey> signer_public_key =
SignerPublicKey::Create(signer.public_key(), signer.algorithm());
if (signer_public_key == nullptr) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-public-key");
}
if (!signer_key->VerifySignature(cert, signature)) {
if (!signer_public_key->VerifySignature(cert, signature)) {
return Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature");
}
// Add signature to cache.
absl::WriterMutexLock write_lock(&signature_cache_mutex_);
signature_cache_.emplace(
serial_number,
VerifiedCertSignature(cert, signature, signer_serial_number));
VerifiedCertSignature(cert, signature, signer.serial_number(),
signer.public_key()));
return OkStatus();
}
private:
VerifiedCertSignatures signature_cache_ GUARDED_BY(&signature_cache_mutex_);
VerifiedCertSignatures signature_cache_
ABSL_GUARDED_BY(&signature_cache_mutex_);
absl::Mutex signature_cache_mutex_;
const RsaKeyFactory* key_factory_;
};
@@ -330,7 +351,8 @@ std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
}
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);
CertificateType cert_type;
@@ -400,9 +422,9 @@ Status DrmRootCertificate::Create(CertificateType cert_type,
"missing-root-certificate-signature");
}
std::unique_ptr<RsaPublicKey> public_key(
key_factory->CreateFromPkcs1PublicKey(root_cert.public_key()));
if (!public_key) {
std::unique_ptr<SignerPublicKey> public_key =
SignerPublicKey::Create(root_cert.public_key(), root_cert.algorithm());
if (public_key == nullptr) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-public-key");
}
@@ -424,10 +446,11 @@ DrmRootCertificate::DrmRootCertificate(
std::unique_ptr<RsaKeyFactory> key_factory)
: type_(type),
serialized_certificate_(serialized_certificate),
serial_number_(serial_number),
public_key_(public_key),
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() {}
@@ -472,8 +495,9 @@ Status DrmRootCertificate::VerifyCertificate(
}
// Verify signature chain, but do not use cache for leaf certificates.
uint32_t certs_in_chain = 0;
return VerifySignatures(*signed_certificate, certificate->serial_number(),
!kUseCache);
!kUseCache, &certs_in_chain);
}
// Recursively verifies certificates with their signing certs or the root.
@@ -483,39 +507,48 @@ Status DrmRootCertificate::VerifyCertificate(
// Signatures for root-signed certificates are always cached, even if they are
// leaf certificates. For example service, and provisioner certificates.
Status DrmRootCertificate::VerifySignatures(
const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number,
bool use_cache) const {
const SignedDrmCertificate& signed_cert,
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()) {
// Always use cache for root-signed certificates.
return signature_cache_->VerifySignature(
signed_cert.drm_certificate(), cert_serial_number,
signed_cert.signature(), public_key(), serial_number_);
signed_cert.signature(), root_cert_);
}
DrmCertificate signer;
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
return Status(error_space, INVALID_DRM_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.
Status status =
VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache);
Status status = VerifySignatures(signed_cert.signer(), signer.serial_number(),
kUseCache, certs_in_chain);
if (!status.ok()) {
return status;
}
if (use_cache) {
status = signature_cache_->VerifySignature(
signed_cert.drm_certificate(), cert_serial_number,
signed_cert.signature(), signer.public_key(), signer.serial_number());
status = signature_cache_->VerifySignature(signed_cert.drm_certificate(),
cert_serial_number,
signed_cert.signature(), signer);
if (!status.ok()) {
return status;
}
} else {
std::unique_ptr<RsaPublicKey> signer_public_key(
key_factory_->CreateFromPkcs1PublicKey(signer.public_key()));
if (!signer_public_key) {
std::unique_ptr<SignerPublicKey> signer_public_key =
SignerPublicKey::Create(signer.public_key(), signer.algorithm());
if (signer_public_key == nullptr) {
return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-leaf-signer-public-key");
}

View File

@@ -18,10 +18,10 @@
#include <memory>
#include <string>
#include "base/macros.h"
#include "common/status.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 {
@@ -35,6 +35,9 @@ class VerifiedCertSignatureCache;
// This object is thread-safe.
class DrmRootCertificate {
public:
DrmRootCertificate(const DrmRootCertificate&) = delete;
DrmRootCertificate& operator=(const DrmRootCertificate&) = delete;
virtual ~DrmRootCertificate();
// Creates a DrmRootCertificate object given a certificate type.
@@ -72,12 +75,15 @@ class DrmRootCertificate {
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:
DrmRootCertificate(CertificateType cert_type,
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);
private:
@@ -88,17 +94,16 @@ class DrmRootCertificate {
std::unique_ptr<DrmRootCertificate>* cert);
Status VerifySignatures(const SignedDrmCertificate& signed_cert,
const std::string& cert_serial_number,
bool use_cache) const;
const std::string& cert_serial_number, bool use_cache,
uint32_t* certs_in_chain) const;
CertificateType type_;
std::string serialized_certificate_;
std::string serial_number_;
std::string public_key_;
DrmCertificate root_cert_;
// TODO(b/143309971): Either add an ec key_factory object, or drop the rsa
// |key_factory_|.
std::unique_ptr<RsaKeyFactory> key_factory_;
mutable std::unique_ptr<VerifiedCertSignatureCache> signature_cache_;
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmRootCertificate);
};
} // namespace widevine

View File

@@ -13,9 +13,13 @@
#include <memory>
#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/ec_key.h"
#include "common/ec_test_keys.h"
#include "common/error_space.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h"
@@ -25,6 +29,7 @@
#include "protos/public/signed_drm_certificate.pb.h"
using google::protobuf::util::MessageDifferencer;
using ::testing::Values;
namespace widevine {
@@ -80,76 +85,214 @@ TEST(DrmRootCertificateTestCertificatesTest, Success) {
->VerifyCertificate(test_certs.test_user_device_certificate(),
nullptr, nullptr)
.ok());
EXPECT_TRUE(root_cert
->VerifyCertificate(test_certs.test_service_certificate(),
EXPECT_TRUE(
root_cert
->VerifyCertificate(test_certs.test_service_certificate_no_type(),
nullptr, nullptr)
.ok());
}
class DrmRootCertificateTest : public testing::Test {
protected:
DrmRootCertificateTest() {
private_keys_.emplace_back(
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
private_keys_.emplace_back(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
private_keys_.emplace_back(
RsaPrivateKey::Create(test_keys_.private_test_key_3_2048_bits()));
TEST(DrmRootCertificateTestCertificatesTest, NonWidevineRootSigner) {
// TODO(b/138929855): Add test to verify certificate chain is signed by
// Widevine.
}
class SignerPrivateKey {
public:
virtual ~SignerPrivateKey() {}
virtual bool GenerateSignature(const std::string& message,
std::string* signature) const = 0;
virtual DrmCertificate::Algorithm algorithm() const = 0;
static std::unique_ptr<SignerPrivateKey> Create(
const std::string& private_key,
widevine::DrmCertificate::Algorithm algorithm);
protected:
SignerPrivateKey() {}
};
template <typename T>
class SignerPrivateKeyImpl : public SignerPrivateKey {
public:
SignerPrivateKeyImpl(std::unique_ptr<T> private_key,
DrmCertificate::Algorithm algorithm)
: private_key_(std::move(private_key)), algorithm_(algorithm) {}
~SignerPrivateKeyImpl() override {}
bool GenerateSignature(const std::string& message,
std::string* signature) const override {
return private_key_->GenerateSignature(message, signature);
}
DrmCertificate::Algorithm algorithm() const override { return algorithm_; }
private:
std::unique_ptr<T> private_key_;
DrmCertificate::Algorithm algorithm_;
};
std::unique_ptr<SignerPrivateKey> SignerPrivateKey::Create(
const std::string& private_key,
widevine::DrmCertificate::Algorithm algorithm) {
DCHECK(algorithm != DrmCertificate::UNKNOWN_ALGORITHM);
switch (algorithm) {
case DrmCertificate::RSA: {
auto rsa_key =
std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key));
CHECK(rsa_key);
std::unique_ptr<SignerPrivateKey> new_rsa_signer_private_key =
absl::make_unique<SignerPrivateKeyImpl<RsaPrivateKey>>(
std::move(rsa_key), algorithm);
CHECK(new_rsa_signer_private_key);
return new_rsa_signer_private_key;
}
case DrmCertificate::ECC_SECP256R1:
case DrmCertificate::ECC_SECP384R1:
case DrmCertificate::ECC_SECP521R1: {
auto ec_key = ECPrivateKey::Create(private_key);
CHECK(ec_key);
std::unique_ptr<SignerPrivateKey> new_ec_signer_private_key =
absl::make_unique<SignerPrivateKeyImpl<ECPrivateKey>>(
std::move(ec_key), algorithm);
CHECK(new_ec_signer_private_key);
return new_ec_signer_private_key;
}
default:
break;
}
return nullptr;
}
static const int kDrmRootKey = 0;
static const int kInterMediateKey = 1;
static const int kClientKey = 2;
class DrmRootCertificateTest : public testing::TestWithParam<const char*> {
protected:
DrmRootCertificateTest() {}
void SetUp() override {
drm_certificates_[0].set_serial_number("level 0");
drm_certificates_[0].set_creation_time_seconds(0);
drm_certificates_[0].set_public_key(
test_keys_.public_test_key_1_3072_bits());
drm_certificates_[1].set_serial_number("level 1");
drm_certificates_[1].set_creation_time_seconds(1);
drm_certificates_[1].set_public_key(
test_keys_.public_test_key_2_2048_bits());
drm_certificates_[2].set_serial_number("level 2");
drm_certificates_[2].set_creation_time_seconds(2);
drm_certificates_[2].set_public_key(
test_keys_.public_test_key_3_2048_bits());
bool algorithm_status = false;
std::string algorithm(GetParam());
if (algorithm == "RSA") {
RsaTestSetup();
algorithm_status = true;
}
if (algorithm == "ECC") {
EcTestSetup();
algorithm_status = true;
}
CHECK(algorithm_status);
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
kCertificateTypeTesting, &root_cert_));
}
void RsaTestSetup() {
private_keys_.resize(3);
private_keys_[kDrmRootKey] =
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits(),
widevine::DrmCertificate::RSA);
drm_certificates_[kDrmRootKey].set_serial_number("level 0");
drm_certificates_[kDrmRootKey].set_creation_time_seconds(0);
drm_certificates_[kDrmRootKey].set_public_key(
rsa_test_keys_.public_test_key_1_3072_bits());
private_keys_[kInterMediateKey] =
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_2_2048_bits(),
widevine::DrmCertificate::RSA);
drm_certificates_[kInterMediateKey].set_serial_number("level 1");
drm_certificates_[kInterMediateKey].set_creation_time_seconds(1);
drm_certificates_[kInterMediateKey].set_public_key(
rsa_test_keys_.public_test_key_2_2048_bits());
private_keys_[kClientKey] =
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits(),
widevine::DrmCertificate::RSA);
drm_certificates_[kClientKey].set_serial_number("level 2");
drm_certificates_[kClientKey].set_creation_time_seconds(2);
drm_certificates_[kClientKey].set_public_key(
rsa_test_keys_.public_test_key_3_2048_bits());
}
void EcTestSetup() {
private_keys_.resize(3);
private_keys_[kDrmRootKey] =
SignerPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits(),
widevine::DrmCertificate::RSA);
drm_certificates_[kDrmRootKey].set_serial_number("level 0");
drm_certificates_[kDrmRootKey].set_creation_time_seconds(0);
drm_certificates_[kDrmRootKey].set_public_key(
rsa_test_keys_.public_test_key_1_3072_bits());
private_keys_[kInterMediateKey] =
SignerPrivateKey::Create(ec_test_keys_.private_test_key_1_secp521r1(),
DrmCertificate::ECC_SECP521R1);
drm_certificates_[kInterMediateKey].set_serial_number("level 1");
drm_certificates_[kInterMediateKey].set_creation_time_seconds(1);
drm_certificates_[kInterMediateKey].set_public_key(
ec_test_keys_.public_test_key_1_secp521r1());
drm_certificates_[kInterMediateKey].set_algorithm(
DrmCertificate::ECC_SECP521R1);
private_keys_[kClientKey] =
SignerPrivateKey::Create(ec_test_keys_.private_test_key_1_secp256r1(),
DrmCertificate::ECC_SECP256R1);
drm_certificates_[kClientKey].set_serial_number("level 2");
drm_certificates_[kClientKey].set_creation_time_seconds(2);
drm_certificates_[kClientKey].set_public_key(
ec_test_keys_.public_test_key_1_secp256r1());
drm_certificates_[kClientKey].set_algorithm(DrmCertificate::ECC_SECP256R1);
// Client certificate.
// Intermediate certificate.
}
void GenerateSignedDrmCertificate() {
SignedDrmCertificate* current_sc(&signed_drm_certificate_);
ASSERT_TRUE(drm_certificates_[2].SerializeToString(
drm_certificates_[kClientKey].set_algorithm(
private_keys_[kClientKey]->algorithm());
ASSERT_TRUE(drm_certificates_[kClientKey].SerializeToString(
current_sc->mutable_drm_certificate()));
ASSERT_TRUE(private_keys_[1]->GenerateSignature(
ASSERT_TRUE(private_keys_[kInterMediateKey]->GenerateSignature(
current_sc->drm_certificate(), current_sc->mutable_signature()));
current_sc = current_sc->mutable_signer();
ASSERT_TRUE(drm_certificates_[1].SerializeToString(
drm_certificates_[kInterMediateKey].set_algorithm(
private_keys_[kInterMediateKey]->algorithm());
ASSERT_TRUE(drm_certificates_[kInterMediateKey].SerializeToString(
current_sc->mutable_drm_certificate()));
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature(
current_sc->drm_certificate(), current_sc->mutable_signature()));
current_sc = current_sc->mutable_signer();
ASSERT_TRUE(drm_certificates_[0].SerializeToString(
drm_certificates_[kDrmRootKey].set_algorithm(
private_keys_[kDrmRootKey]->algorithm());
ASSERT_TRUE(drm_certificates_[kDrmRootKey].SerializeToString(
current_sc->mutable_drm_certificate()));
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
ASSERT_TRUE(private_keys_[kDrmRootKey]->GenerateSignature(
current_sc->drm_certificate(), current_sc->mutable_signature()));
}
RsaTestKeys test_keys_;
std::vector<std::unique_ptr<RsaPrivateKey>> private_keys_;
RsaTestKeys rsa_test_keys_;
ECTestKeys ec_test_keys_;
std::vector<std::unique_ptr<SignerPrivateKey>> private_keys_;
SignedDrmCertificate signed_drm_certificate_;
DrmCertificate drm_certificates_[3];
std::unique_ptr<DrmRootCertificate> root_cert_;
};
TEST_F(DrmRootCertificateTest, SuccessNoOutput) {
INSTANTIATE_TEST_SUITE_P(SuccessNoOutput, DrmRootCertificateTest,
Values("RSA", "ECC"));
TEST_P(DrmRootCertificateTest, SuccessNoOutput) {
GenerateSignedDrmCertificate();
ASSERT_EQ(OkStatus(),
root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, SuccessWithOutput) {
TEST_P(DrmRootCertificateTest, SuccessWithOutput) {
GenerateSignedDrmCertificate();
SignedDrmCertificate out_signed_cert;
DrmCertificate out_cert;
@@ -161,13 +304,13 @@ TEST_F(DrmRootCertificateTest, SuccessWithOutput) {
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,
"invalid-signed-drm-certificate"),
root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
TEST_P(DrmRootCertificateTest, InvalidSignerCertificate) {
GenerateSignedDrmCertificate();
signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage");
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
@@ -176,7 +319,7 @@ TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
TEST_P(DrmRootCertificateTest, MissingDrmCertificate) {
GenerateSignedDrmCertificate();
signed_drm_certificate_.clear_drm_certificate();
EXPECT_EQ(
@@ -185,7 +328,7 @@ TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) {
TEST_P(DrmRootCertificateTest, InvalidDrmCertificate) {
GenerateSignedDrmCertificate();
signed_drm_certificate_.set_drm_certificate("junk");
EXPECT_EQ(
@@ -194,7 +337,7 @@ TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) {
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidPublicKey) {
TEST_P(DrmRootCertificateTest, InvalidPublicKey) {
drm_certificates_[0].set_public_key("rubbish");
GenerateSignedDrmCertificate();
EXPECT_EQ(
@@ -203,7 +346,7 @@ TEST_F(DrmRootCertificateTest, InvalidPublicKey) {
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, MissingPublicKey) {
TEST_P(DrmRootCertificateTest, MissingPublicKey) {
drm_certificates_[2].clear_public_key();
GenerateSignedDrmCertificate();
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"),
@@ -211,7 +354,7 @@ TEST_F(DrmRootCertificateTest, MissingPublicKey) {
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, MissingCreationTime) {
TEST_P(DrmRootCertificateTest, MissingCreationTime) {
drm_certificates_[2].clear_creation_time_seconds();
GenerateSignedDrmCertificate();
EXPECT_EQ(
@@ -220,7 +363,7 @@ TEST_F(DrmRootCertificateTest, MissingCreationTime) {
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, MissingSerialNumber) {
TEST_P(DrmRootCertificateTest, MissingSerialNumber) {
drm_certificates_[2].set_serial_number("");
GenerateSignedDrmCertificate();
EXPECT_EQ(
@@ -229,7 +372,7 @@ TEST_F(DrmRootCertificateTest, MissingSerialNumber) {
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
TEST_P(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
GenerateSignedDrmCertificate();
signed_drm_certificate_.mutable_signer()->set_signature(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
@@ -239,7 +382,7 @@ TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
nullptr, nullptr));
}
TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) {
TEST_P(DrmRootCertificateTest, InvalidSignatureWithCache) {
GenerateSignedDrmCertificate();
// Verify and cache.
ASSERT_EQ(OkStatus(),

View File

@@ -44,16 +44,18 @@ class DrmServiceCertificateMap {
void AddCert(std::unique_ptr<DrmServiceCertificate> new_cert);
void ClearDefaultDrmServiceCertificate();
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();
private:
absl::Mutex mutex_;
// Certificate serial number to certificate map.
std::map<std::string, std::unique_ptr<DrmServiceCertificate>> map_
GUARDED_BY(mutex_);
DrmServiceCertificate* default_cert_ GUARDED_BY(mutex_);
ABSL_GUARDED_BY(mutex_);
DrmServiceCertificate* default_cert_ ABSL_GUARDED_BY(mutex_);
};
DrmServiceCertificateMap::DrmServiceCertificateMap() : default_cert_(nullptr) {}
@@ -94,12 +96,30 @@ const DrmServiceCertificate* DrmServiceCertificateMap::GetDefaultCert() {
return default_cert_;
}
const DrmServiceCertificate* DrmServiceCertificateMap::GetCert(
const DrmServiceCertificate* DrmServiceCertificateMap::GetCertBySerialNumber(
const std::string& serial_number) {
absl::ReaderMutexLock lock(&mutex_);
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() {
static auto* const kInstance = new DrmServiceCertificateMap();
return kInstance;
@@ -108,7 +128,8 @@ DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
} // namespace
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_passphrase) {
DrmCertificate drm_cert;
@@ -166,13 +187,22 @@ DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() {
return default_cert;
}
const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate(
const DrmServiceCertificate*
DrmServiceCertificate::GetDrmServiceCertificateBySerialNumber(
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(
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_passphrase) {
DrmServiceCertificateMap::GetInstance()->ClearDefaultDrmServiceCertificate();
@@ -207,7 +237,7 @@ Status DrmServiceCertificate::DecryptClientIdentification(
}
std::string privacy_key;
std::string provider_id;
const DrmServiceCertificate* cert = GetDrmServiceCertificate(
const DrmServiceCertificate* cert = GetDrmServiceCertificateBySerialNumber(
encrypted_client_id.service_certificate_serial_number());
if (!cert) {
return Status(

View File

@@ -17,7 +17,6 @@
#include <string>
#include <cstdint>
#include "base/macros.h"
#include "common/certificate_type.h"
#include "common/rsa_key.h"
#include "common/status.h"
@@ -36,6 +35,9 @@ class EncryptedClientIdentification;
// functionality.
class DrmServiceCertificate {
public:
DrmServiceCertificate(const DrmServiceCertificate&) = delete;
DrmServiceCertificate& operator=(const DrmServiceCertificate&) = delete;
// 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
// of certifiate being added. |service_certificate| is a
@@ -50,7 +52,8 @@ class DrmServiceCertificate {
// This method is thread-safe.
static Status AddDrmServiceCertificate(
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);
// Same as AddDrmServiceCertificate(), but will clear the default service
@@ -58,7 +61,8 @@ class DrmServiceCertificate {
// being set as the default service certificate.
static Status SetDefaultDrmServiceCertificate(
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);
// Returns the default service certificate. Will return null if no default
@@ -69,11 +73,17 @@ class DrmServiceCertificate {
// Certificate is set. This method is thread-safe.
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.
static const DrmServiceCertificate* GetDrmServiceCertificate(
static const DrmServiceCertificate* GetDrmServiceCertificateBySerialNumber(
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
// |encrypted_client_id| into |client_id| using the private key for the
// certificate which was used to encrypt the information. |client_id| must
@@ -86,6 +96,7 @@ class DrmServiceCertificate {
const std::string& certificate() const { return certificate_; }
const std::string& provider_id() const { return provider_id_; }
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 RsaPublicKey* const public_key() const { return public_key_.get(); }
@@ -100,17 +111,20 @@ class DrmServiceCertificate {
friend class widevine::RequestInspectorTest;
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_passphrase);
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_passphrase);
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,
std::unique_ptr<RsaPublicKey> public_key,
std::unique_ptr<RsaPrivateKey> private_key);
@@ -123,8 +137,6 @@ class DrmServiceCertificate {
uint32_t creation_time_seconds_;
std::unique_ptr<RsaPublicKey> public_key_;
std::unique_ptr<RsaPrivateKey> private_key_;
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmServiceCertificate);
};
} // namespace widevine

View File

@@ -160,6 +160,9 @@ TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) {
uint32_t creation_time_seconds1(1234);
std::string serial_number2("serial_number2");
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");
EXPECT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate());
@@ -172,6 +175,9 @@ TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) {
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id1,
creation_time_seconds2));
EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id3,
creation_time_seconds3));
EncryptedClientIdentification encrypted_client_id;
EncryptClientIdentification(serial_number1, provider_id1,
test_keys_.public_test_key_2_2048_bits(),
@@ -199,6 +205,61 @@ TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) {
.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) {
std::string serial_number1("serial_number1");
std::string serial_number2("serial_number2");

280
common/ec_key.cc Normal file
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.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Definition of elliptic curve public and private key classes.
#include "common/ec_key.h"
#include "glog/logging.h"
#include "absl/memory/memory.h"
#include "openssl/base.h"
#include "openssl/bn.h"
#include "openssl/ec.h"
#include "openssl/ecdh.h"
#include "openssl/ecdsa.h"
#include "openssl/err.h"
#include "openssl/evp.h"
#include "openssl/sha.h"
#include "common/aes_cbc_util.h"
#include "common/ec_util.h"
#include "common/openssl_util.h"
#include "common/sha_util.h"
namespace widevine {
namespace {
void* SHA256KDF(const void* in, size_t inlen, void* out, size_t* outlen) {
std::string shared_session_key(static_cast<const char*>(in), inlen);
std::string derived_shared_session_key = Sha256_Hash(shared_session_key);
if (*outlen != SHA256_DIGEST_LENGTH) {
return nullptr;
}
memcpy(out, derived_shared_session_key.data(), *outlen);
return out;
}
ECPrivateKey::EllipticCurve ECKeyToCurve(const EC_KEY* key) {
if (key == nullptr) {
return ECPrivateKey::UNDEFINED_CURVE;
}
return ec_util::NidToCurve(EC_GROUP_get_curve_name(EC_KEY_get0_group(key)));
}
std::string OpenSSLErrorString(uint32_t error) {
char buf[ERR_ERROR_STRING_BUF_LEN];
ERR_error_string_n(error, buf, sizeof(buf));
return buf;
}
} // namespace
ECPrivateKey::ECPrivateKey(EC_KEY* ec_key) : key_(ec_key) {
CHECK(key() != nullptr);
CHECK(EC_KEY_get0_private_key(key()) != nullptr);
CHECK_NE(ECKeyToCurve(key()), ECPrivateKey::UNDEFINED_CURVE);
CHECK_EQ(EC_KEY_check_key(key()), 1);
}
ECPrivateKey::ECPrivateKey(ScopedECKEY ec_key) : key_(std::move(ec_key)) {}
ECPrivateKey::ECPrivateKey(const ECPrivateKey& ec_key)
: ECPrivateKey(EC_KEY_dup(ec_key.key())) {}
std::unique_ptr<ECPrivateKey> ECPrivateKey::Create(
const std::string& serialized_key) {
EC_KEY* key;
if (!ec_util::DeserializeECPrivateKey(serialized_key, &key)) {
return nullptr;
}
ScopedECKEY scoped_ec_key(key);
if (EC_KEY_check_key(scoped_ec_key.get()) != 1) {
LOG(ERROR) << "Invalid private EC key: "
<< OpenSSLErrorString(ERR_get_error());
return nullptr;
}
if (ECKeyToCurve(scoped_ec_key.get()) == ECPrivateKey::UNDEFINED_CURVE) {
LOG(ERROR) << "Key has unsupported curve";
return nullptr;
}
return absl::make_unique<ECPrivateKey>(scoped_ec_key.release());
}
std::unique_ptr<ECPublicKey> ECPrivateKey::PublicKey() const {
if (key_ == nullptr) {
return nullptr;
}
return absl::make_unique<ECPublicKey>(ScopedECKEY(EC_KEY_dup(key_.get())));
}
bool ECPrivateKey::DeriveSharedSessionKey(
const ECPublicKey& public_key,
std::string* derived_shared_session_key) const {
if (derived_shared_session_key == nullptr) {
LOG(ERROR) << "|derived_shared_session_key| cannot be nullptr";
return false;
}
const EC_GROUP* group1 = EC_KEY_get0_group(key());
const EC_GROUP* group2 = EC_KEY_get0_group(public_key.key());
if (EC_GROUP_cmp(group1, group2, nullptr) != 0) {
LOG(ERROR) << "EC_GROUPs do not match";
return false;
}
const EC_POINT* public_key_point = EC_KEY_get0_public_key(public_key.key());
derived_shared_session_key->resize(SHA256_DIGEST_LENGTH, 0);
int result = ECDH_compute_key(
reinterpret_cast<void*>(
const_cast<char*>(derived_shared_session_key->data())),
derived_shared_session_key->size(), public_key_point, key(), SHA256KDF);
if (result == -1) {
LOG(ERROR) << "Could not write shared session key: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
if (result != SHA256_DIGEST_LENGTH) {
LOG(ERROR) << "Wrote " << result
<< " bytes to derived shared session key instead of "
<< SHA256_DIGEST_LENGTH << " bytes";
return false;
}
return true;
}
bool ECPrivateKey::GenerateSignature(const std::string& message,
std::string* signature) const {
if (message.empty()) {
LOG(ERROR) << "|message| cannot be empty";
return false;
}
if (signature == nullptr) {
LOG(ERROR) << "|signature| cannot be nullptr";
return false;
}
std::string message_digest = Sha256_Hash(message);
size_t max_signature_size = ECDSA_size(key());
if (max_signature_size == 0) {
LOG(ERROR) << "key_ does not have a group set";
return false;
}
signature->resize(max_signature_size);
unsigned int bytes_written = 0;
int result = ECDSA_sign(
0 /* unused type */,
reinterpret_cast<const uint8_t*>(message_digest.data()),
message_digest.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&bytes_written, key());
if (result != 1) {
LOG(ERROR) << "Could not calculate signature: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
signature->resize(bytes_written);
return true;
}
bool ECPrivateKey::MatchesPrivateKey(const ECPrivateKey& private_key) const {
return BN_cmp(EC_KEY_get0_private_key(key()),
EC_KEY_get0_private_key(private_key.key())) == 0;
}
bool ECPrivateKey::MatchesPublicKey(const ECPublicKey& public_key) const {
return EC_POINT_cmp(EC_KEY_get0_group(key()), EC_KEY_get0_public_key(key()),
EC_KEY_get0_public_key(public_key.key()), nullptr) == 0;
}
ECPrivateKey::EllipticCurve ECPrivateKey::Curve() const {
return ECKeyToCurve(key());
}
bool ECPrivateKey::SerializedKey(std::string* serialized_key) const {
CHECK(serialized_key);
return ec_util::SerializeECPrivateKey(key_.get(), serialized_key);
}
ECPublicKey::ECPublicKey(EC_KEY* ec_key) : key_(ec_key) {
CHECK(key() != nullptr);
CHECK(EC_KEY_get0_private_key(key()) == nullptr);
CHECK_NE(ECKeyToCurve(key()), ECPrivateKey::UNDEFINED_CURVE);
CHECK_EQ(EC_KEY_check_key(key()), 1);
}
ECPublicKey::ECPublicKey(ScopedECKEY ec_key) : key_(std::move(ec_key)) {}
ECPublicKey::ECPublicKey(const ECPublicKey& ec_key)
: ECPublicKey(EC_KEY_dup(ec_key.key())) {}
std::unique_ptr<ECPublicKey> ECPublicKey::Create(
const std::string& serialized_key) {
EC_KEY* key;
if (!ec_util::DeserializeECPublicKey(serialized_key, &key)) {
return nullptr;
}
ScopedECKEY scoped_ec_key(key);
if (EC_KEY_check_key(scoped_ec_key.get()) != 1) {
LOG(ERROR) << "Invalid public EC key: "
<< OpenSSLErrorString(ERR_get_error());
return nullptr;
}
if (ECKeyToCurve(scoped_ec_key.get()) == ECPrivateKey::UNDEFINED_CURVE) {
LOG(ERROR) << "Key has unsupported curve";
return nullptr;
}
return absl::make_unique<ECPublicKey>(scoped_ec_key.release());
}
std::unique_ptr<ECPublicKey> ECPublicKey::CreateFromKeyPoint(
ECPrivateKey::EllipticCurve curve, const std::string& key_point) {
EC_KEY* key;
if (!ec_util::GetPublicKeyFromKeyPoint(curve, key_point, &key)) {
return nullptr;
}
ScopedECKEY scoped_ec_key(key);
if (EC_KEY_check_key(scoped_ec_key.get()) != 1) {
LOG(ERROR) << "Invalid public EC key: "
<< OpenSSLErrorString(ERR_get_error());
return nullptr;
}
if (ECKeyToCurve(scoped_ec_key.get()) == ECPrivateKey::UNDEFINED_CURVE) {
LOG(ERROR) << "Key has unsupported curve";
return nullptr;
}
return absl::make_unique<ECPublicKey>(scoped_ec_key.release());
}
bool ECPublicKey::VerifySignature(const std::string& message,
const std::string& signature) const {
if (message.empty()) {
LOG(ERROR) << "|message| cannot be empty";
return false;
}
if (signature.empty()) {
LOG(ERROR) << "|signature| cannot be empty";
return false;
}
std::string message_digest = Sha256_Hash(message);
int result = ECDSA_verify(
0 /* unused type */,
reinterpret_cast<const uint8_t*>(message_digest.data()),
message_digest.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature.data())),
signature.size(), key());
if (result != 1) {
LOG(ERROR) << "Could not verify signature: "
<< OpenSSLErrorString(ERR_get_error());
return false;
}
return true;
}
bool ECPublicKey::MatchesPrivateKey(const ECPrivateKey& private_key) const {
return private_key.MatchesPublicKey(*this);
}
bool ECPublicKey::MatchesPublicKey(const ECPublicKey& public_key) const {
return EC_POINT_cmp(EC_KEY_get0_group(key()), EC_KEY_get0_public_key(key()),
EC_KEY_get0_public_key(public_key.key()), nullptr) == 0;
}
ECPrivateKey::EllipticCurve ECPublicKey::Curve() const {
return ECKeyToCurve(key());
}
bool ECPublicKey::SerializedKey(std::string* serialized_key) const {
CHECK(serialized_key);
return ec_util::SerializeECPublicKey(key_.get(), serialized_key);
}
bool ECPublicKey::GetPointEncodedKey(std::string* encoded_key) const {
CHECK(encoded_key);
return ec_util::GetPublicKeyPoint(key_.get(), encoded_key);
}
} // namespace widevine

144
common/ec_key.h Normal file
View File

@@ -0,0 +1,144 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Generic elliptic curve classes for private and public keys. Declares
// methods for deriving shared session keys and signatures.
#ifndef COMMON_EC_KEY_H_
#define COMMON_EC_KEY_H_
#include <memory>
#include <string>
#include "openssl/ec.h"
#include "common/openssl_util.h"
namespace widevine {
class ECPublicKey;
class ECPrivateKey {
public:
explicit ECPrivateKey(EC_KEY* ec_key);
explicit ECPrivateKey(ScopedECKEY ec_key);
ECPrivateKey(const ECPrivateKey&);
ECPrivateKey() = delete;
virtual ~ECPrivateKey() = default;
// Accepted standard curves for key generation. Names are derived from SECG.
enum EllipticCurve {
UNDEFINED_CURVE = 0,
SECP256R1,
SECP384R1,
SECP521R1,
};
// Creates an ECPrivateKey using a DER encoded RFC5915 std::string
// representing a private key. Returns an ECPrivateKey on success or nullptr
// on failure.
static std::unique_ptr<ECPrivateKey> Create(
const std::string& serialized_key);
// Exports the matching public key for this private key.
virtual std::unique_ptr<ECPublicKey> PublicKey() const;
// Calculates a shared session key using ECDH and then uses a key derivation
// function, SHA256, to derive a key.
// |public_key| is an EC public key with the same curve parameters as key_. It
// is used as the public key component of ECDH.
// |derived_shared_session_key| will be the result of the derivation.
// Caller retains ownership of all pointers.
// Returns true on success and false on error.
virtual bool DeriveSharedSessionKey(
const ECPublicKey& public_key,
std::string* derived_shared_session_key) const;
// Given a message, calculates a signature using ECDSA with the key_.
// |message| is the message to be signed.
// |signature| will contain the resulting signature. This will be an ASN.1
// DER-encoded signature.
// Caller retains ownership of all pointers.
// Returns true on success and false on error.
virtual bool GenerateSignature(const std::string& message,
std::string* signature) const;
// Returns whether the given private key is the same as key_.
virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const;
// Returns whether the given public key is part of the same key pair as key_.
virtual bool MatchesPublicKey(const ECPublicKey& public_key) const;
// Returns the EllipticCurve associated with key_.
virtual EllipticCurve Curve() const;
virtual bool SerializedKey(std::string* serialized_key) const;
private:
friend class ECPublicKey;
ECPrivateKey& operator=(const ECPrivateKey&) = delete;
const EC_KEY* key() const { return key_.get(); }
ScopedECKEY key_;
};
class ECPublicKey {
public:
explicit ECPublicKey(EC_KEY* ec_key);
explicit ECPublicKey(ScopedECKEY ec_key);
ECPublicKey(const ECPublicKey&);
virtual ~ECPublicKey() = default;
// Creates an ECPublicKey using a DER encoded RFC5208 std::string representing
// a public key. Returns an ECPublicKey on success or nullptr on failure.
static std::unique_ptr<ECPublicKey> Create(const std::string& serialized_key);
// Creates an ECPublicKey from an uncompressed point compatible with X9.62.
// Returns an ECPublicKey on success or nullptr on failure.
static std::unique_ptr<ECPublicKey> CreateFromKeyPoint(
ECPrivateKey::EllipticCurve curve, const std::string& key_point);
// Given a message and a signature, verifies that the signature was created
// using the private key associated with key_.
// |message| is the message that was signed.
// |signature| is an ASN.1 DER-encoded signature.
// Returns true on success and false on error.
virtual bool VerifySignature(const std::string& message,
const std::string& signature) const;
// Returns whether the given private key is part of the same key pair as key_.
virtual bool MatchesPrivateKey(const ECPrivateKey& private_key) const;
// Returns whether the given public key is the same as key_.
virtual bool MatchesPublicKey(const ECPublicKey& public_key) const;
// Returns the EllipticCurve associated with key_.
virtual ECPrivateKey::EllipticCurve Curve() const;
virtual bool SerializedKey(std::string* serialized_key) const;
// Returns the key in the uncompressed point format. Compatible with X9.62
// The lead byte indicates uncompressed format (0x4). This is followed by the
// octets for the X and Y coordinates.
virtual bool GetPointEncodedKey(std::string* encoded_key) const;
private:
friend class ECPrivateKey;
ECPublicKey& operator=(const ECPublicKey&) = delete;
const EC_KEY* key() const { return key_.get(); }
ScopedECKEY key_;
};
} // namespace widevine
#endif // COMMON_EC_KEY_H_

43
common/ec_key_source.h Normal file
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_

274
common/ec_key_test.cc Normal file
View File

@@ -0,0 +1,274 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Unit tests for the ECPrivateKey and ECPublicKey classes.
#include "common/ec_key.h"
#include <memory>
#include <string>
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "openssl/sha.h"
#include "common/ec_test_keys.h"
#include "common/ec_util.h"
#include "common/random_util.h"
using ::testing::IsNull;
namespace widevine {
class ECKeyTest : public ::testing::Test {
public:
static std::vector<std::tuple<std::string, std::string> > GetTestKeyList() {
std::vector<std::tuple<std::string, std::string> > test_key_list;
ECTestKeys test_keys;
test_key_list.push_back(
std::make_tuple(test_keys.private_test_key_1_secp521r1(),
test_keys.public_test_key_1_secp521r1()));
test_key_list.push_back(
std::make_tuple(test_keys.private_test_key_1_secp384r1(),
test_keys.public_test_key_1_secp384r1()));
test_key_list.push_back(
std::make_tuple(test_keys.private_test_key_1_secp256r1(),
test_keys.public_test_key_1_secp256r1()));
return test_key_list;
}
protected:
ECKeyTest() { plaintext_message_ = "This is a plaintext message."; }
void GenerateEphemeralKeyPair(ECPrivateKey::EllipticCurve curve) {
ASSERT_NE(curve, ECPrivateKey::UNDEFINED_CURVE);
bssl::UniquePtr<EC_KEY> ephemeral_key_pair(
EC_KEY_new_by_curve_name(ec_util::CurveToNid(curve)));
ASSERT_TRUE(ephemeral_key_pair != nullptr);
ASSERT_TRUE(EC_KEY_generate_key(ephemeral_key_pair.get()));
// Separate them out into their private and public parts.
std::string private_key;
std::string public_key;
ASSERT_TRUE(
ec_util::SerializeECPrivateKey(ephemeral_key_pair.get(), &private_key));
ASSERT_TRUE(
ec_util::SerializeECPublicKey(ephemeral_key_pair.get(), &public_key));
ephemeral_private_key_ = ECPrivateKey::Create(private_key);
ephemeral_public_key_ = ECPublicKey::Create(public_key);
}
std::unique_ptr<ECPrivateKey> ephemeral_private_key_;
std::unique_ptr<ECPublicKey> ephemeral_public_key_;
std::string plaintext_message_;
};
TEST_F(ECKeyTest, KeyCurve) {
ECTestKeys test_keys;
EXPECT_EQ(
ECPrivateKey::Create(test_keys.private_test_key_1_secp521r1())->Curve(),
ECPrivateKey::SECP521R1);
EXPECT_EQ(
ECPrivateKey::Create(test_keys.private_test_key_1_secp384r1())->Curve(),
ECPrivateKey::SECP384R1);
EXPECT_EQ(
ECPrivateKey::Create(test_keys.private_test_key_1_secp256r1())->Curve(),
ECPrivateKey::SECP256R1);
EXPECT_EQ(
ECPublicKey::Create(test_keys.public_test_key_1_secp521r1())->Curve(),
ECPrivateKey::SECP521R1);
EXPECT_EQ(
ECPublicKey::Create(test_keys.public_test_key_1_secp384r1())->Curve(),
ECPrivateKey::SECP384R1);
EXPECT_EQ(
ECPublicKey::Create(test_keys.public_test_key_1_secp256r1())->Curve(),
ECPrivateKey::SECP256R1);
}
TEST_F(ECKeyTest, KeyPointEncodingNullDeath) {
EXPECT_DEATH(ephemeral_public_key_->GetPointEncodedKey(nullptr), "");
}
class ECKeyTestKeyPairs : public ECKeyTest,
public ::testing::WithParamInterface<
std::tuple<std::string, std::string> > {
protected:
ECKeyTestKeyPairs() {
test_private_key_ = std::get<0>(GetParam());
test_public_key_ = std::get<1>(GetParam());
private_key_ = ECPrivateKey::Create(test_private_key_);
public_key_ = ECPublicKey::Create(test_public_key_);
}
std::string test_private_key_;
std::string test_public_key_;
std::unique_ptr<ECPrivateKey> private_key_;
std::unique_ptr<ECPublicKey> public_key_;
};
TEST_P(ECKeyTestKeyPairs, CreateWrongKey) {
EXPECT_EQ(ECPrivateKey::Create(test_public_key_), nullptr);
EXPECT_EQ(ECPublicKey::Create(test_private_key_), nullptr);
}
TEST_P(ECKeyTestKeyPairs, InvalidKeyConstructorParameters) {
EXPECT_DEATH(ECPrivateKey(nullptr), "");
EXPECT_DEATH(ECPublicKey(nullptr), "");
EC_KEY* key;
ASSERT_TRUE(ec_util::DeserializeECPrivateKey(test_private_key_, &key));
// Brace initialization to avoid most vexing parse.
EXPECT_DEATH(ECPublicKey{key}, "");
EC_KEY_free(key);
ASSERT_TRUE(ec_util::DeserializeECPublicKey(test_public_key_, &key));
EXPECT_DEATH(ECPrivateKey{key}, "");
EC_KEY_free(key);
}
TEST_P(ECKeyTestKeyPairs, KeyMatch) {
EXPECT_TRUE(private_key_->MatchesPrivateKey(*private_key_));
EXPECT_TRUE(private_key_->MatchesPublicKey(*public_key_));
EXPECT_TRUE(public_key_->MatchesPrivateKey(*private_key_));
EXPECT_TRUE(public_key_->MatchesPublicKey(*public_key_));
}
TEST_P(ECKeyTestKeyPairs, DeriveSharedSessionKey) {
std::string derived_shared_session_key_1;
// Generate a temporary key pair as part of ECDH.
GenerateEphemeralKeyPair(private_key_->Curve());
EXPECT_TRUE(private_key_->DeriveSharedSessionKey(
*ephemeral_public_key_, &derived_shared_session_key_1));
EXPECT_EQ(derived_shared_session_key_1.size(), SHA256_DIGEST_LENGTH);
std::string derived_shared_session_key_2;
EXPECT_TRUE(ephemeral_private_key_->DeriveSharedSessionKey(
*public_key_, &derived_shared_session_key_2));
EXPECT_EQ(derived_shared_session_key_1, derived_shared_session_key_2);
}
TEST_P(ECKeyTestKeyPairs, InvalidDeriveSharedSessionKeyParameters) {
GenerateEphemeralKeyPair(private_key_->Curve());
EXPECT_FALSE(
private_key_->DeriveSharedSessionKey(*ephemeral_public_key_, nullptr));
EXPECT_FALSE(
ephemeral_private_key_->DeriveSharedSessionKey(*public_key_, nullptr));
}
TEST_P(ECKeyTestKeyPairs, SignVerify) {
std::string signature;
EXPECT_TRUE(private_key_->GenerateSignature(plaintext_message_, &signature));
EXPECT_TRUE(public_key_->VerifySignature(plaintext_message_, signature));
}
TEST_P(ECKeyTestKeyPairs, InvalidSignVerifyParameters) {
std::string signature;
EXPECT_FALSE(private_key_->GenerateSignature("", &signature));
EXPECT_FALSE(public_key_->VerifySignature("", "signature"));
EXPECT_FALSE(private_key_->GenerateSignature(plaintext_message_, nullptr));
EXPECT_FALSE(public_key_->VerifySignature(plaintext_message_, ""));
}
TEST_P(ECKeyTestKeyPairs, KeyMismatch) {
GenerateEphemeralKeyPair(private_key_->Curve());
EXPECT_FALSE(private_key_->MatchesPrivateKey(*ephemeral_private_key_));
EXPECT_FALSE(private_key_->MatchesPublicKey(*ephemeral_public_key_));
EXPECT_FALSE(public_key_->MatchesPrivateKey(*ephemeral_private_key_));
EXPECT_FALSE(public_key_->MatchesPublicKey(*ephemeral_public_key_));
}
TEST_P(ECKeyTestKeyPairs, SignVerifyKeyMismatch) {
std::string signature;
GenerateEphemeralKeyPair(private_key_->Curve());
EXPECT_TRUE(private_key_->GenerateSignature(plaintext_message_, &signature));
EXPECT_FALSE(
ephemeral_public_key_->VerifySignature(plaintext_message_, signature));
}
TEST_P(ECKeyTestKeyPairs, KeyPointEncodingCreateBadKeyPoint) {
ASSERT_THAT(ECPublicKey::CreateFromKeyPoint(public_key_->Curve(), ""),
IsNull());
}
TEST_P(ECKeyTestKeyPairs, KeyPointEncodingCreateBadCurve) {
std::string serialized_key_point;
ASSERT_TRUE(public_key_->GetPointEncodedKey(&serialized_key_point));
ASSERT_THAT(ECPublicKey::CreateFromKeyPoint(ECPrivateKey::UNDEFINED_CURVE,
serialized_key_point),
IsNull());
}
TEST_P(ECKeyTestKeyPairs, KeyPointEncodingSuccess) {
std::string serialized_key_point;
ASSERT_TRUE(public_key_->GetPointEncodedKey(&serialized_key_point));
ASSERT_EQ(ec_util::GetPublicKeyPointSize(public_key_->Curve()),
serialized_key_point.size());
// Check that the leading byte indicates uncompressed.
ASSERT_EQ(4, serialized_key_point[0]);
std::unique_ptr<ECPublicKey> new_public_key = ECPublicKey::CreateFromKeyPoint(
public_key_->Curve(), serialized_key_point);
ASSERT_THAT(new_public_key, testing::NotNull());
EXPECT_TRUE(new_public_key->MatchesPublicKey(*public_key_));
EXPECT_TRUE(new_public_key->MatchesPrivateKey(*private_key_));
};
INSTANTIATE_TEST_SUITE_P(ECKeyTestKeyPairs, ECKeyTestKeyPairs,
::testing::ValuesIn(ECKeyTest::GetTestKeyList()));
class ECKeyTestCurveMismatch
: public ECKeyTest,
public ::testing::WithParamInterface<
std::tuple<std::tuple<std::string, std::string>,
std::tuple<std::string, std::string> > > {
protected:
ECKeyTestCurveMismatch() {
private_key_ = ECPrivateKey::Create(std::get<0>(std::get<0>(GetParam())));
public_key_ = ECPublicKey::Create(std::get<1>(std::get<0>(GetParam())));
wrong_curve_private_key_ =
ECPrivateKey::Create(std::get<0>(std::get<1>(GetParam())));
wrong_curve_public_key_ =
ECPublicKey::Create(std::get<1>(std::get<1>(GetParam())));
}
void SetUp() override {
// Only run combinations where the two curves differ.
if (std::get<0>(GetParam()) == std::get<1>(GetParam())) GTEST_SKIP();
}
std::unique_ptr<ECPrivateKey> private_key_;
std::unique_ptr<ECPublicKey> public_key_;
std::unique_ptr<ECPrivateKey> wrong_curve_private_key_;
std::unique_ptr<ECPublicKey> wrong_curve_public_key_;
};
TEST_P(ECKeyTestCurveMismatch, CurveMismatch) {
EXPECT_FALSE(private_key_->MatchesPrivateKey(*wrong_curve_private_key_));
EXPECT_FALSE(private_key_->MatchesPublicKey(*wrong_curve_public_key_));
EXPECT_FALSE(public_key_->MatchesPrivateKey(*wrong_curve_private_key_));
EXPECT_FALSE(public_key_->MatchesPublicKey(*wrong_curve_public_key_));
}
TEST_P(ECKeyTestCurveMismatch, DeriveSharedSessionKeyCurveMismatch) {
std::string derived_shared_session_key;
EXPECT_FALSE(private_key_->DeriveSharedSessionKey(
*wrong_curve_public_key_, &derived_shared_session_key));
}
TEST_P(ECKeyTestCurveMismatch, SignVerifyCurveMismatch) {
std::string signature;
EXPECT_TRUE(private_key_->GenerateSignature(plaintext_message_, &signature));
EXPECT_FALSE(
wrong_curve_public_key_->VerifySignature(plaintext_message_, signature));
}
INSTANTIATE_TEST_SUITE_P(
ECKeyTestCurveMismatch, ECKeyTestCurveMismatch,
::testing::Combine(::testing::ValuesIn(ECKeyTest::GetTestKeyList()),
::testing::ValuesIn(ECKeyTest::GetTestKeyList())));
} // namespace widevine

223
common/ec_test_keys.cc Normal file
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;
}
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);
}
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);
}
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);
dst->clear();
if (src.size() % 16 != 0) {
@@ -86,7 +89,8 @@ bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* ds
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);
dst->clear();
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
// bundle, and the last 8 bytes are key1 and key3. |src| must be a multiple of
// 8 bytes.
bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst);
bool Encrypt3DesEcb(absl::string_view key, absl::string_view src,
std::string* dst);
// Decrypts |src| into |dst| using 3DES ECB2 mode with the given (16-byte)
// key. This is used for protecting content keys on some older Widevine
@@ -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,
// and the last 8 bytes are key1 and key3. |src| must be a multiple of
// 8 bytes.
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst);
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src,
std::string* dst);
// Encrypts |src| into |dst| using AES ECB mode with the given
// key. This is used for protecting content keys on Widevine devices,
@@ -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
// fail if invalid key or data sizes are passed in.
// Key must be 16 bytes, and src must be a multiple of 16 bytes.
bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst);
bool EncryptAesEcb(absl::string_view key, absl::string_view src,
std::string* dst);
// Decrypts |src| into |dst| using AES ECB mode with the given
// key. This is used for protecting content keys on Widevine devices,
@@ -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
// fail if invalid key or data sizes are passed in.
// Key must be 16 bytes, and src must be a multiple of 16 bytes.
bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst);
bool DecryptAesEcb(absl::string_view key, absl::string_view src,
std::string* dst);
} // namespace crypto_util
} // 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_

258
common/ecies_crypto_test.cc Normal file
View File

@@ -0,0 +1,258 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/ecies_crypto.h"
#include <cstddef>
#include <memory>
#include <tuple>
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "common/ec_key.h"
#include "common/ec_key_source.h"
#include "common/ec_test_keys.h"
#include "common/ec_util.h"
#include "common/fake_ec_key_source.h"
using ::testing::_;
using ::testing::Return;
namespace {
const size_t kMacSizeBytes = 32;
} // anonymous namespace
namespace widevine {
class EciesCryptoTest
: public ::testing::Test,
public ::testing::WithParamInterface<
std::tuple<std::string, std::string, std::string, std::string> > {
public:
static std::vector<
std::tuple<std::string, std::string, std::string, std::string> >
GetTestKeyList() {
ECTestKeys test_keys;
std::vector<std::tuple<std::string, std::string, std::string, std::string> >
keys({std::make_tuple(test_keys.private_test_key_1_secp521r1(),
test_keys.public_test_key_1_secp521r1(),
test_keys.private_test_key_2_secp521r1(),
test_keys.public_test_key_2_secp521r1()),
std::make_tuple(test_keys.private_test_key_1_secp384r1(),
test_keys.public_test_key_1_secp384r1(),
test_keys.private_test_key_2_secp384r1(),
test_keys.public_test_key_2_secp384r1()),
std::make_tuple(test_keys.private_test_key_1_secp256r1(),
test_keys.public_test_key_1_secp256r1(),
test_keys.private_test_key_2_secp256r1(),
test_keys.public_test_key_2_secp256r1())});
return keys;
}
protected:
EciesCryptoTest() {
test_private_key_ = std::get<0>(GetParam());
test_public_key_ = std::get<1>(GetParam());
test_ephemeral_private_key_ = std::get<2>(GetParam());
test_ephemeral_public_key_ = std::get<3>(GetParam());
private_key_ = ECPrivateKey::Create(test_private_key_);
public_key_ = ECPublicKey::Create(test_public_key_);
ephemeral_private_key_ = ECPrivateKey::Create(test_ephemeral_private_key_);
ephemeral_public_key_ = ECPublicKey::Create(test_ephemeral_public_key_);
}
std::string test_private_key_;
std::string test_public_key_;
std::string test_ephemeral_private_key_;
std::string test_ephemeral_public_key_;
std::unique_ptr<ECPrivateKey> private_key_;
std::unique_ptr<ECPublicKey> public_key_;
std::unique_ptr<ECPrivateKey> ephemeral_private_key_;
std::unique_ptr<ECPublicKey> ephemeral_public_key_;
};
TEST_P(EciesCryptoTest, EciesEncryptSuccess) {
std::string serialized_public_key;
const std::string context = "test context";
const std::string plaintext = "test plaintext";
std::string ecies_message;
// Use the test ephemeral key in the key source.
FakeECKeySource fake_key_source;
ASSERT_TRUE(fake_key_source.SetKey(ephemeral_public_key_->Curve(),
test_ephemeral_private_key_,
test_ephemeral_public_key_));
std::unique_ptr<EciesEncryptor> encryptor =
EciesEncryptor::Create(test_public_key_, &fake_key_source);
ASSERT_TRUE(encryptor->Encrypt(plaintext, context, &ecies_message));
EXPECT_TRUE(ecies_message.find(plaintext) == std::string::npos);
// Verify the decrypted_message.
std::string decrypted_message;
std::unique_ptr<EciesDecryptor> decryptor =
EciesDecryptor::Create(test_private_key_);
ASSERT_TRUE(decryptor != nullptr);
ASSERT_TRUE(decryptor->Decrypt(ecies_message, context, &decrypted_message));
EXPECT_EQ(plaintext, decrypted_message);
}
TEST_P(EciesCryptoTest, EciesDecryptShortEciesMessage) {
std::unique_ptr<EciesDecryptor> decryptor =
EciesDecryptor::Create(test_private_key_);
ASSERT_NE(nullptr, decryptor.get());
std::string decrypted_message;
std::string short_message(
ec_util::GetPublicKeyPointSize(private_key_->Curve()) + kMacSizeBytes - 1,
'a');
ASSERT_FALSE(
decryptor->Decrypt(short_message, "test context", &decrypted_message));
}
TEST_P(EciesCryptoTest, EciesDecryptBadSignature) {
std::string serialized_public_key;
const std::string context = "test context";
const std::string plaintext = "test plaintext";
std::string ecies_message;
// Use the test ephemeral key in the key source.
FakeECKeySource fake_key_source;
ASSERT_TRUE(fake_key_source.SetKey(ephemeral_public_key_->Curve(),
test_ephemeral_private_key_,
test_ephemeral_public_key_));
std::unique_ptr<EciesEncryptor> encryptor =
EciesEncryptor::Create(test_public_key_, &fake_key_source);
ASSERT_TRUE(encryptor->Encrypt(plaintext, context, &ecies_message));
// Corrupt the signature (at end of message) and verify that decryption fails.
ecies_message[ecies_message.size() - 1]++;
std::string decrypted_message;
std::unique_ptr<EciesDecryptor> decryptor =
EciesDecryptor::Create(test_private_key_);
ASSERT_TRUE(decryptor != nullptr);
ASSERT_FALSE(decryptor->Decrypt(ecies_message, context, &decrypted_message));
EXPECT_EQ("", decrypted_message);
}
TEST_P(EciesCryptoTest, EciesEncryptMismatchContext) {
std::string serialized_public_key;
const std::string context = "test context";
const std::string bogus_context = "bogus_context";
const std::string plaintext = "test plaintext";
std::string ecies_message;
// Use the test ephemeral key in the key source.
FakeECKeySource fake_key_source;
ASSERT_TRUE(fake_key_source.SetKey(ephemeral_public_key_->Curve(),
test_ephemeral_private_key_,
test_ephemeral_public_key_));
std::unique_ptr<EciesEncryptor> encryptor =
EciesEncryptor::Create(test_public_key_, &fake_key_source);
ASSERT_TRUE(encryptor != nullptr);
ASSERT_TRUE(encryptor->Encrypt(plaintext, context, &ecies_message));
EXPECT_GT(
ecies_message.size(),
kMacSizeBytes + ec_util::GetPublicKeyPointSize(public_key_->Curve()));
// Verify the decrypted_message using the invalid context.
std::string decrypted_message;
std::unique_ptr<EciesDecryptor> decryptor =
EciesDecryptor::Create(test_private_key_);
ASSERT_TRUE(decryptor != nullptr);
ASSERT_FALSE(
decryptor->Decrypt(ecies_message, bogus_context, &decrypted_message));
EXPECT_TRUE(decrypted_message.empty());
}
INSTANTIATE_TEST_SUITE_P(
EciesCryptoTest, EciesCryptoTest,
::testing::ValuesIn(EciesCryptoTest::GetTestKeyList()));
TEST(EciesEncryptorTest, EciesEncryptBadPublicKey) {
// Use the test ephemeral key in the key source.
FakeECKeySource fake_key_source;
std::unique_ptr<EciesEncryptor> encryptor =
EciesEncryptor::Create("bad public key.", &fake_key_source);
ASSERT_TRUE(encryptor == nullptr);
}
TEST(EciesEncryptorTest, EciesEncryptNullKeySource) {
std::unique_ptr<EciesEncryptor> encryptor =
EciesEncryptor::Create("bad public key.", nullptr);
ASSERT_TRUE(encryptor == nullptr);
}
class MockEcKeySource : public ECKeySource {
public:
MockEcKeySource() = default;
MOCK_METHOD3(GetECKey,
bool(ECPrivateKey::EllipticCurve curve, std::string* private_key,
std::string* public_key));
};
TEST(EciesEncryptorTest, EciesEncryptKeysourceFail) {
MockEcKeySource mock_ec_key_source;
EXPECT_CALL(mock_ec_key_source, GetECKey(_, _, _)).WillOnce(Return(false));
ECTestKeys test_keys;
std::unique_ptr<EciesEncryptor> encryptor = EciesEncryptor::Create(
test_keys.public_test_key_1_secp256r1(), &mock_ec_key_source);
ASSERT_TRUE(encryptor != nullptr);
std::string ecies_message;
ASSERT_FALSE(
encryptor->Encrypt("test plaintext", "test context", &ecies_message));
}
TEST(EciesEncryptorTest, EciesEncryptNullEciesMessage) {
FakeECKeySource fake_key_source;
ECTestKeys test_keys;
std::unique_ptr<EciesEncryptor> encryptor = EciesEncryptor::Create(
test_keys.public_test_key_1_secp256r1(), &fake_key_source);
ASSERT_TRUE(encryptor != nullptr);
ASSERT_FALSE(encryptor->Encrypt("test plaintext", "test context", nullptr));
}
TEST(EciesDecryptorTest, EciesDecryptBadPrivateKey) {
ECTestKeys test_keys;
std::string invalid_private_key(test_keys.private_test_key_1_secp521r1());
invalid_private_key[0]++;
std::unique_ptr<EciesDecryptor> decryptor =
EciesDecryptor::Create(invalid_private_key);
ASSERT_TRUE(decryptor == nullptr);
}
TEST(EciesDecryptorTest, EciesDecryptEmptyEciesMessage) {
ECTestKeys test_keys;
std::unique_ptr<EciesDecryptor> decryptor =
EciesDecryptor::Create(test_keys.private_test_key_1_secp521r1());
ASSERT_NE(nullptr, decryptor);
std::string decrypted_message;
ASSERT_FALSE(decryptor->Decrypt("", "test context", &decrypted_message));
}
TEST(EciesDecryptorTest, EciesDecryptNullPlaintext) {
ECTestKeys test_keys;
std::unique_ptr<EciesDecryptor> decryptor =
EciesDecryptor::Create(test_keys.private_test_key_1_secp521r1());
ASSERT_NE(nullptr, decryptor);
std::string decrypted_message;
ASSERT_FALSE(decryptor->Decrypt("foo", "test context", nullptr));
}
} // namespace widevine

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

@@ -19,7 +19,8 @@ TEST(FileUtilTest, EmptyFileName) {
}
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"));
std::string contents;
EXPECT_TRUE(GetContents(file_path, &contents));

View File

@@ -0,0 +1,56 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/keybox_client_cert.h"
#include "glog/logging.h"
#include "common/crypto_util.h"
#include "common/error_space.h"
#include "common/sha_util.h"
#include "common/signing_key_util.h"
#include "common/wvm_token_handler.h"
#include "protos/public/errors.pb.h"
namespace widevine {
Status KeyboxClientCert::Initialize(const std::string& keybox_token) {
system_id_ = WvmTokenHandler::GetSystemId(keybox_token);
serial_number_ = WvmTokenHandler::GetEncryptedUniqueId(keybox_token);
bool insecure_keybox = false;
Status status = WvmTokenHandler::DecryptDeviceKey(keybox_token, &device_key_,
nullptr, &insecure_keybox);
if (!status.ok()) {
Errors new_code = status.error_code() == error::NOT_FOUND
? MISSING_PRE_PROV_KEY
: KEYBOX_DECRYPT_ERROR;
return Status(error_space, new_code, status.error_message());
}
return OkStatus();
}
Status KeyboxClientCert::VerifySignature(
const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) const {
DCHECK(!signing_key_.empty());
using crypto_util::VerifySignatureHmacSha256;
if (!VerifySignatureHmacSha256(
GetClientSigningKey(signing_key_, protocol_version), signature,
message)) {
return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
}
return OkStatus();
}
void KeyboxClientCert::GenerateSigningKey(const std::string& message,
ProtocolVersion protocol_version) {
signing_key_ = crypto_util::DeriveKey(
key(), crypto_util::kSigningKeyLabel,
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
SigningKeyMaterialSizeBits(protocol_version));
}
} // namespace widevine

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.
////////////////////////////////////////////////////////////////////////////////
#ifndef COMMON_KEYBOX_CLIENT_CERT_H_
#define COMMON_KEYBOX_CLIENT_CERT_H_
#include "common/client_cert.h"
namespace widevine {
//
class KeyboxClientCert : public ClientCert {
public:
KeyboxClientCert() {}
~KeyboxClientCert() override {}
KeyboxClientCert(const KeyboxClientCert&) = delete;
KeyboxClientCert& operator=(const KeyboxClientCert&) = delete;
Status Initialize(const std::string& keybox_token);
Status VerifySignature(const std::string& message,
const std::string& signature,
ProtocolVersion protocol_version) const override;
void GenerateSigningKey(const std::string& message,
ProtocolVersion protocol_version) override;
const std::string& encrypted_key() const override { return unimplemented_; }
const std::string& key() const override { return device_key_; }
const std::string& serial_number() const override { return serial_number_; }
const std::string& service_id() const override { return unimplemented_; }
const std::string& signing_key() const override { return signing_key_; }
const std::string& signer_serial_number() const override {
return unimplemented_;
}
uint32_t signer_creation_time_seconds() const override { return 0; }
bool signed_by_provisioner() const override { return false; }
uint32_t system_id() const override { return system_id_; }
widevine::ClientIdentification::TokenType type() const override {
return ClientIdentification::KEYBOX;
}
// Set the system-wide pre-provisioning keys; argument must be human-readable
// hex digits.
// Must be called before any other method of this class is called, unless
// created by ClientCert::CreateWithPreProvisioningKey(...).
static void SetPreProvisioningKeys(
const std::multimap<uint32_t, std::string>& keymap);
static bool IsSystemIdKnown(const uint32_t system_id);
static uint32_t GetSystemId(const std::string& keybox_bytes);
private:
std::string unimplemented_;
std::string device_key_;
uint32_t system_id_;
std::string serial_number_;
std::string signing_key_;
};
} // namespace widevine
#endif // COMMON_KEYBOX_CLIENT_CERT_H_

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

@@ -39,8 +39,8 @@ class MockRsaPublicKey : public RsaPublicKey {
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
std::string* encrypted_message));
MOCK_CONST_METHOD2(VerifySignature,
bool(const std::string& message, const std::string& signature));
MOCK_CONST_METHOD2(VerifySignature, bool(const std::string& message,
const std::string& signature));
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
@@ -54,13 +54,15 @@ class MockRsaKeyFactory : public RsaKeyFactory {
MockRsaKeyFactory() {}
~MockRsaKeyFactory() override {}
MOCK_CONST_METHOD1(CreateFromPkcs1PrivateKey,
MOCK_CONST_METHOD1(
CreateFromPkcs1PrivateKey,
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
MOCK_CONST_METHOD2(
CreateFromPkcs8PrivateKey,
std::unique_ptr<RsaPrivateKey>(const std::string& private_key,
MOCK_CONST_METHOD2(CreateFromPkcs8PrivateKey,
std::unique_ptr<RsaPrivateKey>(
const std::string& private_key,
const std::string& private_key_passphrase));
MOCK_CONST_METHOD1(CreateFromPkcs1PublicKey,
MOCK_CONST_METHOD1(
CreateFromPkcs1PublicKey,
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
private:

View File

@@ -47,6 +47,9 @@ using ScopedOpenSSLStackOnly =
using ScopedBIGNUM = ScopedOpenSSLType<BIGNUM, BN_free>;
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 ScopedPKEY = ScopedOpenSSLType<EVP_PKEY, EVP_PKEY_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

84
common/private_key_util.h Normal file
View File

@@ -0,0 +1,84 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Shared private key utilities between RSA and EC.
#ifndef COMMON_PRIVATE_KEY_UTIL_H_
#define COMMON_PRIVATE_KEY_UTIL_H_
#include <string>
#include "glog/logging.h"
#include "openssl/bio.h"
namespace widevine {
namespace private_key_util {
template <class Key>
bool SerializeKey(const Key* key, int (*serialization_func)(BIO*, Key*),
std::string* serialized_key) {
if (key == nullptr) {
LOG(ERROR) << "Key is nullptr.";
return false;
}
if (serialized_key == nullptr) {
LOG(ERROR) << "Pointer to hold serialized key is nullptr.";
return false;
}
BIO* bio = BIO_new(BIO_s_mem());
if (bio == nullptr) {
LOG(ERROR) << "BIO_new returned nullptr";
return false;
}
bool success = false;
if (serialization_func(bio, const_cast<Key*>(key)) != 0) {
int serialized_size = BIO_pending(bio);
serialized_key->assign(serialized_size, 0);
if (BIO_read(bio, &(*serialized_key)[0], serialized_size) ==
serialized_size) {
success = true;
} else {
LOG(ERROR) << "BIO_read failure";
}
} else {
LOG(ERROR) << "Key serialization failure";
}
BIO_free(bio);
return success;
}
template <class Key>
bool DeserializeKey(const std::string& serialized_key,
Key* (*deserialization_func)(BIO*, Key**), Key** key) {
if (serialized_key.empty()) {
LOG(ERROR) << "Serialized key is empty.";
return false;
}
if (key == nullptr) {
LOG(ERROR) << "Pointer to hold new key is nullptr.";
return false;
}
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
serialized_key.size());
if (bio == nullptr) {
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
return false;
}
*key = deserialization_func(bio, nullptr);
BIO_free(bio);
if (*key == nullptr) {
LOG(ERROR) << "Key deserialization failure";
}
return *key != nullptr;
}
} // namespace private_key_util
} // namespace widevine
#endif // COMMON_PRIVATE_KEY_UTIL_H_

View File

@@ -185,7 +185,8 @@ Status RemoteAttestationVerifier::VerifyRemoteAttestation(
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
const std::string& message, const RemoteAttestation& remote_attestation,
const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) {
const ClientIdentification& client_id,
std::string* remote_attestation_cert_sn) {
if (!client_id.has_token()) {
return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-token-missing"));
@@ -217,7 +218,8 @@ Status RemoteAttestationVerifier::VerifyRemoteAttestation(
status = ca_->VerifyCertChain(*cert_chain);
ca_mutex_.ReaderUnlock();
if (!status.ok()) {
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
return (Status(
error_space, REMOTE_ATTESTATION_FAILED,
std::string("remote-attestation-cert-chain-validation-failed: ") +
status.error_message()));
}

View File

@@ -16,7 +16,6 @@
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "common/status.h"
@@ -33,6 +32,11 @@ namespace widevine {
class RemoteAttestationVerifier {
public:
RemoteAttestationVerifier() : enable_test_certificates_(false) {}
RemoteAttestationVerifier(const RemoteAttestationVerifier&) = delete;
RemoteAttestationVerifier& operator=(const RemoteAttestationVerifier&) =
delete;
virtual ~RemoteAttestationVerifier() {}
// Singleton accessor.
@@ -82,9 +86,7 @@ class RemoteAttestationVerifier {
bool enable_test_certificates_;
absl::Mutex ca_mutex_;
std::unique_ptr<X509CA> ca_ GUARDED_BY(ca_mutex_);
DISALLOW_COPY_AND_ASSIGN(RemoteAttestationVerifier);
std::unique_ptr<X509CA> ca_ ABSL_GUARDED_BY(ca_mutex_);
};
} // namespace widevine

107
common/rot_id_generator.cc Normal file
View File

@@ -0,0 +1,107 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Classes for generating and decrypting the root of trust id which is
// included in generated DRM Certificates.
#include "common/rot_id_generator.h"
#include <memory>
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "common/crypto_util.h"
#include "common/ec_key.h"
#include "common/rot_id_util.h"
#include "common/sha_util.h"
#include "common/status.h"
#include "protos/public/drm_certificate.pb.h"
namespace {
constexpr char kRotIdLabel[] = "ROOT_OF_TRUST_ID ENCRYPTION LABEL";
constexpr int32_t kKeyId = 0;
std::string GenerateContext(uint32_t system_id) {
return absl::StrCat(kRotIdLabel, system_id);
}
} // anonymous namespace
namespace widevine {
Status RootOfTrustIdGenerator::Generate(uint32_t system_id,
const std::string& unique_id,
RootOfTrustId* root_of_trust_id) const {
CHECK(root_of_trust_id != nullptr) << "root_of_trust_id was null.";
if (system_id == 0) {
return Status(error::INVALID_ARGUMENT, "system id should not be 0.");
}
if (unique_id.empty()) {
return Status(error::INVALID_ARGUMENT,
"The unique id should not be empty.");
}
root_of_trust_id->set_version(
widevine::RootOfTrustId::ROOT_OF_TRUST_ID_VERSION_1);
root_of_trust_id->set_key_id(kKeyId);
if (!ecies_encryptor_->Encrypt(
unique_id, GenerateContext(system_id),
root_of_trust_id->mutable_encrypted_unique_id())) {
root_of_trust_id->Clear();
return Status(error::INTERNAL, "Encrypt failed.");
}
std::string unique_id_hash = GenerateUniqueIdHash(unique_id);
if (unique_id_hash.empty()) {
root_of_trust_id->Clear();
return Status(error::INTERNAL, "Could not generate unique id hash.");
}
root_of_trust_id->set_unique_id_hash(GenerateRotIdHash(
root_of_trust_id->encrypted_unique_id(), system_id, unique_id_hash));
if (root_of_trust_id->unique_id_hash().empty()) {
root_of_trust_id->Clear();
return Status(error::INTERNAL, "Failed to generate revoked id hash.");
}
return OkStatus();
}
std::string RootOfTrustIdGenerator::GenerateUniqueIdHash(
const std::string& unique_id) const {
if (unique_id.empty()) {
LOG(WARNING) << "unique_id should not be empty.";
return "";
}
return Sha256_Hash(absl::StrCat(unique_id, wv_shared_salt_));
}
Status RootOfTrustIdDecryptor::DecryptUniqueId(
uint32_t system_id, const std::string& rot_encrypted_id,
std::string* unique_id) const {
CHECK(unique_id != nullptr) << "unique_id was null.";
if (system_id == 0) {
return Status(error::INVALID_ARGUMENT, "system id should not be 0.");
}
if (rot_encrypted_id.empty()) {
return Status(error::INVALID_ARGUMENT,
"The rot_encrypted_id should not be empty.");
}
if (!ecies_decryptor_->Decrypt(rot_encrypted_id, GenerateContext(system_id),
unique_id)) {
return Status(error::INTERNAL, "Failed to decrypt rot_encrypted_id");
}
return OkStatus();
}
} // namespace widevine

91
common/rot_id_generator.h Normal file
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:
// Classes for generating and decrypting the root of trust id which is
// included in generated DRM Certificates.
#ifndef COMMON_ROT_ID_GENERATOR_H_
#define COMMON_ROT_ID_GENERATOR_H_
#include <memory>
#include <utility>
#include <cstdint>
#include "common/ecies_crypto.h"
#include "common/rot_id_util.h"
#include "common/status.h"
#include "protos/public/drm_certificate.pb.h"
namespace widevine {
// The RootOfTrustIdGenerator is used to create a root of trust id that is
// associated with the keybox or OEM X.509 certificate that was used as
// authentication during provisioning.
class RootOfTrustIdGenerator {
public:
// The constructor for creating the RootOfTrustIdGenerator. |ecies_encryptor|
// is used to encrypt the unique identifier. It must not be null. The
// |wv_shared_salt| is a secret salt used in creating the hash
// component of the Root of Trust Id. The secret salt is stored securely. The
// same value is used as part of generating all Root of Trust Ids.
// The |wv_shared_salt| is also used for creating the unique id hash
// values. The unique id hash values identify revoked devices and are
// published in the DCSL and consumed by the License SDK.
RootOfTrustIdGenerator(std::unique_ptr<EciesEncryptor> ecies_encryptor,
std::string wv_shared_salt)
: ecies_encryptor_(std::move(ecies_encryptor)),
wv_shared_salt_(std::move(wv_shared_salt)) {}
virtual ~RootOfTrustIdGenerator() {}
// Creates the root of trust identifier. This fills the fields of the
// |root_of_trust_id|. The fields are generated per the spec at go/wv-kb-id.
// The |system_id| is required and must match the system id to which the
// |unique_id| belongs.
// Returns INVALID_ARGUMENT if |system_id| is 0, |unique_id| is empty.
// |roof_of_trust_id| is owned by the caller and must not be null.
virtual Status Generate(uint32_t system_id, const std::string& unique_id,
RootOfTrustId* root_of_trust_id) const;
// Generates the hash of the |unique_id| per the spec in go/wv-kb-id. This
// unique id hash (aka inner hash) is the identifier used when revoking an
// individual device. The result of this hash is one of the values used to
// generate Root of Trust Id Hash.
// |unique_id| should not be empty. If it is, an empty hash
// is returned.
std::string GenerateUniqueIdHash(const std::string& unique_id) const;
private:
std::unique_ptr<EciesEncryptor> ecies_encryptor_;
std::string wv_shared_salt_;
};
// The RootOfTrustIdDecryptor is used to decrypt the root of trust id. It
// requires an |ecies_decryptor| which must use the private key that matches
// the public key used with the RootOfTrustIdGenerator. |ecies_decryptor|
// must not be null. The RootOfTrustIdDecryptor will take ownership.
class RootOfTrustIdDecryptor {
public:
explicit RootOfTrustIdDecryptor(
std::unique_ptr<EciesDecryptor> ecies_decryptor)
: ecies_decryptor_(std::move(ecies_decryptor)) {}
// Decrypts the |rot_encrypted_id| using the |system_id| as part of the
// context. |unique_id| contains the decrypted value on success.
// |rot_encrypted_id| must not be empty. |unique_id| must not be null.
// Returns true on success, false on failure.
Status DecryptUniqueId(uint32_t system_id, const std::string& rot_encrypted_id,
std::string* unique_id) const;
private:
std::unique_ptr<EciesDecryptor> ecies_decryptor_;
};
} // namespace widevine
#endif // COMMON_ROT_ID_GENERATOR_H_

View File

@@ -0,0 +1,258 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Unit tests for RootOfTrustIdGenerator and RootO
#include "common/rot_id_generator.h"
#include <memory>
#include "google/protobuf/util/message_differencer.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "common/ec_key.h"
#include "common/ec_test_keys.h"
#include "common/ecies_crypto.h"
#include "common/fake_ec_key_source.h"
#include "common/rot_id_util.h"
#include "common/status.h"
#include "protos/public/drm_certificate.pb.h"
using ::testing::_;
using ::testing::NotNull;
using ::testing::Return;
namespace {
constexpr char kTestSharedSalt[] = "test shared salt";
constexpr char kTestUniqueId[] = "test unique id";
constexpr uint32_t kTestSystemId = 1234;
constexpr char kExpectedRotIdEncryptedIdHex[] =
"048aea3f16ff24a9bf03283015ee52509a551c60c7a7cc4b995b4055ce4619d4d45efde068"
"27ea78f3071f024a785244d3dfbeac5fa51c8a498da65aaca1252bd1f174166d9645b6ecd6"
"669aa0afb7b766682c02f794d67050f779ebd104a767bedef288be13d321ae79d7209b5cd3"
"4698";
constexpr char kExpectedRotIdHashHex[] =
"2c4dbc37092ab3e55897639d4f0dd3c824001d07eb69a3f9e6db3b846bf31828";
} // anonymous namespace
namespace widevine {
class RootOfTrustIdGeneratorTest : public ::testing::Test {
public:
RootOfTrustIdGeneratorTest() {
expected_root_of_trust_id_.set_encrypted_unique_id(
absl::HexStringToBytes(kExpectedRotIdEncryptedIdHex));
expected_root_of_trust_id_.set_unique_id_hash(
absl::HexStringToBytes(kExpectedRotIdHashHex));
expected_root_of_trust_id_.set_key_id(0);
expected_root_of_trust_id_.set_version(
RootOfTrustId::ROOT_OF_TRUST_ID_VERSION_1);
}
std::unique_ptr<EciesEncryptor> CreateEncryptor() {
return EciesEncryptor::Create(test_keys_.public_test_key_2_secp256r1(),
&fake_ec_key_source_);
}
std::unique_ptr<EciesDecryptor> CreateDecryptor() {
return EciesDecryptor::Create(test_keys_.private_test_key_2_secp256r1());
}
protected:
ECTestKeys test_keys_;
FakeECKeySource fake_ec_key_source_;
RootOfTrustId expected_root_of_trust_id_;
};
class MockEciesEncryptor : public EciesEncryptor {
public:
static MockEciesEncryptor* Create() {
ECTestKeys test_keys;
std::unique_ptr<ECPublicKey> ec_key =
ECPublicKey::Create(test_keys.public_test_key_1_secp256r1());
return new MockEciesEncryptor(std::move(ec_key));
}
MOCK_CONST_METHOD3(Encrypt, bool(const std::string&, const std::string&,
std::string*));
private:
explicit MockEciesEncryptor(std::unique_ptr<ECPublicKey> ec_key)
: EciesEncryptor(std::move(ec_key), &fake_ec_key_source_) {}
FakeECKeySource fake_ec_key_source_;
};
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdSuccess) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
// Generate the root of trust id.
RootOfTrustId root_of_trust_id;
ASSERT_OK(
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_root_of_trust_id_, root_of_trust_id));
// Verify decrypted unique id.
std::string decrypted_unique_id;
EXPECT_OK(decryptor.DecryptUniqueId(kTestSystemId,
root_of_trust_id.encrypted_unique_id(),
&decrypted_unique_id));
EXPECT_EQ(kTestUniqueId, decrypted_unique_id);
// Verify hashed unique id matches.
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId);
EXPECT_TRUE(IsRotIdRevoked(root_of_trust_id.encrypted_unique_id(),
kTestSystemId, root_of_trust_id.unique_id_hash(),
{unique_id_hash}));
}
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdUniqueSuccess) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
std::string rot_encrypted_id;
std::string rot_id_hash;
// Generate the root of trust id.
RootOfTrustId root_of_trust_id;
ASSERT_OK(
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_root_of_trust_id_, root_of_trust_id));
// Generate a second root of trust id for the same unique id.
// This must generate a different looking id.
// First, assign a new ephemeral to the fake key source.
fake_ec_key_source_.SetKey(ECPrivateKey::SECP256R1,
test_keys_.private_test_key_2_secp256r1(),
test_keys_.public_test_key_2_secp256r1());
RootOfTrustId second_root_of_trust_id;
ASSERT_OK(generator.Generate(kTestSystemId, kTestUniqueId,
&second_root_of_trust_id));
EXPECT_FALSE(second_root_of_trust_id.encrypted_unique_id().empty());
EXPECT_NE(kTestUniqueId, second_root_of_trust_id.encrypted_unique_id());
EXPECT_FALSE(second_root_of_trust_id.unique_id_hash().empty());
// Verify that the second id does not equal the first.
EXPECT_NE(root_of_trust_id.encrypted_unique_id(),
second_root_of_trust_id.encrypted_unique_id());
EXPECT_NE(root_of_trust_id.unique_id_hash(),
second_root_of_trust_id.unique_id_hash());
// Verify second decrypted unique id.
std::string decrypted_unique_id;
EXPECT_OK(decryptor.DecryptUniqueId(
kTestSystemId, second_root_of_trust_id.encrypted_unique_id(),
&decrypted_unique_id));
EXPECT_EQ(kTestUniqueId, decrypted_unique_id);
// Verify hashed unique id matches.
std::string unique_id_hash = generator.GenerateUniqueIdHash(kTestUniqueId);
EXPECT_TRUE(IsRotIdRevoked(
second_root_of_trust_id.encrypted_unique_id(), kTestSystemId,
second_root_of_trust_id.unique_id_hash(), {unique_id_hash}));
}
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdFailedEncryption) {
MockEciesEncryptor* mock_encryptor = MockEciesEncryptor::Create();
ASSERT_THAT(mock_encryptor, NotNull());
RootOfTrustIdGenerator generator(
std::unique_ptr<EciesEncryptor>(mock_encryptor), kTestSharedSalt);
EXPECT_CALL(*mock_encryptor, Encrypt(_, _, _))
.Times(1)
.WillOnce(Return(false))
.RetiresOnSaturation();
std::string rot_encrypted_id;
std::string rot_id_hash;
// Attempt to generate the root of trust id.
RootOfTrustId root_of_trust_id;
EXPECT_EQ(error::INTERNAL,
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id)
.error_code());
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
RootOfTrustId::default_instance(), root_of_trust_id));
}
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdEmptyIdFail) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustId root_of_trust_id;
// Should fail because the id is blank.
EXPECT_EQ(
error::INVALID_ARGUMENT,
generator.Generate(kTestSystemId, "", &root_of_trust_id).error_code());
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
RootOfTrustId::default_instance(), root_of_trust_id));
}
TEST_F(RootOfTrustIdGeneratorTest, GenerateIdNullRotIdFail) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
// Should fail because the id is blank.
EXPECT_DEATH(generator.Generate(kTestSystemId, kTestUniqueId,
nullptr /* root of trust id*/),
"root_of_trust_id");
}
TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdMismatchFails) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
// Generate the root of trust id.
RootOfTrustId root_of_trust_id;
ASSERT_OK(
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_root_of_trust_id_, root_of_trust_id));
// Attempt to decrypt with different system id. Should fail.
std::string decrypted_unique_id;
EXPECT_EQ(error::INTERNAL,
decryptor
.DecryptUniqueId(kTestSystemId + 1,
root_of_trust_id.encrypted_unique_id(),
&decrypted_unique_id)
.error_code());
}
TEST_F(RootOfTrustIdGeneratorTest, DecryptorBlankUniqueId) {
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
// Attempt to decrypt empty encrypted id.
std::string decrypted_unique_id;
EXPECT_EQ(error::INVALID_ARGUMENT,
decryptor.DecryptUniqueId(kTestSystemId, "", &decrypted_unique_id)
.error_code());
}
TEST_F(RootOfTrustIdGeneratorTest, DecryptorSystemIdNullDecryptedIdFails) {
RootOfTrustIdGenerator generator(CreateEncryptor(), kTestSharedSalt);
RootOfTrustIdDecryptor decryptor(CreateDecryptor());
// Generate the root of trust id.
RootOfTrustId root_of_trust_id;
ASSERT_OK(
generator.Generate(kTestSystemId, kTestUniqueId, &root_of_trust_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
expected_root_of_trust_id_, root_of_trust_id));
// Attempt to decrypt with a nullptr for the decrypted id.
std::string decrypted_unique_id;
EXPECT_DEATH(
decryptor.DecryptUniqueId(
kTestSystemId, root_of_trust_id.encrypted_unique_id(), nullptr),
"unique_id");
}
} // namespace widevine

51
common/rot_id_util.cc Normal file
View File

@@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Helper methods for the Root of Trust Id.
#include "common/rot_id_util.h"
#include <memory>
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "common/crypto_util.h"
#include "common/ec_key.h"
#include "common/sha_util.h"
namespace widevine {
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
const std::string& rot_id_hash,
const std::vector<std::string>& revoked_ids) {
// This could conceivably happen for legacy DRM certificates without a ROT id.
// No need to match if there's nothing to match against.
if (encrypted_unique_id.empty() || rot_id_hash.empty()) {
return false;
}
for (const auto& revoked_id : revoked_ids) {
std::string revoked_hash =
GenerateRotIdHash(encrypted_unique_id, system_id, revoked_id);
if (rot_id_hash == revoked_hash) {
return true;
}
}
return false;
}
std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
const std::string& unique_id_hash) {
if (salt.empty() || unique_id_hash.empty()) {
return "";
}
return Sha256_Hash(absl::StrCat(salt, system_id, unique_id_hash));
}
} // namespace widevine

45
common/rot_id_util.h Normal file
View File

@@ -0,0 +1,45 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Helper methods for the Root of Trust Id.
#ifndef COMMON_ROT_ID_UTIL_H_
#define COMMON_ROT_ID_UTIL_H_
#include <memory>
#include <vector>
#include <cstdint>
#include "common/ec_key.h"
#include "common/local_ec_key_source.h"
namespace widevine {
// Helper function that compares the |rot_id_hash| to a hash of each of the
// |revoked_ids|. The |revoked_ids| are the unique id hash (aka inner hash)
// values as defined in the spec at go/wv-kb-id. The |encrypted_unique_id| and
// |system_id| are used to compute the hash of each of the |revoked_ids|.
// Returns true if any of the revoked_ids match.
bool IsRotIdRevoked(const std::string& encrypted_unique_id, uint32_t system_id,
const std::string& rot_id_hash,
const std::vector<std::string>& revoked_ids);
// Helper function that generates the hash for the ROT id from the
// |unique_id_hash|, the |system_id| and the |salt|. |salt| is typically an
// encrypted unique id. Since we use an ephemeral eliptic curve key as part of
// the encrypted unique id, the value is effectively random can be used as a
// salt.
// Returns the hash value on success.
// If |salt| or |unique_id_hash| are empty, this will return an empty
// string.
std::string GenerateRotIdHash(const std::string& salt, uint32_t system_id,
const std::string& unique_id_hash);
} // namespace widevine
#endif // COMMON_ROT_ID_UTIL_H_

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.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Unit tests for the rot_id_util helper methods.
#include "common/rot_id_util.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
namespace {
constexpr char kFakeEncryptedId[] = "fake encrypted id";
constexpr char kFakeUniqueIdHash[] = "fake unique_id hash";
// This is the ROT ID Hash generated from the fake values.
constexpr char kRotIdHashHex[] =
"0a757dde0f1080b60f34bf8e46af573ce987b5ed1c831b44952e2feed5243a95";
constexpr uint32_t kFakeSystemId = 1234;
constexpr uint32_t kOtherFakeSystemId = 9876;
} // anonymous namespace
namespace widevine {
TEST(RotIdUtilTest, IsRotIdRevokedMatches) {
ASSERT_TRUE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId,
absl::HexStringToBytes(kRotIdHashHex),
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
}
TEST(RotIdUtilTest, IsRotIdRevokedNoMatchSystemId) {
ASSERT_FALSE(
IsRotIdRevoked(kFakeEncryptedId, kOtherFakeSystemId,
absl::HexStringToBytes(kRotIdHashHex),
{"NO MATCH UNIQUE ID HASH 1", kFakeUniqueIdHash}));
}
TEST(RotIdUtilTest, IsRotIdRevokedNoMatch) {
ASSERT_FALSE(IsRotIdRevoked(
kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash,
{"NO MATCH UNIQUE ID HASH 1", "NO MATCH UNIQUE ID HASH 2"}));
}
TEST(RotIdUtilTest, IsRotIdRevokedEmptyList) {
ASSERT_FALSE(IsRotIdRevoked(kFakeEncryptedId, kFakeSystemId,
kFakeUniqueIdHash,
{/* Intentionally empty vector */}));
}
// This test really only ensures the stability of the implementation. If the
// hash ever changes, then it will introduce problems into the ecosystem.
TEST(RotIdUtilTest, GenerateRotIdHashSuccess) {
ASSERT_EQ(
absl::HexStringToBytes(kRotIdHashHex),
GenerateRotIdHash(kFakeEncryptedId, kFakeSystemId, kFakeUniqueIdHash));
}
} // namespace widevine

View File

@@ -172,6 +172,11 @@ RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key)
CHECK(key_ != nullptr);
}
RsaPublicKey::RsaPublicKey(const RsaPrivateKey& rsa_key)
: key_(RSAPublicKey_dup(rsa_key.key_)) {
CHECK(key_ != nullptr);
}
RsaPublicKey::~RsaPublicKey() { RSA_free(key_); }
RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) {
@@ -248,8 +253,8 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
return true;
}
bool RsaPublicKey::VerifySignatureSha256Pkcs7(const std::string& message,
const std::string& signature) const {
bool RsaPublicKey::VerifySignatureSha256Pkcs7(
const std::string& message, const std::string& signature) const {
if (message.empty()) {
LOG(ERROR) << "Empty signature verification message";
return false;
@@ -291,7 +296,8 @@ std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs1PrivateKey(
}
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
const std::string& private_key, const std::string& private_key_passphrase) const {
const std::string& private_key,
const std::string& private_key_passphrase) const {
std::string pkcs1_key;
const bool result =
private_key_passphrase.empty()

View File

@@ -18,7 +18,6 @@
#include <string>
#include <cstdint>
#include "base/macros.h"
#include "openssl/rsa.h"
namespace widevine {
@@ -79,7 +78,13 @@ class RsaPrivateKey {
class RsaPublicKey {
public:
explicit RsaPublicKey(RSA* key);
RsaPublicKey(const RsaPublicKey&);
// Copy constructor.
RsaPublicKey(const RsaPublicKey& rsa_key);
// Construct RsaPublicKey object from RsaPrivateKey.
explicit RsaPublicKey(const RsaPrivateKey& rsa_key);
virtual ~RsaPublicKey();
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
@@ -132,6 +137,10 @@ class RsaPublicKey {
class RsaKeyFactory {
public:
RsaKeyFactory();
RsaKeyFactory(const RsaKeyFactory&) = delete;
RsaKeyFactory& operator=(const RsaKeyFactory&) = delete;
virtual ~RsaKeyFactory();
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
@@ -141,14 +150,12 @@ class RsaKeyFactory {
// Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or
// EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty).
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs8PrivateKey(
const std::string& private_key, const std::string& private_key_passphrase) const;
const std::string& private_key,
const std::string& private_key_passphrase) const;
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
virtual std::unique_ptr<RsaPublicKey> CreateFromPkcs1PublicKey(
const std::string& public_key) const;
private:
DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory);
};
} // namespace widevine

View File

@@ -161,6 +161,19 @@ TEST_F(RsaKeyTest, EncryptAndDecrypt_2048) {
factory_.CreateFromPkcs1PublicKey(public_key));
}
TEST_F(RsaKeyTest, RsaPublicKeyFromPrivateKey) {
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
ASSERT_TRUE(private_key);
std::unique_ptr<RsaPublicKey> public_key(new RsaPublicKey(*private_key));
ASSERT_TRUE(public_key);
EXPECT_TRUE(private_key->MatchesPublicKey(*public_key));
EXPECT_TRUE(public_key->MatchesPrivateKey(*private_key));
TestEncryption(std::move(private_key), std::move(public_key));
}
TEST_F(RsaKeyTest, SignAndVerify_3072) {
const std::string& private_key = test_keys_.private_test_key_1_3072_bits();
const std::string& public_key = test_keys_.public_test_key_1_3072_bits();

View File

@@ -5,7 +5,6 @@
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// RSA utility functions for serializing and deserializing RSA keys,
@@ -14,12 +13,14 @@
#include "common/rsa_util.h"
#include <limits.h>
#include <cstring>
#include <memory>
#include "glog/logging.h"
#include "openssl/pem.h"
#include "openssl/x509.h"
#include "common/private_key_util.h"
namespace {
int BigNumGreaterThanPow2(const BIGNUM* b, int n) {
@@ -34,92 +35,28 @@ int BigNumGreaterThanPow2(const BIGNUM* b, int n) {
namespace widevine {
namespace rsa_util {
static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
bool serialize_private_key) {
if (key == nullptr) {
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
<< " RSA key is nullptr.";
return false;
}
if (serialized_key == nullptr) {
LOG(ERROR) << "Pointer to hold serialized RSA"
<< (serialize_private_key ? "Private" : "Public")
<< "Key is nullptr.";
return false;
}
BIO* bio = BIO_new(BIO_s_mem());
if (bio == nullptr) {
LOG(ERROR) << "BIO_new returned nullptr";
return false;
}
bool success = false;
if ((serialize_private_key
? i2d_RSAPrivateKey_bio(bio, const_cast<RSA*>(key))
: i2d_RSAPublicKey_bio(bio, const_cast<RSA*>(key))) != 0) {
int serialized_size = BIO_pending(bio);
serialized_key->assign(serialized_size, 0);
if (BIO_read(bio, &(*serialized_key)[0], serialized_size) ==
serialized_size) {
success = true;
} else {
LOG(ERROR) << "BIO_read failure";
}
} else {
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
<< " key serialization failure";
}
BIO_free(bio);
return success;
}
static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key,
bool deserialize_private_key) {
if (serialized_key.empty()) {
LOG(ERROR) << "Serialized RSA"
<< (deserialize_private_key ? "Private" : "Public")
<< "Key is empty.";
return false;
}
if (key == nullptr) {
LOG(ERROR) << "Pointer to hold new RSA "
<< (deserialize_private_key ? "private" : "public")
<< " key is nullptr.";
return false;
}
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
serialized_key.size());
if (bio == nullptr) {
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
return false;
}
*key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, nullptr)
: d2i_RSAPublicKey_bio(bio, nullptr);
BIO_free(bio);
if (*key == nullptr) {
LOG(ERROR) << (deserialize_private_key ? "Private" : "Public")
<< " RSA key deserialization failure";
}
return *key != nullptr;
}
bool SerializeRsaPrivateKey(const RSA* private_key,
std::string* serialized_private_key) {
return SerializeRsaKey(private_key, serialized_private_key, true);
return private_key_util::SerializeKey<RSA>(private_key, i2d_RSAPrivateKey_bio,
serialized_private_key);
}
bool DeserializeRsaPrivateKey(const std::string& serialized_private_key,
RSA** private_key) {
return DeserializeRsaKey(serialized_private_key, private_key, true);
return private_key_util::DeserializeKey<RSA>(
serialized_private_key, d2i_RSAPrivateKey_bio, private_key);
}
bool SerializeRsaPublicKey(const RSA* public_key,
std::string* serialized_public_key) {
return SerializeRsaKey(public_key, serialized_public_key, false);
return private_key_util::SerializeKey<RSA>(public_key, i2d_RSAPublicKey_bio,
serialized_public_key);
}
bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
RSA** public_key) {
return DeserializeRsaKey(serialized_public_key, public_key, false);
return private_key_util::DeserializeKey<RSA>(
serialized_public_key, d2i_RSAPublicKey_bio, public_key);
}
bool SerializePrivateKeyInfo(const RSA* private_key,
@@ -327,8 +264,8 @@ int get_password(char* buf, int size, int rwflag, void* u) {
}
} // namespace
bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key,
const std::string& passphrase,
bool DeserializeEncryptedPrivateKeyInfo(
const std::string& serialized_private_key, const std::string& passphrase,
RSA** private_key) {
if (serialized_private_key.empty()) {
LOG(ERROR) << "Serialized RSAEncryptedPrivateKeyInfo is empty.";
@@ -349,8 +286,8 @@ bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_ke
return false;
}
bool success = false;
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, nullptr, get_password,
const_cast<std::string*>(&passphrase));
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(
bio, nullptr, get_password, const_cast<std::string*>(&passphrase));
if (evp == nullptr) {
LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned nullptr.";
goto cleanup;

View File

@@ -119,8 +119,8 @@ bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
// which is not allocated if the method fails. This parameter must not be
// NULL.
// Returns true if successful, false otherwise.
bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_key,
const std::string& passphrase,
bool DeserializeEncryptedPrivateKeyInfo(
const std::string& serialized_private_key, const std::string& passphrase,
RSA** private_key);
// Convert DER-encoded PKCS#1 RSAPrivateKey to DER-encoded PKCS#8

View File

@@ -0,0 +1,185 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Implementation of the SecurityProfileList class.
#include "common/security_profile_list.h"
#include <vector>
#include "common/client_id_util.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/provisioned_device_info.pb.h"
#include "protos/public/security_profile.pb.h"
namespace widevine {
using ClientCapabilities = ClientIdentification::ClientCapabilities;
const char kModDrmMake[] = "company_name";
const char kModDrmModel[] = "model_name";
int SecurityProfileList::Init() { return AddDefaultProfiles(); }
int SecurityProfileList::AddDefaultProfiles() {
const uint32_t oemcrypto_8 = 8;
const uint32_t oemcrypto_12 = 12;
const bool make_model_not_verified = false;
SecurityProfile profile;
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_1,
ClientCapabilities::HDCP_NONE,
ClientCapabilities::ANALOG_OUTPUT_UNKNOWN, oemcrypto_8,
make_model_not_verified, ProvisionedDeviceInfo::LEVEL_3,
kResourceTierLow, &profile);
InsertProfile(profile);
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_2,
ClientCapabilities::HDCP_NONE,
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A,
oemcrypto_12, make_model_not_verified,
ProvisionedDeviceInfo::LEVEL_2, kResourceTierLow, &profile);
InsertProfile(profile);
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_3,
ClientCapabilities::HDCP_V1,
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A,
oemcrypto_12, make_model_not_verified,
ProvisionedDeviceInfo::LEVEL_1, kResourceTierMed, &profile);
InsertProfile(profile);
PopulateProfile(SecurityProfile::SECURITY_PROFILE_LEVEL_4,
ClientCapabilities::HDCP_V2_2,
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A,
oemcrypto_12, make_model_not_verified,
ProvisionedDeviceInfo::LEVEL_1, kResourceTierHigh, &profile);
InsertProfile(profile);
absl::ReaderMutexLock lock(&mutex_);
return security_profiles_.size();
}
SecurityProfile::Level SecurityProfileList::GetProfileLevel(
const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info,
SecurityProfile::DrmInfo* drm_info) const {
// Iterate through each SP starting from the strictest first.
absl::ReaderMutexLock lock(&mutex_);
for (auto& profile : security_profiles_) {
if (profile.min_security_requirements().security_level() <
device_info.security_level()) {
continue;
}
if (profile.min_security_requirements().oemcrypto_version() >
client_id.client_capabilities().oem_crypto_api_version()) {
continue;
}
if (profile.min_output_requirements().hdcp_version() >
client_id.client_capabilities().max_hdcp_version()) {
continue;
}
if (profile.min_output_requirements().analog_output_capabilities() >
client_id.client_capabilities().analog_output_capabilities()) {
continue;
}
if (profile.min_security_requirements().resource_rating_tier() >
client_id.client_capabilities().resource_rating_tier()) {
continue;
}
if (drm_info != nullptr) {
GetDrmInfo(client_id, device_info, drm_info);
}
return profile.level();
}
return SecurityProfile::SECURITY_PROFILE_LEVEL_UNDEFINED;
}
bool SecurityProfileList::GetDrmInfo(const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info,
SecurityProfile::DrmInfo* drm_info) const {
if (drm_info == nullptr) {
return false;
}
drm_info->mutable_output()->set_hdcp_version(
client_id.client_capabilities().max_hdcp_version());
drm_info->mutable_output()->set_analog_output_capabilities(
client_id.client_capabilities().analog_output_capabilities());
drm_info->mutable_security()->set_oemcrypto_version(
client_id.client_capabilities().oem_crypto_api_version());
drm_info->mutable_security()->set_resource_rating_tier(
client_id.client_capabilities().resource_rating_tier());
drm_info->mutable_security()->set_security_level(
device_info.security_level());
drm_info->mutable_security()->set_request_model_info_status(false);
drm_info->mutable_request_model_info()->set_manufacturer(
GetClientInfo(client_id, kModDrmMake));
drm_info->mutable_request_model_info()->set_model(
GetClientInfo(client_id, kModDrmModel));
drm_info->set_system_id(device_info.system_id());
return true;
}
bool SecurityProfileList::PopulateProfile(
const SecurityProfile::Level profile_level,
const ClientCapabilities::HdcpVersion min_hdcp_version,
const ClientCapabilities::AnalogOutputCapabilities
analog_output_capabilities,
const uint32_t min_oemcrypto_version, const bool make_model_verified,
const ProvisionedDeviceInfo::WvSecurityLevel security_level,
const uint32_t resource_rating_tier,
SecurityProfile* profile_to_create) const {
if (profile_to_create == nullptr) {
return false;
}
profile_to_create->set_level(profile_level);
profile_to_create->mutable_min_output_requirements()->set_hdcp_version(
min_hdcp_version);
profile_to_create->mutable_min_output_requirements()
->set_analog_output_capabilities(analog_output_capabilities);
profile_to_create->mutable_min_security_requirements()->set_oemcrypto_version(
min_oemcrypto_version);
profile_to_create->mutable_min_security_requirements()->set_security_level(
security_level);
profile_to_create->mutable_min_security_requirements()
->set_resource_rating_tier(resource_rating_tier);
profile_to_create->mutable_min_security_requirements()
->set_request_model_info_status(make_model_verified);
return true;
}
bool SecurityProfileList::GetProfile(SecurityProfile::Level level,
SecurityProfile* security_profile) {
absl::ReaderMutexLock lock(&mutex_);
for (auto& profile : security_profiles_) {
if (profile.level() == level) {
if (security_profile != nullptr) {
*security_profile = profile;
}
return true;
}
}
return false;
}
bool SecurityProfileList::InsertProfile(
const SecurityProfile& profile_to_insert) {
// Check if profile already exist.
if (GetProfile(profile_to_insert.level(), nullptr)) {
return false;
}
absl::WriterMutexLock lock(&mutex_);
security_profiles_.push_back(profile_to_insert);
sort(security_profiles_.begin(), security_profiles_.end(),
CompareProfileLevel);
return true;
}
bool SecurityProfileList::CompareProfileLevel(const SecurityProfile& p1,
const SecurityProfile& p2) {
// Profiles are sorted from highest to lowest (strictest) level.
return (p1.level() > p2.level());
}
} // namespace widevine

View File

@@ -0,0 +1,96 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// Description:
// Container of Widevine security profiles. Security profiles indicate the
// level of security of a device based on the device's output protections,
// version of OEMCrypto and security level.
#ifndef COMMON_SECURITY_PROFILE_LIST_H_
#define COMMON_SECURITY_PROFILE_LIST_H_
#include "absl/synchronization/mutex.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/provisioned_device_info.pb.h"
#include "protos/public/security_profile.pb.h"
namespace widevine {
using ClientCapabilities = ClientIdentification::ClientCapabilities;
const uint32_t kResourceTierLow = 1;
const uint32_t kResourceTierMed = 2;
const uint32_t kResourceTierHigh = 3;
// The SecurityProfileList will hold all security profiles. During license
// acquisition, information from the client and information from the server are
// combined to deternmine the device's security profile level.
class SecurityProfileList {
public:
SecurityProfileList() {}
~SecurityProfileList() {}
// Initialize the security profile list. The list is initially empty, this
// function will populate the list with default profiles. The size of the
// list is returned.
int Init();
// Add the specified profile to the existing list of profiles. Returns true
// if successfully inserted, false if unable to insert.
bool InsertProfile(const SecurityProfile& profile_to_insert);
// Return the highest security level based on the device capabilities.
// If |drm_info| is not null, |drm_info| is populated with the device data.
SecurityProfile::Level GetProfileLevel(
const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info,
SecurityProfile::DrmInfo* drm_info) const;
// Return the device security capabilities. |drm_info| is populated with
// data from |client_id| and |device_info|. |drm_info| must not be null and
// is owned by the caller.
bool GetDrmInfo(const ClientIdentification& client_id,
const ProvisionedDeviceInfo& device_info,
SecurityProfile::DrmInfo* drm_info) const;
// Populate |profile_to_create| with the specified output protections and
// security parameters. All input parameters are used hence should be set.
bool PopulateProfile(
const SecurityProfile::Level profile_level,
const ClientCapabilities::HdcpVersion min_hdcp_version,
const ClientCapabilities::AnalogOutputCapabilities
analog_output_capabilities,
const uint32_t min_oemcrypto_version, const bool make_model_verified,
const ProvisionedDeviceInfo::WvSecurityLevel security_level,
const uint32_t resource_rating_tier,
SecurityProfile* profile_to_create) const;
// Return true if a profile exist matching the specified |level|.
// |security_profile| is owned by the caller and is populated if a profile
// exist.
bool GetProfile(SecurityProfile::Level level,
SecurityProfile* security_profile);
private:
// Initialize the list with Widevine default profiles. The size of the
// profile list after the additions is returned.
int AddDefaultProfiles();
static bool CompareProfileLevel(const SecurityProfile& p1,
const SecurityProfile& p2);
mutable absl::Mutex mutex_;
// Widevine security profiles
std::vector<SecurityProfile> security_profiles_ GUARDED_BY(mutex_);
// Custom security profiles
std::map<std::string, SecurityProfile> custom_security_profiles_
GUARDED_BY(mutex_);
};
} // namespace widevine
#endif // COMMON_SECURITY_PROFILE_LIST_H_

View File

@@ -0,0 +1,157 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2020 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "common/security_profile_list.h"
#include "glog/logging.h"
#include "google/protobuf/util/message_differencer.h"
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "protos/public/security_profile.pb.h"
namespace widevine {
namespace security_profile {
const char kMakeName[] = "company_name";
const char kMakeValue[] = "Google";
const char kModelName[] = "model_name";
const char kModelValue[] = "model1";
const uint32_t kSystemId = 1234;
class SecurityProfileListTest : public ::testing::Test {
public:
SecurityProfileListTest() {}
~SecurityProfileListTest() override {}
void SetUp() override {
const uint32_t oemcrypto_12 = 12;
const bool make_model_not_verified = false;
const ClientIdentification::ClientCapabilities::HdcpVersion hdcp_version =
ClientCapabilities::HDCP_V2_2;
test_profile_1_.set_level(SecurityProfile::SECURITY_PROFILE_LEVEL_1);
test_profile_1_.mutable_min_output_requirements()->set_hdcp_version(
hdcp_version);
test_profile_1_.mutable_min_output_requirements()
->set_analog_output_capabilities(
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
test_profile_1_.mutable_min_security_requirements()->set_oemcrypto_version(
oemcrypto_12);
test_profile_1_.mutable_min_security_requirements()->set_security_level(
ProvisionedDeviceInfo::LEVEL_1);
test_profile_1_.mutable_min_security_requirements()
->set_resource_rating_tier(kResourceTierHigh);
test_profile_1_.mutable_min_security_requirements()
->set_request_model_info_status(make_model_not_verified);
ClientIdentification_NameValue *nv = client_id_.add_client_info();
nv->set_name(kMakeName);
nv->set_value(kMakeValue);
nv = client_id_.add_client_info();
nv->set_name(kModelName);
nv->set_value(kModelValue);
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(
oemcrypto_12);
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
hdcp_version);
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierHigh);
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
device_info_.set_system_id(kSystemId);
}
SecurityProfile test_profile_1_;
SecurityProfileList profile_list_;
ClientIdentification client_id_;
ProvisionedDeviceInfo device_info_;
};
TEST_F(SecurityProfileListTest, InsertProfile) {
// This test will not initialize the SecurityProfileList, hence it's empty.
// Insert test profile 1 into the list.
EXPECT_TRUE(profile_list_.InsertProfile(test_profile_1_));
// Should not allow insertion of an alreadfy existing level.
EXPECT_FALSE(profile_list_.InsertProfile(test_profile_1_));
SecurityProfile profile;
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_1,
profile_list_.GetProfile(test_profile_1_.level(), &profile));
EXPECT_TRUE(
google::protobuf::util::MessageDifferencer::Equals(test_profile_1_, profile));
}
TEST_F(SecurityProfileListTest, GetDrmInfo) {
SecurityProfile::DrmInfo drm_info;
ASSERT_TRUE(profile_list_.GetDrmInfo(client_id_, device_info_, &drm_info));
EXPECT_EQ(client_id_.client_capabilities().max_hdcp_version(),
drm_info.output().hdcp_version());
EXPECT_EQ(client_id_.client_capabilities().analog_output_capabilities(),
drm_info.output().analog_output_capabilities());
EXPECT_EQ(client_id_.client_capabilities().oem_crypto_api_version(),
drm_info.security().oemcrypto_version());
EXPECT_EQ(client_id_.client_capabilities().resource_rating_tier(),
drm_info.security().resource_rating_tier());
EXPECT_EQ(device_info_.security_level(),
drm_info.security().security_level());
EXPECT_EQ(device_info_.system_id(), drm_info.system_id());
// make_mode status is currently hard-coded to false.
EXPECT_EQ(false, drm_info.security().request_model_info_status());
EXPECT_EQ(kMakeValue, drm_info.request_model_info().manufacturer());
EXPECT_EQ(kModelValue, drm_info.request_model_info().model());
}
TEST_F(SecurityProfileListTest, ProfileLevels) {
SecurityProfile::DrmInfo drm_info;
profile_list_.Init();
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
ClientCapabilities::HDCP_NONE);
client_id_.mutable_client_capabilities()->set_analog_output_capabilities(
ClientCapabilities::ANALOG_OUTPUT_UNKNOWN);
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(7);
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierLow);
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_3);
// Lowest profile level requires OEMCrypto version 8.
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_UNDEFINED,
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
// Move up to profile 1
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(8);
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_1,
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
// Move up to profile 2
client_id_.mutable_client_capabilities()->set_analog_output_capabilities(
ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A);
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(12);
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_2);
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_2,
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
// Move up to profile 3
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
ClientCapabilities::HDCP_V1);
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierMed);
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_3,
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
// Move up to profile 4
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
ClientCapabilities::HDCP_V2_2);
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
kResourceTierHigh);
ASSERT_EQ(SecurityProfile::SECURITY_PROFILE_LEVEL_4,
profile_list_.GetProfileLevel(client_id_, device_info_, &drm_info));
}
} // namespace security_profile
} // namespace widevine

View File

@@ -27,7 +27,8 @@ std::string Sha512_Hash(const std::string& message);
// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based,
// 16-byte binary representation. Name_space is a GUID prefix; name is a unique
// name in the namespace.
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name);
std::string GenerateSha1Uuid(const std::string& name_space,
const std::string& name);
} // namespace widevine

View File

@@ -17,7 +17,8 @@ TEST(ShaUtilTest, Sha1Empty) {
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
};
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha1_Hash(""));
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
Sha1_Hash(""));
}
TEST(ShaUtilTest, Sha256Empty) {
@@ -26,7 +27,8 @@ TEST(ShaUtilTest, Sha256Empty) {
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
};
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)), Sha256_Hash(""));
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
Sha256_Hash(""));
}
TEST(ShaUtilTest, Sha1) {

View File

@@ -19,7 +19,8 @@
namespace widevine {
namespace signature_util {
Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
Status GenerateAesSignature(const std::string& message,
const std::string& aes_key,
const std::string& aes_iv, std::string* signature) {
if (signature == nullptr) {
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
@@ -36,7 +37,8 @@ Status GenerateAesSignature(const std::string& message, const std::string& aes_k
return OkStatus();
}
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
Status GenerateRsaSignature(const std::string& message,
const std::string& private_key,
std::string* signature) {
if (signature == nullptr) {
return Status(error::INVALID_ARGUMENT, "signature is nullptr");

View File

@@ -19,13 +19,15 @@ namespace signature_util {
// Generates an AES signature of |message| using |aes_key| and |aes_iv|.
// Signature is returned via |signature| if generation was successful.
// Returns a Status that carries the details of error if generation failed.
Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
Status GenerateAesSignature(const std::string& message,
const std::string& aes_key,
const std::string& aes_iv, std::string* signature);
// Generates a RSA signature of |message| using |private_key|.
// Signature is returned via |sigature| if generation was successful.
// Returns a Status that carries the details of error if generation failed.
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
Status GenerateRsaSignature(const std::string& message,
const std::string& private_key,
std::string* signature);
} // namespace signature_util

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.
////////////////////////////////////////////////////////////////////////////////
#include "common/signer_public_key.h"
#include "absl/memory/memory.h"
#include "common/ec_key.h"
#include "common/rsa_key.h"
namespace widevine {
// SignerPublicKeyImpl is a generic implementation of SignerPublicKey. The
// initialization details are in the SignerPublicKey factory method.
template <typename T>
class SignerPublicKeyImpl : public SignerPublicKey {
public:
explicit SignerPublicKeyImpl(std::unique_ptr<T> signer_public_key)
: signer_public_key_(std::move(signer_public_key)) {}
~SignerPublicKeyImpl() override {}
SignerPublicKeyImpl(const SignerPublicKeyImpl&) = delete;
SignerPublicKeyImpl& operator=(const SignerPublicKeyImpl&) = delete;
bool VerifySignature(const std::string& message,
const std::string& signature) const override {
if (!signer_public_key_->VerifySignature(message, signature)) {
return false;
}
return true;
}
private:
std::unique_ptr<T> signer_public_key_;
};
std::unique_ptr<SignerPublicKey> SignerPublicKey::Create(
const std::string& signer_public_key, DrmCertificate::Algorithm algorithm) {
switch (algorithm) {
case DrmCertificate::RSA: {
std::unique_ptr<RsaPublicKey> public_key(
RsaPublicKey::Create(signer_public_key));
if (public_key == nullptr) {
return nullptr;
}
return absl::make_unique<SignerPublicKeyImpl<RsaPublicKey>>(
std::move(public_key));
}
// All supported ECC curves are specified here.
case DrmCertificate::ECC_SECP256R1:
case DrmCertificate::ECC_SECP384R1:
case DrmCertificate::ECC_SECP521R1: {
std::unique_ptr<ECPublicKey> public_key =
ECPublicKey::Create(signer_public_key);
if (public_key == nullptr) {
return nullptr;
}
return absl::make_unique<SignerPublicKeyImpl<ECPublicKey>>(
std::move(public_key));
}
default:
return nullptr;
}
}
} // namespace widevine

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.
////////////////////////////////////////////////////////////////////////////////
#ifndef COMMON_SIGNER_PUBLIC_KEY_H_
#define COMMON_SIGNER_PUBLIC_KEY_H_
#include <string>
#include "protos/public/drm_certificate.pb.h"
namespace widevine {
// SignerPublicKey is implemented for each provisioning key type that are
// defined in video/widevine/proto/public.drm_certificate.proto.
class SignerPublicKey {
public:
SignerPublicKey() = default;
virtual ~SignerPublicKey() = default;
SignerPublicKey(const SignerPublicKey&) = delete;
SignerPublicKey& operator=(const SignerPublicKey&) = delete;
// Verify message using |signer_public_key_|.
virtual bool VerifySignature(const std::string& message,
const std::string& signature) const = 0;
// A factory method to create a SignerPublicKey. The |algorithm| is used to
// create an appropriate SignerPublicKey for the key type.
// The returned pointer is a nullptr if the key cannot be deserialized.
static std::unique_ptr<SignerPublicKey> Create(
const std::string& signer_public_key,
DrmCertificate::Algorithm algorithm);
};
} // namespace widevine
#endif // COMMON_SIGNER_PUBLIC_KEY_H_

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.
////////////////////////////////////////////////////////////////////////////////
#include "common/signer_public_key.h"
#include <memory>
#include "testing/gunit.h"
#include "common/ec_key.h"
#include "common/ec_test_keys.h"
#include "common/rsa_key.h"
#include "common/rsa_test_keys.h"
#include "protos/public/drm_certificate.pb.h"
namespace widevine {
static const char kMessage[] = "The rain in Spain falls mainly in the blank?";
class SignerPublicKeyTest : public ::testing::Test {
public:
RsaTestKeys rsa_test_keys_;
ECTestKeys ec_test_keys_;
};
TEST_F(SignerPublicKeyTest, RSA) {
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(rsa_test_keys_.private_test_key_1_3072_bits()));
std::string signature;
ASSERT_TRUE(private_key->GenerateSignature(kMessage, &signature));
std::unique_ptr<SignerPublicKey> public_key = SignerPublicKey::Create(
rsa_test_keys_.public_test_key_1_3072_bits(), DrmCertificate::RSA);
ASSERT_NE(public_key, nullptr);
EXPECT_TRUE(public_key->VerifySignature(kMessage, signature));
}
TEST_F(SignerPublicKeyTest, ECC) {
std::unique_ptr<ECPrivateKey> private_key =
ECPrivateKey::Create(ec_test_keys_.private_test_key_1_secp521r1());
std::string signature;
ASSERT_TRUE(private_key->GenerateSignature(kMessage, &signature));
std::unique_ptr<SignerPublicKey> public_key =
SignerPublicKey::Create(ec_test_keys_.public_test_key_1_secp521r1(),
DrmCertificate::ECC_SECP521R1);
ASSERT_NE(public_key, nullptr);
EXPECT_TRUE(public_key->VerifySignature(kMessage, signature));
}
TEST_F(SignerPublicKeyTest, IncorrectAlgorithm) {
std::unique_ptr<SignerPublicKey> rsa_public_key =
SignerPublicKey::Create(rsa_test_keys_.public_test_key_1_3072_bits(),
DrmCertificate::ECC_SECP521R1);
ASSERT_EQ(rsa_public_key, nullptr);
std::unique_ptr<SignerPublicKey> ec_public_key = SignerPublicKey::Create(
ec_test_keys_.public_test_key_1_secp521r1(), DrmCertificate::RSA);
ASSERT_EQ(ec_public_key, nullptr);
}
TEST_F(SignerPublicKeyTest, BadKey) {
std::unique_ptr<SignerPublicKey> rsa_public_key =
SignerPublicKey::Create("GobbletyGook", DrmCertificate::RSA);
ASSERT_EQ(rsa_public_key, nullptr);
std::unique_ptr<SignerPublicKey> ec_public_key = SignerPublicKey::Create(
"MoreGobbletyGook", DrmCertificate::ECC_SECP521R1);
ASSERT_EQ(ec_public_key, nullptr);
}
} // namespace widevine

View File

@@ -28,7 +28,6 @@
#include <string>
#include "base/macros.h"
#include "protos/public/license_protocol.pb.h"
namespace widevine {

View File

@@ -20,7 +20,8 @@ const char* kFrontKeyHex =
const char* kBackKeyHex =
"0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d";
std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) {
std::string GenerateDerivedKey(
widevine::ProtocolVersion protocol_version) {
if (protocol_version == widevine::VERSION_2_0) {
return absl::HexStringToBytes(kFrontKeyHex);
} else {

View File

@@ -37,6 +37,12 @@ enum StatusCode {
// instead for those errors).
PERMISSION_DENIED = 7,
// The operation was rejected because the system is not in a state
// required for the operation's execution. For example, the directory
// to be deleted is non-empty, an rmdir operation is applied to
// a non-directory, etc.
FAILED_PRECONDITION = 9,
// Operation is not implemented or not supported/enabled in this service.
UNIMPLEMENTED = 12,
@@ -70,7 +76,8 @@ class Status {
const std::string& error_message)
: error_space_(e), status_code_(c), error_message_(error_message) {}
Status(const util::ErrorSpace* e, int error, const std::string& error_message)
Status(const util::ErrorSpace* e, int error,
const std::string& error_message)
: error_space_(e), status_code_(error), error_message_(error_message) {}
bool ok() const { return status_code_ == error::OK; }

View File

@@ -18,7 +18,8 @@
namespace widevine {
namespace string_util {
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) {
Status BitsetStringToBinaryString(const std::string& bitset,
std::string* output) {
if (output == nullptr) {
return Status(error::INTERNAL, "output is nullptr.");
}

View File

@@ -17,7 +17,8 @@ namespace string_util {
// Converts std::string representation of a bitset to its binary equivalent string.
// For example, converts "01110100011001010111001101110100" to "test".
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output);
Status BitsetStringToBinaryString(const std::string& bitset,
std::string* output);
} // namespace string_util
} // namespace widevine

View File

@@ -13,7 +13,7 @@
namespace widevine {
static const unsigned char kTestRootCertificate[] = {
0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
0x0a, 0x9b, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5,
0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78,
0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f,
@@ -47,42 +47,43 @@ static const unsigned char kTestRootCertificate[] = {
0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33,
0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67,
0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02,
0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a,
0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35,
0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe,
0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49,
0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6,
0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58,
0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce,
0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27,
0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5,
0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c,
0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e,
0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12,
0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54,
0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8,
0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1,
0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1,
0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8,
0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4,
0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9,
0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd,
0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9,
0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f,
0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92,
0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15,
0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0,
0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9,
0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1,
0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2,
0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c,
0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36,
0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35,
0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7,
0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44};
0x03, 0x01, 0x00, 0x01, 0x48, 0x01, 0x12, 0x80, 0x03, 0x45, 0x3d, 0x03,
0x60, 0xd1, 0x13, 0x9e, 0xcd, 0x69, 0x5f, 0xd5, 0xa7, 0x62, 0x12, 0x28,
0x49, 0x4a, 0x73, 0x05, 0x1b, 0xf3, 0xd4, 0x4e, 0x54, 0x3f, 0x5f, 0x43,
0x2c, 0x17, 0x56, 0xbf, 0xc3, 0xb9, 0xe1, 0xb8, 0xb7, 0xc7, 0xd6, 0x52,
0x8e, 0xfb, 0x1c, 0x24, 0x9b, 0x84, 0x13, 0x08, 0xec, 0x0b, 0xd9, 0xfa,
0xe3, 0x9d, 0x37, 0x55, 0x72, 0x69, 0xfc, 0x39, 0x50, 0xbb, 0x49, 0x86,
0xe2, 0x85, 0x01, 0x20, 0x3e, 0x08, 0x2c, 0xdc, 0xee, 0x36, 0x04, 0xff,
0x24, 0x50, 0x88, 0x17, 0xfb, 0x8e, 0x86, 0xf6, 0xc5, 0xd6, 0xc5, 0x5b,
0x32, 0xe1, 0x3f, 0xff, 0x9c, 0x23, 0xd8, 0x84, 0x61, 0x26, 0x1d, 0x46,
0x82, 0x99, 0x3f, 0x1a, 0x5a, 0xc7, 0xd5, 0x97, 0x6d, 0xdb, 0x3a, 0x80,
0xef, 0x80, 0x2d, 0x11, 0x06, 0xf2, 0x14, 0x2b, 0x40, 0x61, 0x6f, 0x91,
0xea, 0x8a, 0xc5, 0xde, 0xad, 0x68, 0x31, 0xda, 0x11, 0x82, 0x11, 0x2b,
0x19, 0x3c, 0x89, 0xbc, 0x4a, 0xed, 0x87, 0x44, 0x1b, 0x79, 0xa9, 0x22,
0xb7, 0x81, 0xb3, 0xa9, 0xa2, 0x9b, 0x77, 0xf9, 0x40, 0x31, 0x4a, 0x9a,
0x5a, 0x9d, 0x56, 0xf9, 0x81, 0x2f, 0x9b, 0xe1, 0xd1, 0xca, 0xe7, 0xc5,
0xdc, 0x43, 0x92, 0x96, 0x5a, 0x22, 0x07, 0xcd, 0x0e, 0xec, 0x70, 0xe8,
0xd7, 0xdb, 0x52, 0xbe, 0x23, 0x23, 0x4c, 0xb8, 0x9e, 0x0a, 0x94, 0x64,
0xa7, 0xc8, 0xd8, 0x30, 0x78, 0xb9, 0x31, 0x8f, 0x5f, 0x98, 0x71, 0x24,
0xbd, 0xc2, 0xdc, 0x52, 0xf5, 0x0a, 0xf7, 0x0d, 0x48, 0x58, 0x6b, 0xdd,
0xa9, 0x95, 0xc6, 0x03, 0x13, 0x39, 0x87, 0xf8, 0x7a, 0x0e, 0x32, 0xd5,
0x77, 0x46, 0x59, 0x12, 0xae, 0x52, 0xd1, 0x48, 0xdf, 0x4c, 0xdd, 0xbf,
0xd7, 0xcc, 0x38, 0x1e, 0x07, 0x35, 0x3f, 0x1b, 0xe5, 0xa4, 0x2a, 0x01,
0x77, 0x22, 0xe6, 0x02, 0x90, 0x4d, 0x8b, 0x02, 0x75, 0x07, 0x36, 0xb0,
0xfa, 0x82, 0xf6, 0x7e, 0x74, 0xde, 0xba, 0xfa, 0x0e, 0x5a, 0x9a, 0x70,
0x50, 0xf4, 0x42, 0x05, 0xb1, 0xca, 0xc7, 0x18, 0xb7, 0x76, 0xff, 0x04,
0x8e, 0x2e, 0xe3, 0x44, 0x41, 0x38, 0x16, 0xa4, 0x34, 0x84, 0x66, 0x72,
0x0f, 0xc8, 0x2f, 0x9c, 0xe1, 0x5f, 0xe6, 0x35, 0x79, 0x64, 0x67, 0xa0,
0x53, 0x89, 0x4c, 0x51, 0xc8, 0x34, 0x6e, 0x70, 0xba, 0xfe, 0xdd, 0xca,
0xc2, 0xc6, 0x91, 0x8b, 0x08, 0x5e, 0x25, 0x96, 0xd0, 0x0d, 0xe7, 0xee,
0x25, 0x92, 0x39, 0xa3, 0xba, 0xa4, 0x0b, 0xab, 0xa4, 0x2e, 0x16, 0xfc,
0xad, 0xed, 0xcf, 0x12, 0xda, 0x9b, 0xe9, 0x67, 0x4d, 0xb2, 0x4e, 0xe9,
0xb3, 0xe8, 0x53, 0xc8, 0x5a, 0xc7, 0xbd, 0x69, 0xa7, 0x12, 0x4e, 0x43,
0x20, 0x62, 0x34, 0xb0, 0xbd, 0xb2, 0xea, 0x95, 0xf6,
};
const unsigned char kTestIntermediateCertificate[] = {
0x0a, 0xaf, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
0x0a, 0xb1, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a,
@@ -107,42 +108,92 @@ const unsigned char kTestIntermediateCertificate[] = {
0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d,
0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb,
0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01,
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40,
0xa8, 0xd0, 0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf,
0xc9, 0x26, 0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4,
0x33, 0x8e, 0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b,
0xc3, 0x4f, 0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51,
0xce, 0x45, 0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4,
0x5a, 0x02, 0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9,
0xee, 0xc9, 0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb,
0x1f, 0x7a, 0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9,
0x26, 0xbe, 0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25,
0xdb, 0x85, 0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba,
0xb9, 0x3d, 0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80,
0x12, 0x72, 0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98,
0xab, 0x73, 0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1,
0x75, 0x79, 0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98,
0xe8, 0x40, 0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f,
0x6f, 0x23, 0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30,
0xaa, 0xcf, 0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b,
0x7f, 0x91, 0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a,
0x98, 0xad, 0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e,
0x6e, 0x9d, 0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04,
0x4f, 0x32, 0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52,
0xfc, 0xcc, 0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa,
0xb3, 0x77, 0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d,
0x3c, 0x74, 0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f,
0x13, 0x97, 0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94,
0x3b, 0x31, 0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b,
0x65, 0xe9, 0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d,
0x0a, 0xfd, 0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4,
0x83, 0xcf, 0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16,
0x7b, 0x66, 0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d,
0x94, 0x5d, 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b,
0x55, 0xf8, 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x48, 0x01, 0x12, 0x80, 0x03, 0x06,
0xe2, 0xc2, 0x94, 0x0e, 0x81, 0x87, 0x59, 0xe3, 0xe8, 0x15, 0x7f, 0xc6,
0xff, 0x6b, 0xc8, 0x7e, 0x0c, 0xd9, 0x9b, 0x40, 0x34, 0x22, 0x44, 0x00,
0xdf, 0x0e, 0x9e, 0xcd, 0xb9, 0x1d, 0x3d, 0xfe, 0x5a, 0xb9, 0x28, 0xdc,
0x94, 0x43, 0xc4, 0x1c, 0x66, 0xa9, 0x8a, 0xa4, 0x61, 0xdf, 0x8a, 0xf3,
0x7c, 0xf0, 0xbe, 0x66, 0xe9, 0xdf, 0x65, 0x93, 0x6c, 0xc7, 0xb5, 0x1a,
0x76, 0x07, 0x40, 0xde, 0xa1, 0xc5, 0x40, 0xde, 0xac, 0x5b, 0x9f, 0x32,
0xbb, 0xd4, 0xf2, 0x09, 0x13, 0x20, 0xbe, 0xee, 0xf4, 0xb5, 0xb0, 0xec,
0xeb, 0x1e, 0xfa, 0x03, 0x1b, 0x9d, 0x5a, 0xa0, 0x2f, 0x71, 0x1a, 0x76,
0xe7, 0x6f, 0x71, 0x7d, 0x3a, 0x7d, 0x8c, 0x46, 0xaf, 0x93, 0x94, 0x47,
0x27, 0xec, 0x1b, 0x1e, 0xd7, 0x8c, 0x7c, 0xec, 0x42, 0xaf, 0x55, 0x82,
0x3b, 0x6d, 0x07, 0x24, 0xb3, 0xfa, 0x2d, 0x1e, 0x12, 0x02, 0x94, 0x04,
0x23, 0xeb, 0xf3, 0x74, 0x04, 0x7e, 0x2a, 0x7f, 0x00, 0x34, 0x2b, 0x5c,
0x5b, 0x10, 0xe7, 0x36, 0x52, 0xde, 0x9f, 0x56, 0x10, 0xe3, 0x0b, 0xa5,
0x29, 0x85, 0xa5, 0x95, 0xed, 0xf5, 0x39, 0x0a, 0x03, 0x51, 0x29, 0x64,
0xa1, 0x4f, 0x38, 0xde, 0x3b, 0x4d, 0x0a, 0xf3, 0x7e, 0x37, 0x14, 0xce,
0xdf, 0x9d, 0x86, 0x16, 0xad, 0x62, 0xa8, 0xf8, 0xa7, 0xc2, 0xa4, 0xc1,
0xe2, 0xd6, 0x40, 0xa4, 0x7b, 0x20, 0x1b, 0x6d, 0x7c, 0x97, 0x0b, 0x73,
0x85, 0xbf, 0xdb, 0xc3, 0xa1, 0xf5, 0xd4, 0xb7, 0x95, 0xf2, 0xe7, 0x10,
0x77, 0xc6, 0x82, 0xb2, 0x68, 0x24, 0x31, 0xdc, 0x69, 0x43, 0x56, 0xf5,
0x76, 0x20, 0x0a, 0x82, 0x1a, 0x98, 0xb3, 0x02, 0x0f, 0x67, 0xcd, 0x4f,
0xab, 0x43, 0x44, 0xbd, 0xdb, 0x07, 0xd3, 0xff, 0x8b, 0x68, 0x33, 0x24,
0x35, 0xe5, 0xc6, 0x1a, 0x94, 0x14, 0x4f, 0x40, 0xef, 0x92, 0xfb, 0xfd,
0x72, 0x15, 0xd4, 0x10, 0x60, 0x22, 0x3e, 0x60, 0x49, 0x3d, 0x58, 0xc6,
0x3d, 0x28, 0x70, 0x55, 0x32, 0xd5, 0x78, 0x03, 0x51, 0xff, 0xd6, 0x4f,
0x4e, 0x89, 0x0e, 0x50, 0x85, 0x6e, 0x1c, 0x6a, 0x5f, 0x11, 0xd0, 0xf5,
0xee, 0xe5, 0x1c, 0xa8, 0xb2, 0xdb, 0x26, 0x93, 0xb1, 0xe2, 0xc1, 0x05,
0xe0, 0x7f, 0x16, 0xe7, 0x9c, 0xcf, 0xe7, 0xb7, 0x7e, 0xaa, 0x96, 0x21,
0x64, 0x39, 0x6d, 0x7a, 0xdc, 0x70, 0x6e, 0xc8, 0xf5, 0x44, 0x2e, 0x9f,
0xc1, 0xe9, 0x46, 0x8c, 0x1b, 0x58, 0xec, 0x73, 0x1b, 0x9a, 0x04, 0xcb,
0x68, 0x58, 0x21, 0x0e, 0xd6, 0xd7, 0x7a, 0x2b, 0x60, 0x02, 0x20, 0x7b,
0x85, 0xe5, 0x84, 0x2c, 0x5f, 0x24, 0x90, 0x2d, 0xc5, 0x19, 0xea, 0xf3,
0x91, 0x78, 0xc2, 0xa7, 0x36, 0x5a, 0x72, 0x64, 0x45, 0x13, 0x49,
};
const unsigned char kTestIntermediateCertificateWithECKey[] = {
0x0a, 0x9a, 0x01, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
0xb2, 0x92, 0x04, 0x22, 0x78, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00,
0x22, 0x03, 0x62, 0x00, 0x04, 0xb0, 0x50, 0x2a, 0x13, 0x20, 0x3e, 0x66,
0x67, 0xdf, 0x11, 0x2a, 0xbc, 0x0f, 0x76, 0x69, 0x4b, 0xa1, 0x88, 0xec,
0xb8, 0x71, 0xcf, 0xc9, 0xbb, 0xd2, 0xbc, 0xf8, 0x53, 0xfd, 0x8b, 0x8d,
0x14, 0x6f, 0xda, 0xea, 0x60, 0x51, 0xc8, 0xd3, 0x3a, 0xd4, 0x75, 0x81,
0x05, 0x16, 0x03, 0x0b, 0xcb, 0x33, 0x2c, 0x8b, 0xe6, 0xd3, 0x57, 0x6c,
0xfb, 0x81, 0x4b, 0xfe, 0x79, 0x56, 0xf7, 0x6a, 0x2b, 0xca, 0xa7, 0x04,
0xe9, 0x37, 0xd6, 0x49, 0xe5, 0x8b, 0x2c, 0xe9, 0x8e, 0xcd, 0xe7, 0xe3,
0xc9, 0xf5, 0x4c, 0x90, 0x82, 0x5f, 0xf0, 0x53, 0xd2, 0xa4, 0x1a, 0xb3,
0x53, 0x3d, 0xa7, 0xa7, 0xfd, 0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x48,
0x03, 0x12, 0x80, 0x03, 0x42, 0x90, 0xc4, 0x87, 0x0b, 0x55, 0x78, 0xb5,
0x25, 0x64, 0x23, 0xf2, 0x6a, 0x28, 0x7b, 0x1e, 0x12, 0xeb, 0x94, 0x08,
0x4f, 0xce, 0x6b, 0x53, 0x35, 0xda, 0xa6, 0xf3, 0x90, 0x3b, 0x1b, 0xa8,
0x2f, 0x17, 0x8e, 0x09, 0x12, 0x4b, 0xc6, 0x10, 0xfc, 0x8a, 0x63, 0xda,
0xf6, 0x7e, 0x18, 0x3e, 0x49, 0x4c, 0x85, 0x5b, 0x2c, 0xcb, 0x09, 0x67,
0x3b, 0xd3, 0xf3, 0x90, 0xe7, 0x4e, 0x06, 0x2f, 0x25, 0xbe, 0x22, 0x7f,
0xd6, 0x5c, 0xd5, 0xda, 0xac, 0x60, 0x29, 0x83, 0x53, 0x54, 0x73, 0x0d,
0x96, 0xca, 0x50, 0x6e, 0x45, 0xd6, 0x3c, 0xe6, 0xab, 0xf7, 0xe8, 0x69,
0x9e, 0xe2, 0x8e, 0xfd, 0x46, 0x11, 0x40, 0x9d, 0xfc, 0xd8, 0x2d, 0xe0,
0x94, 0xbc, 0x05, 0x74, 0x3c, 0x0a, 0xdd, 0x64, 0x37, 0x93, 0x9d, 0x5a,
0x08, 0xfe, 0x5f, 0xae, 0xa3, 0xc6, 0x48, 0xbd, 0x8a, 0x4e, 0x3c, 0xac,
0x7c, 0x66, 0xad, 0xc4, 0x7b, 0x7b, 0x89, 0xd9, 0xae, 0xf5, 0x8d, 0xf3,
0x0e, 0x3b, 0x1c, 0xb6, 0xf0, 0xff, 0x52, 0x22, 0xbc, 0xdd, 0x1e, 0xe5,
0x90, 0xe1, 0x09, 0xe2, 0x65, 0x42, 0x70, 0x4b, 0xfa, 0xf0, 0x41, 0x41,
0x53, 0xa2, 0x2c, 0x32, 0xc4, 0x1a, 0x42, 0x0d, 0xbe, 0x8d, 0x5b, 0x14,
0xae, 0x8f, 0xca, 0x85, 0xda, 0xfb, 0xe1, 0x25, 0x71, 0xc6, 0x8a, 0x3c,
0xe1, 0x99, 0x09, 0x30, 0x9d, 0xa7, 0xec, 0x10, 0x7b, 0x43, 0xee, 0xf8,
0xa5, 0x58, 0x9d, 0xd7, 0x31, 0x6a, 0x22, 0x45, 0xf5, 0x0c, 0x30, 0xb2,
0x77, 0x05, 0x13, 0x10, 0xfd, 0xc1, 0xf9, 0x13, 0xc2, 0x88, 0xc8, 0x71,
0xd9, 0x14, 0x5f, 0xc9, 0xde, 0x96, 0xe7, 0x55, 0xb9, 0x4a, 0xb0, 0x18,
0x22, 0x17, 0x9f, 0x95, 0xe2, 0xa7, 0x66, 0x9b, 0xfd, 0x38, 0xf9, 0x5c,
0xa0, 0xaa, 0xf4, 0x60, 0xee, 0x00, 0x53, 0x87, 0x29, 0x63, 0x53, 0x55,
0xfb, 0x32, 0x7a, 0x80, 0x56, 0xea, 0xaa, 0x95, 0x22, 0x08, 0x9e, 0x25,
0x53, 0x50, 0xb4, 0xd0, 0x07, 0x3c, 0x29, 0x3f, 0x03, 0xab, 0x68, 0xf5,
0xa5, 0xc6, 0xd2, 0x73, 0xf6, 0xee, 0xa2, 0x6c, 0xec, 0xd4, 0xf3, 0x20,
0xdc, 0x56, 0x00, 0x3d, 0xea, 0x57, 0x14, 0xc7, 0x90, 0x86, 0x82, 0x1b,
0x14, 0x57, 0x68, 0xec, 0x24, 0x0e, 0x8d, 0x6b, 0xcc, 0x5f, 0x7a, 0x53,
0xac, 0x60, 0x20, 0x5f, 0xe7, 0x79, 0xb6, 0x2c, 0xfb, 0x23, 0xa3, 0x43,
0x91, 0x0f, 0x53, 0x38, 0x0e, 0xcc, 0x27, 0xaf, 0x57, 0x01, 0x26, 0xd8,
0x01, 0x41, 0x27, 0x63, 0xca, 0x9f, 0xf5, 0xa7, 0x43, 0x26, 0x74, 0x59,
0xec, 0xce, 0x71, 0x09, 0x0d, 0xda, 0x5d, 0x63, 0xef, 0xfd, 0x6e, 0x92,
0x53, 0x12, 0xbc, 0x6a, 0x5b, 0x4d, 0x4a, 0x43, 0x04, 0x5d, 0x8e, 0x93,
0xd7, 0x89, 0x21, 0xff,
};
const unsigned char kTestUserDrmCertificate[] = {
0x0a, 0xc1, 0x02, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42,
0x0a, 0xc3, 0x02, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42,
0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18,
0x91, 0xab, 0x4b, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51,
@@ -169,89 +220,282 @@ const unsigned char kTestUserDrmCertificate[] = {
0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01,
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65,
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
0x12, 0x80, 0x02, 0x62, 0xd5, 0x8b, 0xb6, 0x52, 0x94, 0xcb, 0x25, 0xba,
0x68, 0x44, 0xdc, 0x6f, 0x03, 0xf6, 0x24, 0xc5, 0xba, 0x46, 0xd1, 0xa1,
0x83, 0xe7, 0xaf, 0x94, 0x96, 0x8b, 0x57, 0x89, 0xd6, 0xa0, 0x99, 0x6f,
0xed, 0xac, 0xfe, 0x6c, 0x9d, 0x80, 0x1c, 0xae, 0x34, 0xda, 0x49, 0x4f,
0x10, 0x22, 0x3c, 0xdd, 0x77, 0xa0, 0x9a, 0x79, 0x73, 0x68, 0x66, 0xa4,
0x6d, 0x1e, 0x82, 0xbf, 0xce, 0x06, 0x1a, 0x83, 0xcd, 0xa3, 0xed, 0x91,
0xbe, 0xb1, 0xfe, 0xf3, 0xde, 0x63, 0x96, 0xd5, 0x24, 0x44, 0x46, 0x94,
0x7f, 0xc2, 0x14, 0x19, 0x42, 0x08, 0x64, 0xef, 0x93, 0x81, 0x7a, 0x54,
0x8b, 0x6e, 0xd9, 0xf5, 0x14, 0x88, 0x6c, 0x39, 0x6f, 0x0f, 0x70, 0x91,
0x97, 0xd4, 0x24, 0x73, 0x9d, 0x12, 0x7a, 0xc8, 0x83, 0xd7, 0x2b, 0xc7,
0xb7, 0xe1, 0x20, 0x6c, 0x28, 0x11, 0x6f, 0x56, 0x82, 0xf6, 0x1c, 0x4f,
0x2d, 0x51, 0x0f, 0xd6, 0xd4, 0x14, 0xea, 0xac, 0x28, 0x66, 0xeb, 0x37,
0xca, 0x00, 0x49, 0xff, 0xed, 0x8e, 0x8c, 0x3e, 0x4b, 0x9b, 0x12, 0x0e,
0xbf, 0xcd, 0xb7, 0xe6, 0xed, 0xd6, 0x1f, 0x88, 0xe8, 0x99, 0x68, 0x1a,
0xf8, 0xbb, 0xa2, 0x33, 0xfa, 0xb6, 0x21, 0xdf, 0xba, 0x24, 0x5c, 0x19,
0xa2, 0xe7, 0x6f, 0x61, 0x90, 0x78, 0x21, 0xca, 0x2f, 0x84, 0xab, 0x9f,
0xff, 0x37, 0x14, 0x33, 0x83, 0x43, 0x98, 0xeb, 0xa9, 0x88, 0xde, 0xad,
0x3a, 0xd9, 0xe2, 0x5c, 0x26, 0xd3, 0x95, 0x72, 0xba, 0x8c, 0x77, 0xdf,
0x90, 0x67, 0x4e, 0xbc, 0xda, 0x83, 0x09, 0x22, 0x70, 0x51, 0x84, 0x70,
0x31, 0x25, 0x8b, 0xae, 0x5e, 0x19, 0xba, 0x97, 0xd7, 0x1f, 0x6a, 0xd7,
0x95, 0xcf, 0xde, 0x8f, 0x93, 0x69, 0x88, 0x11, 0xbe, 0x8c, 0x6a, 0xfb,
0x3c, 0x13, 0x87, 0x0e, 0x6c, 0xa5, 0xa0, 0x1a, 0xb5, 0x05, 0x0a, 0xaf,
0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18, 0xb2, 0x92,
0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40,
0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7,
0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56,
0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e,
0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34,
0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31,
0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e,
0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39,
0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2,
0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54,
0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3,
0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71,
0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96,
0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a,
0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c,
0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f,
0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca,
0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77,
0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27,
0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c,
0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c,
0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x28, 0xd2,
0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40, 0xa8, 0xd0,
0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf, 0xc9, 0x26,
0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4, 0x33, 0x8e,
0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b, 0xc3, 0x4f,
0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51, 0xce, 0x45,
0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4, 0x5a, 0x02,
0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9, 0xee, 0xc9,
0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb, 0x1f, 0x7a,
0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9, 0x26, 0xbe,
0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25, 0xdb, 0x85,
0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba, 0xb9, 0x3d,
0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80, 0x12, 0x72,
0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98, 0xab, 0x73,
0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1, 0x75, 0x79,
0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98, 0xe8, 0x40,
0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f, 0x6f, 0x23,
0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30, 0xaa, 0xcf,
0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b, 0x7f, 0x91,
0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a, 0x98, 0xad,
0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e, 0x6e, 0x9d,
0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04, 0x4f, 0x32,
0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52, 0xfc, 0xcc,
0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa, 0xb3, 0x77,
0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d, 0x3c, 0x74,
0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f, 0x13, 0x97,
0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94, 0x3b, 0x31,
0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b, 0x65, 0xe9,
0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d, 0x0a, 0xfd,
0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4, 0x83, 0xcf,
0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16, 0x7b, 0x66,
0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d, 0x94, 0x5d,
0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, 0x55, 0xf8,
0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
0x48, 0x01, 0x12, 0x80, 0x02, 0x23, 0x61, 0xfb, 0xd0, 0xf4, 0xcf, 0xf2,
0x58, 0xd7, 0xb0, 0x79, 0x4e, 0x4e, 0xf3, 0x2c, 0x83, 0x63, 0x34, 0x6c,
0x49, 0x80, 0xdd, 0x85, 0xf4, 0xa5, 0x23, 0x89, 0x95, 0x0c, 0x8f, 0xf6,
0xc6, 0xdc, 0x90, 0x8b, 0x83, 0xd3, 0x0b, 0x1c, 0x34, 0xd2, 0xa0, 0x08,
0xdc, 0x05, 0x76, 0x8f, 0xff, 0xa3, 0x2e, 0xf8, 0x93, 0x9e, 0xe6, 0xf3,
0x62, 0x0f, 0x70, 0x1c, 0x31, 0x15, 0xbb, 0x98, 0xf4, 0xa6, 0x22, 0x2c,
0x90, 0x59, 0xc2, 0x16, 0x48, 0xe0, 0x5a, 0xb8, 0x94, 0x6f, 0xde, 0x80,
0xaf, 0x83, 0x8e, 0x77, 0x6a, 0xa4, 0xf4, 0x9b, 0xf8, 0x76, 0xd1, 0x1b,
0x6d, 0x87, 0x85, 0x35, 0xd9, 0xd0, 0x62, 0x55, 0xfe, 0x11, 0xed, 0x4a,
0x6c, 0xc9, 0x14, 0x67, 0x72, 0xb6, 0x46, 0x56, 0xbc, 0x81, 0xac, 0xe6,
0xf0, 0x7a, 0x0e, 0x57, 0x95, 0x4d, 0x53, 0xf5, 0x33, 0x2e, 0xa5, 0x7e,
0x71, 0x8e, 0x04, 0x64, 0x50, 0x88, 0x6b, 0xb9, 0x6e, 0xbc, 0x6b, 0x74,
0xfc, 0x69, 0xa3, 0x81, 0x30, 0x1f, 0xac, 0x9d, 0x7b, 0xa0, 0xf5, 0x7f,
0x42, 0xfd, 0x14, 0xca, 0x89, 0x5b, 0xb0, 0xcd, 0xa2, 0x4b, 0xef, 0xcf,
0x84, 0x8f, 0xe8, 0xe4, 0xf7, 0xd2, 0x63, 0xe2, 0x95, 0x94, 0x45, 0xd5,
0xc2, 0xe3, 0x99, 0xfc, 0x34, 0xcb, 0x6a, 0x15, 0x74, 0x6e, 0x16, 0xe3,
0x6f, 0x8e, 0xe7, 0x9b, 0x01, 0xed, 0x7f, 0xf8, 0x90, 0xc6, 0x87, 0xf4,
0x9e, 0x45, 0x64, 0x09, 0xf9, 0xaa, 0x46, 0xe4, 0x83, 0x3b, 0x4f, 0x36,
0xdb, 0x32, 0x72, 0x00, 0xcf, 0x3c, 0x4c, 0x41, 0x67, 0x59, 0xf2, 0x93,
0xff, 0x4e, 0x07, 0x22, 0x6e, 0x5a, 0x03, 0xf5, 0xe1, 0x48, 0x72, 0x9d,
0x2f, 0xfc, 0xcd, 0x38, 0x5f, 0x2d, 0x69, 0x47, 0xd3, 0xa8, 0x09, 0x8e,
0xd5, 0x9b, 0x24, 0x45, 0x43, 0x08, 0xca, 0xc6, 0xed, 0x1a, 0xb7, 0x05,
0x0a, 0xb1, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a,
0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd,
0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67,
0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d,
0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f,
0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f,
0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf,
0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73,
0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5,
0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13,
0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad,
0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57,
0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24,
0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06,
0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61,
0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8,
0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb,
0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b,
0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b,
0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d,
0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb,
0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01,
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x48, 0x01, 0x12, 0x80, 0x03, 0x06,
0xe2, 0xc2, 0x94, 0x0e, 0x81, 0x87, 0x59, 0xe3, 0xe8, 0x15, 0x7f, 0xc6,
0xff, 0x6b, 0xc8, 0x7e, 0x0c, 0xd9, 0x9b, 0x40, 0x34, 0x22, 0x44, 0x00,
0xdf, 0x0e, 0x9e, 0xcd, 0xb9, 0x1d, 0x3d, 0xfe, 0x5a, 0xb9, 0x28, 0xdc,
0x94, 0x43, 0xc4, 0x1c, 0x66, 0xa9, 0x8a, 0xa4, 0x61, 0xdf, 0x8a, 0xf3,
0x7c, 0xf0, 0xbe, 0x66, 0xe9, 0xdf, 0x65, 0x93, 0x6c, 0xc7, 0xb5, 0x1a,
0x76, 0x07, 0x40, 0xde, 0xa1, 0xc5, 0x40, 0xde, 0xac, 0x5b, 0x9f, 0x32,
0xbb, 0xd4, 0xf2, 0x09, 0x13, 0x20, 0xbe, 0xee, 0xf4, 0xb5, 0xb0, 0xec,
0xeb, 0x1e, 0xfa, 0x03, 0x1b, 0x9d, 0x5a, 0xa0, 0x2f, 0x71, 0x1a, 0x76,
0xe7, 0x6f, 0x71, 0x7d, 0x3a, 0x7d, 0x8c, 0x46, 0xaf, 0x93, 0x94, 0x47,
0x27, 0xec, 0x1b, 0x1e, 0xd7, 0x8c, 0x7c, 0xec, 0x42, 0xaf, 0x55, 0x82,
0x3b, 0x6d, 0x07, 0x24, 0xb3, 0xfa, 0x2d, 0x1e, 0x12, 0x02, 0x94, 0x04,
0x23, 0xeb, 0xf3, 0x74, 0x04, 0x7e, 0x2a, 0x7f, 0x00, 0x34, 0x2b, 0x5c,
0x5b, 0x10, 0xe7, 0x36, 0x52, 0xde, 0x9f, 0x56, 0x10, 0xe3, 0x0b, 0xa5,
0x29, 0x85, 0xa5, 0x95, 0xed, 0xf5, 0x39, 0x0a, 0x03, 0x51, 0x29, 0x64,
0xa1, 0x4f, 0x38, 0xde, 0x3b, 0x4d, 0x0a, 0xf3, 0x7e, 0x37, 0x14, 0xce,
0xdf, 0x9d, 0x86, 0x16, 0xad, 0x62, 0xa8, 0xf8, 0xa7, 0xc2, 0xa4, 0xc1,
0xe2, 0xd6, 0x40, 0xa4, 0x7b, 0x20, 0x1b, 0x6d, 0x7c, 0x97, 0x0b, 0x73,
0x85, 0xbf, 0xdb, 0xc3, 0xa1, 0xf5, 0xd4, 0xb7, 0x95, 0xf2, 0xe7, 0x10,
0x77, 0xc6, 0x82, 0xb2, 0x68, 0x24, 0x31, 0xdc, 0x69, 0x43, 0x56, 0xf5,
0x76, 0x20, 0x0a, 0x82, 0x1a, 0x98, 0xb3, 0x02, 0x0f, 0x67, 0xcd, 0x4f,
0xab, 0x43, 0x44, 0xbd, 0xdb, 0x07, 0xd3, 0xff, 0x8b, 0x68, 0x33, 0x24,
0x35, 0xe5, 0xc6, 0x1a, 0x94, 0x14, 0x4f, 0x40, 0xef, 0x92, 0xfb, 0xfd,
0x72, 0x15, 0xd4, 0x10, 0x60, 0x22, 0x3e, 0x60, 0x49, 0x3d, 0x58, 0xc6,
0x3d, 0x28, 0x70, 0x55, 0x32, 0xd5, 0x78, 0x03, 0x51, 0xff, 0xd6, 0x4f,
0x4e, 0x89, 0x0e, 0x50, 0x85, 0x6e, 0x1c, 0x6a, 0x5f, 0x11, 0xd0, 0xf5,
0xee, 0xe5, 0x1c, 0xa8, 0xb2, 0xdb, 0x26, 0x93, 0xb1, 0xe2, 0xc1, 0x05,
0xe0, 0x7f, 0x16, 0xe7, 0x9c, 0xcf, 0xe7, 0xb7, 0x7e, 0xaa, 0x96, 0x21,
0x64, 0x39, 0x6d, 0x7a, 0xdc, 0x70, 0x6e, 0xc8, 0xf5, 0x44, 0x2e, 0x9f,
0xc1, 0xe9, 0x46, 0x8c, 0x1b, 0x58, 0xec, 0x73, 0x1b, 0x9a, 0x04, 0xcb,
0x68, 0x58, 0x21, 0x0e, 0xd6, 0xd7, 0x7a, 0x2b, 0x60, 0x02, 0x20, 0x7b,
0x85, 0xe5, 0x84, 0x2c, 0x5f, 0x24, 0x90, 0x2d, 0xc5, 0x19, 0xea, 0xf3,
0x91, 0x78, 0xc2, 0xa7, 0x36, 0x5a, 0x72, 0x64, 0x45, 0x13, 0x49,
};
const unsigned char kTestDrmServiceCertificate[] = {
0x0a, 0xbc, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32,
const unsigned char kTestUserDrmCertificateWithECKey[] = {
0x0a, 0x8f, 0x01, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42,
0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18,
0x91, 0xab, 0x4b, 0x22, 0x5b, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x8a, 0xea, 0x3f, 0x16,
0xff, 0x24, 0xa9, 0xbf, 0x03, 0x28, 0x30, 0x15, 0xee, 0x52, 0x50, 0x9a,
0x55, 0x1c, 0x60, 0xc7, 0xa7, 0xcc, 0x4b, 0x99, 0x5b, 0x40, 0x55, 0xce,
0x46, 0x19, 0xd4, 0xd4, 0x5e, 0xfd, 0xe0, 0x68, 0x27, 0xea, 0x78, 0xf3,
0x07, 0x1f, 0x02, 0x4a, 0x78, 0x52, 0x44, 0xd3, 0xdf, 0xbe, 0xac, 0x5f,
0xa5, 0x1c, 0x8a, 0x49, 0x8d, 0xa6, 0x5a, 0xac, 0xa1, 0x25, 0x2b, 0xd1,
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65,
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
0x48, 0x02, 0x12, 0x66, 0x30, 0x64, 0x02, 0x30, 0x4c, 0x7a, 0x79, 0x4a,
0x18, 0xf2, 0x2f, 0x9f, 0x29, 0x3b, 0x6f, 0x8f, 0x8f, 0xe4, 0xe0, 0xf2,
0xd9, 0x38, 0x18, 0x8a, 0x9a, 0x88, 0x85, 0x95, 0x72, 0xb7, 0x3c, 0xb6,
0x47, 0xa4, 0x6b, 0x0a, 0x56, 0x4a, 0x38, 0x1d, 0x2f, 0x4a, 0xc6, 0x61,
0x97, 0x35, 0x81, 0x87, 0x4b, 0xca, 0xdc, 0x20, 0x02, 0x30, 0x28, 0x4e,
0xf1, 0x23, 0x6d, 0x3f, 0x4f, 0x29, 0x29, 0x86, 0x75, 0x46, 0x83, 0x03,
0xa0, 0xe7, 0x23, 0x07, 0x2b, 0x28, 0x4d, 0xa9, 0x72, 0xb6, 0x5e, 0x3b,
0xd2, 0x90, 0x05, 0xd3, 0x33, 0x35, 0x99, 0xdc, 0xe9, 0x54, 0xa0, 0x6e,
0xca, 0x38, 0x63, 0x4d, 0x95, 0xab, 0x99, 0x77, 0x87, 0x38, 0x1a, 0xa0,
0x04, 0x0a, 0x9a, 0x01, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
0x18, 0xb2, 0x92, 0x04, 0x22, 0x78, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04,
0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xb0, 0x50, 0x2a, 0x13, 0x20, 0x3e,
0x66, 0x67, 0xdf, 0x11, 0x2a, 0xbc, 0x0f, 0x76, 0x69, 0x4b, 0xa1, 0x88,
0xec, 0xb8, 0x71, 0xcf, 0xc9, 0xbb, 0xd2, 0xbc, 0xf8, 0x53, 0xfd, 0x8b,
0x8d, 0x14, 0x6f, 0xda, 0xea, 0x60, 0x51, 0xc8, 0xd3, 0x3a, 0xd4, 0x75,
0x81, 0x05, 0x16, 0x03, 0x0b, 0xcb, 0x33, 0x2c, 0x8b, 0xe6, 0xd3, 0x57,
0x6c, 0xfb, 0x81, 0x4b, 0xfe, 0x79, 0x56, 0xf7, 0x6a, 0x2b, 0xca, 0xa7,
0x04, 0xe9, 0x37, 0xd6, 0x49, 0xe5, 0x8b, 0x2c, 0xe9, 0x8e, 0xcd, 0xe7,
0xe3, 0xc9, 0xf5, 0x4c, 0x90, 0x82, 0x5f, 0xf0, 0x53, 0xd2, 0xa4, 0x1a,
0xb3, 0x53, 0x3d, 0xa7, 0xa7, 0xfd, 0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04,
0x48, 0x03, 0x12, 0x80, 0x03, 0x42, 0x90, 0xc4, 0x87, 0x0b, 0x55, 0x78,
0xb5, 0x25, 0x64, 0x23, 0xf2, 0x6a, 0x28, 0x7b, 0x1e, 0x12, 0xeb, 0x94,
0x08, 0x4f, 0xce, 0x6b, 0x53, 0x35, 0xda, 0xa6, 0xf3, 0x90, 0x3b, 0x1b,
0xa8, 0x2f, 0x17, 0x8e, 0x09, 0x12, 0x4b, 0xc6, 0x10, 0xfc, 0x8a, 0x63,
0xda, 0xf6, 0x7e, 0x18, 0x3e, 0x49, 0x4c, 0x85, 0x5b, 0x2c, 0xcb, 0x09,
0x67, 0x3b, 0xd3, 0xf3, 0x90, 0xe7, 0x4e, 0x06, 0x2f, 0x25, 0xbe, 0x22,
0x7f, 0xd6, 0x5c, 0xd5, 0xda, 0xac, 0x60, 0x29, 0x83, 0x53, 0x54, 0x73,
0x0d, 0x96, 0xca, 0x50, 0x6e, 0x45, 0xd6, 0x3c, 0xe6, 0xab, 0xf7, 0xe8,
0x69, 0x9e, 0xe2, 0x8e, 0xfd, 0x46, 0x11, 0x40, 0x9d, 0xfc, 0xd8, 0x2d,
0xe0, 0x94, 0xbc, 0x05, 0x74, 0x3c, 0x0a, 0xdd, 0x64, 0x37, 0x93, 0x9d,
0x5a, 0x08, 0xfe, 0x5f, 0xae, 0xa3, 0xc6, 0x48, 0xbd, 0x8a, 0x4e, 0x3c,
0xac, 0x7c, 0x66, 0xad, 0xc4, 0x7b, 0x7b, 0x89, 0xd9, 0xae, 0xf5, 0x8d,
0xf3, 0x0e, 0x3b, 0x1c, 0xb6, 0xf0, 0xff, 0x52, 0x22, 0xbc, 0xdd, 0x1e,
0xe5, 0x90, 0xe1, 0x09, 0xe2, 0x65, 0x42, 0x70, 0x4b, 0xfa, 0xf0, 0x41,
0x41, 0x53, 0xa2, 0x2c, 0x32, 0xc4, 0x1a, 0x42, 0x0d, 0xbe, 0x8d, 0x5b,
0x14, 0xae, 0x8f, 0xca, 0x85, 0xda, 0xfb, 0xe1, 0x25, 0x71, 0xc6, 0x8a,
0x3c, 0xe1, 0x99, 0x09, 0x30, 0x9d, 0xa7, 0xec, 0x10, 0x7b, 0x43, 0xee,
0xf8, 0xa5, 0x58, 0x9d, 0xd7, 0x31, 0x6a, 0x22, 0x45, 0xf5, 0x0c, 0x30,
0xb2, 0x77, 0x05, 0x13, 0x10, 0xfd, 0xc1, 0xf9, 0x13, 0xc2, 0x88, 0xc8,
0x71, 0xd9, 0x14, 0x5f, 0xc9, 0xde, 0x96, 0xe7, 0x55, 0xb9, 0x4a, 0xb0,
0x18, 0x22, 0x17, 0x9f, 0x95, 0xe2, 0xa7, 0x66, 0x9b, 0xfd, 0x38, 0xf9,
0x5c, 0xa0, 0xaa, 0xf4, 0x60, 0xee, 0x00, 0x53, 0x87, 0x29, 0x63, 0x53,
0x55, 0xfb, 0x32, 0x7a, 0x80, 0x56, 0xea, 0xaa, 0x95, 0x22, 0x08, 0x9e,
0x25, 0x53, 0x50, 0xb4, 0xd0, 0x07, 0x3c, 0x29, 0x3f, 0x03, 0xab, 0x68,
0xf5, 0xa5, 0xc6, 0xd2, 0x73, 0xf6, 0xee, 0xa2, 0x6c, 0xec, 0xd4, 0xf3,
0x20, 0xdc, 0x56, 0x00, 0x3d, 0xea, 0x57, 0x14, 0xc7, 0x90, 0x86, 0x82,
0x1b, 0x14, 0x57, 0x68, 0xec, 0x24, 0x0e, 0x8d, 0x6b, 0xcc, 0x5f, 0x7a,
0x53, 0xac, 0x60, 0x20, 0x5f, 0xe7, 0x79, 0xb6, 0x2c, 0xfb, 0x23, 0xa3,
0x43, 0x91, 0x0f, 0x53, 0x38, 0x0e, 0xcc, 0x27, 0xaf, 0x57, 0x01, 0x26,
0xd8, 0x01, 0x41, 0x27, 0x63, 0xca, 0x9f, 0xf5, 0xa7, 0x43, 0x26, 0x74,
0x59, 0xec, 0xce, 0x71, 0x09, 0x0d, 0xda, 0x5d, 0x63, 0xef, 0xfd, 0x6e,
0x92, 0x53, 0x12, 0xbc, 0x6a, 0x5b, 0x4d, 0x4a, 0x43, 0x04, 0x5d, 0x8e,
0x93, 0xd7, 0x89, 0x21, 0xff,
};
const unsigned char kTestUserDrmCertificateWithRotId[] = {
0x0a, 0xdf, 0x03, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42,
0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18,
0x91, 0xab, 0x4b, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51,
0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d,
0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c,
0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32,
0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7,
0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3,
0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7,
0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc,
0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93,
0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99,
0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc,
0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6,
0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a,
0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44,
0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec,
0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea,
0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e,
0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94,
0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8,
0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16,
0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd,
0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01,
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65,
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
0x48, 0x01, 0x52, 0x99, 0x01, 0x08, 0x01, 0x10, 0x00, 0x1a, 0x71, 0x04,
0x8a, 0xea, 0x3f, 0x16, 0xff, 0x24, 0xa9, 0xbf, 0x03, 0x28, 0x30, 0x15,
0xee, 0x52, 0x50, 0x9a, 0x55, 0x1c, 0x60, 0xc7, 0xa7, 0xcc, 0x4b, 0x99,
0x5b, 0x40, 0x55, 0xce, 0x46, 0x19, 0xd4, 0xd4, 0x5e, 0xfd, 0xe0, 0x68,
0x27, 0xea, 0x78, 0xf3, 0x07, 0x1f, 0x02, 0x4a, 0x78, 0x52, 0x44, 0xd3,
0xdf, 0xbe, 0xac, 0x5f, 0xa5, 0x1c, 0x8a, 0x49, 0x8d, 0xa6, 0x5a, 0xac,
0xa1, 0x25, 0x2b, 0xd1, 0xd9, 0xeb, 0xa7, 0x91, 0x26, 0x46, 0x3a, 0xe4,
0x5b, 0x06, 0x6b, 0x77, 0x83, 0xa9, 0x0f, 0xa3, 0xf3, 0x2a, 0x39, 0x36,
0xc1, 0xbd, 0x37, 0xeb, 0xd7, 0x83, 0x3e, 0xbd, 0x17, 0x53, 0x82, 0x69,
0xc3, 0xe4, 0x48, 0xb5, 0x0d, 0x8c, 0xe1, 0x30, 0x17, 0xef, 0x01, 0x88,
0x30, 0x62, 0x5a, 0xb3, 0x22, 0x20, 0x91, 0x69, 0x9e, 0xbc, 0xa2, 0x5c,
0xd4, 0x51, 0x79, 0xfd, 0xbc, 0x2f, 0x92, 0xcd, 0x48, 0x2d, 0xd3, 0x30,
0xe6, 0x1e, 0xbd, 0x4e, 0x23, 0x96, 0x2b, 0xb0, 0x3a, 0xfc, 0xb4, 0x7b,
0x0e, 0x3d, 0x12, 0x80, 0x02, 0xa0, 0xe0, 0x2b, 0x1d, 0xe4, 0x28, 0x7b,
0x57, 0x64, 0x3f, 0xe2, 0xf7, 0x10, 0xda, 0x97, 0x7f, 0x49, 0xf4, 0xd2,
0x57, 0xc5, 0xf5, 0xe1, 0xd9, 0xd9, 0xbb, 0x34, 0xac, 0x90, 0x0c, 0xfa,
0x9f, 0x0d, 0xd7, 0x14, 0xfc, 0xbb, 0xc9, 0xfa, 0xa5, 0x41, 0xd8, 0x87,
0xa5, 0xef, 0x39, 0xf8, 0x70, 0x4f, 0x93, 0xfc, 0xb8, 0x62, 0xb8, 0x7e,
0x6e, 0x9f, 0x25, 0xe9, 0x47, 0x08, 0xe6, 0x89, 0xe9, 0xc2, 0x41, 0x6a,
0xc6, 0x0f, 0x84, 0x71, 0xa7, 0x90, 0xf2, 0x86, 0x7b, 0xbc, 0x99, 0xca,
0xf1, 0xd4, 0xa5, 0xc0, 0x9f, 0x4e, 0x53, 0x27, 0xde, 0x3e, 0x4b, 0x7b,
0x7d, 0x6a, 0xa4, 0xaa, 0x53, 0x9c, 0x4f, 0xff, 0xf6, 0x61, 0xbe, 0x4d,
0xa7, 0x9b, 0xa6, 0xc9, 0xb1, 0x00, 0x57, 0xd6, 0x47, 0xb2, 0x0e, 0xf6,
0x56, 0x1b, 0xc3, 0x7d, 0xe0, 0x97, 0x85, 0x93, 0xe9, 0xa4, 0x43, 0xfa,
0x02, 0xd9, 0x40, 0x97, 0x22, 0x86, 0x15, 0xd8, 0x24, 0x92, 0x35, 0x32,
0x15, 0xc2, 0x19, 0xbc, 0x32, 0x21, 0x3f, 0x8c, 0xdf, 0x9f, 0x5c, 0x3a,
0x57, 0x73, 0x4f, 0x25, 0x3b, 0xa1, 0x88, 0x6f, 0xbb, 0x4a, 0xb7, 0xe5,
0xc6, 0x33, 0x1b, 0x59, 0xa9, 0xe9, 0xc3, 0x0b, 0xdb, 0xe9, 0x41, 0xb0,
0x06, 0x49, 0xdf, 0x0a, 0xa9, 0x85, 0xf5, 0xc7, 0xe6, 0x2c, 0x20, 0x25,
0x50, 0x45, 0xf2, 0x86, 0x57, 0xdb, 0x3f, 0x28, 0x0b, 0xd6, 0xc4, 0xac,
0xb0, 0x1c, 0xc3, 0xed, 0x7a, 0x8d, 0xa9, 0x83, 0x20, 0x43, 0xc7, 0x42,
0x03, 0xca, 0x23, 0xc8, 0xf7, 0xbf, 0x2e, 0x70, 0x9a, 0xff, 0x23, 0xff,
0x17, 0x22, 0xca, 0xe0, 0x58, 0x2f, 0xd3, 0x0d, 0xa4, 0xa2, 0x90, 0x6c,
0xf1, 0x78, 0x7c, 0xee, 0x1a, 0xe7, 0x0c, 0xe2, 0x89, 0xf0, 0x5b, 0x9a,
0x24, 0x4e, 0x10, 0xcf, 0x58, 0xa1, 0xdb, 0x3f, 0x1b, 0x1a, 0xb7, 0x05,
0x0a, 0xb1, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a,
0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd,
0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67,
0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d,
0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f,
0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f,
0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf,
0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73,
0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5,
0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13,
0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad,
0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57,
0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24,
0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06,
0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61,
0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8,
0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb,
0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b,
0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b,
0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d,
0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb,
0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01,
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x48, 0x01, 0x12, 0x80, 0x03, 0x06,
0xe2, 0xc2, 0x94, 0x0e, 0x81, 0x87, 0x59, 0xe3, 0xe8, 0x15, 0x7f, 0xc6,
0xff, 0x6b, 0xc8, 0x7e, 0x0c, 0xd9, 0x9b, 0x40, 0x34, 0x22, 0x44, 0x00,
0xdf, 0x0e, 0x9e, 0xcd, 0xb9, 0x1d, 0x3d, 0xfe, 0x5a, 0xb9, 0x28, 0xdc,
0x94, 0x43, 0xc4, 0x1c, 0x66, 0xa9, 0x8a, 0xa4, 0x61, 0xdf, 0x8a, 0xf3,
0x7c, 0xf0, 0xbe, 0x66, 0xe9, 0xdf, 0x65, 0x93, 0x6c, 0xc7, 0xb5, 0x1a,
0x76, 0x07, 0x40, 0xde, 0xa1, 0xc5, 0x40, 0xde, 0xac, 0x5b, 0x9f, 0x32,
0xbb, 0xd4, 0xf2, 0x09, 0x13, 0x20, 0xbe, 0xee, 0xf4, 0xb5, 0xb0, 0xec,
0xeb, 0x1e, 0xfa, 0x03, 0x1b, 0x9d, 0x5a, 0xa0, 0x2f, 0x71, 0x1a, 0x76,
0xe7, 0x6f, 0x71, 0x7d, 0x3a, 0x7d, 0x8c, 0x46, 0xaf, 0x93, 0x94, 0x47,
0x27, 0xec, 0x1b, 0x1e, 0xd7, 0x8c, 0x7c, 0xec, 0x42, 0xaf, 0x55, 0x82,
0x3b, 0x6d, 0x07, 0x24, 0xb3, 0xfa, 0x2d, 0x1e, 0x12, 0x02, 0x94, 0x04,
0x23, 0xeb, 0xf3, 0x74, 0x04, 0x7e, 0x2a, 0x7f, 0x00, 0x34, 0x2b, 0x5c,
0x5b, 0x10, 0xe7, 0x36, 0x52, 0xde, 0x9f, 0x56, 0x10, 0xe3, 0x0b, 0xa5,
0x29, 0x85, 0xa5, 0x95, 0xed, 0xf5, 0x39, 0x0a, 0x03, 0x51, 0x29, 0x64,
0xa1, 0x4f, 0x38, 0xde, 0x3b, 0x4d, 0x0a, 0xf3, 0x7e, 0x37, 0x14, 0xce,
0xdf, 0x9d, 0x86, 0x16, 0xad, 0x62, 0xa8, 0xf8, 0xa7, 0xc2, 0xa4, 0xc1,
0xe2, 0xd6, 0x40, 0xa4, 0x7b, 0x20, 0x1b, 0x6d, 0x7c, 0x97, 0x0b, 0x73,
0x85, 0xbf, 0xdb, 0xc3, 0xa1, 0xf5, 0xd4, 0xb7, 0x95, 0xf2, 0xe7, 0x10,
0x77, 0xc6, 0x82, 0xb2, 0x68, 0x24, 0x31, 0xdc, 0x69, 0x43, 0x56, 0xf5,
0x76, 0x20, 0x0a, 0x82, 0x1a, 0x98, 0xb3, 0x02, 0x0f, 0x67, 0xcd, 0x4f,
0xab, 0x43, 0x44, 0xbd, 0xdb, 0x07, 0xd3, 0xff, 0x8b, 0x68, 0x33, 0x24,
0x35, 0xe5, 0xc6, 0x1a, 0x94, 0x14, 0x4f, 0x40, 0xef, 0x92, 0xfb, 0xfd,
0x72, 0x15, 0xd4, 0x10, 0x60, 0x22, 0x3e, 0x60, 0x49, 0x3d, 0x58, 0xc6,
0x3d, 0x28, 0x70, 0x55, 0x32, 0xd5, 0x78, 0x03, 0x51, 0xff, 0xd6, 0x4f,
0x4e, 0x89, 0x0e, 0x50, 0x85, 0x6e, 0x1c, 0x6a, 0x5f, 0x11, 0xd0, 0xf5,
0xee, 0xe5, 0x1c, 0xa8, 0xb2, 0xdb, 0x26, 0x93, 0xb1, 0xe2, 0xc1, 0x05,
0xe0, 0x7f, 0x16, 0xe7, 0x9c, 0xcf, 0xe7, 0xb7, 0x7e, 0xaa, 0x96, 0x21,
0x64, 0x39, 0x6d, 0x7a, 0xdc, 0x70, 0x6e, 0xc8, 0xf5, 0x44, 0x2e, 0x9f,
0xc1, 0xe9, 0x46, 0x8c, 0x1b, 0x58, 0xec, 0x73, 0x1b, 0x9a, 0x04, 0xcb,
0x68, 0x58, 0x21, 0x0e, 0xd6, 0xd7, 0x7a, 0x2b, 0x60, 0x02, 0x20, 0x7b,
0x85, 0xe5, 0x84, 0x2c, 0x5f, 0x24, 0x90, 0x2d, 0xc5, 0x19, 0xea, 0xf3,
0x91, 0x78, 0xc2, 0xa7, 0x36, 0x5a, 0x72, 0x64, 0x45, 0x13, 0x49};
const unsigned char kTestDrmServiceCertificateLicenseSdk[] = {
0x0a, 0xc0, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32,
0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18,
0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02,
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54,
@@ -277,39 +521,166 @@ const unsigned char kTestDrmServiceCertificate[] = {
0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b,
0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00,
0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0x6e, 0xc3,
0x5a, 0x17, 0xa8, 0xf9, 0xef, 0xee, 0x67, 0x4d, 0x0a, 0xef, 0x57, 0x5e,
0xbc, 0x59, 0x3d, 0x22, 0x84, 0xa0, 0x0a, 0xf5, 0x84, 0x26, 0xb7, 0x8b,
0xab, 0x91, 0x3e, 0x4b, 0xb9, 0x91, 0x3c, 0x50, 0xc9, 0x08, 0x2f, 0x97,
0x0a, 0x91, 0xb5, 0x48, 0xe4, 0xba, 0xfd, 0x7b, 0xbd, 0xf0, 0xba, 0x08,
0xb3, 0x29, 0xb4, 0x23, 0x74, 0xaf, 0x3f, 0xe9, 0x77, 0x78, 0x3f, 0xdc,
0x3d, 0x8a, 0x37, 0xec, 0x1c, 0x3a, 0xff, 0x60, 0x8e, 0x10, 0x72, 0xaa,
0x97, 0x98, 0x56, 0xa0, 0x35, 0xa9, 0xbf, 0x43, 0x21, 0x6a, 0x15, 0x88,
0xba, 0xc0, 0x68, 0x01, 0x7b, 0xd7, 0x88, 0x2f, 0x1a, 0xc5, 0x1f, 0x54,
0xf0, 0xea, 0x36, 0xb7, 0xed, 0x49, 0x78, 0x09, 0xb1, 0x07, 0x46, 0xfe,
0xf4, 0xfa, 0x16, 0x0c, 0x46, 0x91, 0xe2, 0xa9, 0xe0, 0x8e, 0x97, 0xe5,
0xea, 0x2f, 0xd9, 0x94, 0x1e, 0xe7, 0xba, 0x28, 0x98, 0x92, 0xae, 0xb8,
0xb6, 0x6e, 0xf6, 0xd2, 0x50, 0xd3, 0x5b, 0x25, 0x12, 0x68, 0x5e, 0x07,
0x82, 0x64, 0x27, 0xfe, 0x1a, 0xcd, 0x38, 0xa8, 0x00, 0x53, 0x8c, 0x69,
0x51, 0x75, 0x71, 0xc2, 0x6a, 0x5f, 0x05, 0x13, 0x77, 0x2b, 0xc8, 0x6c,
0xab, 0xd2, 0x64, 0x27, 0xbd, 0x21, 0xfc, 0x33, 0x0a, 0x3a, 0x53, 0xa6,
0x28, 0x1c, 0x2a, 0xad, 0x23, 0x0a, 0x95, 0xe4, 0x38, 0x6b, 0x9b, 0x3e,
0x77, 0x7d, 0x96, 0x20, 0x42, 0xf5, 0x18, 0xbe, 0xb0, 0x78, 0xe4, 0xf0,
0x95, 0x6c, 0xd5, 0x30, 0xd6, 0xfc, 0x04, 0xe2, 0xf7, 0xff, 0x06, 0x6b,
0xaf, 0xf1, 0x9c, 0x10, 0xa6, 0xdb, 0xed, 0x4a, 0x18, 0x68, 0x87, 0xda,
0x43, 0x2c, 0x60, 0xc6, 0x0a, 0x72, 0x1e, 0x9f, 0x4b, 0x05, 0x80, 0x15,
0x17, 0x84, 0xf1, 0xee, 0xcc, 0x80, 0x25, 0x33, 0x87, 0x74, 0x02, 0x8c,
0xa1, 0xbb, 0xd9, 0x29, 0x33, 0x97, 0xbd, 0x5b, 0x1c, 0xed, 0xcc, 0x47,
0xda, 0x73, 0xae, 0xb1, 0x75, 0xac, 0xf7, 0x39, 0xbe, 0x67, 0xc3, 0xaf,
0x60, 0x07, 0xf5, 0xba, 0x81, 0xf4, 0x42, 0xad, 0x28, 0x8d, 0xe6, 0x63,
0xea, 0x8a, 0x0e, 0x71, 0x53, 0x6e, 0x62, 0x8a, 0x23, 0x4f, 0xad, 0x2a,
0x9a, 0xf6, 0xeb, 0xa8, 0x82, 0x83, 0xbb, 0x5f, 0xc9, 0x86, 0xd8, 0x76,
0xb9, 0xf3, 0xe7, 0x32, 0xdd, 0xe0, 0x44, 0x6a, 0xab, 0x78, 0xa0, 0x8c,
0xa4, 0x99, 0x6f, 0x71, 0x42, 0x8b, 0x31, 0x32, 0xbb, 0x80, 0x36, 0x61,
0x1c, 0xe5, 0x6d, 0x87, 0xf2, 0x68, 0xca, 0xcd, 0xe0, 0x5f, 0xa2, 0x68,
0x5b, 0xfc, 0x73, 0xc9, 0x26, 0x2b, 0x13, 0x05, 0x1c, 0xde, 0x19, 0xdf,
0x34, 0xba, 0xf5, 0xec, 0xaf, 0x26, 0xfb, 0x64, 0xc4, 0x38, 0x7e, 0xdb,
0x51, 0x28, 0x49, 0xa7, 0x12, 0x88, 0xa5, 0x6d, 0xa2, 0xfa};
0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x40, 0x01, 0x48, 0x01, 0x12,
0x80, 0x03, 0x20, 0x54, 0x65, 0x70, 0x93, 0x53, 0x0a, 0x76, 0x95, 0xe5,
0xaa, 0x48, 0x94, 0xa1, 0xb8, 0x25, 0xa6, 0xa4, 0x5a, 0x56, 0x59, 0x8d,
0x80, 0x64, 0x7c, 0xb2, 0x1e, 0xeb, 0xda, 0xfb, 0x30, 0x26, 0x09, 0xc0,
0x25, 0x65, 0xdb, 0xb8, 0x7d, 0x0e, 0xe0, 0x2b, 0xfb, 0xfe, 0xe6, 0x3a,
0x5b, 0x9b, 0x1f, 0xbb, 0xa4, 0x89, 0xf5, 0x7c, 0x52, 0x6f, 0x52, 0xe3,
0xb3, 0xc7, 0x27, 0x9d, 0xca, 0x01, 0x78, 0x08, 0x9b, 0x59, 0x37, 0x9f,
0x27, 0x52, 0x90, 0x80, 0x22, 0xfb, 0x0d, 0xca, 0x57, 0xc2, 0xd9, 0x89,
0xb6, 0x69, 0x45, 0x1f, 0x15, 0x23, 0xf1, 0xf8, 0x39, 0xbb, 0x45, 0xb9,
0x39, 0xe5, 0x1e, 0x8b, 0x71, 0x82, 0x25, 0x4a, 0x32, 0xc2, 0x44, 0xee,
0x76, 0x91, 0x61, 0xa2, 0xe2, 0x7a, 0xb4, 0x68, 0x56, 0xaf, 0x33, 0xe4,
0x97, 0x44, 0xfe, 0x6d, 0x70, 0x85, 0x4f, 0x16, 0x1a, 0xda, 0xa4, 0x30,
0x66, 0xf7, 0x95, 0xe9, 0x7b, 0x84, 0x42, 0xd6, 0x7c, 0x4b, 0x05, 0xca,
0x67, 0x2f, 0xf4, 0xdc, 0x81, 0x9b, 0x7c, 0x80, 0xc4, 0x9e, 0x25, 0x98,
0x84, 0xc4, 0x43, 0x35, 0x13, 0xf4, 0x9d, 0x57, 0x02, 0x1e, 0x67, 0x86,
0x00, 0x6c, 0x46, 0xde, 0x91, 0x9f, 0x1f, 0x42, 0xbb, 0xa7, 0xd1, 0xb8,
0x80, 0x2c, 0x33, 0x51, 0x87, 0x93, 0x6d, 0x75, 0x03, 0xb0, 0x42, 0xc9,
0xe6, 0xa1, 0xc7, 0xa5, 0xd3, 0x40, 0xe7, 0x99, 0x6d, 0x07, 0x78, 0x13,
0x8a, 0x01, 0x4d, 0x3e, 0xb4, 0x9a, 0x1b, 0x52, 0xb7, 0xac, 0x6d, 0x27,
0xda, 0x5c, 0xa2, 0x78, 0x01, 0xe3, 0x4d, 0x5d, 0x0a, 0xd0, 0xc7, 0xb5,
0x73, 0xcf, 0x6e, 0xdd, 0x89, 0xc6, 0xd4, 0x9c, 0xc7, 0xfa, 0x87, 0xe9,
0x74, 0x01, 0xe9, 0xdd, 0x16, 0x0f, 0x3a, 0x8e, 0x38, 0x8d, 0x0b, 0x5a,
0xc8, 0x01, 0xca, 0xb2, 0x7f, 0xcb, 0xe3, 0x25, 0xaa, 0x10, 0xc9, 0x4f,
0x5a, 0x17, 0xe0, 0x31, 0x30, 0x34, 0xe8, 0xe6, 0x06, 0x27, 0xb3, 0x26,
0xee, 0x44, 0x5d, 0x34, 0x2d, 0xc0, 0xff, 0x98, 0x1d, 0x33, 0x99, 0x96,
0x29, 0xa9, 0xc6, 0x31, 0xc1, 0xe1, 0x2f, 0xb9, 0x3a, 0xd2, 0x80, 0x16,
0xc2, 0x4a, 0x38, 0x58, 0x9f, 0x78, 0x7b, 0x11, 0x6c, 0x4e, 0xb0, 0x6b,
0x3b, 0x8f, 0x77, 0x59, 0xb7, 0xca, 0x08, 0x85, 0xc5, 0xe2, 0x03, 0xa7,
0x33, 0xe7, 0x34, 0xc5, 0x64, 0x37, 0x9b, 0x19, 0x48, 0x54, 0xa7, 0xe5,
0x74, 0xe3, 0xa9, 0xfc, 0xe7, 0x6f, 0x9f, 0x04, 0xd4, 0xbd, 0x4a, 0x70,
0xc9, 0x09, 0x67, 0x1a, 0xc5, 0x7b, 0xe9, 0x88, 0x71, 0xcb, 0x96, 0x46,
0x2f, 0x5e, 0x47, 0x19, 0x48, 0xc0, 0xc6, 0xc1, 0xef, 0xb1, 0x26, 0x95,
0x0c, 0x16, 0xed, 0x88, 0x4d, 0xaf, 0x96, 0x3f, 0xa2, 0xf8, 0xc7, 0x38,
0x95, 0x48,
};
const unsigned char kTestDrmServiceCertificateAllTypes[] = {
0x0a, 0xc4, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32,
0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18,
0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02,
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54,
0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58,
0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57,
0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa,
0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e,
0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda,
0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9,
0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2,
0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e,
0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a,
0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67,
0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56,
0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b,
0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83,
0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1,
0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f,
0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40,
0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46,
0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86,
0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b,
0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b,
0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00,
0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x40, 0x01, 0x40, 0x02, 0x40,
0x03, 0x48, 0x01, 0x12, 0x80, 0x03, 0x72, 0x7c, 0xb3, 0x19, 0x62, 0x88,
0x4d, 0xc5, 0x46, 0x11, 0xcc, 0x6b, 0x32, 0x47, 0x65, 0x7a, 0x5e, 0xe7,
0xf7, 0x58, 0x7e, 0xbc, 0x76, 0xa6, 0x55, 0x75, 0x32, 0x19, 0xaa, 0xf6,
0xb7, 0x02, 0x16, 0x75, 0xfd, 0x05, 0x0b, 0x1c, 0x2c, 0xc5, 0x44, 0x6c,
0xcf, 0x96, 0x02, 0x93, 0x23, 0x60, 0x4b, 0xd4, 0x97, 0x81, 0xa4, 0xe5,
0x5f, 0x0a, 0xe0, 0x5f, 0x53, 0x23, 0x66, 0x19, 0x70, 0xe0, 0x8c, 0x08,
0x19, 0x88, 0xf2, 0xda, 0xd9, 0x7c, 0x69, 0x52, 0xc0, 0x8a, 0x7a, 0x62,
0x81, 0xaa, 0x2b, 0x0e, 0xe8, 0x28, 0x21, 0xce, 0x4a, 0x63, 0x52, 0x23,
0xab, 0xea, 0x65, 0xf9, 0x0f, 0x8a, 0xd2, 0x77, 0xa5, 0x94, 0x05, 0x77,
0x8b, 0x35, 0x99, 0x8b, 0xa9, 0xdd, 0xe4, 0x69, 0x00, 0xbe, 0x4d, 0xd2,
0xce, 0x6c, 0xe4, 0xf9, 0x34, 0x97, 0xaa, 0x59, 0xd8, 0xc9, 0x58, 0x11,
0xa9, 0xb4, 0x9b, 0x0e, 0xce, 0xb7, 0x15, 0x73, 0x6b, 0xb5, 0x5f, 0x87,
0xea, 0xf1, 0x1c, 0x8d, 0x66, 0x5a, 0x28, 0x7e, 0x9a, 0x01, 0x15, 0xfb,
0xb1, 0x92, 0x28, 0x3b, 0x62, 0x6a, 0xcb, 0xb0, 0xe7, 0x11, 0x76, 0x44,
0x5a, 0x97, 0x4b, 0x1a, 0x26, 0xbe, 0x3c, 0xa6, 0x33, 0xd8, 0x99, 0x7b,
0x97, 0xb2, 0xec, 0xa3, 0x18, 0xdf, 0x6b, 0xeb, 0x9f, 0x43, 0x14, 0x0a,
0x49, 0xdb, 0xb0, 0xe2, 0xc0, 0xe0, 0xe3, 0x73, 0x66, 0x0f, 0x14, 0x1c,
0x48, 0x21, 0x28, 0x3a, 0x90, 0xfd, 0x32, 0x1d, 0x35, 0x9a, 0x11, 0x6c,
0xf8, 0x39, 0xd1, 0xcb, 0x45, 0x76, 0xc0, 0x5e, 0xc5, 0xa8, 0xba, 0xd9,
0x09, 0xfc, 0x44, 0xf2, 0x9e, 0xaf, 0x95, 0xc4, 0xe0, 0x06, 0x0e, 0xbf,
0x99, 0x6e, 0x57, 0xfa, 0xa8, 0xcd, 0x2b, 0x4b, 0x97, 0x62, 0xf7, 0x92,
0x14, 0x8f, 0xf4, 0x8c, 0xba, 0xb1, 0xb4, 0x8e, 0x07, 0xd2, 0x7b, 0x93,
0xd1, 0xbf, 0x90, 0x60, 0xe0, 0xbf, 0x1a, 0x3b, 0xd2, 0xee, 0xad, 0xf8,
0x4f, 0x5b, 0xee, 0xe5, 0xf4, 0x8e, 0x97, 0x5b, 0x24, 0xd2, 0xa6, 0x80,
0x5c, 0x09, 0x27, 0x8e, 0x14, 0xa9, 0xcc, 0xff, 0x5a, 0xc1, 0xb4, 0x5f,
0xb5, 0x07, 0x04, 0xd0, 0x3a, 0xef, 0xa9, 0x45, 0xa1, 0x23, 0x0f, 0xc2,
0x13, 0x4f, 0xc8, 0xd4, 0x7f, 0xef, 0x54, 0x5c, 0xcc, 0xdb, 0xdf, 0x89,
0x4c, 0x31, 0x8f, 0x27, 0x50, 0x2b, 0x99, 0xd8, 0xee, 0xdf, 0x8b, 0x06,
0x93, 0xc0, 0xd0, 0x59, 0xf5, 0x66, 0xaf, 0x4b, 0x98, 0x68, 0xb6, 0x8c,
0xd7, 0x70, 0xad, 0x69, 0x60, 0xca, 0xae, 0x64, 0xc2, 0x53, 0xcb, 0xa1,
0x39, 0xb7, 0x08, 0x50, 0x31, 0x12, 0xc1, 0x02, 0x08, 0x58, 0x59, 0xc8,
0x69, 0x9e, 0x43, 0xac, 0x60, 0x7d, 0x4c, 0x8b, 0x1a, 0xe8, 0xa8, 0x3d,
0x65, 0x29, 0x83, 0x85, 0x9b, 0x8e,
};
const unsigned char kTestDrmServiceCertificateNoType[] = {
0x0a, 0xbe, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32,
0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18,
0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02,
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54,
0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58,
0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57,
0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa,
0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e,
0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda,
0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9,
0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2,
0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e,
0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a,
0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67,
0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56,
0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b,
0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83,
0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1,
0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f,
0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40,
0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46,
0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86,
0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b,
0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b,
0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00,
0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x48, 0x01, 0x12, 0x80, 0x03,
0x21, 0xa4, 0x86, 0x36, 0x40, 0x9d, 0x0f, 0x7d, 0x31, 0x8a, 0xbe, 0x98,
0x85, 0x0f, 0x4a, 0xe6, 0x5e, 0x2e, 0x94, 0xc3, 0x4a, 0x69, 0x26, 0x55,
0x76, 0xf6, 0x11, 0xfe, 0xd4, 0x1d, 0xfc, 0xb3, 0xbe, 0x94, 0x6a, 0x5c,
0x51, 0x2d, 0xda, 0x95, 0x70, 0x6f, 0x62, 0x23, 0x69, 0xfa, 0x3d, 0xce,
0x09, 0x0c, 0x2f, 0xc7, 0x30, 0xac, 0xf2, 0x46, 0x6a, 0x14, 0x42, 0x24,
0x8f, 0x2a, 0xb4, 0xa7, 0x32, 0xbb, 0x8c, 0x02, 0xe2, 0x1f, 0x63, 0xfd,
0xf4, 0x40, 0x14, 0xd9, 0x12, 0x88, 0xa6, 0x46, 0x15, 0x67, 0x85, 0xa3,
0xe2, 0xe4, 0x88, 0x5f, 0xdd, 0x20, 0xcd, 0xa7, 0xd1, 0x26, 0x71, 0x63,
0x89, 0x2e, 0x1a, 0x8a, 0x52, 0x59, 0xba, 0x3b, 0x09, 0x8a, 0x1c, 0x67,
0x0c, 0xfd, 0xc9, 0xad, 0x7b, 0x4f, 0xd8, 0x0e, 0x47, 0x9e, 0x47, 0x44,
0x20, 0x67, 0x7a, 0xec, 0x3b, 0xfe, 0x58, 0xf2, 0x25, 0xb2, 0x72, 0xe1,
0x05, 0xbd, 0x7c, 0x02, 0xc9, 0xdb, 0x40, 0x3c, 0x90, 0x04, 0xab, 0xa7,
0xb6, 0x06, 0xb2, 0x30, 0x64, 0x42, 0x84, 0x3e, 0x89, 0xd7, 0xd5, 0x90,
0xaa, 0x14, 0x00, 0xa6, 0x45, 0x26, 0xe1, 0x16, 0xa8, 0x24, 0x65, 0x01,
0xe1, 0x0e, 0xd3, 0xff, 0x68, 0x0c, 0x0f, 0x8f, 0xc3, 0x68, 0xed, 0x82,
0x7c, 0xae, 0x21, 0x97, 0x6a, 0x5a, 0x55, 0x76, 0x4b, 0x4e, 0x65, 0x9b,
0xbd, 0x18, 0x8b, 0xf4, 0xc4, 0x28, 0xab, 0x6c, 0x52, 0xc7, 0xfc, 0xb3,
0x12, 0x79, 0x3c, 0x29, 0x82, 0x15, 0xd1, 0x45, 0xae, 0x0b, 0xbc, 0x46,
0x13, 0x24, 0x80, 0xb1, 0x6a, 0x15, 0x7f, 0x68, 0x91, 0xfe, 0xa6, 0x5d,
0x7a, 0xdb, 0x81, 0xc4, 0x14, 0xf0, 0x7d, 0x59, 0x15, 0x7c, 0x11, 0x6d,
0xe5, 0x97, 0xc2, 0x5e, 0x00, 0xfc, 0x36, 0x4f, 0xf7, 0xc1, 0xe6, 0x62,
0x92, 0x03, 0x92, 0x4d, 0x91, 0x61, 0x7e, 0xdc, 0xe9, 0xd3, 0x16, 0x17,
0xa7, 0x21, 0xb8, 0xf7, 0x27, 0xc4, 0x72, 0xe9, 0xff, 0x66, 0x60, 0x65,
0x50, 0x6d, 0x2d, 0xe9, 0x02, 0xf2, 0x48, 0xbc, 0xfa, 0x7b, 0x3e, 0xdc,
0x51, 0x4a, 0xd8, 0x2a, 0xd2, 0x32, 0x95, 0xff, 0x32, 0xf0, 0x68, 0xae,
0x74, 0xb3, 0xaf, 0x93, 0x79, 0x2e, 0x4e, 0xd8, 0x23, 0x61, 0xf4, 0xca,
0x34, 0x3f, 0x2d, 0x12, 0x5d, 0xef, 0x05, 0xd0, 0xdc, 0x72, 0x0b, 0x02,
0xc4, 0x2d, 0x97, 0x65, 0x6d, 0x44, 0x25, 0x50, 0x8e, 0xd1, 0x34, 0x8b,
0xc3, 0xff, 0xca, 0x17, 0xcb, 0x5c, 0x64, 0x8e, 0xd1, 0xe5, 0x81, 0xb2,
0x5c, 0xd6, 0xd3, 0x2a, 0x0c, 0x74, 0x41, 0x14, 0xc5, 0x27, 0x3c, 0x3f,
0x2b, 0xf8, 0xba, 0x33, 0x0b, 0xf4, 0x88, 0x9c, 0x8b, 0x75, 0xf5, 0xf0,
0x29, 0x08, 0x23, 0x91, 0xe1, 0xcc, 0xc3, 0x6e, 0x5e, 0x60, 0x5d, 0x5a,
};
TestDrmCertificates::TestDrmCertificates()
: test_root_certificate_(
@@ -318,11 +689,32 @@ TestDrmCertificates::TestDrmCertificates()
test_intermediate_certificate_(
kTestIntermediateCertificate,
kTestIntermediateCertificate + sizeof(kTestIntermediateCertificate)),
test_intermediate_certificate_with_ec_key_(
kTestIntermediateCertificateWithECKey,
kTestIntermediateCertificateWithECKey +
sizeof(kTestIntermediateCertificateWithECKey)),
test_user_device_certificate_(
kTestUserDrmCertificate,
kTestUserDrmCertificate + sizeof(kTestUserDrmCertificate)),
test_service_certificate_(
kTestDrmServiceCertificate,
kTestDrmServiceCertificate + sizeof(kTestDrmServiceCertificate)) {}
test_user_device_certificate_with_ec_key_(
kTestUserDrmCertificateWithECKey,
kTestUserDrmCertificateWithECKey +
sizeof(kTestUserDrmCertificateWithECKey)),
test_user_device_certificate_with_rot_id_(
kTestUserDrmCertificateWithRotId,
kTestUserDrmCertificateWithRotId +
sizeof(kTestUserDrmCertificateWithRotId)),
test_service_certificate_license_sdk_(
kTestDrmServiceCertificateLicenseSdk,
kTestDrmServiceCertificateLicenseSdk +
sizeof(kTestDrmServiceCertificateLicenseSdk)),
test_service_certificate_all_types_(
kTestDrmServiceCertificateAllTypes,
kTestDrmServiceCertificateAllTypes +
sizeof(kTestDrmServiceCertificateAllTypes)),
test_service_certificate_no_type_(
kTestDrmServiceCertificateNoType,
kTestDrmServiceCertificateNoType +
sizeof(kTestDrmServiceCertificateNoType)) {}
} // namespace widevine

View File

@@ -14,41 +14,78 @@
#define COMMON_TEST_DRM_CERTIFICATES_H_
#include <string>
#include "base/macros.h"
namespace widevine {
class TestDrmCertificates {
public:
TestDrmCertificates();
TestDrmCertificates(const TestDrmCertificates&) = delete;
TestDrmCertificates& operator=(const TestDrmCertificates&) = delete;
virtual ~TestDrmCertificates() {}
// returns a test root certificate
const std::string& test_root_certificate() const { return test_root_certificate_; }
const std::string& test_root_certificate() const {
return test_root_certificate_;
}
// returns a test intermediate certificate with an RSA key
// returns a test intermediate certificate
const std::string& test_intermediate_certificate() const {
return test_intermediate_certificate_;
}
// returns an user device certificate
// returns a test intermediate certificate with an EC key
const std::string& test_intermediate_certificate_with_ec_key() const {
return test_intermediate_certificate_with_ec_key_;
}
// returns a user device certificate with an RSA key
const std::string& test_user_device_certificate() const {
return test_user_device_certificate_;
}
// returns a service certificate
const std::string& test_service_certificate() const {
return test_service_certificate_;
// returns a user device certificate with an EC key
const std::string& test_user_device_certificate_with_ec_key() const {
return test_user_device_certificate_with_ec_key_;
}
// returns a user device certificate with a Root of Trust Id.
const std::string& test_user_device_certificate_with_rot_id() const {
return test_user_device_certificate_with_rot_id_;
}
// returns a service certificate with license sdk service type
const std::string& test_service_certificate_license_sdk() const {
return test_service_certificate_license_sdk_;
}
// returns a service certificate with all service types
const std::string& test_service_certificate_all_types() const {
return test_service_certificate_all_types_;
}
// returns a service certificate prior to a change requiring the service
// type to be specified.
const std::string& test_service_certificate_no_type() const {
return test_service_certificate_no_type_;
}
private:
const std::string test_root_certificate_;
const std::string test_intermediate_certificate_;
const std::string test_intermediate_certificate_with_ec_key_;
const std::string test_user_device_certificate_;
const std::string test_service_certificate_;
DISALLOW_COPY_AND_ASSIGN(TestDrmCertificates);
const std::string test_user_device_certificate_with_ec_key_;
const std::string test_user_device_certificate_with_rot_id_;
const std::string test_service_certificate_license_sdk_;
const std::string test_service_certificate_all_types_;
const std::string test_service_certificate_no_type_;
};
} // namespace widevine
#endif // COMMON_TEST_DRM_CERTIFICATES_H_

View File

@@ -167,7 +167,8 @@ class VmpCheckerTest : public ::testing::Test {
// |kSameAsPrevious| (empty), then the binary is signed using the same
// certificate as the previously added binary. This means that the first
// call to this function should not use |kSameAsPrevious|.
void AddVmpBinary(const std::string& signing_cert, const std::string& file_name,
void AddVmpBinary(const std::string& signing_cert,
const std::string& file_name,
const std::string& binary_hash, uint32_t flags) {
DCHECK(!signing_cert.empty() || !vmp_data_.certificates().empty());

View File

@@ -85,7 +85,8 @@ PreprovKeysMap* PreprovKeysMap::GetSingleton() {
} // namespace
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
const std::string& key_bytes, Cipher cipher,
const std::string& key_bytes,
Cipher cipher,
const std::string& model_filter)
: system_id(system_id),
key_bytes(key_bytes),
@@ -93,7 +94,8 @@ WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
model_filter(model_filter) {}
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
const std::string& key_bytes, Cipher cipher)
const std::string& key_bytes,
Cipher cipher)
: system_id(system_id), key_bytes(key_bytes), cipher(cipher) {}
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
@@ -230,8 +232,10 @@ Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
}
if (version) {
// Bytes 0-3 contain the keybox version.
*version = BigEndian::Load32(token.data());
}
// This was checked at initialization, so if it fails now something is wrong.
CHECK_EQ(preprov_key.size(), kPreProvisioningKeySizeBytes);

View File

@@ -12,7 +12,6 @@
#include <map>
#include <vector>
#include "base/macros.h"
#include "absl/strings/string_view.h"
#include "common/status.h"
@@ -34,6 +33,9 @@ namespace widevine {
// INTERNAL_ERROR - something went wrong that shouldn't have been able to.
class WvmTokenHandler {
public:
WvmTokenHandler(const WvmTokenHandler&) = delete;
WvmTokenHandler& operator=(const WvmTokenHandler&) = delete;
// Cipher type to use for encrypting asset keys. This matches the enum in
// video/widevine/lockbox/public/key.proto.
enum Cipher {
@@ -71,22 +73,23 @@ class WvmTokenHandler {
// insecure_out may be null; if not, *insecure_out will be set to the
// decrypted value of the 'insecure keybox' flag.
static Status DecryptDeviceKey(absl::string_view token,
std::string* device_key_out, Cipher* cipher_out,
bool* insecure_out);
std::string* device_key_out,
Cipher* cipher_out, bool* insecure_out);
// Same as above, except takes in the make/model from the license request.
// For legacy WVM license, we have some special cases where we need to inspect
// the make/model as we apply alternate keys.
static Status DecryptDeviceKey(absl::string_view token,
const std::string& make_model,
std::string* device_key_out, Cipher* cipher_out,
bool* insecure_out);
std::string* device_key_out,
Cipher* cipher_out, bool* insecure_out);
// Decrypt a token using the preprov key for its system ID, and use the
// decrypted device key to encrypt the given asset key. Returns the encrypted
// asset key in result.
static Status GetEncryptedAssetKey(absl::string_view token,
absl::string_view raw_asset_key,
const std::string& make_model, std::string* result);
const std::string& make_model,
std::string* result);
// Extract the system ID component of a token (bytes 4-8).
static uint32_t GetSystemId(absl::string_view token);
@@ -115,9 +118,6 @@ class WvmTokenHandler {
static Status EncryptAssetKey(absl::string_view device_key,
absl::string_view raw_asset_key, Cipher cipher,
std::string* result);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler);
};
} // namespace widevine

View File

@@ -203,7 +203,8 @@ bool X509Cert::IsCaCertificate() const {
return X509_check_ca(openssl_cert_) != 0;
}
bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const {
bool X509Cert::GetV3BooleanExtension(const std::string& oid,
bool* value) const {
ScopedAsn1Object extension_name(OBJ_txt2obj(oid.c_str(), 1));
int ext_pos = X509_get_ext_by_OBJ(openssl_cert_, extension_name.get(), -1);
if (ext_pos < 0) return false;

View File

@@ -18,7 +18,6 @@
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "openssl/pem.h"
@@ -39,6 +38,10 @@ class X509Cert {
static std::unique_ptr<X509Cert> FromOpenSslCert(ScopedX509 openssl_cert_);
X509Cert();
X509Cert(const X509Cert&) = delete;
X509Cert& operator=(const X509Cert&) = delete;
virtual ~X509Cert();
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
@@ -98,14 +101,15 @@ class X509Cert {
std::string subject_name_;
friend class X509CertChain;
DISALLOW_COPY_AND_ASSIGN(X509Cert);
};
// Class which holds a chain of X.509 certificates.
class X509CertChain {
public:
X509CertChain() {}
X509CertChain() = default;
X509CertChain(const X509CertChain&) = delete;
X509CertChain& operator=(const X509CertChain&) = delete;
virtual ~X509CertChain();
// Loads a chain of PEM-encoded X.509 certificates. Takes a single parameter,
@@ -135,8 +139,6 @@ class X509CertChain {
void Reset();
std::vector<X509Cert*> cert_chain_;
DISALLOW_COPY_AND_ASSIGN(X509CertChain);
};
// CA class which holds the root CA cert, and verifies certificate chains.
@@ -144,6 +146,10 @@ class X509CA {
public:
// New object assumes ownership of |ca_cert|.
explicit X509CA(X509Cert* ca_cert);
X509CA(const X509CA&) = delete;
X509CA& operator=(const X509CA&) = delete;
virtual ~X509CA();
// Does X.509 PKI validation of |cert| against the root CA certificate
@@ -166,9 +172,7 @@ class X509CA {
std::unique_ptr<X509Cert> ca_cert_;
absl::Mutex openssl_store_mutex_;
X509_STORE* openssl_store_ GUARDED_BY(openssl_store_mutex_);
DISALLOW_IMPLICIT_CONSTRUCTORS(X509CA);
X509_STORE* openssl_store_ ABSL_GUARDED_BY(openssl_store_mutex_);
};
} // namespace widevine

View File

@@ -59,7 +59,7 @@ cc_binary(
"//base",
"//common:status",
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
"//protos/public:media_cas_encryption_proto",
"//protos/public:media_cas_encryption_cc_proto",
],
)

View File

@@ -14,7 +14,7 @@
namespace widevine {
namespace cas {
const char kTestEcmgChannelSetup[] = {
constexpr char kTestEcmgChannelSetup[] = {
'\x03', // protocol_version
'\x00', '\x01', // message_type - Channel_setup
'\x00', '\x0e', // message_length
@@ -26,7 +26,55 @@ const char kTestEcmgChannelSetup[] = {
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
};
const char kTestEcmgChannelStatus[] = {
constexpr char kTestEcmgChannelSetupWithPrivateParameters[] = {
'\x03', // protocol_version
'\x00', '\x01', // message_type - Channel_setup
'\x00', '\x70', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x01', // parameter_type- SUPER_CAS_ID
'\x00', '\x04', // parameter_length
'\x4a', '\xd4', '\x00', '\x00', // parameter_value
'\x80', '\x00', // parameter_type - AGE_RESTRICTION
'\x00', '\x01', // parameter_length
'\x00', // parameter_value
'\x80', '\x01', // parameter_type - CRYPTO_MODE
'\x00', '\x07', // parameter_length
'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value
'\x80', '\x04', // parameter_type - TRACK_TYPES
'\x00', '\x02', // parameter_length
'S', 'D', // parameter_value
'\x80', '\x04', // parameter_type - TRACK_TYPES
'\x00', '\x02', // parameter_length
'H', 'D', // parameter_value
'\x80', '\x02', // parameter_type - CONTENT_ID
'\x00', '\x09', // parameter_length
'C', 'a', 's', 'T', 's', 'F', 'a', 'k',
'e', // parameter_value - CasTsFake
'\x80', '\x03', // parameter_type - CONTENT_PROVIDER
'\x00', '\x0d', // parameter_length
'w', 'i', 'd', 'e', 'v', 'i', 'n', 'e',
'_', 't', 'e', 's', 't', // parameter_value - widevine_test
'\x80', '\x06', // parameter_type - CONTENT_IV
'\x00', '\x10', // parameter_length
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
'\x80', '\x06', // parameter_type - CONTENT_IV
'\x00', '\x10', // parameter_length
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
constexpr char kTestEcmgChannelTest[] = {
'\x03', // protocol_version
'\x00', '\x02', // message_type - Channel_test
'\x00', '\x06', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
};
constexpr char kTestEcmgChannelStatus[] = {
'\x03', // protocol_version
'\x00', '\x03', // message_type - Channel_status
'\x00', '\x39', // message_length
@@ -62,7 +110,28 @@ const char kTestEcmgChannelStatus[] = {
'\x00', '\x64' // parameter_value
};
const char kTestEcmgStreamSetup[] = {
constexpr char kTestEcmgStreamSetupWithPrivateParameters[] = {
'\x03', // protocol_version
'\x01', '\x01', // message_type - Stream_setup
'\x00', '\x1e', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x19', // parameter_type - ECM_id
'\x00', '\x02', // parameter_length
'\x00', '\x02', // parameter_value
'\x00', '\x10', // parameter_type - nominal_CP_duration
'\x00', '\x02', // parameter_length
'\x00', '\x64', // parameter_value
'\x80', '\x05', // parameter_type - STREAM_TRACK_TYPE
'\x00', '\x02', // parameter_length
'S', 'D', // parameter_value
};
constexpr char kTestEcmgStreamSetup[] = {
'\x03', // protocol_version
'\x01', '\x01', // message_type - Stream_setup
'\x00', '\x18', // message_length
@@ -80,7 +149,19 @@ const char kTestEcmgStreamSetup[] = {
'\x00', '\x64' // parameter_value
};
const char kTestEcmgStreamStatus[] = {
constexpr char kTestEcmgStreamTest[] = {
'\x03', // protocol_version
'\x01', '\x02', // message_type - Stream_test
'\x00', '\x0c', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
};
constexpr char kTestEcmgStreamStatus[] = {
'\x03', // protocol_version
'\x01', '\x03', // message_type - Stream_status
'\x00', '\x17', // message_length
@@ -98,7 +179,7 @@ const char kTestEcmgStreamStatus[] = {
'\x01' // parameter_value
};
const char kTestEcmgCwProvision[] = {
constexpr char kTestEcmgCwProvision[] = {
'\x03', // protocol_version
'\x02', '\x01', // message_type - CW_provision
'\x00', '\x44', // message_length
@@ -125,8 +206,87 @@ const char kTestEcmgCwProvision[] = {
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = {
'\x03', // protocol_version
'\x02', '\x01', // message_type - CW_provision
'\x00', '\xaa', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x12', // parameter_type - CP_number
'\x00', '\x02', // parameter_length
'\x00', '\x00', // parameter_value
'\x00', '\x13', // parameter_type - CP_duration
'\x00', '\x02', // parameter_length
'\x00', '\x64', // parameter_value
'\x00', '\x14', // parameter_type - CP_CW_Combination
'\x00', '\x12', // parameter_length
'\x00', '\x00', // parameter_value - CP (2 bytes) then CW next (16 bytes)
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
'\x00', '\x14', // parameter_type - CP_CW_Combination
'\x00', '\x12', // parameter_length
'\x00', '\x01', // parameter_value - CP (2 bytes) then CW next (16 bytes)
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
'\x00', '\x0d', // parameter_type - access_criteria
'\x00', '\x62', // parameter_length
'\x80', '\x00', // access_criteria parameter_type - AGE_RESTRICTION
'\x00', '\x01', // parameter_length
'\x00', // parameter_value
'\x80', '\x01', // access_criteria parameter_type - CRYPTO_MODE
'\x00', '\x07', // parameter_length
'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value
'\x80', '\x04', // access_criteria parameter_type - TRACK_TYPES
'\x00', '\x02', // parameter_length
'S', 'D', // parameter_value
'\x80', '\x05', // access_criteria parameter_type - STREAM_TRACK_TYPE
'\x00', '\x02', // parameter_length
'S', 'D', // parameter_value
'\x80', '\x02', // access_criteria parameter_type - CONTENT_ID
'\x00', '\x09', // parameter_length
'C', 'a', 's', 'T', 's', 'F', 'a', 'k',
'e', // parameter_value - CasTsFake
'\x80', '\x03', // access_criteria parameter_type - CONTENT_PROVIDER
'\x00', '\x0d', // parameter_length
'w', 'i', 'd', 'e', 'v', 'i', 'n', 'e',
'_', 't', 'e', 's', 't', // parameter_value - widevine_test
'\x80', '\x06', // access_criteria parameter_type - CONTENT_IV
'\x00', '\x10', // parameter_length
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
'\x80', '\x06', // access_criteria parameter_type - CONTENT_IV
'\x00', '\x10', // parameter_length
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
constexpr char kTestEcmgCwProvisionSingleKey[] = {
'\x03', // protocol_version
'\x02', '\x01', // message_type - CW_provision
'\x00', '\x2e', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x12', // parameter_type - CP_number
'\x00', '\x02', // parameter_length
'\x00', '\x00', // parameter_value
'\x00', '\x13', // parameter_type - CP_duration
'\x00', '\x02', // parameter_length
'\x00', '\x64', // parameter_value
'\x00', '\x14', // parameter_type - CP_CW_Combination
'\x00', '\x12', // parameter_length
'\x00', '\x00', // parameter_value - CP (2 bytes) then CW next (16 bytes)
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f'};
// CW is encrypted using hardcoded fixed entitlement key.
const char kTestEcmgEcmResponse[] = {
constexpr char kTestEcmgEcmResponse[] = {
'\x03', // protocol_version
'\x02', '\x02', // message_type - ECM_response
'\x00', '\xd2', // message_length
@@ -142,8 +302,8 @@ const char kTestEcmgEcmResponse[] = {
'\x00', '\x15', // parameter_type - ECM_datagram
'\x00', '\xbc', // parameter_length
// parameter_value - ECM_datagram
'\x47', '\x40', '\x02', '\x10', '\x00', '\x80', '\x70', '\x95', '\x4a',
'\xd4', '\x01', '\x05', '\x80', '\x66', '\x61', '\x6b', '\x65', '\x5f',
'\x47', '\x40', '\x00', '\x10', '\x00', '\x80', '\x70', '\xa5', '\x4a',
'\xd4', '\x02', '\x0b', '\xc0', '\x66', '\x61', '\x6b', '\x65', '\x5f',
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x31', '\x2e', '\x2e',
'\x2e', '\x2e', '\xef', '\x40', '\x57', '\x48', '\xa7', '\xad', '\xdd',
'\x34', '\x73', '\xfe', '\x5d', '\x1c', '\x65', '\xa0', '\xbf', '\x93',
@@ -164,7 +324,7 @@ const char kTestEcmgEcmResponse[] = {
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'};
const char kTestEcmgStreamCloseRequest[] = {
constexpr char kTestEcmgStreamClose[] = {
'\x03', // protocol_version
'\x01', '\x04', // message_type - Stream_close_request
'\x00', '\x0c', // message_length
@@ -176,7 +336,7 @@ const char kTestEcmgStreamCloseRequest[] = {
'\x00', '\x01' // parameter_value
};
const char kTestEcmgStreamCloseResponse[] = {
constexpr char kTestEcmgStreamCloseResponse[] = {
'\x03', // protocol_version
'\x01', '\x05', // message_type - Stream_close_response
'\x00', '\x0c', // message_length
@@ -188,7 +348,7 @@ const char kTestEcmgStreamCloseResponse[] = {
'\x00', '\x01' // parameter_value
};
const char kTestEcmgChannelClose[] = {
constexpr char kTestEcmgChannelClose[] = {
'\x03', // protocol_version
'\x00', '\x04', // message_type - Channel_close
'\x00', '\x06', // message_length
@@ -197,6 +357,33 @@ const char kTestEcmgChannelClose[] = {
'\x00', '\x01' // parameter_value
};
constexpr char kTestChannelErrorResponse[] = {
'\x03', // protocol_version
'\x00', '\x05', // message_type - Channel_error_response
'\x00', '\x0c', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x70', '\x00', // parameter_type - error_status
'\x00', '\x02', // parameter_length
'\x00', '\x00' // parameter_value: actual value varies.
};
constexpr char kTestStreamErrorResponse[] = {
'\x03', // protocol_version
'\x01', '\x06', // message_type - Stream_error_response
'\x00', '\x12', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x70', '\x00', // parameter_type - error_status
'\x00', '\x02', // parameter_length
'\x00', '\x00' // parameter_value: actual value varies.
};
} // namespace cas
} // namespace widevine

View File

@@ -32,8 +32,8 @@ cc_library(
"//common:status",
"//common:string_util",
"//media_cas_packager_sdk/public:wv_cas_types",
"//protos/public:media_cas_encryption_proto",
"//protos/public:media_cas_proto",
"//protos/public:media_cas_cc_proto",
"//protos/public:media_cas_encryption_cc_proto",
],
)
@@ -46,7 +46,7 @@ cc_test(
"//testing:gunit_main",
"//common:status",
"//media_cas_packager_sdk/public:wv_cas_types",
"//protos/public:media_cas_encryption_proto",
"//protos/public:media_cas_encryption_cc_proto",
],
)
@@ -71,7 +71,7 @@ cc_test(
"//testing:gunit_main",
"@abseil_repo//absl/memory",
"//common:aes_cbc_util",
"//protos/public:media_cas_encryption_proto",
"//protos/public:media_cas_encryption_cc_proto",
],
)
@@ -85,19 +85,23 @@ cc_library(
],
deps = [
":ecm",
":ecm_generator",
":fixed_key_fetcher",
":key_fetcher",
":mpeg2ts",
":simulcrypt_util",
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/container:node_hash_map",
"@abseil_repo//absl/memory",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/strings:str_format",
"//common:crypto_util",
"//common:random_util",
"//common:status",
"//example:constants",
"//media_cas_packager_sdk/public:wv_cas_ecm",
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
"//media_cas_packager_sdk/public:wv_cas_types",
],
)
@@ -108,8 +112,12 @@ cc_test(
srcs = ["ecmg_client_handler_test.cc"],
deps = [
":ecmg_client_handler",
":simulcrypt_util",
":util",
"//testing:gunit_main",
"@abseil_repo//absl/memory",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/strings:str_format",
"//example:test_ecmg_messages",
],
)
@@ -127,6 +135,7 @@ cc_library(
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings:str_format",
"//common:status",
"//example:test_emmg_messages",
],
@@ -140,6 +149,7 @@ cc_test(
":emmg",
"//testing:gunit_main",
"@abseil_repo//absl/memory",
"@abseil_repo//absl/strings:str_format",
"//example:test_emmg_messages",
],
)
@@ -155,7 +165,7 @@ cc_library(
deps = [
":key_fetcher",
"//common:status",
"//protos/public:media_cas_encryption_proto",
"//protos/public:media_cas_encryption_cc_proto",
],
)

View File

@@ -37,14 +37,12 @@ static constexpr int kNumBitsCaSystemIdField = 16;
static constexpr int kNumBitsEcmVersionField = 8;
// Byte 3
static constexpr int kNumBitsEcmGenerationCountField = 5;
// Unused bits (mbz, must be zero)
static constexpr int kNumBitsUnusedFieldByte3 = 3;
// Values for Decrypt Mode are from enum CryptoMode
static constexpr int kNumBitsDecryptModeField = 2;
static constexpr int kNumBitsDecryptModeField = 4;
static constexpr int kNumBitsRotationEnabledField = 1;
static constexpr int kMaxGeneration =
(1 << kNumBitsEcmGenerationCountField) - 1;
// Byte 4
// Size of IVs.
// Values for IV size fields are from enum EcmIvSize
@@ -52,8 +50,12 @@ static constexpr int kMaxGeneration =
// always be set to 1.
static constexpr int kNumBitsWrappedKeyIvSizeField = 1;
static constexpr int kNumBitsContentIvSizeField = 1;
static constexpr int kNumBitsAgeRestriction = 5;
// Unused bits (mbz, must be zero)
static constexpr int kNumBitsUnusedField = 6;
static constexpr int kNumBitsUnusedFieldByte4 = 1;
// Total bits equals number of bytes * 8.
static constexpr int kBitSetTotalSize = 40;
// Remaining bytes (starting from the 6th byte) hold entitled key info.
static constexpr size_t kKeyIdSizeBytes = 16;
@@ -71,7 +73,7 @@ static constexpr int kWvCasCaSystemId = 0x4AD4;
// Version - this should be incremented if there are non-backwards compatible
// changes to the ECM.
static constexpr int kEcmVersion = 1;
static constexpr int kEcmVersion = 2;
// Settings for RotationEnabled field.
static constexpr int kRotationDisabled = 0;
@@ -144,10 +146,7 @@ Status Ecm::Initialize(const std::string& content_id,
content_provider_ = content_provider;
paired_keys_required_ = ecm_init_parameters.key_rotation_enabled;
track_types_ = ecm_init_parameters.track_types;
// Generation is incremented before the ECM is generated.
// Initializing to kMaxGeneration ensures the first generated ECM has a gen
// count of zero.
generation_ = kMaxGeneration;
age_restriction_ = ecm_init_parameters.age_restriction;
// Construct and return CasEncryptionRequest message for caller to use.
Status status = CreateEntitlementRequest(key_request_message);
@@ -162,6 +161,43 @@ Status Ecm::Initialize(const std::string& content_id,
return OkStatus();
}
Status Ecm::Initialize(
const EcmInitParameters& ecm_init_parameters,
const std::vector<InjectedEntitlementKeyInfo>& injected_entitlements) {
if (initialized_) {
return {error::INTERNAL, "Already initialized."};
}
if (ecm_init_parameters.track_types.empty()) {
return {error::INVALID_ARGUMENT,
"Parameter track_ids must be set with one or more Track IDs."};
}
if (!ConvertIvSizeParam(ecm_init_parameters.content_iv_size,
&content_iv_size_)) {
return {error::INVALID_ARGUMENT,
"Parameter content_iv_size must be kIvSize8 or kIvSize16."};
}
crypto_mode_ = ecm_init_parameters.crypto_mode;
paired_keys_required_ = ecm_init_parameters.key_rotation_enabled;
track_types_ = ecm_init_parameters.track_types;
age_restriction_ = ecm_init_parameters.age_restriction;
ClearEntitlementKeys();
for (const auto& entitlement : injected_entitlements) {
PushEntitlementKey(entitlement.track_type, entitlement.is_even_key,
{entitlement.key_id, entitlement.key_value});
}
if (!CheckEntitlementKeys()) {
return Status(error::INVALID_ARGUMENT,
"Improper injected entitlement keys.");
}
// Everything is set up including entitlement keys.
initialized_ = true;
return OkStatus();
}
Status Ecm::ProcessCasEncryptionResponse(const std::string& response) {
if (!initialized_) {
return {error::INTERNAL, "Not initialized."};
@@ -173,8 +209,8 @@ Status Ecm::ProcessCasEncryptionResponse(const std::string& response) {
}
Status Ecm::GenerateEcm(EntitledKeyInfo* even_key, EntitledKeyInfo* odd_key,
const std::string& track_type, std::string* serialized_ecm,
uint32_t* generation) {
const std::string& track_type,
std::string* serialized_ecm) const {
if (!initialized_) {
return {error::INTERNAL, "Not initialized."};
}
@@ -190,11 +226,12 @@ Status Ecm::GenerateEcm(EntitledKeyInfo* even_key, EntitledKeyInfo* odd_key,
keys.push_back(even_key);
keys.push_back(odd_key);
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
return GenerateEcmCommon(keys, track_type, serialized_ecm);
}
Status Ecm::GenerateSingleKeyEcm(EntitledKeyInfo* key, const std::string& track_type,
std::string* serialized_ecm, uint32_t* generation) {
Status Ecm::GenerateSingleKeyEcm(EntitledKeyInfo* key,
const std::string& track_type,
std::string* serialized_ecm) const {
if (!initialized_) {
return {error::INTERNAL, "Not initialized."};
}
@@ -209,18 +246,15 @@ Status Ecm::GenerateSingleKeyEcm(EntitledKeyInfo* key, const std::string& track_
std::vector<EntitledKeyInfo*> keys;
keys.push_back(key);
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
return GenerateEcmCommon(keys, track_type, serialized_ecm);
}
Status Ecm::GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
const std::string& track_type, std::string* serialized_ecm,
uint32_t* generation) {
const std::string& track_type,
std::string* serialized_ecm) const {
if (serialized_ecm == nullptr) {
return {error::INVALID_ARGUMENT, "No return ecm std::string pointer."};
}
if (generation == nullptr) {
return {error::INVALID_ARGUMENT, "No return generation pointer."};
}
Status status = ValidateKeys(keys);
if (!status.ok()) {
@@ -240,29 +274,13 @@ Status Ecm::GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
// TODO(user): validate inputs, compare against current values
// TODO(user): replace current values.
// Updates complete, we are complete and consistent.
// Update generation before serializing.
uint32_t previous_generation = generation_;
IncrementGeneration();
// Generate TS packet payload for ECM, pass back to caller.
serialized_ecm->assign(SerializeEcm(keys));
if (kMaxEcmSizeBytes < serialized_ecm->size()) {
generation_ = previous_generation;
serialized_ecm->clear();
return Status(error::INTERNAL, "Maximum size of ECM has been exceeded.");
}
*generation = generation_;
return OkStatus();
}
void Ecm::IncrementGeneration() {
generation_ = (generation_ >= kMaxGeneration) ? 0 : generation_ + 1;
}
Status Ecm::WrapEntitledKeys(const std::string& track_type,
const std::vector<EntitledKeyInfo*> keys) {
const std::vector<EntitledKeyInfo*>& keys) const {
if (!initialized_) {
return {error::INTERNAL, "Not initialized."};
}
@@ -296,8 +314,10 @@ Status Ecm::WrapEntitledKeys(const std::string& track_type,
return OkStatus();
}
Status Ecm::WrapKey(const std::string& wrapping_key, const std::string& wrapping_iv,
const std::string& key_value, std::string* wrapped_key) {
Status Ecm::WrapKey(const std::string& wrapping_key,
const std::string& wrapping_iv,
const std::string& key_value,
std::string* wrapped_key) const {
Status status = ValidateKeyValue(wrapping_key, kWrappingKeySizeBytes);
if (!status.ok()) {
return status;
@@ -319,7 +339,7 @@ Status Ecm::WrapKey(const std::string& wrapping_key, const std::string& wrapping
return OkStatus();
}
Status Ecm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) {
Status Ecm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) const {
for (const auto& key : keys) {
Status status;
status = ValidateKeyId(key->key_id);
@@ -342,7 +362,8 @@ Status Ecm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) {
return OkStatus();
}
Status Ecm::ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys) {
Status Ecm::ValidateWrappedKeys(
const std::vector<EntitledKeyInfo*>& keys) const {
for (const auto& key : keys) {
Status status;
status = ValidateKeyId(key->key_id);
@@ -366,14 +387,15 @@ Status Ecm::ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys) {
return OkStatus();
}
Status Ecm::ValidateKeyId(const std::string& key_id) {
Status Ecm::ValidateKeyId(const std::string& key_id) const {
if (key_id.size() != kKeyIdSizeBytes) {
return {error::INVALID_ARGUMENT, "Key ID must be 16 bytes."};
}
return OkStatus();
}
Status Ecm::ValidateKeyValue(const std::string& key_value, size_t key_value_size) {
Status Ecm::ValidateKeyValue(const std::string& key_value,
size_t key_value_size) const {
if (key_value.size() != key_value_size) {
return Status(
error::INVALID_ARGUMENT,
@@ -382,19 +404,18 @@ Status Ecm::ValidateKeyValue(const std::string& key_value, size_t key_value_size
return OkStatus();
}
Status Ecm::ValidateIv(const std::string& iv, size_t size) {
Status Ecm::ValidateIv(const std::string& iv, size_t size) const {
if (iv.size() != size) {
return {error::INVALID_ARGUMENT, "IV is wrong size."};
}
return OkStatus();
}
std::string Ecm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
std::string Ecm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) const {
// Five bytes (40 bits including padding)
std::bitset<kNumBitsCaSystemIdField> ca_system_id(kWvCasCaSystemId);
std::bitset<kNumBitsEcmVersionField> ecm_version(kEcmVersion);
std::bitset<kNumBitsEcmGenerationCountField> ecm_generation_count(
generation());
std::bitset<kNumBitsUnusedFieldByte3> unused(kUnusedZero);
std::bitset<kNumBitsDecryptModeField> decrypt_mode(
static_cast<int>(crypto_mode()));
std::bitset<kNumBitsRotationEnabledField> rotation_enabled(
@@ -403,21 +424,23 @@ std::string Ecm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
IvSizeFieldValue(kWrappedKeyIvSizeBytes));
std::bitset<kNumBitsContentIvSizeField> content_iv_size(
IvSizeFieldValue(this->content_iv_size()));
std::bitset<kNumBitsUnusedField> padding(kUnusedZero);
std::bitset<kNumBitsAgeRestriction> age_restriction(
static_cast<int>(this->age_restriction()));
std::bitset<kNumBitsUnusedFieldByte4> padding(kUnusedZero);
// Converts bitset to string.
std::string ecm_bitset = absl::StrCat(
ca_system_id.to_string(), ecm_version.to_string(),
ecm_generation_count.to_string(), decrypt_mode.to_string(),
rotation_enabled.to_string(), wrapped_key_iv_size.to_string(),
content_iv_size.to_string(), padding.to_string());
if (ecm_bitset.size() != 40) {
LOG(FATAL) << "ECM bitset incorret size: " << ecm_bitset.size();
ca_system_id.to_string(), ecm_version.to_string(), unused.to_string(),
decrypt_mode.to_string(), rotation_enabled.to_string(),
wrapped_key_iv_size.to_string(), content_iv_size.to_string(),
age_restriction.to_string(), padding.to_string());
if (ecm_bitset.size() != kBitSetTotalSize) {
LOG(FATAL) << "ECM bitset incorrect size: " << ecm_bitset.size();
}
std::string serialized_ecm;
Status status =
string_util::BitsetStringToBinaryString(ecm_bitset, &serialized_ecm);
if (!status.ok()) {
if (!status.ok() || serialized_ecm.empty()) {
LOG(FATAL) << "Failed to convert ECM bitset to std::string";
}
@@ -429,7 +452,7 @@ std::string Ecm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
return serialized_ecm;
}
Status Ecm::CreateEntitlementRequest(std::string* request_string) {
Status Ecm::CreateEntitlementRequest(std::string* request_string) const {
CasEncryptionRequest request;
request.set_content_id(content_id_);
@@ -460,7 +483,7 @@ Status Ecm::ParseEntitlementResponse(const std::string& response_string) {
if (!response.ParseFromString(signed_response.response())) {
return {error::INTERNAL, "Failure parsing signed response."};
}
if (response.status() != CasEncryptionResponse_Status_OK) {
if (response.status() != CasEncryptionResponse::OK) {
return Status(error::INTERNAL, absl::StrCat("Failure reported by server: ",
response.status(), " : ",
response.status_message()));
@@ -507,13 +530,13 @@ Status Ecm::ParseEntitlementResponse(const std::string& response_string) {
continue;
}
if (paired_keys_required()) {
if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_EVEN) {
if (key.key_slot() == CasEncryptionResponse::KeyInfo::EVEN) {
PushEntitlementKey(key.track_type(), /* is_even_key= */ true, ekey);
} else if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_ODD) {
} else if (key.key_slot() == CasEncryptionResponse::KeyInfo::ODD) {
PushEntitlementKey(key.track_type(), /* is_even_key= */ false, ekey);
}
} else {
if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_SINGLE) {
if (key.key_slot() == CasEncryptionResponse::KeyInfo::SINGLE) {
PushEntitlementKey(key.track_type(), /* is_even_key= */ true, ekey);
}
}

View File

@@ -53,11 +53,22 @@ enum EcmIvSize { kIvSize8 = 0, kIvSize16 = 1 };
// A constant of type CryptoMode.
// |track_types| a vector of track ID (std::string) that specify the set of track
// types of interest; controls the entitlement keys returned by the server.
// |age_restriction| minimum age required; the value represents actual age.
struct EcmInitParameters {
EcmIvSize content_iv_size = kIvSize8;
bool key_rotation_enabled = true;
CryptoMode crypto_mode = CryptoMode::kAesCtr;
std::vector<std::string> track_types;
uint8_t age_restriction = 0;
};
// Information needed for the injected entitlement keys. Used for Ecm
// initialization.
struct InjectedEntitlementKeyInfo {
std::string track_type;
bool is_even_key;
std::string key_id; // must be 16 bytes.
std::string key_value; // must be 32 bytes.
};
// Generator for producing Widevine CAS-compliant ECMs. Used to construct the
@@ -83,7 +94,7 @@ class Ecm {
// Args:
// |content_id| uniquely identifies the content (with |content_provider|)
// |content_provider| unique std::string for provider of the content stream.
// |wv_ECM_parameters| encryption-related parameters for configuring
// |ecm_init_parameters| encryption-related parameters for configuring
// the ECM stream.
// |key_request_message| pointer to a std::string to receive a CasEncryptionRequest
// message.
@@ -95,6 +106,16 @@ class Ecm {
const EcmInitParameters& ecm_init_parameters,
std::string* key_request_message);
// Perform initialization for a new ECM stream with injected entitlement keys.
// Args:
// |ecm_init_parameters| encryption-related parameters for configuring
// the ECM stream.
// |injected_entitlement| entitlement key info directly provided. No need to
// fetch keys from server.
virtual Status Initialize(
const EcmInitParameters& ecm_init_parameters,
const std::vector<InjectedEntitlementKeyInfo>& injected_entitlements);
// Parse a CasEncryptionResponse message holding the entitlement keys for
// generating the ECM stream. The entitlement keys are used to encrypt the
// keys conveyed in the ECM. The entitlement key IDs are also part of the ECM.
@@ -110,16 +131,14 @@ class Ecm {
// |odd_key| information for odd key to be encoded into ECM.
// |track_type| the track that the keys are being used to encrypt.
// |serialized_ecm| caller-supplied std::string pointer to receive the ECM.
// |generation| pointer to uint32_t to receive the generation number of the ECM.
// The |even_key| and |odd_key| contents (specifically IV sizes) must be
// consistent with the initialized settings.
// The even_key and odd_key will be wrapped using the appropriate
// entitlement key. Wrapping modifies the original structure.
// Generation is a mod 32 counter. If the ECM has any changes from the
// previous ECM, the generation is increased by one.
virtual Status GenerateEcm(EntitledKeyInfo* even_key,
EntitledKeyInfo* odd_key, const std::string& track_type,
std::string* serialized_ecm, uint32_t* generation);
EntitledKeyInfo* odd_key,
const std::string& track_type,
std::string* serialized_ecm) const;
// Accept a key and IV and construct an ECM that will fit into a Transport
// Stream packet payload (184 bytes). This call is specifically for the case
@@ -128,23 +147,17 @@ class Ecm {
// |key| information for key to be encoded into ECM.
// |track_type| the track that the key is being used to encrypt.
// |serialized_ecm| caller-supplied std::string pointer to receive the ECM.
// |generation| pointer to uint32_t to receive the generation number of the ECM.
// The |key| contents (specifically IV sizes) must be consistent
// with the initialized settings.
// Generation is a mod 32 counter. If the ECM has any changes from the
// previous ECM, the generation is increased by one.
virtual Status GenerateSingleKeyEcm(EntitledKeyInfo* key,
const std::string& track_type,
std::string* serialized_ecm,
uint32_t* generation);
std::string* serialized_ecm) const;
protected: // For unit tests.
// Take the input entitled |keys| and our current state, and generate
// the ECM representing the keys.
virtual std::string SerializeEcm(const std::vector<EntitledKeyInfo*>& keys);
// Increment the ECM's generation count by one (mod kMaxGeneration).
virtual void IncrementGeneration();
virtual std::string SerializeEcm(
const std::vector<EntitledKeyInfo*>& keys) const;
// Apply the Entitlement key with the given track type and polarity
// (even vs. odd key) to wrap the given Entitled key.
@@ -152,8 +165,9 @@ class Ecm {
// |track_type| the track type for the keys. The type_track must match one
// of the |EcmInitParameters::track_types| strings passed into Initialize().
// |key| the Entitled Key to be wrapped.
virtual Status WrapEntitledKeys(const std::string& track_type,
const std::vector<EntitledKeyInfo*> keys);
virtual Status WrapEntitledKeys(
const std::string& track_type,
const std::vector<EntitledKeyInfo*>& keys) const;
private:
// Entitlement key - |key_value| is used to wrap the content key, and |key_id|
@@ -194,36 +208,37 @@ class Ecm {
// Common helper for GenerateEcm() and GenerateSingleKeyEcm()
virtual Status GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
const std::string& track_type,
std::string* serialized_ecm, uint32_t* generation);
std::string* serialized_ecm) const;
// Wrap |key_value| using |wrapping_key| (entitlement key) and |wrapping_iv|.
// Returns the resulting wrapped key in |wrapped_key|.
// Return a status indicating whether there has been any error.
virtual Status WrapKey(const std::string& wrapping_key, const std::string& wrapping_iv,
const std::string& key_value, std::string* wrapped_key);
virtual Status WrapKey(const std::string& wrapping_key,
const std::string& wrapping_iv,
const std::string& key_value,
std::string* wrapped_key) const;
virtual Status ValidateKeys(const std::vector<EntitledKeyInfo*>& keys);
virtual Status ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys);
virtual Status ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) const;
virtual Status ValidateWrappedKeys(
const std::vector<EntitledKeyInfo*>& keys) const;
Status ValidateKeyId(const std::string& key_id);
Status ValidateKeyValue(const std::string& key_value, size_t key_value_size);
Status ValidateIv(const std::string& iv, size_t size);
Status ValidateKeyId(const std::string& key_id) const;
Status ValidateKeyValue(const std::string& key_value,
size_t key_value_size) const;
Status ValidateIv(const std::string& iv, size_t size) const;
// TODO(user): need unit tests for CreateEntitlementRequest.
virtual Status CreateEntitlementRequest(std::string* request_string);
virtual Status CreateEntitlementRequest(std::string* request_string) const;
// TODO(user): need unit tests for ParseEntitlementResponse.
virtual Status ParseEntitlementResponse(const std::string& response_string);
virtual uint32_t generation() const { return generation_; }
virtual CryptoMode crypto_mode() const { return crypto_mode_; }
virtual uint8_t age_restriction() const { return age_restriction_; }
virtual bool paired_keys_required() const { return paired_keys_required_; }
virtual size_t content_iv_size() const { return content_iv_size_; }
// Set to true when the object has been properly initialized.
bool initialized_ = false;
// Current generation. This will be incremented (mod 32) each time a new
// ECM has changes from the previous ECM.
uint32_t generation_ = 0;
// Content ID for this ECM stream.
std::string content_id_;
// Provider ID for this ECM stream.
@@ -235,6 +250,7 @@ class Ecm {
// Remember if a pair of keys is required (for key rotation).
bool paired_keys_required_ = false;
CryptoMode crypto_mode_ = CryptoMode::kAesCtr;
uint8_t age_restriction_ = 0;
// Entitlement keys needed for ECM generation.
// The keys are added when the CasEncryptionResponse is processed.
std::map<std::string, std::list<EntitlementKeyInfo>> entitlement_keys_;

View File

@@ -26,15 +26,11 @@ std::string EcmGenerator::GenerateEcm(const EcmParameters& params) {
return "";
}
std::string serialized_ecm;
uint32_t generation;
// TODO(user): need track_type
std::string track_type = "SD";
std::string track_type = params.track_type;
if (params.rotation_enabled) {
status = ecm_->GenerateEcm(&keys[0], &keys[1], track_type, &serialized_ecm,
&generation);
status = ecm_->GenerateEcm(&keys[0], &keys[1], track_type, &serialized_ecm);
} else {
status = ecm_->GenerateSingleKeyEcm(&keys[0], track_type, &serialized_ecm,
&generation);
status = ecm_->GenerateSingleKeyEcm(&keys[0], track_type, &serialized_ecm);
}
if (!status.ok()) {
LOG(ERROR) << " Generate ECM call failed: " << status;
@@ -55,6 +51,11 @@ Status EcmGenerator::ProcessEcmParameters(const EcmParameters& ecm_params,
return {error::INVALID_ARGUMENT,
"Number of supplied keys is wrong (check rotation periods)."};
}
// If content_iv_size_ is zero, use the size of first content IV as the
// expected size for all future IVs in this stream.
if (content_iv_size_ == 0) {
content_iv_size_ = ecm_params.key_params[0].content_iv.size();
}
for (int i = 0; i < keys_needed; i++) {
Status status = ValidateKeyParameters(ecm_params.key_params[i]);
if (!status.ok()) {
@@ -65,7 +66,7 @@ Status EcmGenerator::ProcessEcmParameters(const EcmParameters& ecm_params,
key.key_id = ecm_params.key_params[i].key_id;
key.key_value = ecm_params.key_params[i].key_data;
key.wrapped_key_iv = ecm_params.key_params[i].wrapped_key_iv;
key.content_iv = ecm_params.key_params[i].content_ivs[0];
key.content_iv = ecm_params.key_params[i].content_iv;
}
current_key_index_ = 0;
current_key_even_ = true;
@@ -73,7 +74,7 @@ Status EcmGenerator::ProcessEcmParameters(const EcmParameters& ecm_params,
return OkStatus();
}
Status EcmGenerator::ValidateKeyId(const std::string& id) {
Status EcmGenerator::ValidateKeyId(const std::string& id) const {
if (id.empty()) {
return {error::INVALID_ARGUMENT, "Key id is empty."};
}
@@ -83,7 +84,7 @@ Status EcmGenerator::ValidateKeyId(const std::string& id) {
return OkStatus();
}
Status EcmGenerator::ValidateKeyData(const std::string& key_data) {
Status EcmGenerator::ValidateKeyData(const std::string& key_data) const {
if (key_data.empty()) {
return {error::INVALID_ARGUMENT, "Key data is empty."};
}
@@ -93,7 +94,8 @@ Status EcmGenerator::ValidateKeyData(const std::string& key_data) {
return OkStatus();
}
Status EcmGenerator::ValidateIv(const std::string& iv, size_t required_size) {
Status EcmGenerator::ValidateIv(const std::string& iv,
size_t required_size) const {
if (iv.empty()) {
return {error::INVALID_ARGUMENT, "IV is empty."};
}
@@ -107,7 +109,7 @@ Status EcmGenerator::ValidateIv(const std::string& iv, size_t required_size) {
return OkStatus();
}
Status EcmGenerator::ValidateWrappedKeyIv(const std::string& iv) {
Status EcmGenerator::ValidateWrappedKeyIv(const std::string& iv) const {
// All wrapped key IVs must be 16 bytes.
Status status = ValidateIv(iv, kIvSize16);
if (!status.ok()) {
@@ -116,12 +118,7 @@ Status EcmGenerator::ValidateWrappedKeyIv(const std::string& iv) {
return status;
}
Status EcmGenerator::ValidateContentIv(const std::string& iv) {
// If content_iv_size_ is zero, use this IV as the size for all future IVs in
// this stream.
if (content_iv_size_ == 0) {
content_iv_size_ = iv.size();
}
Status EcmGenerator::ValidateContentIv(const std::string& iv) const {
Status status = ValidateIv(iv, content_iv_size_);
if (!status.ok()) {
LOG(ERROR) << " Content IV is not valid: " << status;
@@ -129,18 +126,12 @@ Status EcmGenerator::ValidateContentIv(const std::string& iv) {
return status;
}
Status EcmGenerator::ValidateKeyParameters(const KeyParameters& key_params) {
Status status;
status = ValidateKeyId(key_params.key_id);
Status EcmGenerator::ValidateKeyParameters(
const KeyParameters& key_params) const {
Status status = ValidateKeyId(key_params.key_id);
if (!status.ok()) return status;
if (key_params.content_ivs.empty()) {
return {error::INVALID_ARGUMENT, "Content IVs is empty."};
}
for (int i = 0; i < key_params.content_ivs.size(); i++) {
status = ValidateContentIv(key_params.content_ivs[i]);
if (!status.ok()) return status;
}
return OkStatus();
return ValidateContentIv(key_params.content_iv);
}
} // namespace cas

View File

@@ -10,6 +10,7 @@
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECM_GENERATOR_H_
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
@@ -27,14 +28,8 @@ namespace cas {
struct KeyParameters {
std::string key_id;
std::string key_data;
// TODO(user): wrapped_key_data does not seem to be used, but assumed
// to exist by unit tests.
std::string wrapped_key_data;
// wrapped_key_iv is randomly generated right before it is used to encrypt
// key_data in ecm.cc.
std::string wrapped_key_iv;
// TODO(user): Probably only need a single content_iv instead of a vector.
std::vector<std::string> content_ivs;
std::string content_iv;
};
// EcmParameters holds information that is needed by the EcmGenerator. It is
@@ -48,6 +43,7 @@ struct EcmParameters {
// TODO(user): rotation_periods does not seem to be used, but assumed
// to exist by unit tests.
uint32_t rotation_periods;
std::string track_type;
// TODO(user): Consider changing the vector to just two variables,
// one for even key, the other for odd key.
std::vector<KeyParameters> key_params;
@@ -64,8 +60,8 @@ class EcmGenerator {
virtual std::string GenerateEcm(const EcmParameters& params);
// Query the state of this ECM Generator
bool initialized() { return initialized_; }
bool rotation_enabled() { return rotation_enabled_; }
bool initialized() const { return initialized_; }
bool rotation_enabled() const { return rotation_enabled_; }
void set_ecm(std::unique_ptr<Ecm> ecm) { ecm_ = std::move(ecm); }
@@ -75,16 +71,14 @@ class EcmGenerator {
Status ProcessEcmParameters(const EcmParameters& ecm_params,
std::vector<EntitledKeyInfo>* keys);
Status ProcessEcmParameters(const EcmParameters& ecm_params);
Status ValidateKeyId(const std::string& id);
Status ValidateKeyData(const std::string& key_data);
Status ValidateWrappedKeyIv(const std::string& iv);
Status ValidateIv(const std::string& iv, size_t required_size);
Status ValidateContentIv(const std::string& iv);
Status ValidateKeyParameters(const KeyParameters& key_params);
Status ValidateKeyId(const std::string& id) const;
Status ValidateKeyData(const std::string& key_data) const;
Status ValidateWrappedKeyIv(const std::string& iv) const;
Status ValidateIv(const std::string& iv, size_t required_size) const;
Status ValidateContentIv(const std::string& iv) const;
Status ValidateKeyParameters(const KeyParameters& key_params) const;
bool initialized_ = false;
uint32_t generation_ = 0;
bool rotation_enabled_ = false;
uint32_t current_key_index_ = 0;
bool current_key_even_ = true;

View File

@@ -34,19 +34,16 @@ constexpr char kEntitlementKeyDouble[] = "testEKIdabcdefgh";
constexpr char kEcmKeyIdSingle[] = "key-id-One123456";
constexpr char kEcmKeyDataSingle[] = "0123456701234567";
constexpr char kEcmWrappedKeySingle[] = "1234567890123456";
constexpr char kEcmWrappedKeyIvSingle[] = "abcdefghacbdefgh";
constexpr char kEcmContentIvSingle[] = "ABCDEFGH";
constexpr char kEcmKeyIdEven[] = "key-Id-One123456";
constexpr char kEcmKeyDataEven[] = "KKEEYYDDAATTAAee";
constexpr char kEcmWrappedKeyEven[] = "1234567890123456";
constexpr char kEcmWrappedKeyIvEven[] = "abcdefghacbdefgh";
constexpr char kEcmContentIvEven[] = "ABCDEFGH";
constexpr char kEcmKeyIdOdd[] = "key-Id-Two456789";
constexpr char kEcmKeyDataOdd[] = "kkeeyyddaattaaOO";
constexpr char kEcmWrappedKeyOdd[] = "9876543210654321";
constexpr char kEcmWrappedKeyIvOdd[] = "a1c2e3g4a1c2e3g4";
constexpr char kEcmContentIvOdd[] = "AaCbEcGd";
@@ -54,13 +51,15 @@ constexpr char kFakeCasEncryptionResponseKeyId[] = "fake_key_id.....";
constexpr char kFakeCasEncryptionResponseKeyData[] =
"fakefakefakefakefakefakefakefake";
constexpr char kTrackType[] = "SD";
Status HandleCasEncryptionRequest(const std::string& request_string,
std::string* signed_response_string) {
CasEncryptionRequest request;
request.ParseFromString(request_string);
CasEncryptionResponse response;
response.set_status(CasEncryptionResponse_Status_OK);
response.set_status(CasEncryptionResponse::OK);
response.set_content_id(request.content_id());
for (const auto& track_type : request.track_types()) {
if (request.key_rotation()) {
@@ -69,19 +68,19 @@ Status HandleCasEncryptionRequest(const std::string& request_string,
key->set_key_id(kFakeCasEncryptionResponseKeyId);
key->set_key(kFakeCasEncryptionResponseKeyData);
key->set_track_type(track_type);
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_EVEN);
key->set_key_slot(CasEncryptionResponse::KeyInfo::EVEN);
// Add the Odd key.
key = response.add_entitlement_keys();
key->set_key_id(kFakeCasEncryptionResponseKeyId);
key->set_key(kFakeCasEncryptionResponseKeyData);
key->set_track_type(track_type);
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_ODD);
key->set_key_slot(CasEncryptionResponse::KeyInfo::ODD);
} else {
auto key = response.add_entitlement_keys();
key->set_key_id(kFakeCasEncryptionResponseKeyId);
key->set_key(kFakeCasEncryptionResponseKeyData);
key->set_track_type(track_type);
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_SINGLE);
key->set_key_slot(CasEncryptionResponse::KeyInfo::SINGLE);
}
}
std::string response_string;
@@ -96,8 +95,7 @@ Status HandleCasEncryptionRequest(const std::string& request_string,
class EcmGeneratorTest : public testing::Test {
protected:
void SetUp() override {
}
void SetUp() override {}
Status ProcessEcmParameters(const EcmParameters& params,
std::vector<EntitledKeyInfo>* keys) {
@@ -112,6 +110,7 @@ class EcmGeneratorTest : public testing::Test {
params->entitlement_key_id = kEntitlementKeySingle;
params->rotation_enabled = false;
params->key_params.push_back(kKeyParamsSingle);
params->track_type = kTrackType;
}
void SetTestConfig2(EcmParameters* params) {
@@ -122,6 +121,7 @@ class EcmGeneratorTest : public testing::Test {
params->rotation_periods = 2;
params->key_params.push_back(kKeyParamsEven);
params->key_params.push_back(kKeyParamsOdd);
params->track_type = kTrackType;
}
// Call this to setup the guts (Ecm) of the ECM Generator.
@@ -130,7 +130,7 @@ class EcmGeneratorTest : public testing::Test {
std::string entitlement_request;
std::string entitlement_response;
ecm_init_params_.key_rotation_enabled = key_rotation_enabled;
ecm_init_params_.track_types.push_back("SD");
ecm_init_params_.track_types.push_back(kTrackType);
ASSERT_OK(ecm_->Initialize(kContentId, kProvider, ecm_init_params_,
&entitlement_request));
ASSERT_OK(
@@ -141,17 +141,14 @@ class EcmGeneratorTest : public testing::Test {
const KeyParameters kKeyParamsSingle{kEcmKeyIdSingle,
kEcmKeyDataSingle,
kEcmWrappedKeySingle,
kEcmWrappedKeyIvSingle,
{kEcmContentIvSingle}};
const KeyParameters kKeyParamsEven{kEcmKeyIdEven,
kEcmKeyDataEven,
kEcmWrappedKeyEven,
kEcmWrappedKeyIvEven,
{kEcmContentIvEven}};
const KeyParameters kKeyParamsOdd{kEcmKeyIdOdd,
kEcmKeyDataOdd,
kEcmWrappedKeyOdd,
kEcmWrappedKeyIvOdd,
{kEcmContentIvOdd}};
@@ -189,7 +186,7 @@ TEST_F(EcmGeneratorTest, GenerateNoRotation) {
// Expected size (bytes):
// CA system ID: 2 bytes
// version: 1 byte
// generation + flags: 1 byte
// flags: 1 byte
// flags: 1 byte
// entitlement key ID: 16 bytes
// Single key: ID (16), Data (16), IV (16), IV (8) = 56
@@ -197,12 +194,13 @@ TEST_F(EcmGeneratorTest, GenerateNoRotation) {
ASSERT_EQ(77, ecm_string.size());
EXPECT_EQ('\x4A', ecm_string[0]); // CA System ID first byte.
EXPECT_EQ('\xD4', ecm_string[1]); // CA System ID second byte.
EXPECT_EQ('\x01', ecm_string[2]); // ECM version
EXPECT_EQ('\x02', ecm_string[3]); // generation + flags
EXPECT_EQ('\x02', ecm_string[2]); // ECM version
EXPECT_EQ('\x02', ecm_string[3]); // flags
EXPECT_EQ('\x80', ecm_string[4]); // flags
EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(5, 16));
EXPECT_EQ(kEcmKeyIdSingle, ecm_string.substr(21, 16));
EXPECT_NE(kEcmWrappedKeySingle, ecm_string.substr(37, 16));
// Key data has been wrapped (encrypted).
EXPECT_NE(kEcmKeyDataSingle, ecm_string.substr(37, 16));
EXPECT_EQ(kEcmWrappedKeyIvSingle, ecm_string.substr(53, 16));
// Unwrap key and compare with original.
std::string wrapping_key = kFakeCasEncryptionResponseKeyData;
@@ -223,16 +221,16 @@ TEST_F(EcmGeneratorTest, Generate2NoRotation) {
std::string ecm_string = ecm_gen_.GenerateEcm(ecm_params);
ecm_string = ecm_gen_.GenerateEcm(ecm_params);
// Second generate should have higher generation number.
ASSERT_EQ(77, ecm_string.size());
EXPECT_EQ('\x4A', ecm_string[0]); // CA System ID first byte.
EXPECT_EQ('\xD4', ecm_string[1]); // CA System ID second byte.
EXPECT_EQ('\x01', ecm_string[2]); // ECM version
EXPECT_EQ('\x0A', ecm_string[3]); // generation + flags
EXPECT_EQ('\x02', ecm_string[2]); // ECM version
EXPECT_EQ('\x02', ecm_string[3]); // flags
EXPECT_EQ('\x80', ecm_string[4]); // flags
EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(5, 16));
EXPECT_EQ(kEcmKeyIdSingle, ecm_string.substr(21, 16));
EXPECT_NE(kEcmWrappedKeySingle, ecm_string.substr(37, 16));
// Key data has been wrapped (encrypted).
EXPECT_NE(kEcmKeyDataSingle, ecm_string.substr(37, 16));
EXPECT_EQ(kEcmWrappedKeyIvSingle, ecm_string.substr(53, 16));
// Unwrap key and compare with original.
std::string wrapping_key = kFakeCasEncryptionResponseKeyData;
@@ -278,17 +276,19 @@ TEST_F(EcmGeneratorTest, GenerateSimpleRotation) {
ASSERT_EQ(149, ecm_string.size());
EXPECT_EQ('\x4A', ecm_string[0]); // CA System ID first byte.
EXPECT_EQ('\xD4', ecm_string[1]); // CA System ID second byte.
EXPECT_EQ('\x01', ecm_string[2]); // ECM version
EXPECT_EQ('\x03', ecm_string[3]); // generation + flags
EXPECT_EQ('\x02', ecm_string[2]); // ECM version
EXPECT_EQ('\x03', ecm_string[3]); // flags
EXPECT_EQ('\x80', ecm_string[4]); // flags
EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(5, 16));
EXPECT_EQ(kEcmKeyIdEven, ecm_string.substr(21, 16));
EXPECT_NE(kEcmWrappedKeyEven, ecm_string.substr(37, 16));
// Key data has been wrapped (encrypted).
EXPECT_NE(kEcmKeyDataEven, ecm_string.substr(37, 16));
EXPECT_EQ(kEcmWrappedKeyIvEven, ecm_string.substr(53, 16));
EXPECT_EQ(kEcmContentIvEven, ecm_string.substr(69, 8));
EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(77, 16));
EXPECT_EQ(kEcmKeyIdOdd, ecm_string.substr(93, 16));
EXPECT_NE(kEcmWrappedKeyOdd, ecm_string.substr(109, 16));
// Key data has been wrapped (encrypted).
EXPECT_NE(kEcmKeyDataOdd, ecm_string.substr(109, 16));
EXPECT_EQ(kEcmWrappedKeyIvOdd, ecm_string.substr(125, 16));
EXPECT_EQ(kEcmContentIvOdd, ecm_string.substr(141, 8));
// Unwrap even key and compare with original.

Some files were not shown because too many files have changed in this diff Show More