Refactor and cleanup codes. No functional changes.
This commit is contained in:
562
common/BUILD
562
common/BUILD
@@ -1,16 +1,209 @@
|
||||
################################################################################
|
||||
# Copyright 2016 Google Inc.
|
||||
# Copyright 2017 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
#
|
||||
# Description:
|
||||
# Build file for code common to multiple Widevine services.
|
||||
# Constants, data structures, util classes for Widevine libraries.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
package(
|
||||
default_visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "binary_release_files",
|
||||
srcs = [
|
||||
"certificate_type.h",
|
||||
"status.h",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "widevine_system_id",
|
||||
srcs = ["widevine_system_id.cc"],
|
||||
hdrs = ["widevine_system_id.h"],
|
||||
deps = ["//base"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "certificate_type",
|
||||
hdrs = ["certificate_type.h"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "status",
|
||||
srcs = ["status.cc"],
|
||||
hdrs = ["status.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//util:error_space",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "status_test",
|
||||
srcs = ["status_test.cc"],
|
||||
deps = [
|
||||
":status",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "client_cert",
|
||||
srcs = ["client_cert.cc"],
|
||||
hdrs = ["client_cert.h"],
|
||||
deps = [
|
||||
":crypto_util",
|
||||
":drm_root_certificate",
|
||||
":error_space",
|
||||
":random_util",
|
||||
":rsa_key",
|
||||
":sha_util",
|
||||
":signing_key_util",
|
||||
":status",
|
||||
":wvm_token_handler",
|
||||
"//base",
|
||||
"//strings",
|
||||
"@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",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "client_cert_test",
|
||||
srcs = ["client_cert_test.cc"],
|
||||
deps = [
|
||||
":client_cert",
|
||||
":drm_root_certificate",
|
||||
":error_space",
|
||||
":sha_util",
|
||||
":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",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "device_status_list",
|
||||
srcs = ["device_status_list.cc"],
|
||||
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",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "device_status_list_test",
|
||||
timeout = "short",
|
||||
srcs = ["device_status_list_test.cc"],
|
||||
deps = [
|
||||
":client_cert",
|
||||
":device_status_list",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:rsa_key",
|
||||
"//common:rsa_test_keys",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:provisioned_device_info_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "drm_root_certificate",
|
||||
srcs = ["drm_root_certificate.cc"],
|
||||
hdrs = ["drm_root_certificate.h"],
|
||||
deps = [
|
||||
":certificate_type",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":sha_util",
|
||||
":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",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "drm_root_certificate_test",
|
||||
timeout = "short",
|
||||
srcs = ["drm_root_certificate_test.cc"],
|
||||
deps = [
|
||||
":drm_root_certificate",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
":test_drm_certificates",
|
||||
"//base",
|
||||
"@protobuf_repo//:protobuf",
|
||||
"//testing:gunit_main",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "client_id_util",
|
||||
srcs = ["client_id_util.cc"],
|
||||
hdrs = ["client_id_util.h"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":drm_service_certificate",
|
||||
":error_space",
|
||||
":status",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:errors_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "rsa_util",
|
||||
@@ -30,8 +223,10 @@ cc_test(
|
||||
deps = [
|
||||
":rsa_test_keys",
|
||||
":rsa_util",
|
||||
"//external:gtest",
|
||||
"//external:gtest_main",
|
||||
"//base",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -63,8 +258,9 @@ cc_test(
|
||||
deps = [
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
"//external:gtest",
|
||||
"//external:gtest_main",
|
||||
":rsa_util",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -84,7 +280,7 @@ cc_library(
|
||||
hdrs = ["mock_rsa_key.h"],
|
||||
deps = [
|
||||
":rsa_key",
|
||||
"//external:gtest",
|
||||
"//testing:gunit",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -104,7 +300,75 @@ cc_test(
|
||||
srcs = ["aes_cbc_util_test.cc"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
"//external:gtest_main",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "crypto_util",
|
||||
srcs = ["crypto_util.cc"],
|
||||
hdrs = ["crypto_util.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//external:openssl",
|
||||
"//util/endian",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "crypto_util_test",
|
||||
size = "medium",
|
||||
srcs = ["crypto_util_test.cc"],
|
||||
deps = [
|
||||
":crypto_util",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "ecb_util",
|
||||
srcs = ["ecb_util.cc"],
|
||||
hdrs = ["ecb_util.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ecb_util_test",
|
||||
size = "small",
|
||||
srcs = ["ecb_util_test.cc"],
|
||||
deps = [
|
||||
":ecb_util",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "file_util",
|
||||
srcs = ["file_util.cc"],
|
||||
hdrs = ["file_util.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "file_util_test",
|
||||
srcs = ["file_util_test.cc"],
|
||||
deps = [
|
||||
":file_util",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -123,7 +387,7 @@ cc_test(
|
||||
srcs = ["random_util_test.cc"],
|
||||
deps = [
|
||||
":random_util",
|
||||
"//external:gtest_main",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -142,24 +406,282 @@ cc_test(
|
||||
srcs = ["sha_util_test.cc"],
|
||||
deps = [
|
||||
":sha_util",
|
||||
"//external:gtest_main",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "file_util",
|
||||
srcs = ["file_util.cc"],
|
||||
hdrs = ["file_util.h"],
|
||||
name = "signature_util",
|
||||
srcs = ["signature_util.cc"],
|
||||
hdrs = ["signature_util.h"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":rsa_key",
|
||||
":sha_util",
|
||||
":status",
|
||||
"//base",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "signing_key_util",
|
||||
srcs = ["signing_key_util.cc"],
|
||||
hdrs = ["signing_key_util.h"],
|
||||
deps = [
|
||||
":crypto_util",
|
||||
"//base",
|
||||
"//protos/public:license_protocol_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "signing_key_util_test",
|
||||
size = "small",
|
||||
srcs = ["signing_key_util_test.cc"],
|
||||
deps = [
|
||||
":crypto_util",
|
||||
":signing_key_util",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:license_protocol_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "test_drm_certificates",
|
||||
testonly = 1,
|
||||
srcs = ["test_drm_certificates.cc"],
|
||||
hdrs = ["test_drm_certificates.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "wvm_token_handler",
|
||||
srcs = ["wvm_token_handler.cc"],
|
||||
hdrs = ["wvm_token_handler.h"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":ecb_util",
|
||||
":sha_util",
|
||||
":status",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//util/endian",
|
||||
"//util/gtl:map_util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "wvm_token_handler_test",
|
||||
size = "small",
|
||||
srcs = ["wvm_token_handler_test.cc"],
|
||||
deps = [
|
||||
":wvm_test_keys",
|
||||
":wvm_token_handler",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "wvm_test_keys",
|
||||
testonly = 1,
|
||||
srcs = ["wvm_test_keys.cc"],
|
||||
hdrs = ["wvm_test_keys.h"],
|
||||
deps = [
|
||||
":wvm_token_handler",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "error_space",
|
||||
srcs = ["error_space.cc"],
|
||||
hdrs = ["error_space.h"],
|
||||
deps = [
|
||||
"//util:error_space",
|
||||
"//util:proto_status",
|
||||
"//protos/public:errors_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "remote_attestation_verifier",
|
||||
srcs = ["remote_attestation_verifier.cc"],
|
||||
hdrs = ["remote_attestation_verifier.h"],
|
||||
deps = [
|
||||
":client_id_util",
|
||||
":drm_service_certificate",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":status",
|
||||
":x509_cert",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:remote_attestation_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "drm_service_certificate",
|
||||
srcs = ["drm_service_certificate.cc"],
|
||||
hdrs = ["drm_service_certificate.h"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":certificate_type",
|
||||
":drm_root_certificate",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":rsa_util",
|
||||
":status",
|
||||
"//base",
|
||||
"@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",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "drm_service_certificate_test",
|
||||
timeout = "short",
|
||||
srcs = ["drm_service_certificate_test.cc"],
|
||||
deps = [
|
||||
":aes_cbc_util",
|
||||
":drm_root_certificate",
|
||||
":drm_service_certificate",
|
||||
":rsa_key",
|
||||
":rsa_test_keys",
|
||||
":rsa_util",
|
||||
":test_drm_certificates",
|
||||
"//base",
|
||||
"@protobuf_repo//:protobuf",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:license_server_sdk_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "verified_media_pipeline",
|
||||
srcs = ["verified_media_pipeline.cc"],
|
||||
hdrs = ["verified_media_pipeline.h"],
|
||||
deps = [
|
||||
":status",
|
||||
":vmp_checker",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:license_protocol_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "x509_cert",
|
||||
srcs = ["x509_cert.cc"],
|
||||
hdrs = ["x509_cert.h"],
|
||||
deps = [
|
||||
":error_space",
|
||||
":openssl_util",
|
||||
":rsa_key",
|
||||
":status",
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "test_utils",
|
||||
testonly = 1,
|
||||
srcs = ["test_utils.cc"],
|
||||
hdrs = ["test_utils.h"],
|
||||
deps = [
|
||||
":status",
|
||||
"//base",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "x509_cert_test",
|
||||
timeout = "short",
|
||||
srcs = ["x509_cert_test.cc"],
|
||||
deps = [
|
||||
":rsa_key",
|
||||
":test_utils",
|
||||
":x509_cert",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "vmp_checker",
|
||||
srcs = ["vmp_checker.cc"],
|
||||
hdrs = ["vmp_checker.h"],
|
||||
deps = [
|
||||
":certificate_type",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":status",
|
||||
":x509_cert",
|
||||
"//base",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:verified_media_pipeline_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "vmp_checker_test",
|
||||
timeout = "short",
|
||||
srcs = ["vmp_checker_test.cc"],
|
||||
deps = [
|
||||
":rsa_key",
|
||||
":vmp_checker",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:verified_media_pipeline_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "string_util",
|
||||
srcs = ["string_util.cc"],
|
||||
hdrs = ["string_util.h"],
|
||||
deps = [
|
||||
":status",
|
||||
"//base",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "file_util_test",
|
||||
srcs = ["file_util_test.cc"],
|
||||
name = "string_util_test",
|
||||
srcs = ["string_util_test.cc"],
|
||||
deps = [
|
||||
":file_util",
|
||||
"//external:gtest_main",
|
||||
":string_util",
|
||||
"//base",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -20,23 +20,35 @@ namespace crypto_util {
|
||||
// Encrypts the provided plantext std::string using AES-CBC encryption.
|
||||
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
||||
const std::string& plaintext) {
|
||||
if (iv.size() != AES_BLOCK_SIZE) return "";
|
||||
|
||||
const size_t num_padding_bytes =
|
||||
AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE);
|
||||
std::string padded_text = plaintext;
|
||||
padded_text.append(num_padding_bytes, static_cast<char>(num_padding_bytes));
|
||||
return EncryptAesCbcNoPad(key, iv, padded_text);
|
||||
}
|
||||
|
||||
std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||
const std::string& plaintext) {
|
||||
if (iv.size() != AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
|
||||
return std::string();
|
||||
}
|
||||
if (plaintext.size() % AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "Invalid AEC-CBC plaintext size: " << plaintext.size();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
AES_KEY aes_key;
|
||||
if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||
key.size() * 8, &aes_key) != 0) {
|
||||
return "";
|
||||
LOG(WARNING) << "Invalid AES key.";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string encrypted(padded_text);
|
||||
std::string encrypted(plaintext.size(), 0);
|
||||
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
|
||||
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(padded_text.data()),
|
||||
reinterpret_cast<uint8_t*>(&encrypted[0]), padded_text.size(),
|
||||
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(plaintext.data()),
|
||||
reinterpret_cast<uint8_t*>(&encrypted[0]), plaintext.size(),
|
||||
&aes_key, &local_iv[0], AES_ENCRYPT);
|
||||
return encrypted;
|
||||
}
|
||||
@@ -45,26 +57,43 @@ std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
||||
// the plaintext on success.
|
||||
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
||||
const std::string& ciphertext) {
|
||||
if (ciphertext.empty()) return "";
|
||||
if (iv.size() != AES_BLOCK_SIZE) return "";
|
||||
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) return "";
|
||||
if (ciphertext.empty()) {
|
||||
LOG(WARNING) << "Empty ciphertext.";
|
||||
return std::string();
|
||||
}
|
||||
if (iv.size() != AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
|
||||
return std::string();
|
||||
}
|
||||
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) {
|
||||
LOG(WARNING) << "Ciphertext not a multiple of AES block size: "
|
||||
<< ciphertext.size();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
AES_KEY aes_key;
|
||||
if (AES_set_decrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||
key.size() * 8, &aes_key) != 0) {
|
||||
return "";
|
||||
LOG(WARNING) << "Invalid AES key.";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string cleartext(ciphertext);
|
||||
std::string cleartext(ciphertext.size(), 0);
|
||||
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
|
||||
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(ciphertext.data()),
|
||||
reinterpret_cast<uint8_t*>(&cleartext[0]), ciphertext.size(),
|
||||
&aes_key, &local_iv[0], AES_DECRYPT);
|
||||
|
||||
const uint8_t num_padding_bytes = cleartext[cleartext.size() - 1];
|
||||
if (num_padding_bytes > AES_BLOCK_SIZE) return "";
|
||||
if (num_padding_bytes > AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "AES padding too long: " << num_padding_bytes;
|
||||
return std::string();
|
||||
}
|
||||
for (uint8_t i = 0; i < num_padding_bytes; ++i) {
|
||||
if (cleartext[cleartext.size() - 1 - i] != num_padding_bytes) return "";
|
||||
if (cleartext[cleartext.size() - 1 - i] != num_padding_bytes) {
|
||||
LOG(WARNING) << "Padding verification failure.";
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
cleartext.resize(cleartext.size() - num_padding_bytes);
|
||||
return cleartext;
|
||||
@@ -73,17 +102,26 @@ std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
||||
std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||
const std::string& ciphertext) {
|
||||
std::vector<uint8_t> local_iv(iv.begin(), iv.end());
|
||||
if (local_iv.empty()) local_iv.resize(AES_BLOCK_SIZE, '\0');
|
||||
else if (local_iv.size() != AES_BLOCK_SIZE) return "";
|
||||
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) return "";
|
||||
if (local_iv.empty()) {
|
||||
local_iv.resize(AES_BLOCK_SIZE, '\0');
|
||||
} else if (local_iv.size() != AES_BLOCK_SIZE) {
|
||||
LOG(WARNING) << "Invalid CBC IV size: " << iv.size();
|
||||
return std::string();
|
||||
}
|
||||
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) {
|
||||
LOG(WARNING) << "Ciphertext not a multiple of AES block size: "
|
||||
<< ciphertext.size();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
AES_KEY aes_key;
|
||||
if (AES_set_decrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||
key.size() * 8, &aes_key) != 0) {
|
||||
return "";
|
||||
LOG(WARNING) << "Invalid AES key.";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string cleartext(ciphertext);
|
||||
std::string cleartext(ciphertext.size(), 0);
|
||||
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(ciphertext.data()),
|
||||
reinterpret_cast<uint8_t*>(&cleartext[0]), ciphertext.size(),
|
||||
&aes_key, &local_iv[0], AES_DECRYPT);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -14,12 +14,18 @@
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
// Helper for wrapping AES CBC encryption. Uses PKCS5 padding.
|
||||
// Helper for wrapping AES CBC encryption. Uses PKCS7 padding.
|
||||
std::string EncryptAesCbc(const std::string& key, const std::string& iv,
|
||||
const std::string& plaintext);
|
||||
|
||||
// Helper for wrapping AES CBC encryption. Adds no padding, so the input
|
||||
// must be an multiple of the 16-byte AES block size. Returns empty std::string
|
||||
// on error.
|
||||
std::string EncryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||
const std::string& plaintext);
|
||||
|
||||
// Helper for common Keybox decrypt operations; wraps AES-CBC. Returns an
|
||||
// empty std::string on error or the plaintext on success. Uses PKCS5 padding.
|
||||
// empty std::string on error or the plaintext on success. Expects PKCS7 padding.
|
||||
std::string DecryptAesCbc(const std::string& key, const std::string& iv,
|
||||
const std::string& ciphertext);
|
||||
|
||||
@@ -35,4 +41,3 @@ std::string DecryptAesCbcNoPad(const std::string& key, const std::string& iv,
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_AES_CBC_UTIL_H_
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -7,9 +7,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/aes_cbc_util.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -97,6 +96,31 @@ TEST(CryptoUtilTest, TestFailedEncrypt) {
|
||||
ASSERT_EQ(ciphertext.size(), 0);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestFailedEncryptNoPad) {
|
||||
std::string plaintext("0123456789abcdef");
|
||||
std::string key(kKey, kKey + sizeof(kKey));
|
||||
std::string iv(kIv, kIv + sizeof(kIv));
|
||||
|
||||
// Control.
|
||||
std::string ciphertext = EncryptAesCbcNoPad(key, iv, plaintext);
|
||||
ASSERT_EQ(plaintext.size(), ciphertext.size());
|
||||
|
||||
// Bogus key.
|
||||
std::string bogus_key("bogus");
|
||||
ciphertext = EncryptAesCbcNoPad(bogus_key, iv, plaintext);
|
||||
EXPECT_EQ(ciphertext.size(), 0);
|
||||
|
||||
// Bogus IV.
|
||||
std::string bogus_iv("bogus");
|
||||
ciphertext = EncryptAesCbcNoPad(key, bogus_iv, plaintext);
|
||||
EXPECT_EQ(ciphertext.size(), 0);
|
||||
|
||||
// Incorrectly-sized plaintext.
|
||||
std::string bad_plaintext("Foo");
|
||||
ciphertext = EncryptAesCbcNoPad(key, iv, bad_plaintext);
|
||||
EXPECT_EQ(ciphertext.size(), 0);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestFailedDecrypt) {
|
||||
// First, encrypt the data.
|
||||
std::string plain_text("Foo");
|
||||
|
||||
22
common/certificate_type.h
Normal file
22
common/certificate_type.h
Normal file
@@ -0,0 +1,22 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_CERTIFICATE_TYPE_H_
|
||||
#define COMMON_CERTIFICATE_TYPE_H_
|
||||
|
||||
namespace widevine {
|
||||
|
||||
enum CertificateType {
|
||||
kCertificateTypeTesting,
|
||||
kCertificateTypeDevelopment,
|
||||
kCertificateTypeProduction,
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_CERTIFICATE_TYPE_H_
|
||||
262
common/client_cert.cc
Normal file
262
common/client_cert.cc
Normal file
@@ -0,0 +1,262 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/client_cert.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "strings/serialize.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/random_util.h"
|
||||
#include "common/sha_util.h"
|
||||
#include "common/signing_key_util.h"
|
||||
#include "common/status.h"
|
||||
#include "common/wvm_token_handler.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace {
|
||||
|
||||
const int 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) {
|
||||
std::vector<WvmTokenHandler::PreprovKey> keyvector;
|
||||
keyvector.reserve(keymap.size());
|
||||
for (std::multimap<uint32_t, std::string>::const_iterator it = keymap.begin();
|
||||
it != keymap.end(); ++it) {
|
||||
std::string key = absl::HexStringToBytes(it->second);
|
||||
DCHECK_EQ(key.size(), 16);
|
||||
keyvector.push_back(WvmTokenHandler::PreprovKey(it->first, key));
|
||||
}
|
||||
WvmTokenHandler::SetPreprovKeys(keyvector);
|
||||
}
|
||||
|
||||
bool KeyboxClientCert::IsSystemIdKnown(const uint32_t system_id) {
|
||||
return WvmTokenHandler::IsSystemIdKnown(system_id);
|
||||
}
|
||||
|
||||
uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
|
||||
return WvmTokenHandler::GetSystemId(keybox_bytes);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
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");
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
CertificateClientCert::CertificateClientCert() {}
|
||||
|
||||
CertificateClientCert::~CertificateClientCert() {}
|
||||
|
||||
Status CertificateClientCert::Initialize(
|
||||
const DrmRootCertificate* drm_root_certificate,
|
||||
const std::string& serialized_certificate) {
|
||||
CHECK(drm_root_certificate);
|
||||
|
||||
SignedDrmCertificate signed_device_cert;
|
||||
DrmCertificate device_cert;
|
||||
Status status = drm_root_certificate->VerifyCertificate(
|
||||
serialized_certificate, &signed_device_cert, &device_cert);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
const SignedDrmCertificate& signer = signed_device_cert.signer();
|
||||
DrmCertificate model_certificate;
|
||||
if (!model_certificate.ParseFromString(signer.drm_certificate())) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-invalid-signer");
|
||||
}
|
||||
if (!model_certificate.has_serial_number()) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-signer-serial-number");
|
||||
}
|
||||
// Check to see if this model certificate is signed by a
|
||||
// provisioner (entity using Widevine Provisioning Server SDK).
|
||||
if (signer.has_signer()) {
|
||||
DrmCertificate provisioner_certificate;
|
||||
if (!provisioner_certificate.ParseFromString(
|
||||
signer.signer().drm_certificate())) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-invalid-signer");
|
||||
}
|
||||
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
|
||||
set_signed_by_provisioner(true);
|
||||
} else {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"expected-provisioning-provider-certificate-type");
|
||||
}
|
||||
if (!provisioner_certificate.has_provider_id() ||
|
||||
provisioner_certificate.provider_id().empty()) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-provisioning-service-id");
|
||||
}
|
||||
set_service_id(provisioner_certificate.provider_id());
|
||||
}
|
||||
set_signer_serial_number(model_certificate.serial_number());
|
||||
set_signer_creation_time_seconds(model_certificate.creation_time_seconds());
|
||||
if (!model_certificate.has_system_id()) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-missing-system-id");
|
||||
}
|
||||
set_system_id(model_certificate.system_id());
|
||||
set_serial_number(device_cert.serial_number());
|
||||
set_public_key(device_cert.public_key());
|
||||
rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
|
||||
if (rsa_public_key_ == nullptr) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-public-key-failed");
|
||||
}
|
||||
|
||||
// TODO(user): Move this somewhere else. It is license protocol.
|
||||
set_key(Random16Bytes());
|
||||
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
|
||||
return Status(error_space, ENCRYPT_ERROR,
|
||||
"drm-certificate-failed-encrypt-session-key");
|
||||
}
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status CertificateClientCert::VerifySignature(
|
||||
const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) {
|
||||
CHECK(rsa_public_key_);
|
||||
|
||||
if (!rsa_public_key_->VerifySignature(
|
||||
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||
signature)) {
|
||||
return Status(error_space, INVALID_SIGNATURE, "");
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
187
common/client_cert.h
Normal file
187
common/client_cert.h
Normal file
@@ -0,0 +1,187 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_CLIENT_CERT_H__
|
||||
#define COMMON_CLIENT_CERT_H__
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/rsa_key.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 {
|
||||
public:
|
||||
virtual ~ClientCert() {}
|
||||
static Status Create(
|
||||
const DrmRootCertificate* root_certificate,
|
||||
widevine::ClientIdentification::TokenType token_type,
|
||||
const std::string& token, ClientCert** client_cert);
|
||||
// Creates a Keybox based ClientCert.
|
||||
static Status CreateWithKeybox(const std::string& keybox_token,
|
||||
ClientCert** client_cert);
|
||||
// Creates a Device Certificate based ClientCert.
|
||||
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);
|
||||
// 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;
|
||||
// Creates a signing_key that is accessible using signing_key(). Signing_key
|
||||
// is constructed by doing a key derivation using the key() and message.
|
||||
virtual void GenerateSigningKey(const std::string& message,
|
||||
ProtocolVersion protocol_version);
|
||||
// Used to create signing keys. For Keybox token types this is the device key.
|
||||
// For Device Certificate token types this the session key.
|
||||
virtual const std::string& key() const = 0;
|
||||
virtual void set_key(const std::string& key) = 0;
|
||||
virtual const std::string& encrypted_key() const = 0;
|
||||
virtual uint32_t system_id() const { return system_id_; }
|
||||
virtual const std::string& signing_key() const { return signing_key_; }
|
||||
virtual const std::string& public_key() const { return public_key_; }
|
||||
virtual const std::string& serial_number() const { return serial_number_; }
|
||||
virtual void set_serial_number(const std::string& serial_number) {
|
||||
serial_number_ = serial_number;
|
||||
}
|
||||
virtual const std::string& signer_serial_number() const {
|
||||
return signer_serial_number_;
|
||||
}
|
||||
virtual uint32_t signer_creation_time_seconds() const {
|
||||
return signer_creation_time_seconds_;
|
||||
}
|
||||
virtual 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__
|
||||
617
common/client_cert_test.cc
Normal file
617
common/client_cert_test.cc
Normal file
@@ -0,0 +1,617 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/client_cert.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/sha_util.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"
|
||||
|
||||
// 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 {
|
||||
public:
|
||||
void SetUp() override {
|
||||
if (!setup_preprov_keys_) {
|
||||
KeyboxClientCert::SetPreProvisioningKeys(
|
||||
wvm_test_keys::GetPreprovKeyMultimap());
|
||||
setup_preprov_keys_ = true;
|
||||
}
|
||||
ASSERT_OK(
|
||||
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
|
||||
}
|
||||
|
||||
protected:
|
||||
// Simple container struct for test value and expected keys.
|
||||
class TestTokenAndKeys {
|
||||
public:
|
||||
const std::string token_;
|
||||
uint32_t expected_system_id_;
|
||||
const std::string expected_serial_number_;
|
||||
const std::string expected_device_key_;
|
||||
TestTokenAndKeys(const std::string& token, uint32_t expected_system_id,
|
||||
const std::string& expected_serial_number,
|
||||
const std::string& expected_device_key)
|
||||
: token_(token),
|
||||
expected_system_id_(expected_system_id),
|
||||
expected_serial_number_(expected_serial_number),
|
||||
expected_device_key_(expected_device_key) {}
|
||||
};
|
||||
|
||||
class TestCertificateAndData {
|
||||
public:
|
||||
const std::string certificate_;
|
||||
const std::string expected_serial_number_;
|
||||
uint32_t expected_system_id_;
|
||||
Status expected_status_;
|
||||
TestCertificateAndData(const std::string& certificate,
|
||||
const std::string& expected_serial_number,
|
||||
uint32_t expected_system_id, Status expected_status)
|
||||
: certificate_(certificate),
|
||||
expected_serial_number_(expected_serial_number),
|
||||
expected_system_id_(expected_system_id),
|
||||
expected_status_(std::move(expected_status)) {}
|
||||
};
|
||||
|
||||
void TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||
const bool expect_success,
|
||||
const bool compare_device_key);
|
||||
void TestBasicValidationDrmCertificate(
|
||||
const TestCertificateAndData& expectation, const bool compare_data);
|
||||
|
||||
void GenerateSignature(const std::string& message, const std::string& private_key,
|
||||
std::string* signature);
|
||||
SignedDrmCertificate* SignCertificate(const DrmCertificate& certificate,
|
||||
SignedDrmCertificate* signer,
|
||||
const std::string& private_key);
|
||||
DrmCertificate* GenerateProvisionerCertificate(uint32_t system_id,
|
||||
const std::string& serial_number,
|
||||
const std::string& provider_id);
|
||||
SignedDrmCertificate* GenerateSignedProvisionerCertificate(
|
||||
uint32_t system_id, const std::string& serial_number, const std::string& service_id);
|
||||
DrmCertificate* GenerateIntermediateCertificate(uint32_t system_id,
|
||||
const std::string& serial_number);
|
||||
SignedDrmCertificate* GenerateSignedIntermediateCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number);
|
||||
DrmCertificate* GenerateDrmCertificate(uint32_t system_id,
|
||||
const std::string& serial_number);
|
||||
SignedDrmCertificate* GenerateSignedDrmCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number);
|
||||
|
||||
RsaTestKeys test_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,
|
||||
const bool expect_success,
|
||||
const bool compare_device_key) {
|
||||
// Test validation of a valid request.
|
||||
Status status;
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
|
||||
// Two ways to create a client cert object, test both.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (i == 0) {
|
||||
status =
|
||||
ClientCert::Create(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);
|
||||
if (expect_success) {
|
||||
ASSERT_EQ(OkStatus(), status);
|
||||
ASSERT_TRUE(keybox_cert.get());
|
||||
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
|
||||
EXPECT_EQ(expectation.expected_serial_number_,
|
||||
keybox_cert->serial_number());
|
||||
if (compare_device_key) {
|
||||
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
|
||||
}
|
||||
} else {
|
||||
EXPECT_NE(OkStatus(), status);
|
||||
EXPECT_FALSE(keybox_cert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientCertTest::TestBasicValidationDrmCertificate(
|
||||
const TestCertificateAndData& expectation, const bool compare_data) {
|
||||
// Reset DRM certificate signature cache since some certificates get
|
||||
// re-generated.
|
||||
ASSERT_OK(
|
||||
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
|
||||
|
||||
// Test validation of a valid request.
|
||||
Status status;
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
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);
|
||||
ASSERT_EQ(expectation.expected_status_, status);
|
||||
if (expectation.expected_status_.ok()) {
|
||||
ASSERT_TRUE(drm_certificate_cert.get());
|
||||
if (compare_data) {
|
||||
ASSERT_EQ(expectation.expected_serial_number_,
|
||||
drm_certificate_cert->signer_serial_number());
|
||||
ASSERT_EQ(expectation.expected_system_id_,
|
||||
drm_certificate_cert->system_id());
|
||||
}
|
||||
} else {
|
||||
ASSERT_FALSE(drm_certificate_cert.get());
|
||||
}
|
||||
}
|
||||
|
||||
void ClientCertTest::GenerateSignature(const std::string& message,
|
||||
const std::string& private_key,
|
||||
std::string* signature) {
|
||||
std::unique_ptr<RsaPrivateKey> rsa_private_key(
|
||||
RsaPrivateKey::Create(private_key));
|
||||
ASSERT_TRUE(rsa_private_key != nullptr);
|
||||
rsa_private_key->GenerateSignature(message, signature);
|
||||
}
|
||||
|
||||
// The caller relinquishes ownership of |signer|, which may also be nullptr.
|
||||
SignedDrmCertificate* ClientCertTest::SignCertificate(
|
||||
const DrmCertificate& certificate, SignedDrmCertificate* signer,
|
||||
const std::string& private_key) {
|
||||
std::unique_ptr<SignedDrmCertificate> signed_certificate(
|
||||
new SignedDrmCertificate);
|
||||
signed_certificate->set_drm_certificate(certificate.SerializeAsString());
|
||||
GenerateSignature(signed_certificate->drm_certificate(), private_key,
|
||||
signed_certificate->mutable_signature());
|
||||
if (signer != nullptr) {
|
||||
signed_certificate->set_allocated_signer(signer);
|
||||
}
|
||||
return signed_certificate.release();
|
||||
}
|
||||
|
||||
DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
|
||||
uint32_t system_id, const std::string& serial_number) {
|
||||
std::unique_ptr<DrmCertificate> intermediate_certificate(new DrmCertificate);
|
||||
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
|
||||
intermediate_certificate->set_serial_number(serial_number);
|
||||
intermediate_certificate->set_public_key(
|
||||
test_rsa_keys_.public_test_key_2_2048_bits());
|
||||
intermediate_certificate->set_system_id(system_id);
|
||||
intermediate_certificate->set_creation_time_seconds(1234);
|
||||
return intermediate_certificate.release();
|
||||
}
|
||||
|
||||
SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number) {
|
||||
std::unique_ptr<DrmCertificate> intermediate_certificate(
|
||||
GenerateIntermediateCertificate(system_id, serial_number));
|
||||
return SignCertificate(*intermediate_certificate, signer,
|
||||
test_rsa_keys_.private_test_key_1_3072_bits());
|
||||
}
|
||||
|
||||
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
||||
uint32_t system_id, const std::string& serial_number) {
|
||||
std::unique_ptr<DrmCertificate> drm_certificate(new DrmCertificate);
|
||||
drm_certificate->set_type(DrmCertificate::DEVICE);
|
||||
drm_certificate->set_serial_number(serial_number);
|
||||
drm_certificate->set_system_id(system_id);
|
||||
drm_certificate->set_public_key(test_rsa_keys_.public_test_key_3_2048_bits());
|
||||
drm_certificate->set_creation_time_seconds(4321);
|
||||
return drm_certificate.release();
|
||||
}
|
||||
|
||||
SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number) {
|
||||
std::unique_ptr<DrmCertificate> drm_certificate(
|
||||
GenerateDrmCertificate(system_id, serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate(
|
||||
*drm_certificate, signer, test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
return signed_drm_certificate.release();
|
||||
}
|
||||
|
||||
DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
|
||||
uint32_t system_id, const std::string& serial_number, const std::string& provider_id) {
|
||||
std::unique_ptr<DrmCertificate> provisioner_certificate(new DrmCertificate);
|
||||
provisioner_certificate->set_type(DrmCertificate::PROVISIONER);
|
||||
provisioner_certificate->set_serial_number(serial_number);
|
||||
// TODO(user): Need to generate 3072 bit test for provisioner certificates.
|
||||
provisioner_certificate->set_public_key(
|
||||
test_rsa_keys_.public_test_key_1_3072_bits());
|
||||
provisioner_certificate->set_system_id(system_id);
|
||||
provisioner_certificate->set_provider_id(provider_id);
|
||||
provisioner_certificate->set_creation_time_seconds(1234);
|
||||
return provisioner_certificate.release();
|
||||
}
|
||||
|
||||
SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
|
||||
uint32_t system_id, const std::string& serial_number, const std::string& service_id) {
|
||||
std::unique_ptr<DrmCertificate> provisioner_certificate(
|
||||
GenerateProvisionerCertificate(system_id, serial_number, service_id));
|
||||
return SignCertificate(*provisioner_certificate, nullptr,
|
||||
test_rsa_keys_.private_test_key_1_3072_bits());
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, BasicValidation) {
|
||||
const TestTokenAndKeys kValidTokenAndExpectedKeys[] = {
|
||||
TestTokenAndKeys(
|
||||
absl::HexStringToBytes(
|
||||
"00000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e"),
|
||||
274, absl::HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"),
|
||||
absl::HexStringToBytes("4071197f1f8910d9bf10c6bc4c987638")),
|
||||
TestTokenAndKeys(
|
||||
absl::HexStringToBytes(
|
||||
"0000000200000112d906feebe1750c5886ff77c2dfa31bb40e002f3adbc0fa5b"
|
||||
"eb2486cf5f419549cdaa23230e5165ac2ffab56d53b692b7ba0c1857400c6add"
|
||||
"3af3ff3d5cb24985"),
|
||||
274, absl::HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"),
|
||||
absl::HexStringToBytes("42cfb1765201042302a404d1e0fac8ed"))};
|
||||
|
||||
for (size_t i = 0; i < ABSL_ARRAYSIZE(kValidTokenAndExpectedKeys); ++i) {
|
||||
SCOPED_TRACE("Test data: " + absl::StrCat(i));
|
||||
TestBasicValidation(kValidTokenAndExpectedKeys[i], true, true);
|
||||
}
|
||||
|
||||
EXPECT_EQ(
|
||||
wvm_test_keys::kTestSystemId,
|
||||
KeyboxClientCert::GetSystemId(kValidTokenAndExpectedKeys[0].token_));
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, BasicCertValidation) {
|
||||
const uint32_t system_id = 1234;
|
||||
const std::string serial_number("serial_number");
|
||||
std::unique_ptr<SignedDrmCertificate> signed_cert(
|
||||
GenerateSignedDrmCertificate(GenerateSignedIntermediateCertificate(
|
||||
nullptr, system_id, serial_number),
|
||||
system_id, serial_number + "-device"));
|
||||
const TestCertificateAndData kValidCertificateAndExpectedData(
|
||||
signed_cert->SerializeAsString(), serial_number, system_id, OkStatus());
|
||||
const bool compare_data = true;
|
||||
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
|
||||
compare_data);
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, InvalidKeybox) {
|
||||
const TestTokenAndKeys kInvalidTokenAndExpectedKeys[] = {
|
||||
// This tests a malformed, but appropriately sized keybox.
|
||||
TestTokenAndKeys(
|
||||
absl::HexStringToBytes(
|
||||
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e"),
|
||||
0, absl::HexStringToBytes(""), absl::HexStringToBytes("")),
|
||||
// This has a length and system_id, but nothing else.
|
||||
TestTokenAndKeys(absl::HexStringToBytes("0000000200000112"), 0,
|
||||
absl::HexStringToBytes(""), absl::HexStringToBytes("")),
|
||||
// This has only a byte.
|
||||
TestTokenAndKeys(absl::HexStringToBytes(""), 0,
|
||||
absl::HexStringToBytes(""), absl::HexStringToBytes("")),
|
||||
// This has an emptry std::string for the keybox.
|
||||
TestTokenAndKeys(absl::HexStringToBytes(""), 0,
|
||||
absl::HexStringToBytes(""), absl::HexStringToBytes(""))};
|
||||
|
||||
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidTokenAndExpectedKeys); ++i) {
|
||||
SCOPED_TRACE("Test data: " + absl::StrCat(i));
|
||||
TestBasicValidation(kInvalidTokenAndExpectedKeys[i], false, false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, InvalidCertificate) {
|
||||
const uint32_t system_id(1234);
|
||||
const std::string device_sn("device-serial-number");
|
||||
const std::string signer_sn("signer-serial-number");
|
||||
std::unique_ptr<DrmCertificate> dev_cert;
|
||||
std::unique_ptr<DrmCertificate> signer_cert;
|
||||
std::unique_ptr<SignedDrmCertificate> signed_signer;
|
||||
|
||||
// Invalid serialized device certificate.
|
||||
std::unique_ptr<SignedDrmCertificate> invalid_drm_cert(
|
||||
new SignedDrmCertificate);
|
||||
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
|
||||
GenerateSignature(invalid_drm_cert->drm_certificate(),
|
||||
test_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 device public key.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
dev_cert->set_public_key("bad-device-public-key");
|
||||
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
|
||||
*dev_cert,
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
// Invalid serialized intermediate certificate.
|
||||
signed_signer.reset(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
|
||||
signed_signer->set_drm_certificate("bad-serialized-cert");
|
||||
GenerateSignature(signed_signer->drm_certificate(),
|
||||
test_rsa_keys_.private_test_key_1_3072_bits(),
|
||||
signed_signer->mutable_signature());
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
std::unique_ptr<SignedDrmCertificate> invalid_signer(
|
||||
SignCertificate(*dev_cert, signed_signer.release(),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
// Invalid signer public key.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||
signer_cert->set_public_key("bad-signer-public-key");
|
||||
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_rsa_keys_.private_test_key_1_3072_bits()),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
// Invalid device certificate signature.
|
||||
std::unique_ptr<SignedDrmCertificate> bad_device_signature(
|
||||
GenerateSignedDrmCertificate(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
||||
system_id, device_sn));
|
||||
bad_device_signature->set_signature("bad-signature");
|
||||
// Missing model system ID.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||
signer_cert->clear_system_id();
|
||||
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_rsa_keys_.private_test_key_1_3072_bits()),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
// Missing signer serial number.
|
||||
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
|
||||
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_sn));
|
||||
signer_cert->clear_serial_number();
|
||||
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_rsa_keys_.private_test_key_1_3072_bits()),
|
||||
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->set_signature("bad-signature");
|
||||
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
|
||||
SignCertificate(*dev_cert, signed_signer.release(),
|
||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
|
||||
const TestCertificateAndData kInvalidCertificate[] = {
|
||||
TestCertificateAndData("f", "", 0,
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signed-drm-certificate")),
|
||||
TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0,
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-drm-certificate")),
|
||||
TestCertificateAndData(bad_device_public_key->SerializeAsString(), "", 0,
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-public-key-failed")),
|
||||
TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0,
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate")),
|
||||
TestCertificateAndData(bad_signer_public_key->SerializeAsString(), "", 0,
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-leaf-signer-public-key")),
|
||||
TestCertificateAndData(bad_device_signature->SerializeAsString(), "", 0,
|
||||
Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature")),
|
||||
TestCertificateAndData(missing_model_sn->SerializeAsString(), "", 0,
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-missing-system-id")),
|
||||
TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0,
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-signer-serial-number")),
|
||||
TestCertificateAndData(bad_signer_signature->SerializeAsString(), "", 0,
|
||||
Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature")),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) {
|
||||
TestBasicValidationDrmCertificate(kInvalidCertificate[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, MissingPreProvKey) {
|
||||
// system ID in token is 0x01234567
|
||||
const std::string token(absl::HexStringToBytes(
|
||||
"00000002012345678e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e"));
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr);
|
||||
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, ValidProvisionerDeviceCert) {
|
||||
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_number("intermediate-serial-number");
|
||||
const std::string provisioner_serial_number("provisioner-serial-number");
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
|
||||
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
|
||||
service_id));
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
|
||||
system_id,
|
||||
intermediate_serial_number));
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, device_serial_number));
|
||||
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
|
||||
EXPECT_OK(ClientCert::Create(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);
|
||||
|
||||
EXPECT_EQ(service_id, drm_cert->service_id());
|
||||
EXPECT_EQ(device_serial_number, drm_cert->serial_number());
|
||||
EXPECT_EQ(intermediate_serial_number, drm_cert->signer_serial_number());
|
||||
EXPECT_EQ(system_id, drm_cert->system_id());
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, InvalidProvisionerDeviceCertEmptyServiceId) {
|
||||
const uint32_t system_id = 4890;
|
||||
const std::string service_id("");
|
||||
const std::string device_serial_number("device-serial-number");
|
||||
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||
const std::string provisioner_serial_number("provisioner-serial-number");
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_provisioner_cert(
|
||||
GenerateSignedProvisionerCertificate(system_id, provisioner_serial_number,
|
||||
service_id));
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
GenerateSignedIntermediateCertificate(signed_provisioner_cert.release(),
|
||||
system_id,
|
||||
intermediate_serial_number));
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, device_serial_number));
|
||||
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
|
||||
EXPECT_EQ("missing-provisioning-service-id",
|
||||
ClientCert::Create(root_cert_.get(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr)
|
||||
.error_message());
|
||||
EXPECT_FALSE(client_cert_ptr);
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
|
||||
const uint32_t system_id = 4890;
|
||||
const uint32_t system_id2 = 4892;
|
||||
const std::string service_id("widevine_test.com");
|
||||
const std::string device_serial_number("device-serial-number");
|
||||
const std::string intermediate_serial_number("intermediate-serial-number");
|
||||
const std::string intermediate_serial_number2("intermediate-serial-number-2");
|
||||
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert2(
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id2,
|
||||
intermediate_serial_number2));
|
||||
|
||||
// Instead of using a provisioner certificate to sign this intermediate
|
||||
// certificate, use another intermediate certificate. This is an invalid
|
||||
// chain and should generate an error when trying to create a client
|
||||
// certificate.
|
||||
std::unique_ptr<SignedDrmCertificate> signed_intermediate_cert(
|
||||
GenerateSignedIntermediateCertificate(signed_intermediate_cert2.release(),
|
||||
system_id,
|
||||
intermediate_serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_device_cert(
|
||||
GenerateSignedDrmCertificate(signed_intermediate_cert.release(),
|
||||
system_id, device_serial_number));
|
||||
std::string serialized_cert;
|
||||
signed_device_cert->SerializeToString(&serialized_cert);
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
|
||||
// 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",
|
||||
ClientCert::Create(root_cert_.get(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
serialized_cert, &client_cert_ptr)
|
||||
.error_message());
|
||||
EXPECT_FALSE(client_cert_ptr);
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, Protocol21WithDrmCert) {
|
||||
const char message[] = "A weekend wasted is a weekend well spent.";
|
||||
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
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);
|
||||
|
||||
std::unique_ptr<RsaPrivateKey> private_key(
|
||||
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
||||
ASSERT_TRUE(private_key);
|
||||
|
||||
// Success
|
||||
std::string signature;
|
||||
ASSERT_TRUE(private_key->GenerateSignature(message, &signature));
|
||||
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_1));
|
||||
|
||||
// Failure
|
||||
ASSERT_EQ(256, signature.size());
|
||||
++signature[127];
|
||||
EXPECT_FALSE(
|
||||
client_cert->VerifySignature(message, signature, VERSION_2_1).ok());
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
std::unique_ptr<RsaPrivateKey> private_key(
|
||||
RsaPrivateKey::Create(test_rsa_keys_.private_test_key_3_2048_bits()));
|
||||
ASSERT_TRUE(private_key);
|
||||
|
||||
// Success
|
||||
std::string signature;
|
||||
ASSERT_TRUE(private_key->GenerateSignature(message_hash, &signature));
|
||||
EXPECT_OK(client_cert->VerifySignature(message, signature, VERSION_2_2));
|
||||
|
||||
// Failure
|
||||
ASSERT_EQ(256, signature.size());
|
||||
++signature[127];
|
||||
EXPECT_FALSE(
|
||||
client_cert->VerifySignature(message, signature, VERSION_2_2).ok());
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
89
common/client_id_util.cc
Normal file
89
common/client_id_util.cc
Normal file
@@ -0,0 +1,89 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/client_id_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/drm_service_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||
absl::string_view value) {
|
||||
ClientIdentification_NameValue* nv = client_id->add_client_info();
|
||||
nv->set_name(std::string(name));
|
||||
nv->set_value(std::string(value));
|
||||
}
|
||||
|
||||
bool SetClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||
absl::string_view value) {
|
||||
int n = client_id->client_info_size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (client_id->client_info(i).name() == name) {
|
||||
client_id->mutable_client_info(i)->set_value(std::string(value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
AddClientInfo(client_id, name, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
absl::string_view name) {
|
||||
return GetClientInfo(client_id, name, std::string());
|
||||
}
|
||||
|
||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
absl::string_view name, const std::string& default_value) {
|
||||
for (const auto& nv : client_id.client_info()) {
|
||||
if (nv.name() == name) {
|
||||
return nv.value();
|
||||
}
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id) {
|
||||
return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id,
|
||||
client_id);
|
||||
}
|
||||
|
||||
Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
const std::string& privacy_key, ClientIdentification* client_id) {
|
||||
DCHECK(client_id);
|
||||
if (!encrypted_client_id.has_encrypted_client_id() ||
|
||||
encrypted_client_id.encrypted_client_id().empty()) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id");
|
||||
}
|
||||
if (!encrypted_client_id.has_encrypted_client_id_iv() ||
|
||||
encrypted_client_id.encrypted_client_id_iv().empty()) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id-iv");
|
||||
}
|
||||
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
||||
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
||||
encrypted_client_id.encrypted_client_id()));
|
||||
if (serialized_client_id.empty()) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-decryption-failed");
|
||||
}
|
||||
if (!client_id->ParseFromString(serialized_client_id)) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-parse-failed");
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
61
common/client_id_util.h
Normal file
61
common/client_id_util.h
Normal file
@@ -0,0 +1,61 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Utilities for manipulating the ClientIdentification proto.
|
||||
// ClientIdentification.client_info() contains a sequence of
|
||||
// arbitrary name-value pairs; this code consolidates the
|
||||
// accessors for them in one place.
|
||||
#ifndef COMMON_CLIENT_ID_UTIL_H_
|
||||
#define COMMON_CLIENT_ID_UTIL_H_
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/status.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Append the given name/value pair to client_id->client_info(). Does not
|
||||
// check for duplicates.
|
||||
void AddClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||
absl::string_view value);
|
||||
|
||||
// Append the given name/value pair to client_id->client_info(). If the
|
||||
// given name already had a value, replaces it and returns true.
|
||||
bool SetClientInfo(ClientIdentification* client_id, absl::string_view name,
|
||||
absl::string_view value);
|
||||
|
||||
// Return the value from client_id.client_info() matching the given name,
|
||||
// or the empty std::string if not found.
|
||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
absl::string_view name);
|
||||
|
||||
// Return the value from client_id.client_info() matching the given name,
|
||||
// or the given default value if not found.
|
||||
std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
absl::string_view name, const std::string& default_value);
|
||||
|
||||
// Decrypts the encrypted client identification in |encrypted_client_id| into
|
||||
// |client_id| using the private key for the service certificate which was
|
||||
// used to encrypt the information.
|
||||
// |client_id| is owned by caller.
|
||||
// Returns Status::OK, if successful, else an error.
|
||||
Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id);
|
||||
|
||||
// Decrypts the encrypted client identification in |encrypted_client_id| into
|
||||
// |client_id| using |privacy_key|.
|
||||
// |client_id| is owned by caller.
|
||||
// Returns Status::OK, if successful, else an error.
|
||||
Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
const std::string& privacy_key, ClientIdentification* client_id);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_CLIENT_ID_UTIL_H_
|
||||
181
common/crypto_util.cc
Normal file
181
common/crypto_util.cc
Normal file
@@ -0,0 +1,181 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Implementation of Common crypto utilities used by Widevine services.
|
||||
|
||||
#include "common/crypto_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/cmac.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/hmac.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "util/endian/endian.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
const char kWrappingKeyLabel[] = "ENCRYPTION";
|
||||
const int kWrappingKeySizeBits = 128;
|
||||
const char kSigningKeyLabel[] = "AUTHENTICATION";
|
||||
const int kSigningKeySizeBits = 256;
|
||||
const size_t kSigningKeySizeBytes = 32;
|
||||
const char kIvMasterKey[] = "1234567890123456";
|
||||
const char kIvLabel[] = "IV_ENCRYPTION";
|
||||
const int kIvSizeBits = 128;
|
||||
const char kKeyIdMasterKey[] = "0123456789abcdef";
|
||||
const char kKeyIdLabel[] = "KEY_ID_ENCRYPTION";
|
||||
const int kKeyIdSizeBits = 128;
|
||||
const char kGroupKeyLabel[] = "GROUP_ENCRYPTION";
|
||||
// TODO(user): This is a temporary key for development. Replace this with
|
||||
// a real group master key in keystore.
|
||||
// TODO(user): figure out why VerifySignatureHmacSha256 can not crypto_mcmcpy
|
||||
// like VerifySignatureHmacSha1.
|
||||
const char kPhonyGroupMasterKey[] = "fedcba9876543210";
|
||||
const int kAes128KeySizeBits = 128;
|
||||
const int kAes128KeySizeBytes = 16;
|
||||
|
||||
const uint32_t kCENCSchemeID = 0x63656E63; // 'cenc' (AES-CTR): 0x63656E63
|
||||
const uint32_t kCBC1SchemeID = 0x63626331; // 'cbc1' (AES-CBC): 0x63626331
|
||||
const uint32_t kCENSSchemeID =
|
||||
0x63656E73; // 'cens' (AES-CTR subsample): 0x63656E73
|
||||
const uint32_t kCBCSSchemeID =
|
||||
0x63626373; // 'cbcs' (AES-CBC subsample): 0x63626373
|
||||
|
||||
// Creates a SHA-256 HMAC signature for the given message.
|
||||
std::string CreateSignatureHmacSha256(absl::string_view key,
|
||||
absl::string_view message) {
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha256());
|
||||
HMAC_Update(&ctx, reinterpret_cast<const unsigned char*>(message.data()),
|
||||
message.size());
|
||||
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||
unsigned int digest_len;
|
||||
HMAC_Final(&ctx, digest, &digest_len);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
std::string s(reinterpret_cast<char*>(digest), digest_len);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Compares the SHA-256 HMAC against the provided signature.
|
||||
bool VerifySignatureHmacSha256(absl::string_view key,
|
||||
absl::string_view signature,
|
||||
absl::string_view message) {
|
||||
return CreateSignatureHmacSha256(key, message) == signature;
|
||||
}
|
||||
|
||||
// Creates a SHA-1 HMAC signature for the given message.
|
||||
std::string CreateSignatureHmacSha1(absl::string_view key,
|
||||
absl::string_view message) {
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_Init(&ctx, key.data(), key.size(), EVP_sha1());
|
||||
HMAC_Update(&ctx, reinterpret_cast<const unsigned char*>(message.data()),
|
||||
message.size());
|
||||
unsigned char digest[SHA_DIGEST_LENGTH];
|
||||
unsigned int digest_len;
|
||||
HMAC_Final(&ctx, digest, &digest_len);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
std::string s(reinterpret_cast<char*>(digest), digest_len);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Compares the SHA-1 HMAC against the provided signature.
|
||||
bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature,
|
||||
absl::string_view message) {
|
||||
return CreateSignatureHmacSha1(key, message) == signature;
|
||||
}
|
||||
|
||||
// Derives an AES 128 key from the provided key and additional info.
|
||||
std::string DeriveKey(absl::string_view key, absl::string_view label,
|
||||
absl::string_view context, const uint32_t size_bits) {
|
||||
if (key.size() != kAes128KeySizeBytes) return "";
|
||||
|
||||
// We only handle even multiples of 16 bytes (128 bits) right now.
|
||||
if ((size_bits % 128) || (size_bits > (128 * 255))) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string result;
|
||||
|
||||
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
|
||||
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
||||
|
||||
for (unsigned char counter = 1; counter <= (size_bits / 128); counter++) {
|
||||
if (CMAC_Init(cmac_ctx, key.data(), key.size(), cipher, 0)) {
|
||||
std::string message;
|
||||
message.append(1, counter);
|
||||
message.append(label.data(), label.size());
|
||||
message.append(1, '\0');
|
||||
message.append(context.data(), context.size());
|
||||
char size_string[4];
|
||||
BigEndian::Store32(&size_string, size_bits);
|
||||
message.append(&size_string[0], &size_string[0] + 4);
|
||||
if (CMAC_Update(cmac_ctx, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size())) {
|
||||
size_t reslen;
|
||||
unsigned char res[AES_BLOCK_SIZE];
|
||||
if (CMAC_Final(cmac_ctx, res, &reslen)) {
|
||||
result.append(reinterpret_cast<const char*>(res), reslen);
|
||||
}
|
||||
DCHECK(reslen == AES_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Derives an IV from the provided info.
|
||||
std::string DeriveIv(absl::string_view context) {
|
||||
return DeriveKey(kIvMasterKey, kIvLabel, context, kIvSizeBits);
|
||||
}
|
||||
|
||||
// Derives a key ID from the provided info.
|
||||
std::string DeriveKeyId(absl::string_view context) {
|
||||
return DeriveKey(kKeyIdMasterKey, kKeyIdLabel, context, kKeyIdSizeBits);
|
||||
}
|
||||
|
||||
std::string DeriveGroupSessionKey(absl::string_view context,
|
||||
const uint32_t size_bits) {
|
||||
return DeriveKey(kPhonyGroupMasterKey, kGroupKeyLabel, context, size_bits);
|
||||
}
|
||||
|
||||
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
|
||||
const uint32_t size_bits) {
|
||||
return DeriveKey(key, kSigningKeyLabel, context, size_bits);
|
||||
}
|
||||
|
||||
bool FourCCEncryptionSchemeIDFromString(const std::string& requested,
|
||||
uint32_t* four_cc_code) {
|
||||
if (requested.size() != 4 || four_cc_code == nullptr) return false;
|
||||
|
||||
uint32_t result = 0;
|
||||
for (auto i = 0; i < 4; ++i) {
|
||||
result <<= 8;
|
||||
result |= requested[i];
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case kCENCSchemeID:
|
||||
case kCBC1SchemeID:
|
||||
case kCENSSchemeID:
|
||||
case kCBCSSchemeID:
|
||||
*four_cc_code = result;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
89
common/crypto_util.h
Normal file
89
common/crypto_util.h
Normal file
@@ -0,0 +1,89 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Contains common crypto routines for widevine protocols. These routines are
|
||||
// used as part of licensing and provisioning request handling.
|
||||
|
||||
#ifndef COMMON_CRYPTO_UTIL_H_
|
||||
#define COMMON_CRYPTO_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
// Default constants used for key derivation for encryption and signing.
|
||||
// TODO(user): These are duplicated in session.cc in the sdk. de-dup.
|
||||
extern const char kWrappingKeyLabel[];
|
||||
extern const int kWrappingKeySizeBits;
|
||||
extern const char kSigningKeyLabel[];
|
||||
extern const int kSigningKeySizeBits;
|
||||
extern const size_t kSigningKeySizeBytes;
|
||||
extern const char kIvMasterKey[];
|
||||
extern const char kIvLabel[];
|
||||
extern const int kIvSizeBits;
|
||||
extern const int kAes128KeySizeBits;
|
||||
extern const int kAes128KeySizeBytes;
|
||||
|
||||
extern const uint32_t kCENCSchemeID; // 'cenc' (AES-CTR): 0x63656E63
|
||||
extern const uint32_t kCBC1SchemeID; // 'cbc1' (AES-CBC): 0x63626331
|
||||
extern const uint32_t kCENSSchemeID; // 'cens' (AES-CTR subsample): 0x63656E73
|
||||
extern const uint32_t kCBCSSchemeID; // 'cbcs' (AES-CBC subsample): 0x63626373
|
||||
|
||||
// DeriveKey uses the NIST 800-108 KDF recommendation, using AES-CMAC PRF.
|
||||
// NIST 800-108:
|
||||
// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf
|
||||
// AES-CMAC:
|
||||
// http://tools.ietf.org/html/rfc4493
|
||||
std::string DeriveKey(absl::string_view key, absl::string_view label,
|
||||
absl::string_view context, const uint32_t size_bits);
|
||||
|
||||
// Derives an IV from the provided |context|.
|
||||
std::string DeriveIv(absl::string_view context);
|
||||
|
||||
// Derives a key ID from the provided |context|.
|
||||
std::string DeriveKeyId(absl::string_view context);
|
||||
|
||||
// Helper function to derive a key using the group master key and context.
|
||||
std::string DeriveGroupSessionKey(absl::string_view context, const uint32_t size_bits);
|
||||
|
||||
// Helper function to derive a signing key for from the signing context.
|
||||
std::string DeriveSigningKey(absl::string_view key, absl::string_view context,
|
||||
const uint32_t size_bits);
|
||||
|
||||
// Helper function to create a SHA-256 HMAC signature for the given message.
|
||||
std::string CreateSignatureHmacSha256(absl::string_view key,
|
||||
absl::string_view message);
|
||||
|
||||
// Helper function which compares the SHA-256 HMAC against the provided
|
||||
// signature.
|
||||
bool VerifySignatureHmacSha256(absl::string_view key,
|
||||
absl::string_view signature,
|
||||
absl::string_view message);
|
||||
|
||||
// Helper function to create a SHA-1 HMAC signature for the given message.
|
||||
std::string CreateSignatureHmacSha1(absl::string_view key,
|
||||
absl::string_view message);
|
||||
|
||||
// Helper function which compares the SHA-1 HMAC against the provided
|
||||
// signature.
|
||||
bool VerifySignatureHmacSha1(absl::string_view key, absl::string_view signature,
|
||||
absl::string_view message);
|
||||
|
||||
// Converts a requested 4CC encryption scheme ID from a std::string to a uint32_t and
|
||||
// verifies it is a correct value.
|
||||
bool FourCCEncryptionSchemeIDFromString(const std::string& requested,
|
||||
uint32_t* four_cc_code);
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_CRYPTO_UTIL_H_
|
||||
236
common/crypto_util_test.cc
Normal file
236
common/crypto_util_test.cc
Normal file
@@ -0,0 +1,236 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Unit tests for the crypto_util helper functions.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/crypto_util.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
const char kCENCStr[] = "cenc";
|
||||
const char kCBC1Str[] = "cbc1";
|
||||
const char kCENSStr[] = "cens";
|
||||
const char kCBCSStr[] = "cbcs";
|
||||
|
||||
static unsigned char key_data[] =
|
||||
{ 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e,
|
||||
0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b };
|
||||
|
||||
static std::string key_str(key_data, key_data + sizeof(key_data));
|
||||
|
||||
static unsigned char iv_data[] =
|
||||
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
|
||||
|
||||
static std::string iv_str(iv_data, iv_data + sizeof(iv_data));
|
||||
|
||||
TEST(CryptoUtilTest, DeriveAes128KeyTest) {
|
||||
unsigned char label[] = { 0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1,
|
||||
0x92, 0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08,
|
||||
0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60,
|
||||
0xd1, 0xcf, 0x6a, 0x4d, 0x40, 0xa1, 0xe3, 0xfe,
|
||||
0xd3, 0xe9, 0xa6, 0x58, 0x4c, 0xd4, 0xad, 0xa4,
|
||||
0xa2 };
|
||||
|
||||
unsigned char context[] = { 0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d, 0x6d,
|
||||
0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5, 0x5b, 0x80,
|
||||
0x81, 0x91, 0xff };
|
||||
|
||||
unsigned char output0[] = { 0xd5, 0xad, 0x2d, 0xb1, 0x5a, 0x06, 0xcb, 0x50,
|
||||
0xf2, 0x59, 0x5a, 0xb2, 0xb2, 0x0d, 0x44, 0x4e };
|
||||
|
||||
unsigned char output1[] = { 0xdf, 0x38, 0x45, 0x97, 0x5d, 0x7a, 0x81, 0xb4,
|
||||
0x94, 0x86, 0xaf, 0x0c, 0xdc, 0x4d, 0xeb, 0x62,
|
||||
0x31, 0x39, 0x67, 0x8f, 0xff, 0x5d, 0x68, 0x35,
|
||||
0xdc, 0x89, 0x5f, 0x47, 0xca, 0xe0, 0x2d, 0x3a,
|
||||
0x10, 0x24, 0xf8, 0x7e, 0x5b, 0x70, 0xe1, 0xa3,
|
||||
0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22 };
|
||||
|
||||
std::string label_str(label, label + sizeof(label));
|
||||
std::string key_str(key_data, key_data + sizeof(key_data));
|
||||
std::string context_str(context, context + sizeof(context));
|
||||
std::string result = DeriveKey(key_str, label_str, context_str, 128);
|
||||
|
||||
std::string output_128(output0, output0 + sizeof(output0));
|
||||
|
||||
ASSERT_EQ(result, output_128);
|
||||
|
||||
result = DeriveKey(key_str, label_str, context_str, 384);
|
||||
|
||||
std::string output_384(output1, output1 + sizeof(output1));
|
||||
|
||||
ASSERT_EQ(result, output_384);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, DeriveGroupSesionKey) {
|
||||
unsigned char output[] = { 0x92, 0x6c, 0x2f, 0x5, 0xa6, 0x4f, 0xff, 0xb1,
|
||||
0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1 };
|
||||
std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128);
|
||||
EXPECT_EQ(crypto_util::kAes128KeySizeBytes, group_session_key.size());
|
||||
const std::string output_128(output, output + sizeof(output));
|
||||
ASSERT_EQ(output_128, group_session_key);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
|
||||
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
|
||||
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
|
||||
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
|
||||
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
|
||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
std::string signature(CreateSignatureHmacSha256(key_str, message));
|
||||
|
||||
ASSERT_EQ(signature.size(), 32);
|
||||
|
||||
ASSERT_TRUE(VerifySignatureHmacSha256(key_str, signature, message));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
|
||||
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
|
||||
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
|
||||
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
|
||||
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
|
||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
// Test with bogus key;
|
||||
std::string bogus_key("bogus");
|
||||
std::string signature(CreateSignatureHmacSha256(bogus_key, message));
|
||||
|
||||
// This should still produce an hmac signature.
|
||||
ASSERT_EQ(signature.size(), 32);
|
||||
|
||||
// Create valid signature to compare.
|
||||
signature = CreateSignatureHmacSha256(key_str, message);
|
||||
|
||||
// Test with bogus key.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message));
|
||||
|
||||
// Test with munged signature.
|
||||
signature[0] = 0xFF;
|
||||
ASSERT_FALSE(VerifySignatureHmacSha256(key_str, signature, message));
|
||||
|
||||
// Test with bogus signature.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha256(key_str, "bogus", message));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
|
||||
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
|
||||
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
|
||||
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
|
||||
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
|
||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
std::string signature(CreateSignatureHmacSha1(key_str, message));
|
||||
|
||||
ASSERT_EQ(20, signature.size());
|
||||
ASSERT_TRUE(VerifySignatureHmacSha1(key_str, signature, message));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53,
|
||||
0x99, 0x7a, 0x7d, 0x9b, 0x0c, 0xcf, 0xfd, 0xb2,
|
||||
0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad, 0x23, 0x91,
|
||||
0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde,
|
||||
0xf6, 0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f,
|
||||
0x99, 0x7a, 0x63, 0x47, 0x2e, 0x54, 0x35, 0xb5,
|
||||
0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2, 0x54,
|
||||
0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda };
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
// Test with bogus key;
|
||||
std::string bogus_key("bogus");
|
||||
std::string signature(CreateSignatureHmacSha1(bogus_key, message));
|
||||
|
||||
// This should still produce an hmac signature.
|
||||
ASSERT_EQ(20, signature.size());
|
||||
// Create valid signature to compare.
|
||||
signature = CreateSignatureHmacSha1(key_str, message);
|
||||
// Test with bogus key.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message));
|
||||
// Test with munged signature.
|
||||
signature[0] = 0xFF;
|
||||
ASSERT_FALSE(VerifySignatureHmacSha1(key_str, signature, message));
|
||||
// Test with bogus signature.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha1(key_str, "bogus", message));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, DeriveIv) {
|
||||
// First value in the pair is the key_id, second value is the expected IV.
|
||||
std::pair<std::string, std::string> id_iv_pairs[] = {
|
||||
{"1234567890123456", "3278234c7682d1a2e153af4912975f5f"},
|
||||
{"0987654321098765", "cf09abd30f04b60544910791a6b904cf"}};
|
||||
for (const auto& id_iv_pair : id_iv_pairs) {
|
||||
SCOPED_TRACE(absl::StrCat("test case:", id_iv_pair.first));
|
||||
EXPECT_EQ(id_iv_pair.second,
|
||||
absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
|
||||
// Repeat same call to verify derivied result is repeatable.
|
||||
EXPECT_EQ(id_iv_pair.second,
|
||||
absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, DeriveKeyId) {
|
||||
// First value in the pair is the context, second value is the expected id.
|
||||
std::pair<std::string, std::string> context_id_pairs[] = {
|
||||
{"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"},
|
||||
{"0987654321098765", "084fc6bece9688ccce6b1672d9b47e22"}};
|
||||
for (const auto& context_id_pair : context_id_pairs) {
|
||||
SCOPED_TRACE(absl::StrCat("test case:", context_id_pair.first));
|
||||
EXPECT_EQ(context_id_pair.second,
|
||||
absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
|
||||
// Repeat same call to verify derivied result is repeatable.
|
||||
EXPECT_EQ(context_id_pair.second,
|
||||
absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) {
|
||||
uint32_t cc_code;
|
||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code));
|
||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code));
|
||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code));
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, Verify4CCEncryptionIDFromString) {
|
||||
uint32_t cc_code = 0;
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code));
|
||||
ASSERT_EQ(kCENCSchemeID, cc_code);
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBC1Str, &cc_code));
|
||||
ASSERT_EQ(kCBC1SchemeID, cc_code);
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENSStr, &cc_code));
|
||||
ASSERT_EQ(kCENSSchemeID, cc_code);
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBCSStr, &cc_code));
|
||||
ASSERT_EQ(kCBCSSchemeID, cc_code);
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
362
common/device_status_list.cc
Normal file
362
common/device_status_list.cc
Normal file
@@ -0,0 +1,362 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Implements the DeviceStatusList class.
|
||||
|
||||
#include "common/device_status_list.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "common/client_cert.h"
|
||||
#include "common/drm_service_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
const char kSignedListTerminator[] = "}";
|
||||
const char kSignedList[] = "signedList\":";
|
||||
const std::size_t kSignedListLen = strlen(kSignedList);
|
||||
} // namespace
|
||||
|
||||
DeviceStatusList* DeviceStatusList::Instance() {
|
||||
// TODO(user): This is "ok" according to Google's Coding for Dummies, but
|
||||
// we should inject the status list into the sessions. This will require
|
||||
// exposing additional objects in the public interface.
|
||||
static DeviceStatusList* device_status_list(nullptr);
|
||||
if (!device_status_list) device_status_list = new DeviceStatusList;
|
||||
return device_status_list;
|
||||
}
|
||||
|
||||
DeviceStatusList::DeviceStatusList()
|
||||
: creation_time_seconds_(0),
|
||||
expiration_period_seconds_(0),
|
||||
allow_unknown_devices_(true),
|
||||
allow_test_only_devices_(false) {}
|
||||
|
||||
DeviceStatusList::~DeviceStatusList() {}
|
||||
|
||||
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()) {
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-status-list");
|
||||
}
|
||||
if (!signed_certificate_status_list.has_signature()) {
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-status-list-signature");
|
||||
}
|
||||
std::unique_ptr<RsaPublicKey> root_key(
|
||||
RsaPublicKey::Create(root_certificate_public_key));
|
||||
if (root_key == nullptr) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-public-key");
|
||||
}
|
||||
if (!root_key->VerifySignature(
|
||||
signed_certificate_status_list.certificate_status_list(),
|
||||
signed_certificate_status_list.signature())) {
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"invalid-status-list-signature");
|
||||
}
|
||||
DeviceCertificateStatusList certificate_status_list;
|
||||
if (!certificate_status_list.ParseFromString(
|
||||
signed_certificate_status_list.certificate_status_list())) {
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-parse-error");
|
||||
}
|
||||
if (expiration_period_seconds &&
|
||||
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
|
||||
expiration_period_seconds))) {
|
||||
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-expired");
|
||||
}
|
||||
|
||||
absl::WriterMutexLock lock(&status_map_lock_);
|
||||
device_status_map_.clear();
|
||||
for (int i = 0, n = certificate_status_list.certificate_status_size(); i < n;
|
||||
i++) {
|
||||
const DeviceCertificateStatus& cert_status =
|
||||
certificate_status_list.certificate_status(i);
|
||||
if (cert_status.has_device_info()) {
|
||||
const ProvisionedDeviceInfo& device_info = cert_status.device_info();
|
||||
if (device_info.has_system_id()) {
|
||||
device_status_map_[device_info.system_id()] = cert_status;
|
||||
} else {
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"device-info-missing-system-id");
|
||||
}
|
||||
}
|
||||
}
|
||||
creation_time_seconds_ = certificate_status_list.creation_time_seconds();
|
||||
expiration_period_seconds_ = expiration_period_seconds;
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
|
||||
ProvisionedDeviceInfo* device_info) {
|
||||
CHECK(device_info);
|
||||
|
||||
// Keybox checks.
|
||||
if (client_cert.type() == ClientIdentification::KEYBOX) {
|
||||
if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) {
|
||||
return Status(error_space, UNSUPPORTED_SYSTEM_ID,
|
||||
"keybox-unsupported-system-id");
|
||||
}
|
||||
// Get device information from certificate status list if available.
|
||||
if (!GetDeviceInfo(client_cert, device_info)) {
|
||||
device_info->Clear();
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
// DRM certificate checks.
|
||||
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-unsupported-token-type");
|
||||
}
|
||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||
if (expiration_period_seconds_ &&
|
||||
(GetCurrentTime() >
|
||||
(creation_time_seconds_ + expiration_period_seconds_))) {
|
||||
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-expired");
|
||||
}
|
||||
DeviceCertificateStatus* device_cert_status =
|
||||
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
||||
if (device_cert_status) {
|
||||
*device_info = device_cert_status->device_info();
|
||||
if (device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_REVOKED) {
|
||||
if (IsRevokedSystemIdAllowed(client_cert.system_id())) {
|
||||
LOG(WARNING) << "Allowing REVOKED device: "
|
||||
<< device_info->ShortDebugString();
|
||||
} else {
|
||||
return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||
"device-certificate-revoked");
|
||||
}
|
||||
}
|
||||
if ((device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_TEST_ONLY) &&
|
||||
!allow_test_only_devices_) {
|
||||
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())) {
|
||||
// Widevine-provisioned device, and the intermediate certificate serial
|
||||
// number does not match that in the status list. If the status list is
|
||||
// newer than the certificate, indicate an invalid certificate, so that
|
||||
// the device re-provisions. If, on the other hand, the certificate status
|
||||
// list is older than the certificate, the certificate is for all purposes
|
||||
// unknown.
|
||||
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"intermediate-certificate-serial-number-mismatch");
|
||||
}
|
||||
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
"device-certificate-status-unknown");
|
||||
}
|
||||
} else {
|
||||
if (!allow_unknown_devices_) {
|
||||
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
"device-certificate-status-unknown");
|
||||
}
|
||||
device_info->Clear();
|
||||
}
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
|
||||
ProvisionedDeviceInfo* device_info) {
|
||||
CHECK(device_info);
|
||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||
DeviceCertificateStatus* device_cert_status =
|
||||
gtl::FindOrNull(device_status_map_, client_cert.system_id());
|
||||
if (device_cert_status) {
|
||||
*device_info = device_cert_status->device_info();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeviceStatusList::IsSystemIdActive(uint32_t system_id) {
|
||||
absl::ReaderMutexLock lock(&status_map_lock_);
|
||||
DeviceCertificateStatus* device_cert_status =
|
||||
gtl::FindOrNull(device_status_map_, system_id);
|
||||
if (!device_cert_status) {
|
||||
return allow_unknown_devices_ ||
|
||||
KeyboxClientCert::IsSystemIdKnown(system_id);
|
||||
}
|
||||
if (device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_TEST_ONLY) {
|
||||
return allow_test_only_devices_;
|
||||
}
|
||||
if (device_cert_status) {
|
||||
ProvisionedDeviceInfo device_info = device_cert_status->device_info();
|
||||
if (device_cert_status->status() ==
|
||||
DeviceCertificateStatus::STATUS_REVOKED) {
|
||||
if (IsRevokedSystemIdAllowed(system_id)) {
|
||||
LOG(WARNING) << "REVOKED system_id: " << system_id
|
||||
<< " is allowed to be active";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return device_cert_status->status() !=
|
||||
DeviceCertificateStatus::STATUS_REVOKED;
|
||||
}
|
||||
|
||||
uint32_t DeviceStatusList::GetCurrentTime() const { return time(nullptr); }
|
||||
|
||||
void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) {
|
||||
for (absl::string_view sp : absl::StrSplit(system_id_list, ',')) {
|
||||
allowed_revoked_devices_.push_back(std::stoi(std::string(sp)));
|
||||
}
|
||||
std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end());
|
||||
}
|
||||
|
||||
bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
|
||||
auto it = std::binary_search(allowed_revoked_devices_.begin(),
|
||||
allowed_revoked_devices_.end(), system_id);
|
||||
return it;
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
// Strip off quotes.
|
||||
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\"'),
|
||||
signed_list.end());
|
||||
// Strip off spaces.
|
||||
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), ' '),
|
||||
signed_list.end());
|
||||
|
||||
// Strip off newlines.
|
||||
signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\n'),
|
||||
signed_list.end());
|
||||
|
||||
// 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)) {
|
||||
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)) {
|
||||
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");
|
||||
}
|
||||
if (!signed_status_list.has_certificate_status_list()) {
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-status-list");
|
||||
}
|
||||
DeviceCertificateStatusList device_certificate_status_list;
|
||||
if (!device_certificate_status_list.ParseFromString(
|
||||
signed_status_list.certificate_status_list())) {
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-parse-error");
|
||||
}
|
||||
*certificate_status_list = signed_status_list.certificate_status_list();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
||||
const std::string& version,
|
||||
std::string* signed_device_certificate_status_list_request) {
|
||||
if (version.empty()) {
|
||||
return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty");
|
||||
}
|
||||
DCHECK(signed_device_certificate_status_list_request);
|
||||
if (signed_device_certificate_status_list_request == nullptr) {
|
||||
return Status(error_space, error::INVALID_ARGUMENT,
|
||||
"Signed_device_certificate_status_list_request is empty");
|
||||
}
|
||||
// Construct SignedDeviceCertificateStatusListRequest.
|
||||
DeviceCertificateStatusListRequest request;
|
||||
request.set_sdk_version(version);
|
||||
request.set_sdk_time_seconds(DeviceStatusList::Instance()->GetCurrentTime());
|
||||
std::string device_certificate_status_list_request;
|
||||
request.SerializeToString(&device_certificate_status_list_request);
|
||||
SignedDeviceCertificateStatusListRequest signed_request;
|
||||
signed_request.set_device_certificate_status_list_request(
|
||||
device_certificate_status_list_request);
|
||||
const DrmServiceCertificate* sc =
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
||||
if (sc == nullptr) {
|
||||
signed_device_certificate_status_list_request->clear();
|
||||
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
|
||||
"Drm service certificate is not loaded.");
|
||||
}
|
||||
const RsaPrivateKey* private_key = sc->private_key();
|
||||
if (private_key == nullptr) {
|
||||
return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
|
||||
"Private key in the service certificate is null.");
|
||||
}
|
||||
std::string signature;
|
||||
private_key->GenerateSignature(device_certificate_status_list_request,
|
||||
&signature);
|
||||
signed_request.set_signature(signature);
|
||||
signed_request.SerializeToString(
|
||||
signed_device_certificate_status_list_request);
|
||||
return OkStatus();
|
||||
}
|
||||
} // namespace widevine
|
||||
122
common/device_status_list.h
Normal file
122
common/device_status_list.h
Normal file
@@ -0,0 +1,122 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// DeviceStatusList class header.
|
||||
|
||||
#ifndef COMMON_DEVICE_STATUS_LIST_H__
|
||||
#define COMMON_DEVICE_STATUS_LIST_H__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "common/status.h"
|
||||
#include "protos/public/device_certificate_status.pb.h"
|
||||
#include "protos/public/provisioned_device_info.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class ClientCert;
|
||||
// Manages the certificate status of devices. The list of
|
||||
// DeviceCertificateStatus is provided by the DRM server. Each license
|
||||
// request is checked to ensure the certificate in the request is valid and
|
||||
// not revoked. Also checks to see if the intermediate certificates were
|
||||
// updated where the system Id is the same, but the serial number changes.
|
||||
// This case should cause the clients to re-provision.
|
||||
class DeviceStatusList {
|
||||
public:
|
||||
// Returns a pointer to a singleton DeviceStatusList.
|
||||
static DeviceStatusList* Instance();
|
||||
|
||||
DeviceStatusList();
|
||||
virtual ~DeviceStatusList();
|
||||
|
||||
// Takes |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);
|
||||
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
|
||||
bool allow_unknown_devices() const { return allow_unknown_devices_; }
|
||||
void set_allow_test_only_devices(bool allow) {
|
||||
allow_test_only_devices_ = allow;
|
||||
}
|
||||
bool allow_test_only_devices() const { return allow_test_only_devices_; }
|
||||
|
||||
// Checks the device status list and returns either:
|
||||
// OK
|
||||
// UNSUPPORTED_SYSTEM_ID
|
||||
// INVALID_DRM_CERTIFICATE
|
||||
// DRM_DEVICE_CERTIFICATE_REVOKED
|
||||
// DRM_DEVICE_CERTIFICATE_UNKNOWN
|
||||
// If status is OK, a copy of the provisioned device info is copied
|
||||
// into |device_info|. Caller owns |device_info| and it must not be null.
|
||||
Status GetCertStatus(const ClientCert& client_cert,
|
||||
widevine::ProvisionedDeviceInfo* device_info);
|
||||
// Returns true if the pre-provisioning key or certificate for the specified
|
||||
// system ID are active (not disallowed or revoked).
|
||||
bool IsSystemIdActive(uint32_t system_id);
|
||||
|
||||
// Returns true if the system ID
|
||||
// Returns true is a ProvisionedDeviceInfo exist based on <client_cert>.
|
||||
// Caller owns <device_info> and it must not be null.
|
||||
bool GetDeviceInfo(const ClientCert& client_cert,
|
||||
widevine::ProvisionedDeviceInfo* device_info);
|
||||
// Returns the current POSIX time.
|
||||
virtual uint32_t GetCurrentTime() const;
|
||||
|
||||
// Enable delivery of licenses to revoked client devices. |system_id_list| is
|
||||
// a comma separated list of systems Ids to allow even if revoked.
|
||||
virtual void AllowRevokedDevices(const std::string& system_id_list);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* Constructs signed device certificate status list request string.
|
||||
*
|
||||
* @param signed_device_certificate_status_list_request
|
||||
* @param version
|
||||
* @return Status - Status::OK if success, else error.
|
||||
*/
|
||||
static Status GenerateSignedDeviceCertificateStatusListRequest(
|
||||
const std::string& version,
|
||||
std::string* signed_device_certificate_status_list_request);
|
||||
|
||||
private:
|
||||
// Returns true if the system ID is allowed to be revoked.
|
||||
// Caller owns |system_id|. They must not be null.
|
||||
bool IsRevokedSystemIdAllowed(uint32_t system_id);
|
||||
|
||||
absl::Mutex status_map_lock_;
|
||||
// Key is the system id for the device.
|
||||
std::map<uint32_t, widevine::DeviceCertificateStatus> device_status_map_;
|
||||
uint32_t creation_time_seconds_;
|
||||
uint32_t expiration_period_seconds_;
|
||||
bool allow_unknown_devices_;
|
||||
bool allow_test_only_devices_;
|
||||
// Contains the list of system_id values that are allowed to succeed even if
|
||||
// revoked.
|
||||
std::vector<uint32_t> allowed_revoked_devices_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DeviceStatusList);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
#endif // COMMON_DEVICE_STATUS_LIST_H__
|
||||
376
common/device_status_list_test.cc
Normal file
376
common/device_status_list_test.cc
Normal file
@@ -0,0 +1,376 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/device_status_list.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "common/client_cert.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/provisioned_device_info.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnRef;
|
||||
using ::testing::ReturnRefOfCopy;
|
||||
|
||||
const uint32_t kValidCertSystemId = 100;
|
||||
const uint32_t kRevokedCertSystemId = 101;
|
||||
const uint32_t kValidPpkSystemId = 102;
|
||||
const uint32_t kTestOnlyCertSystemId = 103;
|
||||
const uint32_t kRevokedAllowedDeviceCertSystemId = 104;
|
||||
const uint32_t kUnknownSystemId = 666;
|
||||
const char kValidSerialNumber[] = "valid-serial-number";
|
||||
const char kRevokedSerialNumber[] = "revoked-serial-number";
|
||||
const char kRevokedAllowDeviceSerialNumber[] =
|
||||
"revoked-allow-device-serial-number";
|
||||
const char kTestOnlySerialNumber[] = "test_only-serial-number";
|
||||
const char kMismatchSerialNumber[] = "mismatch-serial-number";
|
||||
const char kDeviceModel[] = "device-model-x";
|
||||
const char kTestPreprovKey[] = "00112233445566778899aabbccddeeff";
|
||||
const uint32_t kStatusListCreationTime = 17798001;
|
||||
const uint32_t kDefaultExpirePeriod = 0;
|
||||
|
||||
class MockCertificateClientCert : public CertificateClientCert {
|
||||
public:
|
||||
MockCertificateClientCert() {}
|
||||
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());
|
||||
};
|
||||
|
||||
class DeviceStatusListTest : public ::testing::Test {
|
||||
public:
|
||||
~DeviceStatusListTest() override {}
|
||||
|
||||
void SetUp() override {
|
||||
DeviceCertificateStatus *cert_status;
|
||||
|
||||
// Device cert with status RELEASED.
|
||||
cert_status = cert_status_list_.add_certificate_status();
|
||||
cert_status->mutable_device_info()->set_system_id(kValidCertSystemId);
|
||||
cert_status->set_drm_serial_number(kValidSerialNumber);
|
||||
cert_status->mutable_device_info()->set_model(kDeviceModel);
|
||||
cert_status->set_status(DeviceCertificateStatus::STATUS_RELEASED);
|
||||
|
||||
// Device cert with status REVOKED.
|
||||
cert_status = cert_status_list_.add_certificate_status();
|
||||
cert_status->mutable_device_info()->set_system_id(kRevokedCertSystemId);
|
||||
cert_status->set_drm_serial_number(kRevokedSerialNumber);
|
||||
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
|
||||
|
||||
// Device cert with status REVOKED ALLOWED DEVICE.
|
||||
cert_status = cert_status_list_.add_certificate_status();
|
||||
cert_status->mutable_device_info()->set_system_id(
|
||||
kRevokedAllowedDeviceCertSystemId);
|
||||
cert_status->set_drm_serial_number(kRevokedAllowDeviceSerialNumber);
|
||||
cert_status->set_status(DeviceCertificateStatus::STATUS_REVOKED);
|
||||
device_status_list_.AllowRevokedDevices(
|
||||
absl::StrCat(kRevokedAllowedDeviceCertSystemId));
|
||||
|
||||
// Device cert with status TEST_ONLY.
|
||||
cert_status = cert_status_list_.add_certificate_status();
|
||||
cert_status->mutable_device_info()->set_system_id(kTestOnlyCertSystemId);
|
||||
cert_status->set_drm_serial_number(kTestOnlySerialNumber);
|
||||
cert_status->set_status(DeviceCertificateStatus::STATUS_TEST_ONLY);
|
||||
|
||||
cert_status_list_.set_creation_time_seconds(kStatusListCreationTime);
|
||||
cert_status_list_.SerializeToString(
|
||||
signed_cert_status_list_.mutable_certificate_status_list());
|
||||
std::unique_ptr<RsaPrivateKey> root_key(
|
||||
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits()));
|
||||
ASSERT_TRUE(root_key);
|
||||
|
||||
ASSERT_TRUE(root_key->GenerateSignature(
|
||||
signed_cert_status_list_.certificate_status_list(),
|
||||
signed_cert_status_list_.mutable_signature()));
|
||||
ASSERT_TRUE(
|
||||
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
||||
|
||||
ASSERT_EQ(OkStatus(), device_status_list_.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, kDefaultExpirePeriod));
|
||||
}
|
||||
|
||||
DeviceStatusList device_status_list_;
|
||||
RsaTestKeys test_keys_;
|
||||
DeviceCertificateStatusList cert_status_list_;
|
||||
SignedDeviceCertificateStatusList signed_cert_status_list_;
|
||||
std::string serialized_status_list_;
|
||||
};
|
||||
|
||||
// Returns the number of DevcieCertificateStatus messages in the list.
|
||||
|
||||
TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
||||
// Test case where the Certificate status is set to Valid.
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockCertificateClientCert valid_client_cert;
|
||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||
EXPECT_CALL(valid_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(valid_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||
EXPECT_EQ(OkStatus(),
|
||||
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
|
||||
// Test case where the Certificate status is Revoked.
|
||||
MockCertificateClientCert revoked_client_cert;
|
||||
std::string revoked_drm_serial_number(kRevokedSerialNumber);
|
||||
EXPECT_CALL(revoked_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(revoked_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kRevokedCertSystemId));
|
||||
EXPECT_CALL(revoked_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(revoked_drm_serial_number));
|
||||
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||
device_status_list_.GetCertStatus(revoked_client_cert, &device_info)
|
||||
.error_code());
|
||||
|
||||
// Test case where the revoked cert is allowed.
|
||||
device_status_list_.AllowRevokedDevices(absl::StrCat(kRevokedCertSystemId));
|
||||
EXPECT_OK(
|
||||
device_status_list_.GetCertStatus(revoked_client_cert, &device_info));
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, TestOnlyCertAllowed) {
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockCertificateClientCert test_only_client_cert;
|
||||
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
||||
EXPECT_CALL(test_only_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(test_only_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
||||
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||
EXPECT_EQ(
|
||||
DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
device_status_list_.GetCertStatus(test_only_client_cert, &device_info)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockCertificateClientCert test_only_client_cert;
|
||||
std::string test_only_drm_serial_number(kTestOnlySerialNumber);
|
||||
device_status_list_.set_allow_test_only_devices(true);
|
||||
EXPECT_CALL(test_only_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(test_only_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
||||
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(test_only_client_cert,
|
||||
&device_info));
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
||||
std::multimap<uint32_t, std::string> preprov_keys;
|
||||
preprov_keys.insert(std::make_pair(kValidCertSystemId, kTestPreprovKey));
|
||||
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
|
||||
|
||||
// Test case where the Certificate status is set to Valid.
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockKeyboxClientCert valid_client_keybox;
|
||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||
EXPECT_CALL(valid_client_keybox, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||
EXPECT_CALL(valid_client_keybox, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(valid_client_keybox,
|
||||
&device_info));
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
|
||||
MockKeyboxClientCert unknown_client_keybox;
|
||||
EXPECT_CALL(unknown_client_keybox, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||
EXPECT_CALL(unknown_client_keybox, system_id())
|
||||
.WillRepeatedly(Return(kUnknownSystemId));
|
||||
EXPECT_EQ(
|
||||
UNSUPPORTED_SYSTEM_ID,
|
||||
device_status_list_.GetCertStatus(unknown_client_keybox, &device_info)
|
||||
.error_code());
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
||||
device_status_list_.set_allow_unknown_devices(true);
|
||||
|
||||
// Test case where the signer certificate is older than the current status
|
||||
// list.
|
||||
MockCertificateClientCert older_client_cert;
|
||||
ProvisionedDeviceInfo device_info;
|
||||
std::string mismatch_drm_serial_number(kMismatchSerialNumber);
|
||||
EXPECT_CALL(older_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(older_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(older_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||
EXPECT_CALL(older_client_cert, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
||||
EXPECT_EQ(INVALID_DRM_CERTIFICATE,
|
||||
device_status_list_.GetCertStatus(older_client_cert, &device_info)
|
||||
.error_code());
|
||||
|
||||
// We allow this case only for certs signed by a provisioner cert.
|
||||
EXPECT_CALL(older_client_cert, signed_by_provisioner())
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_EQ(OkStatus(),
|
||||
device_status_list_.GetCertStatus(older_client_cert, &device_info));
|
||||
EXPECT_TRUE(device_info.has_system_id());
|
||||
EXPECT_EQ(kValidCertSystemId, device_info.system_id());
|
||||
|
||||
// Test case where the signer certificate is newer than the current status
|
||||
// list, and unknown devices are allowed.
|
||||
MockCertificateClientCert newer_client_cert1;
|
||||
EXPECT_CALL(newer_client_cert1, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(newer_client_cert1, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(newer_client_cert1, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||
EXPECT_CALL(newer_client_cert1, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime));
|
||||
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
device_status_list_.GetCertStatus(newer_client_cert1, &device_info)
|
||||
.error_code());
|
||||
|
||||
// Test case where the signer certificate is newer than the current status
|
||||
// list, and unknown devices are not allowed.
|
||||
device_status_list_.set_allow_unknown_devices(false);
|
||||
MockCertificateClientCert newer_client_cert2;
|
||||
EXPECT_CALL(newer_client_cert2, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(newer_client_cert2, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(newer_client_cert2, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(mismatch_drm_serial_number));
|
||||
EXPECT_CALL(newer_client_cert2, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime + 1));
|
||||
EXPECT_EQ(DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
device_status_list_.GetCertStatus(newer_client_cert2, &device_info)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, InvalidStatusList) {
|
||||
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
||||
device_status_list_
|
||||
.UpdateStatusList(test_keys_.public_test_key_2_2048_bits(),
|
||||
serialized_status_list_, 0)
|
||||
.error_code());
|
||||
|
||||
++(*signed_cert_status_list_.mutable_certificate_status_list())[4];
|
||||
ASSERT_TRUE(
|
||||
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
||||
EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST,
|
||||
device_status_list_
|
||||
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 0)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
class MockDeviceStatusList : public DeviceStatusList {
|
||||
public:
|
||||
MOCK_CONST_METHOD0(GetCurrentTime, uint32_t());
|
||||
};
|
||||
|
||||
TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
|
||||
MockDeviceStatusList mock_device_status_list;
|
||||
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
|
||||
.Times(2)
|
||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 100));
|
||||
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
mock_device_status_list
|
||||
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 100)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
||||
MockDeviceStatusList mock_device_status_list;
|
||||
EXPECT_CALL(mock_device_status_list, GetCurrentTime())
|
||||
.Times(3)
|
||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||
EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 100));
|
||||
|
||||
ProvisionedDeviceInfo device_info;
|
||||
MockCertificateClientCert valid_client_cert;
|
||||
std::string valid_drm_serial_number(kValidSerialNumber);
|
||||
EXPECT_CALL(valid_client_cert, type())
|
||||
.WillRepeatedly(Return(ClientIdentification::DRM_DEVICE_CERTIFICATE));
|
||||
EXPECT_CALL(valid_client_cert, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
||||
EXPECT_EQ(OkStatus(), mock_device_status_list.GetCertStatus(valid_client_cert,
|
||||
&device_info));
|
||||
|
||||
EXPECT_EQ(
|
||||
EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
mock_device_status_list.GetCertStatus(valid_client_cert, &device_info)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, IsSystemIdActive) {
|
||||
std::multimap<uint32_t, std::string> preprov_keys;
|
||||
preprov_keys.insert(
|
||||
std::make_pair(kValidPpkSystemId, "00112233445566778899aabbccddeeff"));
|
||||
KeyboxClientCert::SetPreProvisioningKeys(preprov_keys);
|
||||
device_status_list_.set_allow_unknown_devices(false);
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId));
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId));
|
||||
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId));
|
||||
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kUnknownSystemId));
|
||||
device_status_list_.set_allow_unknown_devices(true);
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidCertSystemId));
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kValidPpkSystemId));
|
||||
EXPECT_FALSE(device_status_list_.IsSystemIdActive(kRevokedCertSystemId));
|
||||
EXPECT_TRUE(device_status_list_.IsSystemIdActive(kUnknownSystemId));
|
||||
EXPECT_TRUE(
|
||||
device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
532
common/drm_root_certificate.cc
Normal file
532
common/drm_root_certificate.cc
Normal file
@@ -0,0 +1,532 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// common_typos_disable. Successful / successfull.
|
||||
|
||||
#include "common/drm_root_certificate.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/sha_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 {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kDevelopmentString[] = "dev"; // QA systems.
|
||||
const char kProductionString[] = "prod"; // Production.
|
||||
const char kTestingString[] = "test"; // Code development / unit tests.
|
||||
|
||||
const bool kUseCache = true;
|
||||
|
||||
// From common::TestDrmCertificates.
|
||||
// TODO(user): common::test_certificates is a testonly target, consider
|
||||
// how to use instead of dupliciating the test cert here.
|
||||
static const unsigned char kTestRootCertificate[] = {
|
||||
0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
|
||||
0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5,
|
||||
0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78,
|
||||
0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f,
|
||||
0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca,
|
||||
0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b,
|
||||
0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46,
|
||||
0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01,
|
||||
0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c,
|
||||
0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14,
|
||||
0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd,
|
||||
0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6,
|
||||
0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde,
|
||||
0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31,
|
||||
0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a,
|
||||
0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90,
|
||||
0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49,
|
||||
0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f,
|
||||
0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda,
|
||||
0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17,
|
||||
0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30,
|
||||
0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf,
|
||||
0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16,
|
||||
0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42,
|
||||
0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9,
|
||||
0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4,
|
||||
0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a,
|
||||
0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72,
|
||||
0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde,
|
||||
0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04,
|
||||
0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b,
|
||||
0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33,
|
||||
0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67,
|
||||
0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02,
|
||||
0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a,
|
||||
0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35,
|
||||
0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe,
|
||||
0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49,
|
||||
0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6,
|
||||
0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58,
|
||||
0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce,
|
||||
0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27,
|
||||
0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5,
|
||||
0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c,
|
||||
0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e,
|
||||
0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12,
|
||||
0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54,
|
||||
0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8,
|
||||
0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1,
|
||||
0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1,
|
||||
0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8,
|
||||
0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4,
|
||||
0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9,
|
||||
0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd,
|
||||
0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9,
|
||||
0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f,
|
||||
0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92,
|
||||
0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15,
|
||||
0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0,
|
||||
0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9,
|
||||
0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1,
|
||||
0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2,
|
||||
0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c,
|
||||
0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36,
|
||||
0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35,
|
||||
0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7,
|
||||
0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44};
|
||||
|
||||
static const unsigned char kDevRootCertificate[] = {
|
||||
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xc3, 0x94, 0x88,
|
||||
0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
|
||||
0x81, 0x00, 0xc0, 0x00, 0x36, 0x6f, 0x8e, 0xe9, 0xcf, 0x86, 0xdb, 0xcd,
|
||||
0xdd, 0x4e, 0xfd, 0xcd, 0x45, 0xbf, 0x6d, 0x96, 0x05, 0x00, 0xb8, 0x72,
|
||||
0xff, 0x9c, 0xb4, 0x39, 0xa8, 0xd8, 0xc0, 0x09, 0x73, 0xc0, 0x24, 0x6a,
|
||||
0x39, 0x4d, 0x36, 0x3f, 0x9a, 0xe4, 0xb8, 0x76, 0xdc, 0x34, 0xe3, 0xee,
|
||||
0x5f, 0xdd, 0x13, 0x20, 0x08, 0xdc, 0x4e, 0x6f, 0x4e, 0x9f, 0xc0, 0x36,
|
||||
0xf9, 0xce, 0xc6, 0xb7, 0xdb, 0xe0, 0x51, 0x2d, 0x30, 0x0b, 0xae, 0x0a,
|
||||
0x20, 0xd2, 0x29, 0x3c, 0x2c, 0x1d, 0x87, 0x65, 0xeb, 0x5f, 0x93, 0xd7,
|
||||
0x3f, 0x12, 0x08, 0x50, 0x0e, 0x55, 0xf3, 0xf1, 0x19, 0xee, 0x18, 0x21,
|
||||
0x6e, 0xea, 0xb6, 0x0a, 0x4a, 0x0b, 0x9c, 0x72, 0x37, 0xeb, 0x0b, 0x68,
|
||||
0xfc, 0x52, 0x46, 0x62, 0xd0, 0xa2, 0x99, 0x66, 0xe2, 0x2b, 0x74, 0xdd,
|
||||
0x5c, 0xaf, 0x9a, 0x03, 0xc4, 0x5d, 0x93, 0xfb, 0xcd, 0x45, 0x9a, 0xee,
|
||||
0xfb, 0x7b, 0x18, 0x94, 0xc1, 0x8c, 0x82, 0x34, 0x7f, 0x02, 0x12, 0x21,
|
||||
0xfc, 0x40, 0xc1, 0x50, 0xc9, 0xf4, 0x7c, 0xd5, 0x96, 0xbe, 0x55, 0x7f,
|
||||
0x3c, 0x1d, 0x70, 0x34, 0xb4, 0xa2, 0x03, 0xc4, 0x3f, 0x89, 0x60, 0xe4,
|
||||
0x24, 0x09, 0x1a, 0x74, 0xc4, 0xb6, 0x39, 0xf0, 0x34, 0x60, 0x8e, 0xa7,
|
||||
0x5f, 0x02, 0x7f, 0xb9, 0x2a, 0xc5, 0xaa, 0xb2, 0x4c, 0x34, 0xd3, 0x5a,
|
||||
0x5d, 0xfa, 0x07, 0xf2, 0xb9, 0xb3, 0xc1, 0xba, 0xab, 0xbe, 0x89, 0x99,
|
||||
0xe3, 0x6d, 0x9b, 0xa9, 0xd3, 0xaf, 0x2a, 0x08, 0x76, 0xf3, 0x0e, 0xc9,
|
||||
0xe0, 0xb3, 0xbf, 0x51, 0x0c, 0xc5, 0xf4, 0xf3, 0x15, 0x7b, 0x08, 0x11,
|
||||
0x8f, 0x61, 0x1f, 0x61, 0x64, 0xdb, 0x15, 0x84, 0x5b, 0x8a, 0xd1, 0x28,
|
||||
0x40, 0xde, 0xc5, 0x32, 0xb5, 0xad, 0xad, 0x65, 0x4c, 0xf5, 0xf7, 0xd1,
|
||||
0x90, 0x14, 0x5d, 0xc2, 0x85, 0x98, 0xcc, 0xe9, 0xe6, 0x95, 0x42, 0xe1,
|
||||
0x3e, 0xfc, 0x7f, 0xc4, 0x49, 0xed, 0x9c, 0xe4, 0x49, 0x3f, 0x03, 0x1b,
|
||||
0x0d, 0xa0, 0xfb, 0xf5, 0x38, 0x49, 0xd2, 0xdf, 0xa3, 0x88, 0xb2, 0x76,
|
||||
0x93, 0x08, 0x20, 0x18, 0xfe, 0xdc, 0x72, 0x6c, 0x6e, 0xbf, 0x61, 0x37,
|
||||
0x03, 0xdb, 0xe5, 0x72, 0x68, 0xe0, 0x99, 0x2f, 0xb9, 0xe0, 0x2e, 0xbb,
|
||||
0x9f, 0x96, 0x36, 0x61, 0xaa, 0x2d, 0xa4, 0x93, 0xe8, 0x50, 0x58, 0xe6,
|
||||
0x61, 0xe1, 0x14, 0xcf, 0xac, 0x86, 0x98, 0x7f, 0x3c, 0x67, 0x16, 0xce,
|
||||
0xb8, 0x70, 0x90, 0x3a, 0x5a, 0xd4, 0xe1, 0xe2, 0x35, 0x98, 0xbf, 0x93,
|
||||
0x41, 0x11, 0xb2, 0x44, 0xb2, 0x64, 0xc2, 0xe7, 0x09, 0x45, 0xb7, 0x6f,
|
||||
0xb0, 0xbd, 0x6e, 0xe8, 0x67, 0xfa, 0x8d, 0xd4, 0xfa, 0x4b, 0xef, 0xa8,
|
||||
0x9d, 0x8a, 0x0a, 0xd9, 0x14, 0x77, 0x09, 0x11, 0x9e, 0xc3, 0x50, 0x14,
|
||||
0x6c, 0x45, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x17, 0x01,
|
||||
0x60, 0x24, 0xe1, 0xfd, 0x75, 0x60, 0x17, 0x5c, 0x5e, 0x6f, 0x9f, 0x7f,
|
||||
0xdf, 0xee, 0xf0, 0xf7, 0x7d, 0xb2, 0x50, 0x65, 0x36, 0x26, 0x14, 0x19,
|
||||
0x01, 0x5e, 0x98, 0x94, 0x65, 0x97, 0x83, 0xaa, 0x4a, 0x2b, 0x98, 0x2e,
|
||||
0x02, 0xf3, 0xb2, 0xc9, 0xb2, 0xed, 0xd3, 0x1b, 0x20, 0x27, 0x9e, 0xe1,
|
||||
0x25, 0xc7, 0x86, 0xf0, 0x66, 0x68, 0x5d, 0xd2, 0x3d, 0xa7, 0xbb, 0xbc,
|
||||
0x22, 0xfc, 0x29, 0xfa, 0x17, 0x16, 0xf4, 0xa2, 0x00, 0x10, 0x87, 0xb4,
|
||||
0x5d, 0x51, 0x45, 0x6b, 0xc8, 0xf4, 0x6b, 0xcc, 0x92, 0x91, 0xe7, 0xa7,
|
||||
0x93, 0xbc, 0xc7, 0x2e, 0xdc, 0xac, 0x82, 0x2b, 0x85, 0x56, 0x7b, 0xae,
|
||||
0xf2, 0xd8, 0xda, 0xa6, 0xd7, 0xfa, 0x6d, 0x70, 0x2a, 0x2e, 0xcf, 0x69,
|
||||
0xef, 0x57, 0x91, 0xa7, 0xaa, 0x40, 0x15, 0x4a, 0x49, 0x1b, 0xbc, 0x36,
|
||||
0xbb, 0x1c, 0x94, 0x33, 0x36, 0x61, 0x22, 0x9d, 0x22, 0x66, 0xf0, 0x88,
|
||||
0x5e, 0x7c, 0x3c, 0xa5, 0xff, 0x81, 0xcf, 0x1a, 0x44, 0xa1, 0x2b, 0xdf,
|
||||
0xc9, 0x3d, 0xd5, 0xc7, 0xc7, 0x3a, 0x75, 0xac, 0x29, 0xfa, 0xfd, 0x5b,
|
||||
0xda, 0xf5, 0x8f, 0xd9, 0xdf, 0x08, 0xa4, 0x8d, 0x19, 0x4a, 0xa4, 0x79,
|
||||
0x6e, 0x47, 0xf6, 0x07, 0xe0, 0xbd, 0xbf, 0x30, 0x3a, 0xf9, 0xf5, 0xc0,
|
||||
0x90, 0x6d, 0x70, 0x27, 0x44, 0xa8, 0x5e, 0x70, 0xcd, 0x43, 0x3e, 0xaf,
|
||||
0xf0, 0xd7, 0x20, 0xd3, 0x5e, 0x97, 0x2d, 0x32, 0x1a, 0x3d, 0x2d, 0x0f,
|
||||
0x0f, 0xcf, 0xac, 0x4e, 0x88, 0x75, 0x98, 0x6c, 0xfa, 0xe8, 0x42, 0x58,
|
||||
0x99, 0xaa, 0x45, 0x0c, 0x41, 0x0c, 0x6e, 0x27, 0x58, 0x57, 0xd2, 0x5b,
|
||||
0x82, 0x3d, 0x75, 0x2f, 0x9e, 0xf3, 0xe4, 0x00, 0xcf, 0x91, 0x48, 0x25,
|
||||
0xca, 0x98, 0xf2, 0x91, 0x6b, 0x41, 0xa5, 0xe8, 0xcd, 0x64, 0xa7, 0x2e,
|
||||
0x78, 0xc7, 0x76, 0x82, 0x3f, 0xf8, 0x57, 0x8a, 0x9d, 0x78, 0x25, 0xad,
|
||||
0xf3, 0x1a, 0x8b, 0xfc, 0x83, 0x9a, 0x98, 0x87, 0xe4, 0x55, 0x3e, 0x1c,
|
||||
0xa7, 0x80, 0x8f, 0xd6, 0x76, 0xab, 0x03, 0xc7, 0x05, 0x66, 0xc3, 0xa0,
|
||||
0x4c, 0x33, 0x1f, 0x39, 0x74, 0x1b, 0x2a, 0xbf, 0xe6, 0xb0, 0x9f, 0x6b,
|
||||
0xc1, 0xd6, 0xd3, 0xf4, 0x46, 0x9b, 0xf3, 0xab, 0xca, 0x2e, 0x88, 0x3d,
|
||||
0x84, 0x5f, 0xc9, 0x9b, 0x47, 0xbb, 0x57, 0x64, 0x08, 0x0e, 0x18, 0x74,
|
||||
0x83, 0x44, 0xd4, 0xc3, 0x18, 0x97, 0xcf, 0x89, 0x6a, 0x49, 0x51, 0xc6,
|
||||
0xff, 0x8d, 0x39, 0xc5, 0x23, 0xf9, 0xd5, 0x01, 0xd7, 0x2f, 0xa9, 0xa5,
|
||||
0x5d, 0xa9, 0xf3, 0xc9, 0xfd, 0xc4, 0x52, 0x19, 0x7d, 0xf6, 0xa4, 0x2c,
|
||||
0x0c, 0xa0, 0x07, 0xdf, 0x7b, 0x44, 0xd7, 0xe5, 0xbf, 0x57, 0x87, 0xc9,
|
||||
0x8c, 0xfe, 0x30, 0xb2, 0x89, 0x5d, 0x00, 0x03, 0x3b, 0xe5};
|
||||
|
||||
static const unsigned char kProdRootCertificate[] = {
|
||||
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xdd, 0x94, 0x88,
|
||||
0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
|
||||
0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11,
|
||||
0x97, 0x09, 0xe8, 0x68, 0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d,
|
||||
0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0,
|
||||
0x57, 0x03, 0x53, 0x4c, 0xf6, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3,
|
||||
0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe,
|
||||
0x66, 0xdf, 0xc5, 0x21, 0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46,
|
||||
0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22,
|
||||
0xbe, 0x34, 0x23, 0x8b, 0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48,
|
||||
0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f,
|
||||
0x0f, 0x92, 0xd6, 0x4c, 0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10,
|
||||
0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53,
|
||||
0x9a, 0x97, 0xeb, 0x84, 0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26,
|
||||
0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e,
|
||||
0x72, 0xa0, 0xfa, 0xc1, 0xbd, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4,
|
||||
0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc,
|
||||
0xfd, 0x82, 0x48, 0x2b, 0xb7, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba,
|
||||
0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e,
|
||||
0x65, 0xf5, 0xc8, 0x6c, 0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca,
|
||||
0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98,
|
||||
0x79, 0x52, 0x5e, 0x45, 0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba,
|
||||
0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa,
|
||||
0x4f, 0x5e, 0x48, 0x77, 0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19,
|
||||
0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf,
|
||||
0xe4, 0x29, 0x06, 0x5e, 0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed,
|
||||
0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35,
|
||||
0xec, 0x1f, 0x11, 0xb3, 0x24, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c,
|
||||
0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d,
|
||||
0x68, 0x15, 0x84, 0xff, 0xa5, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4,
|
||||
0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12,
|
||||
0xd2, 0x88, 0x6d, 0x41, 0x3d, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4,
|
||||
0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5,
|
||||
0xce, 0xa6, 0x96, 0x55, 0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33,
|
||||
0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x58, 0xf1,
|
||||
0xd6, 0x4d, 0x04, 0x09, 0x7b, 0xdf, 0xd7, 0xef, 0x5d, 0x3b, 0x02, 0x39,
|
||||
0x17, 0xfa, 0x14, 0x36, 0x75, 0x4a, 0x38, 0x67, 0x85, 0x57, 0x12, 0xa7,
|
||||
0x14, 0xee, 0x35, 0x16, 0xd5, 0x3d, 0xbf, 0x42, 0x86, 0xf6, 0x69, 0x00,
|
||||
0x76, 0xcd, 0x93, 0xf4, 0x7c, 0xb2, 0xdf, 0x9e, 0x44, 0xcd, 0x4c, 0xd4,
|
||||
0xae, 0x09, 0x18, 0x53, 0x44, 0x32, 0xec, 0xe0, 0x61, 0x1b, 0xe5, 0xda,
|
||||
0x13, 0xd3, 0x55, 0xc5, 0xdd, 0x1a, 0xcb, 0x90, 0x1e, 0x7e, 0x5b, 0xc6,
|
||||
0xe9, 0x0f, 0x22, 0x9f, 0xbe, 0x85, 0x02, 0xfe, 0x90, 0x31, 0xcc, 0x6b,
|
||||
0x03, 0x84, 0xbd, 0x22, 0xc4, 0x55, 0xfa, 0xf5, 0xf2, 0x08, 0xcd, 0x65,
|
||||
0x41, 0x58, 0xe8, 0x7d, 0x29, 0xda, 0x04, 0x58, 0x82, 0xf5, 0x37, 0x69,
|
||||
0xbc, 0xf3, 0x5a, 0x57, 0x84, 0x17, 0x7b, 0x32, 0x87, 0x70, 0xb2, 0xb0,
|
||||
0x76, 0x9c, 0xb2, 0xc3, 0x15, 0xd1, 0x11, 0x26, 0x2a, 0x23, 0x75, 0x99,
|
||||
0x3e, 0xb9, 0x77, 0x22, 0x32, 0x0d, 0xbc, 0x1a, 0x19, 0xc1, 0xd5, 0x65,
|
||||
0x90, 0x76, 0x55, 0x74, 0x0f, 0x0e, 0x69, 0x4d, 0x5f, 0x4d, 0x8f, 0x19,
|
||||
0xaf, 0xdf, 0xd6, 0x16, 0x31, 0x94, 0xa8, 0x92, 0x5f, 0x4f, 0xbc, 0x7a,
|
||||
0x31, 0xf8, 0xae, 0x8e, 0xad, 0x33, 0xb7, 0xe9, 0x30, 0xd0, 0x8c, 0x0a,
|
||||
0x8a, 0x6c, 0x83, 0x35, 0xf8, 0x8a, 0x81, 0xb2, 0xfe, 0x1c, 0x88, 0xac,
|
||||
0x2a, 0x66, 0xc5, 0xff, 0xbd, 0xe6, 0x17, 0xd0, 0x62, 0x0b, 0xdc, 0x8a,
|
||||
0x45, 0xf7, 0xb0, 0x3e, 0x5a, 0xc8, 0x1e, 0x4a, 0x24, 0x2f, 0x6c, 0xa5,
|
||||
0xe3, 0x1c, 0x88, 0x14, 0x83, 0xd5, 0xc5, 0xef, 0x5e, 0x9f, 0x3d, 0x85,
|
||||
0x45, 0x73, 0xe2, 0x6b, 0x50, 0x52, 0x57, 0x4c, 0xfb, 0x92, 0x6c, 0x66,
|
||||
0x75, 0x8a, 0xd6, 0x0d, 0x1b, 0xae, 0xf3, 0xec, 0xaf, 0x51, 0x22, 0x03,
|
||||
0x5d, 0x0a, 0x2e, 0x63, 0x93, 0x9c, 0x0b, 0x01, 0x20, 0xa8, 0xa9, 0x84,
|
||||
0x2e, 0x17, 0xca, 0xae, 0x73, 0xec, 0x22, 0x1b, 0x79, 0xae, 0xf6, 0xa0,
|
||||
0x72, 0x2c, 0xdf, 0x07, 0x47, 0xdb, 0x88, 0x86, 0x30, 0x14, 0x78, 0x21,
|
||||
0x11, 0x22, 0x88, 0xac, 0xd7, 0x54, 0x74, 0xf9, 0xf3, 0x26, 0xc2, 0xa5,
|
||||
0x56, 0xc8, 0x56, 0x4f, 0x00, 0x29, 0x1d, 0x08, 0x7b, 0x7a, 0xfb, 0x95,
|
||||
0x89, 0xc3, 0xee, 0x98, 0x54, 0x9e, 0x3c, 0x6b, 0x94, 0x05, 0x13, 0x12,
|
||||
0xf6, 0x71, 0xb9, 0xab, 0x13, 0xc3, 0x0c, 0x9b, 0x46, 0x08, 0x7b, 0x3d,
|
||||
0x32, 0x6a, 0x68, 0xca, 0x1e, 0x9c, 0x90, 0x62, 0xc5, 0xed, 0x10, 0xb9,
|
||||
0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd, 0xc4, 0x46, 0xf5, 0xa3,
|
||||
0x62, 0x13, 0x74, 0x02, 0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf,
|
||||
0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5, 0x02, 0x0c};
|
||||
} // namespace
|
||||
|
||||
// Caches an individual signature for a certificate with a specific serial
|
||||
// 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) {}
|
||||
|
||||
std::string signed_cert;
|
||||
std::string signature;
|
||||
std::string signer_serial;
|
||||
};
|
||||
|
||||
// Map of certificate serial number to its signature.
|
||||
typedef std::map<std::string, VerifiedCertSignature> VerifiedCertSignatures;
|
||||
class VerifiedCertSignatureCache {
|
||||
public:
|
||||
explicit VerifiedCertSignatureCache(const RsaKeyFactory* key_factory)
|
||||
: key_factory_(key_factory) {}
|
||||
|
||||
// Checks cache, on miss, uses public key. If successful, adds to
|
||||
// cache.
|
||||
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) {
|
||||
{
|
||||
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.
|
||||
return Status(error_space, INVALID_SIGNATURE,
|
||||
"cached-signature-mismatch");
|
||||
}
|
||||
// Cached signature match.
|
||||
return OkStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// Cache miss. Verify signature.
|
||||
std::unique_ptr<RsaPublicKey> signer_key(
|
||||
key_factory_->CreateFromPkcs1PublicKey(signer_public_key));
|
||||
if (!signer_key) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-public-key");
|
||||
}
|
||||
if (!signer_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));
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
private:
|
||||
VerifiedCertSignatures signature_cache_ GUARDED_BY(&signature_cache_mutex_);
|
||||
absl::Mutex signature_cache_mutex_;
|
||||
const RsaKeyFactory* key_factory_;
|
||||
};
|
||||
|
||||
Status DrmRootCertificate::CreateByType(
|
||||
CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
CHECK(cert);
|
||||
|
||||
return Create(cert_type, absl::make_unique<RsaKeyFactory>(), cert);
|
||||
}
|
||||
|
||||
std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
|
||||
CertificateType cert_type, Status* status) {
|
||||
CHECK(status);
|
||||
|
||||
std::unique_ptr<DrmRootCertificate> new_root_cert;
|
||||
*status = CreateByType(cert_type, &new_root_cert);
|
||||
return new_root_cert;
|
||||
}
|
||||
|
||||
Status DrmRootCertificate::CreateByTypeString(
|
||||
const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
CHECK(cert);
|
||||
|
||||
CertificateType cert_type;
|
||||
if (cert_type_string == kDevelopmentString) {
|
||||
cert_type = kCertificateTypeDevelopment;
|
||||
} else if (cert_type_string == kProductionString) {
|
||||
cert_type = kCertificateTypeProduction;
|
||||
} else if (cert_type_string == kTestingString) {
|
||||
cert_type = kCertificateTypeTesting;
|
||||
} else {
|
||||
return Status(error_space, INVALID_PARAMETER,
|
||||
absl::StrCat("invalid-certificate-type ", cert_type_string));
|
||||
}
|
||||
|
||||
return CreateByType(cert_type, cert);
|
||||
}
|
||||
|
||||
Status DrmRootCertificate::Create(CertificateType cert_type,
|
||||
std::unique_ptr<RsaKeyFactory> key_factory,
|
||||
std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
DCHECK(cert);
|
||||
|
||||
std::string serialized_certificate;
|
||||
switch (cert_type) {
|
||||
case kCertificateTypeProduction: {
|
||||
serialized_certificate.assign(
|
||||
kProdRootCertificate,
|
||||
kProdRootCertificate + sizeof(kProdRootCertificate));
|
||||
break;
|
||||
}
|
||||
case kCertificateTypeDevelopment: {
|
||||
serialized_certificate.assign(
|
||||
kDevRootCertificate,
|
||||
kDevRootCertificate + sizeof(kDevRootCertificate));
|
||||
break;
|
||||
}
|
||||
case kCertificateTypeTesting: {
|
||||
serialized_certificate.assign(
|
||||
kTestRootCertificate,
|
||||
kTestRootCertificate + sizeof(kTestRootCertificate));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return Status(error_space, INVALID_PARAMETER, "invalid-certificate-type");
|
||||
}
|
||||
|
||||
SignedDrmCertificate signed_root_cert;
|
||||
if (!signed_root_cert.ParseFromString(serialized_certificate)) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signed-root-cert-deserialize-fail");
|
||||
}
|
||||
DrmCertificate root_cert;
|
||||
if (!signed_root_cert.has_drm_certificate()) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-device-certificate");
|
||||
}
|
||||
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"root-cert-deserialize-fail");
|
||||
}
|
||||
if (!root_cert.has_public_key()) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-cert-public-key");
|
||||
}
|
||||
if (!signed_root_cert.has_signature()) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-certificate-signature");
|
||||
}
|
||||
|
||||
std::unique_ptr<RsaPublicKey> public_key(
|
||||
key_factory->CreateFromPkcs1PublicKey(root_cert.public_key()));
|
||||
if (!public_key) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-public-key");
|
||||
}
|
||||
if (!public_key->VerifySignature(signed_root_cert.drm_certificate(),
|
||||
signed_root_cert.signature())) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-certificate-signature");
|
||||
}
|
||||
|
||||
cert->reset(new DrmRootCertificate(
|
||||
cert_type, serialized_certificate, root_cert.serial_number(),
|
||||
root_cert.public_key(), std::move(key_factory)));
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
DrmRootCertificate::DrmRootCertificate(
|
||||
CertificateType type, const std::string& serialized_certificate,
|
||||
const std::string& serial_number, const std::string& public_key,
|
||||
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())) {}
|
||||
|
||||
DrmRootCertificate::~DrmRootCertificate() {}
|
||||
|
||||
std::string DrmRootCertificate::GetDigest() const {
|
||||
return absl::BytesToHexString(Sha256_Hash(serialized_certificate_));
|
||||
}
|
||||
|
||||
Status DrmRootCertificate::VerifyCertificate(
|
||||
const std::string& serialized_certificate,
|
||||
SignedDrmCertificate* signed_certificate,
|
||||
DrmCertificate* certificate) const {
|
||||
std::unique_ptr<SignedDrmCertificate> local_signed_certificate;
|
||||
if (!signed_certificate) {
|
||||
local_signed_certificate = absl::make_unique<SignedDrmCertificate>();
|
||||
signed_certificate = local_signed_certificate.get();
|
||||
}
|
||||
if (!signed_certificate->ParseFromString(serialized_certificate)) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signed-drm-certificate");
|
||||
}
|
||||
|
||||
std::unique_ptr<DrmCertificate> local_certificate;
|
||||
if (!certificate) {
|
||||
local_certificate = absl::make_unique<DrmCertificate>();
|
||||
certificate = local_certificate.get();
|
||||
}
|
||||
if (signed_certificate->drm_certificate().empty() ||
|
||||
!certificate->ParseFromString(signed_certificate->drm_certificate())) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-drm-certificate");
|
||||
}
|
||||
if (certificate->serial_number().empty()) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-serial-number");
|
||||
}
|
||||
if (!certificate->has_creation_time_seconds()) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-creation-time");
|
||||
}
|
||||
if (certificate->public_key().empty()) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key");
|
||||
}
|
||||
|
||||
// Verify signature chain, but do not use cache for leaf certificates.
|
||||
return VerifySignatures(*signed_certificate, certificate->serial_number(),
|
||||
!kUseCache);
|
||||
}
|
||||
|
||||
// Recursively verifies certificates with their signing certs or the root.
|
||||
// use_cache should be false when initially called so that signatures do not
|
||||
// cached leaf certificates not signed with the root certificate, such as for
|
||||
// the case of device-unique device certificates.
|
||||
// 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 {
|
||||
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_);
|
||||
}
|
||||
|
||||
DrmCertificate signer;
|
||||
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate");
|
||||
}
|
||||
|
||||
// Verify the signer before verifying signed_cert.
|
||||
Status status =
|
||||
VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache);
|
||||
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());
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
std::unique_ptr<RsaPublicKey> signer_public_key(
|
||||
key_factory_->CreateFromPkcs1PublicKey(signer.public_key()));
|
||||
if (!signer_public_key) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-leaf-signer-public-key");
|
||||
}
|
||||
if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(),
|
||||
signed_cert.signature())) {
|
||||
return Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature");
|
||||
}
|
||||
}
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
106
common/drm_root_certificate.h
Normal file
106
common/drm_root_certificate.h
Normal file
@@ -0,0 +1,106 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Root device certificate holder class which deserializes, validates,
|
||||
// and extracts the root certificate public key.
|
||||
|
||||
#ifndef COMMON_DRM_ROOT_CERTIFICATE_H_
|
||||
#define COMMON_DRM_ROOT_CERTIFICATE_H_
|
||||
|
||||
// common_typos_disable. Successful / successfull.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "common/status.h"
|
||||
|
||||
#include "common/certificate_type.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class DrmCertificate;
|
||||
class RsaKeyFactory;
|
||||
class RsaPublicKey;
|
||||
class SignedDrmCertificate;
|
||||
class VerifiedCertSignatureCache;
|
||||
|
||||
// Root certificate and certificate chain verifier with internal caching.
|
||||
// This object is thread-safe.
|
||||
class DrmRootCertificate {
|
||||
public:
|
||||
virtual ~DrmRootCertificate();
|
||||
|
||||
// Creates a DrmRootCertificate object given a certificate type.
|
||||
// |cert| may not be nullptr, and it points to a
|
||||
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
|
||||
// created const DrmRootCertificate* if successful. The caller assumes
|
||||
// ownership of the new DrmRootCertificate. This method returns
|
||||
// Status::OK on success, or appropriate error status otherwise.
|
||||
static Status CreateByType(CertificateType cert_type,
|
||||
std::unique_ptr<DrmRootCertificate>* cert);
|
||||
|
||||
// Variant on the method above to make CLIF happy until b/110539622 is fixed.
|
||||
static std::unique_ptr<DrmRootCertificate> CreateByType(
|
||||
CertificateType cert_type, Status* status);
|
||||
|
||||
// Creates a DrmRootCertificate object given a certificate type std::string, which
|
||||
// must be one of "prod", "qa", or "test".
|
||||
// |cert| may not be nullptr, and it points to a
|
||||
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly
|
||||
// created const DrmRootCertificate* if successful. The caller assumes
|
||||
// ownership of the new DrmRootCertificate. This method returns
|
||||
// Status::OK on success, or appropriate error status otherwise.
|
||||
static Status CreateByTypeString(const std::string& cert_type_string,
|
||||
std::unique_ptr<DrmRootCertificate>* cert);
|
||||
|
||||
// |certificate| will contgain the DRM certificate upon successful return.
|
||||
// May be null.
|
||||
// Returns Status::OK if successful, or an appropriate error code otherwise.
|
||||
virtual Status VerifyCertificate(const std::string& serialized_certificate,
|
||||
SignedDrmCertificate* signed_certificate,
|
||||
DrmCertificate* certificate) const;
|
||||
|
||||
// Returns the hex-encoded SHA-256 digest for this certificate.
|
||||
virtual std::string GetDigest() const;
|
||||
|
||||
const CertificateType type() const { return type_; }
|
||||
|
||||
const std::string& public_key() const { return public_key_; }
|
||||
|
||||
protected:
|
||||
DrmRootCertificate(CertificateType cert_type,
|
||||
const std::string& serialized_certificate,
|
||||
const std::string& serial_number, const std::string& public_key,
|
||||
std::unique_ptr<RsaKeyFactory> key_factory);
|
||||
|
||||
private:
|
||||
friend class DrmRootCertificateTest;
|
||||
|
||||
static Status Create(CertificateType cert_type,
|
||||
std::unique_ptr<RsaKeyFactory> key_factory,
|
||||
std::unique_ptr<DrmRootCertificate>* cert);
|
||||
|
||||
Status VerifySignatures(const SignedDrmCertificate& signed_cert,
|
||||
const std::string& cert_serial_number,
|
||||
bool use_cache) const;
|
||||
|
||||
CertificateType type_;
|
||||
std::string serialized_certificate_;
|
||||
std::string serial_number_;
|
||||
std::string public_key_;
|
||||
std::unique_ptr<RsaKeyFactory> key_factory_;
|
||||
mutable std::unique_ptr<VerifiedCertSignatureCache> signature_cache_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmRootCertificate);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_DRM_ROOT_CERTIFICATE_H_
|
||||
262
common/drm_root_certificate_test.cc
Normal file
262
common/drm_root_certificate_test.cc
Normal file
@@ -0,0 +1,262 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Unit tests for drm_root_certificate.cc
|
||||
|
||||
#include "common/drm_root_certificate.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "google/protobuf/util/message_differencer.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/test_drm_certificates.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
using google::protobuf::util::MessageDifferencer;
|
||||
|
||||
namespace widevine {
|
||||
|
||||
TEST(DrmRootCertificateCreateTest, TestCertificate) {
|
||||
const std::string kTestCertificateHash(
|
||||
"49f917b1bdfed78002a58e799a58e940"
|
||||
"1fffaaed9d8d80752782b066757e2c8c");
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeTesting, &root_cert));
|
||||
ASSERT_TRUE(root_cert != nullptr);
|
||||
EXPECT_EQ(kTestCertificateHash, root_cert->GetDigest());
|
||||
}
|
||||
|
||||
TEST(DrmRootCertificateCreateTest, DevCertificate) {
|
||||
const std::string kDevelopmentCertificateHash(
|
||||
"0e25ee95476a770f30b98ac5ef778b3f"
|
||||
"137b66c29385b84f547a361b4724b17d");
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeDevelopment, &root_cert));
|
||||
ASSERT_TRUE(root_cert != nullptr);
|
||||
EXPECT_EQ(kDevelopmentCertificateHash, root_cert->GetDigest());
|
||||
}
|
||||
|
||||
TEST(DrmRootCertificateCreateTest, ProdCertificate) {
|
||||
const std::string kProductionCertificateHash(
|
||||
"d62fdabc9286648a81f7d3bedaf2f5a5"
|
||||
"27bbad39bc38da034ba98a21569adb9b");
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeProduction, &root_cert));
|
||||
ASSERT_TRUE(root_cert != nullptr);
|
||||
EXPECT_EQ(kProductionCertificateHash, root_cert->GetDigest());
|
||||
}
|
||||
|
||||
TEST(DrmRootCertificateTestCertificatesTest, Success) {
|
||||
TestDrmCertificates test_certs;
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
ASSERT_TRUE(
|
||||
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert)
|
||||
.ok());
|
||||
EXPECT_TRUE(root_cert
|
||||
->VerifyCertificate(test_certs.test_root_certificate(),
|
||||
nullptr, nullptr)
|
||||
.ok());
|
||||
EXPECT_TRUE(
|
||||
root_cert
|
||||
->VerifyCertificate(test_certs.test_intermediate_certificate(),
|
||||
nullptr, nullptr)
|
||||
.ok());
|
||||
EXPECT_TRUE(root_cert
|
||||
->VerifyCertificate(test_certs.test_user_device_certificate(),
|
||||
nullptr, nullptr)
|
||||
.ok());
|
||||
EXPECT_TRUE(root_cert
|
||||
->VerifyCertificate(test_certs.test_service_certificate(),
|
||||
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()));
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeTesting, &root_cert_));
|
||||
}
|
||||
|
||||
void GenerateSignedDrmCertificate() {
|
||||
SignedDrmCertificate* current_sc(&signed_drm_certificate_);
|
||||
ASSERT_TRUE(drm_certificates_[2].SerializeToString(
|
||||
current_sc->mutable_drm_certificate()));
|
||||
ASSERT_TRUE(private_keys_[1]->GenerateSignature(
|
||||
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
||||
|
||||
current_sc = current_sc->mutable_signer();
|
||||
ASSERT_TRUE(drm_certificates_[1].SerializeToString(
|
||||
current_sc->mutable_drm_certificate()));
|
||||
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
|
||||
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
||||
|
||||
current_sc = current_sc->mutable_signer();
|
||||
ASSERT_TRUE(drm_certificates_[0].SerializeToString(
|
||||
current_sc->mutable_drm_certificate()));
|
||||
ASSERT_TRUE(private_keys_[0]->GenerateSignature(
|
||||
current_sc->drm_certificate(), current_sc->mutable_signature()));
|
||||
}
|
||||
|
||||
RsaTestKeys test_keys_;
|
||||
std::vector<std::unique_ptr<RsaPrivateKey>> private_keys_;
|
||||
SignedDrmCertificate signed_drm_certificate_;
|
||||
DrmCertificate drm_certificates_[3];
|
||||
std::unique_ptr<DrmRootCertificate> root_cert_;
|
||||
};
|
||||
|
||||
TEST_F(DrmRootCertificateTest, SuccessNoOutput) {
|
||||
GenerateSignedDrmCertificate();
|
||||
ASSERT_EQ(OkStatus(),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, SuccessWithOutput) {
|
||||
GenerateSignedDrmCertificate();
|
||||
SignedDrmCertificate out_signed_cert;
|
||||
DrmCertificate out_cert;
|
||||
ASSERT_EQ(OkStatus(), root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(),
|
||||
&out_signed_cert, &out_cert));
|
||||
EXPECT_TRUE(
|
||||
MessageDifferencer::Equals(out_signed_cert, signed_drm_certificate_));
|
||||
EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2]));
|
||||
}
|
||||
|
||||
TEST_F(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) {
|
||||
GenerateSignedDrmCertificate();
|
||||
signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage");
|
||||
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
|
||||
GenerateSignedDrmCertificate();
|
||||
signed_drm_certificate_.clear_drm_certificate();
|
||||
EXPECT_EQ(
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"),
|
||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||
nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) {
|
||||
GenerateSignedDrmCertificate();
|
||||
signed_drm_certificate_.set_drm_certificate("junk");
|
||||
EXPECT_EQ(
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"),
|
||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||
nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, InvalidPublicKey) {
|
||||
drm_certificates_[0].set_public_key("rubbish");
|
||||
GenerateSignedDrmCertificate();
|
||||
EXPECT_EQ(
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-signer-public-key"),
|
||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||
nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, MissingPublicKey) {
|
||||
drm_certificates_[2].clear_public_key();
|
||||
GenerateSignedDrmCertificate();
|
||||
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, MissingCreationTime) {
|
||||
drm_certificates_[2].clear_creation_time_seconds();
|
||||
GenerateSignedDrmCertificate();
|
||||
EXPECT_EQ(
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE, "missing-creation-time"),
|
||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||
nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, MissingSerialNumber) {
|
||||
drm_certificates_[2].set_serial_number("");
|
||||
GenerateSignedDrmCertificate();
|
||||
EXPECT_EQ(
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE, "missing-serial-number"),
|
||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||
nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
|
||||
GenerateSignedDrmCertificate();
|
||||
signed_drm_certificate_.mutable_signer()->set_signature(
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
||||
EXPECT_EQ(
|
||||
Status(error_space, INVALID_SIGNATURE, "cache-miss-invalid-signature"),
|
||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||
nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) {
|
||||
GenerateSignedDrmCertificate();
|
||||
// Verify and cache.
|
||||
ASSERT_EQ(OkStatus(),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
|
||||
// Verify success using cache.
|
||||
ASSERT_EQ(OkStatus(),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
|
||||
// Verify failure using cache.
|
||||
signed_drm_certificate_.mutable_signer()->set_signature(
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
||||
EXPECT_EQ(Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
286
common/drm_service_certificate.cc
Normal file
286
common/drm_service_certificate.cc
Normal file
@@ -0,0 +1,286 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/drm_service_certificate.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "base/thread_annotations.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_util.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
|
||||
// Class used to hold global service certificate map.
|
||||
class DrmServiceCertificateMap {
|
||||
public:
|
||||
DrmServiceCertificateMap();
|
||||
~DrmServiceCertificateMap();
|
||||
|
||||
DrmServiceCertificateMap(const DrmServiceCertificateMap&) = delete;
|
||||
DrmServiceCertificateMap& operator=(const DrmServiceCertificateMap&) = delete;
|
||||
|
||||
void Reset();
|
||||
void AddCert(std::unique_ptr<DrmServiceCertificate> new_cert);
|
||||
void ClearDefaultDrmServiceCertificate();
|
||||
const DrmServiceCertificate* GetDefaultCert();
|
||||
const DrmServiceCertificate* GetCert(const std::string& serial_number);
|
||||
|
||||
static DrmServiceCertificateMap* GetInstance();
|
||||
|
||||
private:
|
||||
absl::Mutex mutex_;
|
||||
// Certificate serial number to certificate map.
|
||||
std::map<std::string, std::unique_ptr<DrmServiceCertificate>> map_
|
||||
GUARDED_BY(mutex_);
|
||||
DrmServiceCertificate* default_cert_ GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
DrmServiceCertificateMap::DrmServiceCertificateMap() : default_cert_(nullptr) {}
|
||||
DrmServiceCertificateMap::~DrmServiceCertificateMap() { Reset(); }
|
||||
|
||||
void DrmServiceCertificateMap::Reset() {
|
||||
absl::WriterMutexLock lock(&mutex_);
|
||||
map_.clear();
|
||||
default_cert_ = nullptr;
|
||||
}
|
||||
|
||||
void DrmServiceCertificateMap::AddCert(
|
||||
std::unique_ptr<DrmServiceCertificate> new_cert) {
|
||||
absl::WriterMutexLock lock(&mutex_);
|
||||
|
||||
std::unique_ptr<DrmServiceCertificate>* previous_cert =
|
||||
gtl::FindOrNull(map_, new_cert->serial_number());
|
||||
if (previous_cert != nullptr) {
|
||||
if (default_cert_ == previous_cert->get()) {
|
||||
default_cert_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (default_cert_ == nullptr) {
|
||||
default_cert_ = new_cert.get();
|
||||
}
|
||||
const std::string& serial_number = new_cert->serial_number();
|
||||
map_[serial_number] = std::move(new_cert);
|
||||
}
|
||||
|
||||
void DrmServiceCertificateMap::ClearDefaultDrmServiceCertificate() {
|
||||
absl::WriterMutexLock lock(&mutex_);
|
||||
default_cert_ = nullptr;
|
||||
}
|
||||
|
||||
const DrmServiceCertificate* DrmServiceCertificateMap::GetDefaultCert() {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
return default_cert_;
|
||||
}
|
||||
|
||||
const DrmServiceCertificate* DrmServiceCertificateMap::GetCert(
|
||||
const std::string& serial_number) {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
return map_[serial_number].get();
|
||||
}
|
||||
|
||||
DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
|
||||
static auto* const kInstance = new DrmServiceCertificateMap();
|
||||
return kInstance;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status DrmServiceCertificate::AddDrmServiceCertificate(
|
||||
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;
|
||||
Status status =
|
||||
root_drm_cert->VerifyCertificate(service_certificate, nullptr, &drm_cert);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (drm_cert.type() != DrmCertificate::SERVICE) {
|
||||
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"not-service-certificate");
|
||||
}
|
||||
if (drm_cert.provider_id().empty()) {
|
||||
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"missing-certificate-service-id");
|
||||
}
|
||||
std::unique_ptr<RsaPublicKey> public_key(
|
||||
RsaPublicKey::Create(drm_cert.public_key()));
|
||||
if (!public_key) {
|
||||
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"invalid-certificate-public-key");
|
||||
}
|
||||
std::string pkcs1_key;
|
||||
if (!rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey(
|
||||
service_private_key, service_private_key_passphrase, &pkcs1_key)) {
|
||||
return Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
||||
"key-decryption-failed");
|
||||
}
|
||||
std::unique_ptr<RsaPrivateKey> private_key(RsaPrivateKey::Create(pkcs1_key));
|
||||
if (private_key == nullptr) {
|
||||
return Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
||||
"invalid-private-key");
|
||||
}
|
||||
|
||||
std::unique_ptr<DrmServiceCertificate> new_cert(new DrmServiceCertificate(
|
||||
service_certificate, drm_cert.provider_id(), drm_cert.serial_number(),
|
||||
drm_cert.creation_time_seconds(), std::move(public_key),
|
||||
std::move(private_key)));
|
||||
DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert));
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
const DrmServiceCertificate*
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificate() {
|
||||
return DrmServiceCertificateMap::GetInstance()->GetDefaultCert();
|
||||
}
|
||||
|
||||
const DrmServiceCertificate*
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() {
|
||||
const DrmServiceCertificate* default_cert =
|
||||
DrmServiceCertificateMap::GetInstance()->GetDefaultCert();
|
||||
CHECK(default_cert) << "Service Certificate not set!";
|
||||
return default_cert;
|
||||
}
|
||||
|
||||
const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate(
|
||||
const std::string& serial_number) {
|
||||
return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number);
|
||||
}
|
||||
|
||||
Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||
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();
|
||||
return AddDrmServiceCertificate(root_drm_cert, service_certificate,
|
||||
service_private_key,
|
||||
service_private_key_passphrase);
|
||||
}
|
||||
|
||||
Status DrmServiceCertificate::DecryptClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id) {
|
||||
DCHECK(client_id);
|
||||
if (encrypted_client_id.service_certificate_serial_number().empty()) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-service-certificate-serial-number");
|
||||
}
|
||||
if (encrypted_client_id.provider_id().empty()) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-service-id");
|
||||
}
|
||||
if (encrypted_client_id.encrypted_client_id().empty()) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id");
|
||||
}
|
||||
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id-iv");
|
||||
}
|
||||
if (encrypted_client_id.encrypted_privacy_key().empty()) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-privacy-key");
|
||||
}
|
||||
std::string privacy_key;
|
||||
std::string provider_id;
|
||||
const DrmServiceCertificate* cert = GetDrmServiceCertificate(
|
||||
encrypted_client_id.service_certificate_serial_number());
|
||||
if (!cert) {
|
||||
return Status(
|
||||
error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
"service-certificate-not-found (SN " +
|
||||
absl::BytesToHexString(
|
||||
encrypted_client_id.service_certificate_serial_number()) +
|
||||
")");
|
||||
}
|
||||
if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(),
|
||||
&privacy_key)) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"privacy-key-decryption-failed");
|
||||
}
|
||||
if (cert->provider_id() != encrypted_client_id.provider_id()) {
|
||||
return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
std::string("provider-id-mismatch (") + cert->provider_id() +
|
||||
" / " + encrypted_client_id.provider_id() + ")");
|
||||
}
|
||||
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
||||
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
||||
encrypted_client_id.encrypted_client_id()));
|
||||
if (serialized_client_id.empty()) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-decryption-failed");
|
||||
}
|
||||
if (!client_id->ParseFromString(serialized_client_id)) {
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-parse-failed");
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
void DrmServiceCertificate::ResetServiceCertificates() {
|
||||
DrmServiceCertificateMap::GetInstance()->Reset();
|
||||
}
|
||||
|
||||
Status DrmServiceCertificate::ValidateDrmServiceCertificate() {
|
||||
const DrmServiceCertificate* service_certificate =
|
||||
GetDefaultDrmServiceCertificate();
|
||||
if (!service_certificate) {
|
||||
return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
"drm service certificate is not found.");
|
||||
}
|
||||
SignedDrmCertificate signed_cert;
|
||||
if (!signed_cert.ParseFromString(service_certificate->certificate())) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signed drm service certificate is failed to parse.");
|
||||
}
|
||||
DrmCertificate drm_cert;
|
||||
if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) {
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"Drm service certificate is failed to parse.");
|
||||
}
|
||||
if (!drm_cert.has_creation_time_seconds()) {
|
||||
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"missing certificate creation time");
|
||||
}
|
||||
// TODO(user): Check creation_time_seconds field in DrmCertificate and also
|
||||
// export the absl/time dependency through moe.
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
DrmServiceCertificate::DrmServiceCertificate(
|
||||
const std::string& service_certificate, const std::string& provider_id,
|
||||
const std::string& serial_number, const uint32_t creation_time_seconds,
|
||||
std::unique_ptr<RsaPublicKey> public_key,
|
||||
std::unique_ptr<RsaPrivateKey> private_key)
|
||||
: certificate_(service_certificate),
|
||||
provider_id_(provider_id),
|
||||
serial_number_(serial_number),
|
||||
creation_time_seconds_(creation_time_seconds),
|
||||
public_key_(std::move(public_key)),
|
||||
private_key_(std::move(private_key)) {}
|
||||
|
||||
} // namespace widevine
|
||||
132
common/drm_service_certificate.h
Normal file
132
common/drm_service_certificate.h
Normal file
@@ -0,0 +1,132 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Service certificate holder used to decrypt encrypted client credentials.
|
||||
|
||||
#ifndef COMMON_DRM_SERVICE_CERTIFICATE_H_
|
||||
#define COMMON_DRM_SERVICE_CERTIFICATE_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/macros.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
class RequestInspectorTest;
|
||||
} // namespace widevine
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class ClientIdentification;
|
||||
class DrmRootCertificate;
|
||||
class EncryptedClientIdentification;
|
||||
|
||||
// TODO(user): Add a DrmCertificateList class to provide the static method
|
||||
// functionality.
|
||||
class DrmServiceCertificate {
|
||||
public:
|
||||
// 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
|
||||
// Google-generated certificate used to authenticate the service provider for
|
||||
// purposes of device privacy, |service_private_key| is the encrypted PKCS#8
|
||||
// private RSA key corresponding to the service certificate,
|
||||
// |service_private_key_passphrase| is the password required to decrypt
|
||||
// |service_private_key|.
|
||||
// Returns status::OK if successful, or appropriate error code otherwise.
|
||||
// If the default service certificate is not set, this certificate will be
|
||||
// used as the default service certificate.
|
||||
// This method is thread-safe.
|
||||
static Status AddDrmServiceCertificate(
|
||||
const DrmRootCertificate* root_drm_cert,
|
||||
const std::string& service_certificate, const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
|
||||
// Same as AddDrmServiceCertificate(), but will clear the default service
|
||||
// certificate if it's set. This will result in this service certificate
|
||||
// being set as the default service certificate.
|
||||
static Status SetDefaultDrmServiceCertificate(
|
||||
const DrmRootCertificate* root_drm_cert,
|
||||
const std::string& service_certificate, const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
|
||||
// Returns the default service certificate. Will return null if no default
|
||||
// Service Certificate is set. This method is thread-safe.
|
||||
static const DrmServiceCertificate* GetDefaultDrmServiceCertificate();
|
||||
|
||||
// Returns the default service certificate. Will abort if no default Service
|
||||
// Certificate is set. This method is thread-safe.
|
||||
static const DrmServiceCertificate* GetDefaultDrmServiceCertificateOrDie();
|
||||
|
||||
// Returns the service certificate with the given serial number if found, or
|
||||
// null otherwise.
|
||||
static const DrmServiceCertificate* GetDrmServiceCertificate(
|
||||
const std::string& cert_serial_number);
|
||||
|
||||
// Decrypts the EncryptedClientIdentification message passed in
|
||||
// |encrypted_client_id| into |client_id| using the private key for the
|
||||
// certificate which was used to encrypt the information. |client_id| must
|
||||
// not be NULL. Returns status::OK if successful, or an appropriate error
|
||||
// otherwise. This method is thread-safe.
|
||||
static Status DecryptClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id);
|
||||
|
||||
const std::string& certificate() const { return certificate_; }
|
||||
const std::string& provider_id() const { return provider_id_; }
|
||||
const std::string& serial_number() const { return serial_number_; }
|
||||
const RsaPrivateKey* const private_key() const { return private_key_.get(); }
|
||||
const RsaPublicKey* const public_key() const { return public_key_.get(); }
|
||||
|
||||
// Returns the validation result of drm service certificate. Returns
|
||||
// status::OK if successful, or in case of error, contact
|
||||
// widevine-tam@google.com to get the next valid service certificate renewed
|
||||
// via get deviceCertificate StatusList.
|
||||
static Status ValidateDrmServiceCertificate();
|
||||
|
||||
private:
|
||||
friend class DrmServiceCertificateTest;
|
||||
friend class widevine::RequestInspectorTest;
|
||||
|
||||
static Status AddDrmServiceCertificate(
|
||||
const std::string& root_public_key, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
|
||||
static Status SetDefaultDrmServiceCertificate(
|
||||
const std::string& root_public_key, const std::string& service_certificate,
|
||||
const std::string& service_private_key,
|
||||
const std::string& service_private_key_passphrase);
|
||||
|
||||
DrmServiceCertificate(const std::string& service_certificate,
|
||||
const std::string& provider_id, const std::string& serial_number,
|
||||
const uint32_t creation_time_seconds,
|
||||
std::unique_ptr<RsaPublicKey> public_key,
|
||||
std::unique_ptr<RsaPrivateKey> private_key);
|
||||
|
||||
static void ResetServiceCertificates();
|
||||
|
||||
std::string certificate_;
|
||||
std::string provider_id_;
|
||||
std::string serial_number_;
|
||||
uint32_t creation_time_seconds_;
|
||||
std::unique_ptr<RsaPublicKey> public_key_;
|
||||
std::unique_ptr<RsaPrivateKey> private_key_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(DrmServiceCertificate);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_DRM_SERVICE_CERTIFICATE_H_
|
||||
364
common/drm_service_certificate_test.cc
Normal file
364
common/drm_service_certificate_test.cc
Normal file
@@ -0,0 +1,364 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/drm_service_certificate.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "google/protobuf/util/message_differencer.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/drm_root_certificate.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/rsa_util.h"
|
||||
#include "common/test_drm_certificates.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h" // IWYU pragma: keep
|
||||
#include "protos/public/license_server_sdk.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
const char kPrivacyKey[] = "f7538b38acc78ec68c732ac665c55c65";
|
||||
const char kIv[] = "09e9cda133ff5140bd2793173a04b5a3";
|
||||
const char kPassphrase[] = "passphrase";
|
||||
|
||||
class DrmServiceCertificateTest : public ::testing::Test {
|
||||
public:
|
||||
DrmServiceCertificateTest()
|
||||
: privacy_key_(absl::HexStringToBytes(kPrivacyKey)),
|
||||
iv_(absl::HexStringToBytes(kIv)),
|
||||
root_private_key_(
|
||||
RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())) {
|
||||
EXPECT_TRUE(root_private_key_);
|
||||
EXPECT_OK(
|
||||
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
|
||||
client_id_.set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||
client_id_.set_token(test_certs_.test_user_device_certificate());
|
||||
}
|
||||
|
||||
void SetUp() override { DrmServiceCertificate::ResetServiceCertificates(); }
|
||||
|
||||
protected:
|
||||
std::string GenerateDrmServiceCertificate(const std::string& serial_number,
|
||||
const std::string& provider_id,
|
||||
uint32_t creation_time_seconds,
|
||||
const std::string& public_key) {
|
||||
DrmCertificate cert;
|
||||
cert.set_type(DrmCertificate::SERVICE);
|
||||
cert.set_serial_number(serial_number);
|
||||
cert.set_provider_id(provider_id);
|
||||
cert.set_public_key(public_key);
|
||||
cert.set_creation_time_seconds(creation_time_seconds);
|
||||
SignedDrmCertificate signed_cert;
|
||||
cert.SerializeToString(signed_cert.mutable_drm_certificate());
|
||||
root_private_key_->GenerateSignature(signed_cert.drm_certificate(),
|
||||
signed_cert.mutable_signature());
|
||||
std::string serialized_cert;
|
||||
signed_cert.SerializeToString(&serialized_cert);
|
||||
return serialized_cert;
|
||||
}
|
||||
|
||||
Status SetDefaultDrmServiceCertificate(const std::string& serial_number,
|
||||
const std::string& provider_id,
|
||||
uint32_t creation_time_seconds) {
|
||||
std::string signed_cert(GenerateDrmServiceCertificate(
|
||||
serial_number, provider_id, creation_time_seconds,
|
||||
test_keys_.public_test_key_2_2048_bits()));
|
||||
std::string encrypted_private_key;
|
||||
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
||||
&encrypted_private_key)) {
|
||||
return Status(error::INTERNAL, "");
|
||||
}
|
||||
return DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
|
||||
}
|
||||
|
||||
Status AddDrmServiceCertificate(const std::string& serial_number,
|
||||
const std::string& provider_id,
|
||||
uint32_t creation_time_seconds) {
|
||||
std::string signed_cert(GenerateDrmServiceCertificate(
|
||||
serial_number, provider_id, creation_time_seconds,
|
||||
test_keys_.public_test_key_2_2048_bits()));
|
||||
std::string encrypted_private_key;
|
||||
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
||||
&encrypted_private_key)) {
|
||||
return Status(error::INTERNAL, "");
|
||||
}
|
||||
return DrmServiceCertificate::AddDrmServiceCertificate(
|
||||
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
|
||||
}
|
||||
|
||||
void EncryptClientIdentification(
|
||||
const std::string& serial_number, const std::string& provider_id,
|
||||
const std::string& public_key,
|
||||
EncryptedClientIdentification* encrypted_client_id) {
|
||||
CHECK(encrypted_client_id);
|
||||
encrypted_client_id->set_provider_id(provider_id);
|
||||
encrypted_client_id->set_service_certificate_serial_number(serial_number);
|
||||
std::string serial_client_id;
|
||||
client_id_.SerializeToString(&serial_client_id);
|
||||
encrypted_client_id->set_encrypted_client_id(
|
||||
crypto_util::EncryptAesCbc(privacy_key_, iv_, serial_client_id));
|
||||
encrypted_client_id->set_encrypted_client_id_iv(iv_);
|
||||
std::unique_ptr<RsaPublicKey> rsa_key(RsaPublicKey::Create(public_key));
|
||||
ASSERT_TRUE(rsa_key.get());
|
||||
rsa_key->Encrypt(privacy_key_,
|
||||
encrypted_client_id->mutable_encrypted_privacy_key());
|
||||
}
|
||||
|
||||
RsaTestKeys test_keys_;
|
||||
TestDrmCertificates test_certs_;
|
||||
std::string privacy_key_;
|
||||
std::string iv_;
|
||||
std::unique_ptr<RsaPrivateKey> root_private_key_;
|
||||
std::unique_ptr<DrmRootCertificate> root_cert_;
|
||||
ClientIdentification client_id_;
|
||||
};
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, BasicClientIdDecrypt) {
|
||||
std::string serial_number("serial_number");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||
creation_time_seconds));
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, NoDefaultDrmServiceCertificate) {
|
||||
ASSERT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate());
|
||||
const auto& get_default_sc_or_die = []() {
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie();
|
||||
};
|
||||
EXPECT_DEATH(get_default_sc_or_die(), "Service Certificate not set!");
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) {
|
||||
std::string serial_number1("serial_number1");
|
||||
std::string provider_id1("someservice.com");
|
||||
uint32_t creation_time_seconds1(1234);
|
||||
std::string serial_number2("serial_number2");
|
||||
uint32_t creation_time_seconds2(1234);
|
||||
std::string bogus_serial_number("bogus-serial-number2");
|
||||
|
||||
EXPECT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate());
|
||||
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id1,
|
||||
creation_time_seconds1));
|
||||
|
||||
// Expect this to pass because the serial number is allowed to change as long
|
||||
// as the service Id is the same as before.
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id1,
|
||||
creation_time_seconds2));
|
||||
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number1, provider_id1,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptClientIdentification(serial_number2, provider_id1,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
EXPECT_OK(DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptClientIdentification(bogus_serial_number, provider_id1,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) {
|
||||
std::string serial_number1("serial_number1");
|
||||
std::string serial_number2("serial_number2");
|
||||
std::string serial_number3("serial_number3");
|
||||
std::string serial_number4("serial_number4");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id,
|
||||
creation_time_seconds));
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id,
|
||||
creation_time_seconds + 1));
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id,
|
||||
creation_time_seconds - 1));
|
||||
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number1, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptClientIdentification(serial_number2, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptClientIdentification(serial_number3, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
const DrmServiceCertificate* default_cert(
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificate());
|
||||
ASSERT_TRUE(default_cert);
|
||||
SignedDrmCertificate signed_cert;
|
||||
ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate()));
|
||||
DrmCertificate drm_cert;
|
||||
ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate()));
|
||||
EXPECT_EQ(serial_number1, drm_cert.serial_number());
|
||||
|
||||
EXPECT_OK(SetDefaultDrmServiceCertificate(serial_number4, provider_id,
|
||||
creation_time_seconds));
|
||||
default_cert = DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
||||
ASSERT_TRUE(default_cert);
|
||||
ASSERT_TRUE(signed_cert.ParseFromString(default_cert->certificate()));
|
||||
ASSERT_TRUE(drm_cert.ParseFromString(signed_cert.drm_certificate()));
|
||||
EXPECT_EQ(serial_number4, drm_cert.serial_number());
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, DrmServiceCertificateNotFound) {
|
||||
std::string serial_number("serial_number");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
EXPECT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||
creation_time_seconds));
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification("invalid_serial_number", provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_EQ(SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
||||
std::string serial_number("serial_number");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||
creation_time_seconds));
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
ASSERT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptedClientIdentification invalid;
|
||||
invalid = encrypted_client_id;
|
||||
invalid.clear_encrypted_privacy_key();
|
||||
EXPECT_EQ(
|
||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||
"missing-encrypted-privacy-key",
|
||||
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
||||
&decrypted_client_id)
|
||||
.ToString());
|
||||
|
||||
invalid = encrypted_client_id;
|
||||
++(*invalid.mutable_encrypted_client_id_iv())[4];
|
||||
EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
invalid, &decrypted_client_id));
|
||||
|
||||
invalid.clear_encrypted_client_id_iv();
|
||||
EXPECT_EQ(
|
||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||
"missing-encrypted-client-id-iv",
|
||||
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
||||
&decrypted_client_id)
|
||||
.ToString());
|
||||
|
||||
invalid = encrypted_client_id;
|
||||
++(*invalid.mutable_encrypted_client_id())[0];
|
||||
EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
invalid, &decrypted_client_id));
|
||||
|
||||
invalid.clear_encrypted_client_id();
|
||||
EXPECT_EQ(
|
||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||
"missing-encrypted-client-id",
|
||||
DrmServiceCertificate::DecryptClientIdentification(invalid,
|
||||
&decrypted_client_id)
|
||||
.ToString());
|
||||
}
|
||||
|
||||
TEST_F(DrmServiceCertificateTest, PrivateKeyDecryptError) {
|
||||
std::string serial_number("serial_number");
|
||||
std::string provider_id("someservice.com");
|
||||
uint32_t creation_time_seconds(1234);
|
||||
|
||||
ASSERT_OK(AddDrmServiceCertificate(serial_number, provider_id,
|
||||
creation_time_seconds));
|
||||
EncryptedClientIdentification encrypted_client_id;
|
||||
EncryptClientIdentification(serial_number, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
ASSERT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &decrypted_client_id));
|
||||
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
|
||||
decrypted_client_id));
|
||||
|
||||
EncryptedClientIdentification corrupted;
|
||||
corrupted = encrypted_client_id;
|
||||
++(*corrupted.mutable_encrypted_privacy_key())[20];
|
||||
EXPECT_EQ(
|
||||
"Errors::INVALID_ENCRYPTED_CLIENT_IDENTIFICATION: "
|
||||
"privacy-key-decryption-failed",
|
||||
DrmServiceCertificate::DecryptClientIdentification(corrupted,
|
||||
&decrypted_client_id)
|
||||
.ToString());
|
||||
}
|
||||
|
||||
// TODO(user): Add more unit tests for various fail cases (bad keys having
|
||||
// to do with bad keys and bad certs).
|
||||
|
||||
} // namespace widevine
|
||||
113
common/ecb_util.cc
Normal file
113
common/ecb_util.cc
Normal file
@@ -0,0 +1,113 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Implementation of ecb crypto routines used by Widevine services.
|
||||
|
||||
#include "common/ecb_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/des.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
const int kWvmDESKeySizeBytes = 16;
|
||||
|
||||
static bool EncryptOrDecrypt3DesCbc(absl::string_view key,
|
||||
absl::string_view src, std::string* dst,
|
||||
bool encrypt) {
|
||||
CHECK(dst);
|
||||
if (key.size() != kWvmDESKeySizeBytes) {
|
||||
LOG(WARNING) << "Invalid 3DES key size (" << key.size() << "!=16).";
|
||||
dst->clear();
|
||||
return false;
|
||||
}
|
||||
const int data_size = src.size();
|
||||
if (data_size % DES_KEY_SZ != 0) {
|
||||
// Data must be a multiple of block size
|
||||
LOG(WARNING) << "3DES data is not a multiple of 8 bytes.";
|
||||
dst->clear();
|
||||
return false;
|
||||
}
|
||||
const int data_size_blocks = data_size / DES_KEY_SZ;
|
||||
DES_key_schedule schedule[2];
|
||||
// const_DES_cblock (the type of the first argument to DES_ecb3_encrypt) isn't
|
||||
// actually const, so we have to cast away const.
|
||||
DES_cblock* keyblock =
|
||||
const_cast<DES_cblock*>(reinterpret_cast<const DES_cblock*>(key.data()));
|
||||
DES_set_key(keyblock + 0, schedule + 1);
|
||||
DES_set_key(keyblock + 1, schedule + 0);
|
||||
DES_cblock* srcblock =
|
||||
const_cast<DES_cblock*>(reinterpret_cast<const DES_cblock*>(src.data()));
|
||||
dst->resize(data_size);
|
||||
DES_cblock* dstblock = reinterpret_cast<DES_cblock*>(&*dst->begin());
|
||||
for (int i = 0; i < data_size_blocks; i++) {
|
||||
DES_ecb3_encrypt(srcblock + i, dstblock + i, schedule + 0, schedule + 1,
|
||||
schedule + 0, encrypt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
||||
return EncryptOrDecrypt3DesCbc(key, src, dst, DES_ENCRYPT);
|
||||
}
|
||||
|
||||
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
||||
return EncryptOrDecrypt3DesCbc(key, src, dst, DES_DECRYPT);
|
||||
}
|
||||
|
||||
bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
||||
CHECK(dst);
|
||||
dst->clear();
|
||||
if (src.size() % 16 != 0) {
|
||||
LOG(WARNING) << "AES-ECB data is not a multiple of 16 bytes.";
|
||||
return false;
|
||||
}
|
||||
int num_bits = key.size() * 8;
|
||||
AES_KEY aes_key;
|
||||
int aes_result = AES_set_encrypt_key(
|
||||
reinterpret_cast<const uint8_t*>(key.data()), num_bits, &aes_key);
|
||||
if (aes_result != 0) {
|
||||
LOG(WARNING) << "AES result is not zero.";
|
||||
return false;
|
||||
}
|
||||
dst->resize(src.size());
|
||||
for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) {
|
||||
AES_encrypt(reinterpret_cast<const uint8_t*>(src.data() + i),
|
||||
reinterpret_cast<uint8_t*>(&(*dst)[i]), &aes_key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst) {
|
||||
CHECK(dst);
|
||||
dst->clear();
|
||||
if (src.size() % 16 != 0) {
|
||||
LOG(WARNING) << "AES-ECB data is not a multiple of 16 bytes.";
|
||||
return false;
|
||||
}
|
||||
int num_bits = key.size() * 8;
|
||||
AES_KEY aes_key;
|
||||
int aes_result = AES_set_decrypt_key(
|
||||
reinterpret_cast<const uint8_t*>(key.data()), num_bits, &aes_key);
|
||||
if (aes_result != 0) {
|
||||
LOG(WARNING) << "AES result is not zero.";
|
||||
return false;
|
||||
}
|
||||
dst->resize(src.size());
|
||||
for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) {
|
||||
AES_decrypt(reinterpret_cast<const uint8_t*>(src.data() + i),
|
||||
reinterpret_cast<uint8_t*>(&(*dst)[i]), &aes_key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
59
common/ecb_util.h
Normal file
59
common/ecb_util.h
Normal file
@@ -0,0 +1,59 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Contains ecb crypto routines for widevine protocols. These routines are used
|
||||
// as part of licensing and provisioning request handling.
|
||||
|
||||
#ifndef COMMON_ECB_UTIL_H_
|
||||
#define COMMON_ECB_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
// Encrypts |src| into |dst| using 3DES ECB2 mode with the given key. This is
|
||||
// used for protecting content keys on some older Widevine keyboxes, and is not
|
||||
// intended for any other purpose. Returns false and sets *|dst|="" if
|
||||
// unsuccessful. Key should be 16 bytes. The first 8 are key2 of the 3DES key
|
||||
// bundle, and the last 8 bytes are key1 and key3. |src| must be a multiple of
|
||||
// 8 bytes.
|
||||
bool Encrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
||||
|
||||
// Decrypts |src| into |dst| using 3DES ECB2 mode with the given (16-byte)
|
||||
// key. This is used for protecting content keys on some older Widevine
|
||||
// keyboxes, and is not intended for any other purpose.
|
||||
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
||||
// fail if invalid key or data sizes are passed in.
|
||||
// Key should be 16 bytes. The first 8 are key2 of the 3DES key bundle,
|
||||
// and the last 8 bytes are key1 and key3. |src| must be a multiple of
|
||||
// 8 bytes.
|
||||
bool Decrypt3DesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
||||
|
||||
// Encrypts |src| into |dst| using AES ECB mode with the given
|
||||
// key. This is used for protecting content keys on Widevine devices,
|
||||
// and is not intended for any other purpose.
|
||||
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
||||
// fail if invalid key or data sizes are passed in.
|
||||
// Key must be 16 bytes, and src must be a multiple of 16 bytes.
|
||||
bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
||||
|
||||
// Decrypts |src| into |dst| using AES ECB mode with the given
|
||||
// key. This is used for protecting content keys on Widevine devices,
|
||||
// and is not intended for any other purpose.
|
||||
// Returns false and sets *|dst|="" if unsuccessful. Note that it can only
|
||||
// fail if invalid key or data sizes are passed in.
|
||||
// Key must be 16 bytes, and src must be a multiple of 16 bytes.
|
||||
bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* dst);
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_ECB_UTIL_H_
|
||||
90
common/ecb_util_test.cc
Normal file
90
common/ecb_util_test.cc
Normal file
@@ -0,0 +1,90 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Unit tests for the ecb_util functions.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/ecb_util.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
TEST(CryptoUtilTest, TestEncrypt3DesEcb) {
|
||||
// Test vector generated by (Python):
|
||||
// c = M2Crypto.EVP.Cipher('des_ede3_ecb', '89abcdef0123456789abcdef',
|
||||
// iv='', op=1, padding=False)
|
||||
// (c.update('This is a test message. ')+c.final()).encode('hex')
|
||||
// TODO(user): find some Widevine test vectors and use those
|
||||
std::string key = "0123456789abcdef";
|
||||
std::string plain = "This is a test message. ";
|
||||
std::string cipher;
|
||||
std::string decrypted;
|
||||
EXPECT_TRUE(Encrypt3DesEcb(key, plain, &cipher));
|
||||
EXPECT_EQ("ae7c7accaf99a973e7f89bb7f6dc61a9aa9e226a5ba17376",
|
||||
absl::BytesToHexString(cipher));
|
||||
EXPECT_TRUE(Decrypt3DesEcb(key, cipher, &decrypted));
|
||||
EXPECT_EQ(plain, decrypted);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestEncrypt3DesEcbFail) {
|
||||
// Verify that 3DES fails with invalid key or data size
|
||||
std::string badkey = "0123456789abcde"; // 15 bytes
|
||||
std::string badplain = "This is a test message."; // 23 bytes
|
||||
std::string goodkey = "0123456789abcdef"; // 16 bytes
|
||||
std::string goodplain = "This is a test message. "; // 24 bytes
|
||||
|
||||
// Encryption failure should leave 'decrypted' as empty std::string
|
||||
std::string out = "Not empty";
|
||||
EXPECT_FALSE(Encrypt3DesEcb(badkey, goodplain, &out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
|
||||
out = "Not empty";
|
||||
EXPECT_FALSE(Decrypt3DesEcb(goodkey, badplain, &out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestEncryptAesEcb) {
|
||||
// Test vector generated by (Python):
|
||||
// c = M2Crypto.EVP.Cipher('aes_128_ecb', key, '', 1, padding=False)
|
||||
// encrypted = c.update(data) + c.final();
|
||||
// TODO(user): find some Widevine test vectors and use those
|
||||
std::string key = "0123456789abcdef";
|
||||
std::string plaintext = "This message has 32 bytes in it.";
|
||||
std::string encrypted;
|
||||
std::string decrypted;
|
||||
EXPECT_TRUE(EncryptAesEcb(key, plaintext, &encrypted));
|
||||
EXPECT_EQ("1f6a7d63e0645de25c56c6b39ba7723d640129d65f41e96b87be812bc94ad8a9",
|
||||
absl::BytesToHexString(encrypted));
|
||||
EXPECT_TRUE(DecryptAesEcb(key, encrypted, &decrypted));
|
||||
EXPECT_EQ(plaintext, decrypted);
|
||||
}
|
||||
|
||||
TEST(CryptoUtilTest, TestEncryptAesEcbFail) {
|
||||
// Verify that EncryptAesEcb fails with invalid key or data size
|
||||
std::string badkey = "0123456789abcde"; // 15 bytes
|
||||
std::string badplain = "This message has 31 bytes in it";
|
||||
std::string goodkey = "0123456789abcdef"; // 16 bytes
|
||||
std::string goodplain = "This message has 32 bytes in it.";
|
||||
|
||||
// Encryption failure should leave 'decrypted' as empty std::string
|
||||
std::string out = "Not empty";
|
||||
EXPECT_FALSE(EncryptAesEcb(badkey, goodplain, &out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
|
||||
out = "Not empty";
|
||||
EXPECT_FALSE(EncryptAesEcb(goodkey, badplain, &out));
|
||||
EXPECT_TRUE(out.empty());
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
19
common/error_space.cc
Normal file
19
common/error_space.cc
Normal file
@@ -0,0 +1,19 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/error_space.h"
|
||||
|
||||
#include "util/proto_status.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
const util::ErrorSpace* error_space =
|
||||
util::ProtoEnumErrorSpace<Errors>::Get();
|
||||
|
||||
} // namespace widevine
|
||||
20
common/error_space.h
Normal file
20
common/error_space.h
Normal file
@@ -0,0 +1,20 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_ERROR_SPACE_H_
|
||||
#define COMMON_ERROR_SPACE_H_
|
||||
|
||||
#include "util/error_space.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
extern const util::ErrorSpace* error_space;
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_ERROR_SPACE_H_
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -39,7 +39,7 @@ bool GetContents(const std::string& file_name, std::string* contents) {
|
||||
LOG(WARNING) << "Failed to read all file contents.";
|
||||
return false;
|
||||
}
|
||||
return true;;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetContents(const std::string& file_name, const std::string& contents) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -7,8 +7,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/file_util.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -19,7 +19,7 @@ TEST(FileUtilTest, EmptyFileName) {
|
||||
}
|
||||
|
||||
TEST(FileUtilTest, BasicTest) {
|
||||
const std::string file_path = FLAGS_test_tmpdir + "/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));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -10,7 +10,7 @@
|
||||
#define COMMON_MOCK_RSA_KEY_H_
|
||||
|
||||
#include <string>
|
||||
#include "gmock/gmock.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
namespace widevine {
|
||||
@@ -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));
|
||||
|
||||
@@ -49,19 +49,19 @@ class MockRsaPublicKey : public RsaPublicKey {
|
||||
MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete;
|
||||
};
|
||||
|
||||
class MockRsaKeyFactory : public RsaKeyFactory{
|
||||
class MockRsaKeyFactory : public RsaKeyFactory {
|
||||
public:
|
||||
MockRsaKeyFactory() {}
|
||||
~MockRsaKeyFactory() override {}
|
||||
|
||||
MOCK_METHOD1(CreateFromPkcs1PrivateKey,
|
||||
std::unique_ptr<RsaPrivateKey>(const std::string& private_key));
|
||||
MOCK_METHOD2(
|
||||
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,
|
||||
const std::string& private_key_passphrase));
|
||||
MOCK_METHOD1(CreateFromPkcs1PublicKey,
|
||||
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
|
||||
MOCK_CONST_METHOD1(CreateFromPkcs1PublicKey,
|
||||
std::unique_ptr<RsaPublicKey>(const std::string& public_key));
|
||||
|
||||
private:
|
||||
MockRsaKeyFactory(const MockRsaKeyFactory&) = delete;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -9,11 +9,12 @@
|
||||
// RAII wrapper classes for cleaning up various OpenSSL dynamically allocated
|
||||
// structures.
|
||||
|
||||
#ifndef COMMON_OPENSSL_UTIL_H__
|
||||
#define COMMON_OPENSSL_UTIL_H__
|
||||
#ifndef COMMON_OPENSSL_UTIL_H_
|
||||
#define COMMON_OPENSSL_UTIL_H_
|
||||
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/pkcs7.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/x509v3.h"
|
||||
|
||||
@@ -46,6 +47,7 @@ using ScopedOpenSSLStackOnly =
|
||||
|
||||
using ScopedBIGNUM = ScopedOpenSSLType<BIGNUM, BN_free>;
|
||||
using ScopedBIO = ScopedOpenSSLType<BIO, BIO_vfree>;
|
||||
using ScopedPKCS7 = ScopedOpenSSLType<PKCS7, PKCS7_free>;
|
||||
using ScopedPKEY = ScopedOpenSSLType<EVP_PKEY, EVP_PKEY_free>;
|
||||
using ScopedRSA = ScopedOpenSSLType<RSA, RSA_free>;
|
||||
using ScopedX509 = ScopedOpenSSLType<X509, X509_free>;
|
||||
@@ -59,6 +61,7 @@ using ScopedX509StoreCtx =
|
||||
ScopedOpenSSLType<X509_STORE_CTX, X509_STORE_CTX_free>;
|
||||
using ScopedX509Req = ScopedOpenSSLType<X509_REQ, X509_REQ_free>;
|
||||
using ScopedAsn1UtcTime = ScopedOpenSSLType<ASN1_UTCTIME, ASN1_UTCTIME_free>;
|
||||
using ScopedAsn1Time = ScopedOpenSSLType<ASN1_TIME, ASN1_TIME_free>;
|
||||
using ScopedAsn1Utc8String =
|
||||
ScopedOpenSSLType<ASN1_UTF8STRING, ASN1_UTF8STRING_free>;
|
||||
using ScopedAsn1Integer = ScopedOpenSSLType<ASN1_INTEGER, ASN1_INTEGER_free>;
|
||||
@@ -74,4 +77,4 @@ using ScopedX509InfoStack =
|
||||
ScopedOpenSSLStack<STACK_OF(X509_INFO), X509_INFO, X509_INFO_free>;
|
||||
using ScopedX509InfoStackOnly = ScopedOpenSSLStackOnly<STACK_OF(X509_INFO)>;
|
||||
|
||||
#endif // COMMON_OPENSSL_UTIL_H__
|
||||
#endif // COMMON_OPENSSL_UTIL_H_
|
||||
|
||||
105
common/python/BUILD
Normal file
105
common/python/BUILD
Normal file
@@ -0,0 +1,105 @@
|
||||
################################################################################
|
||||
# Copyright 2016 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
#
|
||||
# Description:
|
||||
# Build file for CLIF wrappers of Widevine common modules.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//devtools/clif/python:clif_build_rule.bzl", "py_clif_cc")
|
||||
|
||||
py_clif_cc(
|
||||
name = "aes_cbc_util",
|
||||
srcs = ["aes_cbc_util.clif"],
|
||||
deps = [
|
||||
"//common:aes_cbc_util",
|
||||
],
|
||||
)
|
||||
|
||||
py_clif_cc(
|
||||
name = "certificate_type",
|
||||
srcs = ["certificate_type.clif"],
|
||||
deps = [
|
||||
"//common:certificate_type",
|
||||
],
|
||||
)
|
||||
|
||||
py_clif_cc(
|
||||
name = "crypto_util",
|
||||
srcs = ["crypto_util.clif"],
|
||||
deps = [
|
||||
"//common:crypto_util",
|
||||
],
|
||||
)
|
||||
|
||||
py_clif_cc(
|
||||
name = "drm_root_certificate",
|
||||
srcs = ["drm_root_certificate.clif"],
|
||||
clif_deps = [
|
||||
":certificate_type",
|
||||
],
|
||||
pyclif_deps = [
|
||||
"//protos/public:drm_certificate_pyclif",
|
||||
"//protos/public:signed_drm_certificate_pyclif",
|
||||
],
|
||||
deps = [
|
||||
"//util/task/python:status_clif",
|
||||
"//common:drm_root_certificate",
|
||||
],
|
||||
)
|
||||
|
||||
py_clif_cc(
|
||||
name = "rsa_test_keys",
|
||||
testonly = 1,
|
||||
srcs = ["rsa_test_keys.clif"],
|
||||
deps = [
|
||||
"//common:rsa_test_keys",
|
||||
],
|
||||
)
|
||||
|
||||
py_clif_cc(
|
||||
name = "rsa_key",
|
||||
srcs = ["rsa_key.clif"],
|
||||
deps = [
|
||||
"//common:rsa_key",
|
||||
],
|
||||
)
|
||||
|
||||
py_clif_cc(
|
||||
name = "drm_service_certificate",
|
||||
srcs = ["drm_service_certificate.clif"],
|
||||
clif_deps = [
|
||||
":certificate_type",
|
||||
":drm_root_certificate",
|
||||
],
|
||||
deps = [
|
||||
"//util/task/python:status_clif",
|
||||
"//common:drm_service_certificate",
|
||||
],
|
||||
)
|
||||
|
||||
py_clif_cc(
|
||||
name = "signing_key_util",
|
||||
srcs = ["signing_key_util.clif"],
|
||||
pyclif_deps = [
|
||||
"//protos/public:license_protocol_pyclif",
|
||||
],
|
||||
deps = [
|
||||
"//common:signing_key_util",
|
||||
],
|
||||
)
|
||||
|
||||
py_clif_cc(
|
||||
name = "test_drm_certificates",
|
||||
testonly = 1,
|
||||
srcs = ["test_drm_certificates.clif"],
|
||||
deps = [
|
||||
"//common:test_drm_certificates",
|
||||
],
|
||||
)
|
||||
11
common/python/aes_cbc_util.clif
Normal file
11
common/python/aes_cbc_util.clif
Normal file
@@ -0,0 +1,11 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
from "common/aes_cbc_util.h":
|
||||
namespace `widevine::crypto_util`:
|
||||
def `EncryptAesCbc` as Encrypt(key: bytes, iv: bytes, plaintext: bytes) -> bytes
|
||||
11
common/python/certificate_type.clif
Normal file
11
common/python/certificate_type.clif
Normal file
@@ -0,0 +1,11 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
from "common/certificate_type.h":
|
||||
namespace `widevine`:
|
||||
enum CertificateType
|
||||
15
common/python/crypto_util.clif
Normal file
15
common/python/crypto_util.clif
Normal file
@@ -0,0 +1,15 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
from "common/crypto_util.h":
|
||||
namespace `widevine::crypto_util`:
|
||||
const `kSigningKeyLabel` as SIGNING_KEY_LABEL: bytes
|
||||
const `kSigningKeySizeBits` as SIGNING_KEY_SIZE_BITS: int
|
||||
|
||||
def DeriveKey(key: bytes, label: bytes, context: bytes, size_bits: uint32_t) -> bytes
|
||||
def CreateSignatureHmacSha256(key: bytes, message: bytes) -> bytes
|
||||
23
common/python/drm_root_certificate.clif
Normal file
23
common/python/drm_root_certificate.clif
Normal file
@@ -0,0 +1,23 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
from "util/task/python/clif.h" import *
|
||||
from "common/python/certificate_type.h" import *
|
||||
from "protos/public/drm_certificate_pyclif.h" import *
|
||||
from "protos/public/signed_drm_certificate_pyclif.h" import *
|
||||
|
||||
from "common/drm_root_certificate.h":
|
||||
namespace `widevine`:
|
||||
class DrmRootCertificate:
|
||||
def VerifyCertificate(self,
|
||||
serialized_certificate: bytes) -> (status: Status,
|
||||
signed_certificate: SignedDrmCertificate,
|
||||
certificate: DrmCertificate)
|
||||
staticmethods from `DrmRootCertificate`:
|
||||
def CreateByType(cert_type: CertificateType) -> (new_root_cert: DrmRootCertificate,
|
||||
status: Status)
|
||||
18
common/python/drm_service_certificate.clif
Normal file
18
common/python/drm_service_certificate.clif
Normal file
@@ -0,0 +1,18 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
from "util/task/python/clif.h" import *
|
||||
from "common/python/drm_root_certificate.h" import *
|
||||
from "common/drm_service_certificate.h":
|
||||
namespace `widevine`:
|
||||
staticmethods from `DrmServiceCertificate`:
|
||||
def AddDrmServiceCertificate(
|
||||
root_certificate: DrmRootCertificate,
|
||||
service_certificate: bytes,
|
||||
service_private_key: bytes,
|
||||
service_private_key_passphrase: bytes) -> Status
|
||||
15
common/python/rsa_key.clif
Normal file
15
common/python/rsa_key.clif
Normal file
@@ -0,0 +1,15 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
from "common/rsa_key.h":
|
||||
namespace `widevine`:
|
||||
class RsaPublicKey:
|
||||
def VerifySignature(self, message: bytes, signature: bytes) -> bool
|
||||
|
||||
staticmethods from `RsaPublicKey`:
|
||||
def Create(serialized_key: bytes) -> RsaPublicKey
|
||||
20
common/python/rsa_test_keys.clif
Normal file
20
common/python/rsa_test_keys.clif
Normal file
@@ -0,0 +1,20 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
from "common/rsa_test_keys.h":
|
||||
namespace `widevine`:
|
||||
class RsaTestKeys:
|
||||
def private_test_key_1_3072_bits(self) -> bytes
|
||||
def public_test_key_1_3072_bits(self) -> bytes
|
||||
def private_test_key_2_2048_bits(self) -> bytes
|
||||
def public_test_key_2_2048_bits(self) -> bytes
|
||||
def private_test_key_3_2048_bits(self) -> bytes
|
||||
def public_test_key_3_2048_bits(self) -> bytes
|
||||
def private_test_key_2_carmichael_totient_2048_bits(self) -> bytes
|
||||
def private_test_key_3_carmichael_totient_2048_bits(self) -> bytes
|
||||
def private_test_key_4_carmichael_totient_2048_bits(self) -> bytes
|
||||
14
common/python/signing_key_util.clif
Normal file
14
common/python/signing_key_util.clif
Normal file
@@ -0,0 +1,14 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
from "protos/public/license_protocol_pyclif.h" import *
|
||||
|
||||
from "common/signing_key_util.h":
|
||||
namespace `widevine`:
|
||||
def SigningKeyMaterialSizeBits(protocol_version: ProtocolVersion) -> uint32_t
|
||||
def GetClientSigningKey(derived_key: bytes, protocol_version: ProtocolVersion) -> bytes
|
||||
15
common/python/test_drm_certificates.clif
Normal file
15
common/python/test_drm_certificates.clif
Normal file
@@ -0,0 +1,15 @@
|
||||
################################################################################
|
||||
# Copyright 2018 Google LLC.
|
||||
#
|
||||
# This software is licensed under the terms defined in the Widevine Master
|
||||
# License Agreement. For a copy of this agreement, please contact
|
||||
# widevine-licensing@google.com.
|
||||
################################################################################
|
||||
|
||||
from "common/test_drm_certificates.h":
|
||||
namespace `widevine`:
|
||||
class TestDrmCertificates:
|
||||
def test_root_certificate(self) -> bytes
|
||||
def test_intermediate_certificate(self) -> bytes
|
||||
def test_user_device_certificate(self) -> bytes
|
||||
def test_service_certificate(self) -> bytes
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -7,8 +7,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/random_util.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
|
||||
260
common/remote_attestation_verifier.cc
Normal file
260
common/remote_attestation_verifier.cc
Normal file
@@ -0,0 +1,260 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/remote_attestation_verifier.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "common/client_id_util.h"
|
||||
#include "common/drm_service_certificate.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
const char kTestRootCaDerCert[] =
|
||||
"30820403308202eba003020102020900a24f94af7ae6831f300d06092a86"
|
||||
"4886f70d0101050500308197310b30090603550406130255533113301106"
|
||||
"035504080c0a57617368696e67746f6e3111300f06035504070c084b6972"
|
||||
"6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f"
|
||||
"060355040b0c085769646576696e653115301306035504030c0c54657374"
|
||||
"20526f6f742043413121301f06092a864886f70d010901161274696e736b"
|
||||
"697040676f6f676c652e636f6d301e170d3133303831363030353731305a"
|
||||
"170d3333303831353030353731305a308197310b30090603550406130255"
|
||||
"533113301106035504080c0a57617368696e67746f6e3111300f06035504"
|
||||
"070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049"
|
||||
"6e633111300f060355040b0c085769646576696e65311530130603550403"
|
||||
"0c0c5465737420526f6f742043413121301f06092a864886f70d01090116"
|
||||
"1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648"
|
||||
"86f70d01010105000382010f003082010a0282010100c6eee629d99f7736"
|
||||
"2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c"
|
||||
"e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319"
|
||||
"0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3"
|
||||
"f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93"
|
||||
"52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7"
|
||||
"e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f"
|
||||
"d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20"
|
||||
"79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7"
|
||||
"3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30"
|
||||
"ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680"
|
||||
"144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405"
|
||||
"30030101ff300d06092a864886f70d01010505000382010100779e9b98d3"
|
||||
"ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee"
|
||||
"79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124"
|
||||
"ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436"
|
||||
"026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140"
|
||||
"ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053"
|
||||
"0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258"
|
||||
"e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82"
|
||||
"d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4"
|
||||
"c270d87191c13aefdd177b";
|
||||
|
||||
const char kProdRootCaDerCert[] =
|
||||
"30820408308202f0a003020102020101300d06092a864886f70d01010505"
|
||||
"00307d311830160603550403130f5072697661637920434120526f6f7431"
|
||||
"123010060355040b13094368726f6d65204f5331133011060355040a130a"
|
||||
"476f6f676c6520496e63311630140603550407130d4d6f756e7461696e20"
|
||||
"56696577311330110603550408130a43616c69666f726e6961310b300906"
|
||||
"0355040613025553301e170d3133303231383130313334325a170d333330"
|
||||
"3231333130313334325a307d311830160603550403130f50726976616379"
|
||||
"20434120526f6f7431123010060355040b13094368726f6d65204f533113"
|
||||
"3011060355040a130a476f6f676c6520496e63311630140603550407130d"
|
||||
"4d6f756e7461696e2056696577311330110603550408130a43616c69666f"
|
||||
"726e6961310b300906035504061302555330820122300d06092a864886f7"
|
||||
"0d01010105000382010f003082010a0282010100e10ea6819d3d066b421d"
|
||||
"d7612de3eef9599f5d9a2a24bfd09caab543511cf22f615e29f989425a65"
|
||||
"7396bf33603747719cfb0b4240cd682c7c558fec0176b4793be440752246"
|
||||
"83648f5b12d02a838a2a8e55a4b645ed0a4a52b19252a23d34bf64a17ac7"
|
||||
"11fe93a889086d943211b17d670f96442c9f367d38026000da79664e600e"
|
||||
"e9259348f4fd74108e973d561e624e9f5eda77a085a6eb15fadb2cc7787c"
|
||||
"7f30ef3b196f2a416a76fa9eb30d65753f5039d97bea70e82431d2962396"
|
||||
"a34864f33b74d60707fea794c03c82e547abc2407fa7bad67bd09cdab49b"
|
||||
"26e68754994d12a3845dbeceffe18de0d51fc6fa78676d89ea1e0fcff931"
|
||||
"59bfb809519b0203010001a3819230818f30290603551d0e042204204b1d"
|
||||
"148aa5380938812ed6a763f5dc2c318610d5fa9604d609cb2e0d8cec3289"
|
||||
"302b0603551d230424302280204b1d148aa5380938812ed6a763f5dc2c31"
|
||||
"8610d5fa9604d609cb2e0d8cec3289300e0603551d0f0101ff0404030201"
|
||||
"06300f0603551d130101ff040530030101ff30140603551d200101ff040a"
|
||||
"300830060604551d2000300d06092a864886f70d01010505000382010100"
|
||||
"c40d84bc8d609b1b68b3caa7e841021838d7e392557d40debab3e0685e72"
|
||||
"80541092dc913b0aa6150228d8fe5ab08cceefbac56952fa00ba614294d1"
|
||||
"ba4fa170c86b27f9bf58666c46940f740c4be2795501b25e40b9702af07c"
|
||||
"884926bd8beed036c503e5e42a223ff36271404ca4360a93dec92a02fd8d"
|
||||
"ae8f756fc68aaa647e2159f0a7a95d1446e92362bd512f59daec02c5d152"
|
||||
"c301b9807db998ba70c616364762a0a497aaa92eb7d92f3635169d3f74c6"
|
||||
"40c738941759a8ab43677b80329d015bdcf8922b779a80f85f1e4a677659"
|
||||
"c60de80152e8c526a7de46cac143a75af58f0806de81e15c97f616e1bffa"
|
||||
"1c1c6b0d2438543bdfb2a21bd9bc7ae4";
|
||||
|
||||
const char kServiceIdFieldName[] = "OU";
|
||||
const char kDeviceModeFieldName[] = "O";
|
||||
const char kExpectedDeviceMode[] = "Chrome Device Content Protection";
|
||||
|
||||
RemoteAttestationVerifier& RemoteAttestationVerifier::get() {
|
||||
static RemoteAttestationVerifier instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void RemoteAttestationVerifier::EnableTestDrmCertificates(bool enable) {
|
||||
absl::WriterMutexLock lock(&ca_mutex_);
|
||||
enable_test_certificates_ = enable;
|
||||
ca_.reset();
|
||||
}
|
||||
|
||||
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
std::string* remote_attestation_cert_sn) {
|
||||
DCHECK(remote_attestation_cert_sn);
|
||||
|
||||
// Sanity check RemoteAttestation.
|
||||
if (!remote_attestation.has_certificate()) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-certificate-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_salt()) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-salt-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_signature()) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-signature-missing"));
|
||||
}
|
||||
// Decrypt ClientIdentification containing remote attestation certificate.
|
||||
// A service cert would be looked up first, then that cert will be used
|
||||
// to decrypt the ClientIdentification.
|
||||
ClientIdentification client_id;
|
||||
Status status = DrmServiceCertificate::DecryptClientIdentification(
|
||||
remote_attestation.certificate(), &client_id);
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (client_id.type() !=
|
||||
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
std::string("remote-attestation-invalid-client-id-type (") +
|
||||
absl::StrCat(client_id.type()) + ")"));
|
||||
}
|
||||
return VerifyRemoteAttestation(message, remote_attestation, client_id,
|
||||
remote_attestation_cert_sn);
|
||||
}
|
||||
|
||||
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
const std::string& privacy_key) {
|
||||
// Sanity check RemoteAttestation.
|
||||
if (!remote_attestation.has_certificate()) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-certificate-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_salt()) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-salt-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_signature()) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-signature-missing"));
|
||||
}
|
||||
// Decrypt ClientIdentification containing remote attestation certificate,
|
||||
// directly using an explicitly provided key |privacy_key|.
|
||||
ClientIdentification client_id;
|
||||
Status status = DecryptEncryptedClientIdentification(
|
||||
remote_attestation.certificate(), privacy_key, &client_id);
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (client_id.type() !=
|
||||
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
std::string("remote-attestation-invalid-client-id-type (") +
|
||||
absl::StrCat(client_id.type()) + ")"));
|
||||
}
|
||||
std::string remote_attestation_cert_sn;
|
||||
return VerifyRemoteAttestation(message, remote_attestation, client_id,
|
||||
&remote_attestation_cert_sn);
|
||||
}
|
||||
|
||||
Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) {
|
||||
if (!client_id.has_token()) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-token-missing"));
|
||||
}
|
||||
// Load and verify the certificate chain.
|
||||
std::unique_ptr<X509CertChain> cert_chain(new X509CertChain);
|
||||
Status status = cert_chain->LoadPem(client_id.token());
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (cert_chain->GetNumCerts() < 1) {
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-empty-certificate-chain"));
|
||||
}
|
||||
std::string device_mode_string =
|
||||
cert_chain->GetCert(0)->GetSubjectNameField(kDeviceModeFieldName);
|
||||
if (device_mode_string != kExpectedDeviceMode) {
|
||||
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
std::string("remote-attestation-device-not-verified (") +
|
||||
device_mode_string + " / " + kDeviceModeFieldName +
|
||||
")"));
|
||||
}
|
||||
ca_mutex_.ReaderLock();
|
||||
if (ca_ == NULL) {
|
||||
ca_mutex_.ReaderUnlock();
|
||||
status = LoadCa();
|
||||
if (!status.ok()) return status;
|
||||
ca_mutex_.ReaderLock();
|
||||
}
|
||||
status = ca_->VerifyCertChain(*cert_chain);
|
||||
ca_mutex_.ReaderUnlock();
|
||||
if (!status.ok()) {
|
||||
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
std::string("remote-attestation-cert-chain-validation-failed: ") +
|
||||
status.error_message()));
|
||||
}
|
||||
// Verify the remote attestation signature.
|
||||
std::unique_ptr<RsaPublicKey> leaf_key;
|
||||
std::string message_with_salt = message + remote_attestation.salt();
|
||||
for (size_t idx = 0; idx < cert_chain->GetNumCerts(); ++idx) {
|
||||
if (!cert_chain->GetCert(idx)->IsCaCertificate()) {
|
||||
leaf_key = cert_chain->GetCert(idx)->GetRsaPublicKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!leaf_key) {
|
||||
return Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
"remote-attestation-cert-chain-no-leaf");
|
||||
}
|
||||
|
||||
if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt,
|
||||
remote_attestation.signature())) {
|
||||
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
"remote-attestation-signature-verification-failed: "));
|
||||
}
|
||||
|
||||
*remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status RemoteAttestationVerifier::LoadCa() {
|
||||
absl::WriterMutexLock lock(&ca_mutex_);
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
Status status = ca_cert->LoadDer(absl::HexStringToBytes(
|
||||
enable_test_certificates_ ? kTestRootCaDerCert : kProdRootCaDerCert));
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
ca_.reset(new X509CA(ca_cert.release()));
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
92
common/remote_attestation_verifier.h
Normal file
92
common/remote_attestation_verifier.h
Normal file
@@ -0,0 +1,92 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Functionality used to verifier ChromeOS remote attestation.
|
||||
|
||||
#ifndef COMMON_REMOTE_ATTESTATION_VERIFIER_H_
|
||||
#define COMMON_REMOTE_ATTESTATION_VERIFIER_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/thread_annotations.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "common/status.h"
|
||||
#include "common/x509_cert.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/remote_attestation.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Singleton class used to do remote attestation. Access singleton instance via
|
||||
// the get() method.
|
||||
// TODO(user): This class is tested as part of the Session unit tests, but
|
||||
// finer unit tests should be implemented for the failure cases.
|
||||
class RemoteAttestationVerifier {
|
||||
public:
|
||||
RemoteAttestationVerifier() : enable_test_certificates_(false) {}
|
||||
virtual ~RemoteAttestationVerifier() {}
|
||||
|
||||
// Singleton accessor.
|
||||
static RemoteAttestationVerifier& get();
|
||||
|
||||
// Call to use the test (non-production) remote attestation root certificate.
|
||||
// This method is thread-safe.
|
||||
void EnableTestDrmCertificates(bool enable);
|
||||
|
||||
// Call to verify a RemoteAttestation challenge response, used in licensing
|
||||
// protocol.
|
||||
// |message| is the challenge message,
|
||||
// |remote_attestation| is the remote attestation response to verify,
|
||||
// |remote_attestation_cert_sn| is a pointer to a std::string which on successful
|
||||
// return will contain the serial number for the client's remote attestation
|
||||
// certificate.
|
||||
// This method is thread-safe.
|
||||
Status VerifyRemoteAttestation(const std::string& message,
|
||||
const RemoteAttestation& remote_attestation,
|
||||
std::string* remote_attestation_cert_sn);
|
||||
|
||||
// Call to verify a RemoteAttestation challenge response, used in certificate
|
||||
// provisioning protocol.
|
||||
// |message| is the challenge message,
|
||||
// |remote_attestation| is the remote attestation response to verify,
|
||||
// |privacy_key| is used to decrypt the EncryptedClientIdentification within
|
||||
// the |remote_attestation| message.
|
||||
// This method is thread-safe.
|
||||
Status VerifyRemoteAttestation(const std::string& message,
|
||||
const RemoteAttestation& remote_attestation,
|
||||
const std::string& privacy_key);
|
||||
|
||||
private:
|
||||
// Common subroutine to perform the verification.
|
||||
// |message| is the challenge message,
|
||||
// |remote_attestation| is the remote attestation response to verify,
|
||||
// |client_id| is the decrypted client identification carrying the token,
|
||||
// |remote_attestation_cert_sn| is a pointer to a std::string which on successful
|
||||
// return will contain the serial number for the client's remote attestation
|
||||
// certificate.
|
||||
Status VerifyRemoteAttestation(const std::string& message,
|
||||
const RemoteAttestation& remote_attestation,
|
||||
const ClientIdentification& client_id,
|
||||
std::string* remote_attestation_cert_sn);
|
||||
|
||||
Status LoadCa();
|
||||
|
||||
bool enable_test_certificates_;
|
||||
absl::Mutex ca_mutex_;
|
||||
std::unique_ptr<X509CA> ca_ GUARDED_BY(ca_mutex_);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RemoteAttestationVerifier);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_REMOTE_ATTESTATION_VERIFIER_H_
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -45,14 +45,22 @@ bool RsaKeyMatch(const RSA* key1, const RSA* key2) {
|
||||
return BN_cmp(key1->n, key2->n) == 0;
|
||||
}
|
||||
|
||||
std::string OpenSSLErrorString(uint32_t error) {
|
||||
char buf[ERR_ERROR_STRING_BUF_LEN];
|
||||
ERR_error_string_n(error, buf, sizeof(buf));
|
||||
return buf;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
|
||||
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(CHECK_NOTNULL(key)) {}
|
||||
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); }
|
||||
|
||||
RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key)
|
||||
: key_(CHECK_NOTNULL(RSAPrivateKey_dup(rsa_key.key_))) {}
|
||||
: key_(RSAPrivateKey_dup(rsa_key.key_)) {
|
||||
CHECK(key_ != nullptr);
|
||||
}
|
||||
|
||||
RsaPrivateKey::~RsaPrivateKey() { RSA_free(key_); }
|
||||
|
||||
@@ -61,7 +69,7 @@ RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) {
|
||||
if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key)) return nullptr;
|
||||
if (RSA_check_key(key) != 1) {
|
||||
LOG(ERROR) << "Invalid private RSA key: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
RSA_free(key);
|
||||
}
|
||||
return new RsaPrivateKey(key);
|
||||
@@ -86,7 +94,7 @@ bool RsaPrivateKey::Decrypt(const std::string& encrypted_message,
|
||||
RSA_PKCS1_OAEP_PADDING);
|
||||
if (decrypted_size == -1) {
|
||||
LOG(ERROR) << "RSA private decrypt failure: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
return false;
|
||||
}
|
||||
decrypted_message->resize(decrypted_size);
|
||||
@@ -112,7 +120,7 @@ bool RsaPrivateKey::GenerateSignature(const std::string& message,
|
||||
reinterpret_cast<unsigned char*>(&message_digest[0]), EVP_sha1(),
|
||||
EVP_sha1(), kPssSaltLength)) {
|
||||
LOG(ERROR) << "RSA padding failure: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
return false;
|
||||
}
|
||||
// Encrypt PSS padded digest.
|
||||
@@ -123,7 +131,7 @@ bool RsaPrivateKey::GenerateSignature(const std::string& message,
|
||||
key_, RSA_NO_PADDING) !=
|
||||
static_cast<int>(signature->size())) {
|
||||
LOG(ERROR) << "RSA private encrypt failure: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -157,10 +165,12 @@ bool RsaPrivateKey::MatchesPublicKey(const RsaPublicKey& public_key) const {
|
||||
|
||||
uint32_t RsaPrivateKey::KeySize() const { return RSA_size(key_); }
|
||||
|
||||
RsaPublicKey::RsaPublicKey(RSA* key) : key_(CHECK_NOTNULL(key)) {}
|
||||
RsaPublicKey::RsaPublicKey(RSA* key) : key_(key) { CHECK(key_ != nullptr); }
|
||||
|
||||
RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key)
|
||||
: key_(CHECK_NOTNULL(RSAPublicKey_dup(rsa_key.key_))) {}
|
||||
: key_(RSAPublicKey_dup(rsa_key.key_)) {
|
||||
CHECK(key_ != nullptr);
|
||||
}
|
||||
|
||||
RsaPublicKey::~RsaPublicKey() { RSA_free(key_); }
|
||||
|
||||
@@ -169,7 +179,7 @@ RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) {
|
||||
if (!rsa_util::DeserializeRsaPublicKey(serialized_key, &key)) return nullptr;
|
||||
if (RSA_size(key) == 0) {
|
||||
LOG(ERROR) << "Invalid public RSA key: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
RSA_free(key);
|
||||
}
|
||||
return new RsaPublicKey(key);
|
||||
@@ -192,7 +202,7 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
||||
reinterpret_cast<unsigned char*>(&(*encrypted_message)[0]), key_,
|
||||
RSA_PKCS1_OAEP_PADDING) != static_cast<int>(rsa_size)) {
|
||||
LOG(ERROR) << "RSA public encrypt failure: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -219,7 +229,7 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
reinterpret_cast<unsigned char*>(&padded_digest[0]), key_,
|
||||
RSA_NO_PADDING) != static_cast<int>(rsa_size)) {
|
||||
LOG(ERROR) << "RSA public decrypt failure: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
return false;
|
||||
}
|
||||
// Hash the message using SHA1.
|
||||
@@ -232,7 +242,7 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
reinterpret_cast<unsigned char*>(&padded_digest[0]),
|
||||
kPssSaltLength) == 0) {
|
||||
LOG(ERROR) << "RSA Verify PSS padding failure: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
<< OpenSSLErrorString(ERR_get_error());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -276,12 +286,12 @@ RsaKeyFactory::RsaKeyFactory() {}
|
||||
RsaKeyFactory::~RsaKeyFactory() {}
|
||||
|
||||
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs1PrivateKey(
|
||||
const std::string& private_key) {
|
||||
const std::string& private_key) const {
|
||||
return std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key));
|
||||
}
|
||||
|
||||
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
|
||||
const std::string& private_key, const std::string& private_key_passphrase) {
|
||||
const std::string& private_key, const std::string& private_key_passphrase) const {
|
||||
std::string pkcs1_key;
|
||||
const bool result =
|
||||
private_key_passphrase.empty()
|
||||
@@ -296,7 +306,7 @@ std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
|
||||
}
|
||||
|
||||
std::unique_ptr<RsaPublicKey> RsaKeyFactory::CreateFromPkcs1PublicKey(
|
||||
const std::string& public_key) {
|
||||
const std::string& public_key) const {
|
||||
return std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -60,9 +60,12 @@ class RsaPrivateKey {
|
||||
// Returns the RSA key size (modulus) in bytes.
|
||||
virtual uint32_t KeySize() const;
|
||||
|
||||
private:
|
||||
friend class RsaPublicKey;
|
||||
friend class X509CertificateBuilder; // TODO(user): Get rid of this.
|
||||
|
||||
const RSA* key() const { return key_; }
|
||||
|
||||
private:
|
||||
RSA* key_;
|
||||
|
||||
// SWIG appears to think this declaration is a syntax error. Excluding it for
|
||||
@@ -93,7 +96,6 @@ class RsaPublicKey {
|
||||
virtual bool VerifySignature(const std::string& message,
|
||||
const std::string& signature) const;
|
||||
|
||||
|
||||
// Verify a signature. This method takes two parameters: |message| which is a
|
||||
// std::string containing the data which was signed, and |signature| which is a
|
||||
// std::string containing the message SHA256 digest signature with PKCS#7
|
||||
@@ -111,9 +113,12 @@ class RsaPublicKey {
|
||||
// Returns the RSA key size (modulus) in bytes.
|
||||
virtual uint32_t KeySize() const;
|
||||
|
||||
private:
|
||||
friend class RsaPrivateKey;
|
||||
friend class X509CertificateBuilder; // TODO(user): Get rid of this.
|
||||
|
||||
const RSA* key() const { return key_; }
|
||||
|
||||
private:
|
||||
RSA* key_;
|
||||
|
||||
// SWIG appears to think this declaration is a syntax error. Excluding it for
|
||||
@@ -131,16 +136,16 @@ class RsaKeyFactory {
|
||||
|
||||
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
|
||||
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs1PrivateKey(
|
||||
const std::string& private_key);
|
||||
const std::string& private_key) const;
|
||||
|
||||
// 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 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 std::string& public_key) const;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(RsaKeyFactory);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/rsa_util.h"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -18,6 +18,10 @@
|
||||
namespace widevine {
|
||||
|
||||
// Container for test RSA keys
|
||||
//
|
||||
// Except for the keys noted below, these keys were generated using Euler's
|
||||
// Totient.
|
||||
//
|
||||
class RsaTestKeys {
|
||||
public:
|
||||
RsaTestKeys();
|
||||
@@ -47,6 +51,25 @@ class RsaTestKeys {
|
||||
return public_key_3_2048_bits_;
|
||||
}
|
||||
|
||||
// This key was converted to a Carmichael totient version from the original
|
||||
// private_key_2_2048_bits_.
|
||||
const std::string& private_test_key_2_carmichael_totient_2048_bits() const {
|
||||
return private_key_2_carmichael_totient_2048_bits_;
|
||||
}
|
||||
|
||||
// This key was converted to a Carmichael totient version from the original
|
||||
// private_key_3_2048_bits_.
|
||||
const std::string& private_test_key_3_carmichael_totient_2048_bits() const {
|
||||
return private_key_3_carmichael_totient_2048_bits_;
|
||||
}
|
||||
|
||||
// This key has been created using the Carmichael totient. This is
|
||||
// useful for testing with some RSA implementations that had challenges
|
||||
// with keys generated this way.
|
||||
const std::string& private_test_key_4_carmichael_totient_2048_bits() const {
|
||||
return private_key_4_carmichael_totient_2048_bits_;
|
||||
}
|
||||
|
||||
private:
|
||||
RsaTestKeys(const RsaTestKeys&) = delete;
|
||||
RsaTestKeys& operator=(const RsaTestKeys&) = delete;
|
||||
@@ -57,6 +80,11 @@ class RsaTestKeys {
|
||||
std::string public_key_2_2048_bits_;
|
||||
std::string private_key_3_2048_bits_;
|
||||
std::string public_key_3_2048_bits_;
|
||||
|
||||
// Tests keys that use the Carmichael totient to calculate d.
|
||||
std::string private_key_2_carmichael_totient_2048_bits_;
|
||||
std::string private_key_3_carmichael_totient_2048_bits_;
|
||||
std::string private_key_4_carmichael_totient_2048_bits_;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -13,38 +13,49 @@
|
||||
|
||||
#include "common/rsa_util.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/x509.h"
|
||||
|
||||
namespace {
|
||||
int BigNumGreaterThanPow2(const BIGNUM* b, int n) {
|
||||
if (BN_is_negative(b) || n == INT_MAX) {
|
||||
return 0;
|
||||
}
|
||||
int b_bits = BN_num_bits(b);
|
||||
return b_bits > n + 1 || (b_bits == n + 1 && !BN_is_pow2(b));
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
namespace widevine {
|
||||
namespace rsa_util {
|
||||
|
||||
static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
|
||||
bool serialize_private_key) {
|
||||
if (key == NULL) {
|
||||
if (key == nullptr) {
|
||||
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
|
||||
<< " RSA key is NULL.";
|
||||
<< " RSA key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (serialized_key == NULL) {
|
||||
LOG(ERROR) << "Pointer to hold serialized RSA" << (serialize_private_key ?
|
||||
"Private" : "Public") << "Key is NULL.";
|
||||
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 == NULL) {
|
||||
LOG(ERROR) << "BIO_new returned NULL";
|
||||
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) {
|
||||
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) ==
|
||||
@@ -54,8 +65,8 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
|
||||
LOG(ERROR) << "BIO_read failure";
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << (serialize_private_key ? "Private" : "Public") <<
|
||||
" key serialization failure";
|
||||
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
|
||||
<< " key serialization failure";
|
||||
}
|
||||
BIO_free(bio);
|
||||
return success;
|
||||
@@ -64,29 +75,31 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
|
||||
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.";
|
||||
LOG(ERROR) << "Serialized RSA"
|
||||
<< (deserialize_private_key ? "Private" : "Public")
|
||||
<< "Key is empty.";
|
||||
return false;
|
||||
}
|
||||
if (key == NULL) {
|
||||
LOG(ERROR) << "Pointer to hold new RSA " << (deserialize_private_key ?
|
||||
"private" : "public") << " key is NULL.";
|
||||
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 == NULL) {
|
||||
LOG(ERROR) << "BIO_new_mem_buf returned NULL";
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
|
||||
return false;
|
||||
}
|
||||
*key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, NULL) :
|
||||
d2i_RSAPublicKey_bio(bio, NULL);
|
||||
*key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, nullptr)
|
||||
: d2i_RSAPublicKey_bio(bio, nullptr);
|
||||
BIO_free(bio);
|
||||
if (*key == NULL) {
|
||||
LOG(ERROR) << (deserialize_private_key ? "Private" : "Public") <<
|
||||
" RSA key deserialization failure";
|
||||
if (*key == nullptr) {
|
||||
LOG(ERROR) << (deserialize_private_key ? "Private" : "Public")
|
||||
<< " RSA key deserialization failure";
|
||||
}
|
||||
return *key != NULL;
|
||||
return *key != nullptr;
|
||||
}
|
||||
|
||||
bool SerializeRsaPrivateKey(const RSA* private_key,
|
||||
@@ -111,12 +124,12 @@ bool DeserializeRsaPublicKey(const std::string& serialized_public_key,
|
||||
|
||||
bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||
std::string* serialized_private_key) {
|
||||
if (private_key == NULL) {
|
||||
LOG(ERROR) << "Private RSA key is NULL.";
|
||||
if (private_key == nullptr) {
|
||||
LOG(ERROR) << "Private RSA key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (serialized_private_key == NULL) {
|
||||
LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is NULL.";
|
||||
if (serialized_private_key == nullptr) {
|
||||
LOG(ERROR) << "Pointer to hold serialized PrivateKeyInfo is nullptr.";
|
||||
return false;
|
||||
}
|
||||
// The following method of serializing a PKCS#8 PrivateKeyInfo object
|
||||
@@ -124,25 +137,25 @@ bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||
// mechanism via i2d_PKCS8PrivateKey_bio is broken in the current openssl
|
||||
// version (1.0.0c). Please refer to b/8560683.
|
||||
EVP_PKEY* evp = EVP_PKEY_new();
|
||||
if (evp == NULL) {
|
||||
LOG(ERROR) << "EVP_PKEY_new returned NULL.";
|
||||
if (evp == nullptr) {
|
||||
LOG(ERROR) << "EVP_PKEY_new returned nullptr.";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
PKCS8_PRIV_KEY_INFO *pkcs8_pki = NULL;
|
||||
BIO* bio = NULL;
|
||||
PKCS8_PRIV_KEY_INFO* pkcs8_pki = nullptr;
|
||||
BIO* bio = nullptr;
|
||||
if (EVP_PKEY_set1_RSA(evp, const_cast<RSA*>(private_key)) == 0) {
|
||||
LOG(ERROR) << "EVP_PKEY_set1_RSA failed.";
|
||||
goto cleanup;
|
||||
}
|
||||
pkcs8_pki = EVP_PKEY2PKCS8(evp);
|
||||
if (pkcs8_pki == NULL) {
|
||||
LOG(ERROR) << "EVP_PKEY2PKCS8 returned NULL.";
|
||||
if (pkcs8_pki == nullptr) {
|
||||
LOG(ERROR) << "EVP_PKEY2PKCS8 returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
if (bio == NULL) {
|
||||
LOG(ERROR) << "BIO_new returned NULL.";
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
if (i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8_pki) == 0) {
|
||||
@@ -161,10 +174,10 @@ bool SerializePrivateKeyInfo(const RSA* private_key,
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
if (bio != NULL) {
|
||||
if (bio != nullptr) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
if (pkcs8_pki != NULL) {
|
||||
if (pkcs8_pki != nullptr) {
|
||||
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
|
||||
}
|
||||
EVP_PKEY_free(evp);
|
||||
@@ -177,8 +190,8 @@ bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
|
||||
LOG(ERROR) << "Serialized PrivateKeyInfo is empty.";
|
||||
return false;
|
||||
}
|
||||
if (private_key == NULL) {
|
||||
LOG(ERROR) << "Pointer to hold new RSA private key is NULL.";
|
||||
if (private_key == nullptr) {
|
||||
LOG(ERROR) << "Pointer to hold new RSA private key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
// The following method of deserializing a PKCS#8 PrivateKeyInfo object
|
||||
@@ -187,34 +200,34 @@ bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
|
||||
// version (1.0.0c). Please refer to b/8560683.
|
||||
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_private_key.data()),
|
||||
serialized_private_key.size());
|
||||
if (bio == NULL) {
|
||||
LOG(ERROR) << "BIO_new_mem_buf returned NULL";
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
EVP_PKEY* evp = NULL;
|
||||
PKCS8_PRIV_KEY_INFO *pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
|
||||
if (pkcs8_pki == NULL) {
|
||||
LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL.";
|
||||
EVP_PKEY* evp = nullptr;
|
||||
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
|
||||
if (pkcs8_pki == nullptr) {
|
||||
LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
evp = EVP_PKCS82PKEY(pkcs8_pki);
|
||||
if (evp == NULL) {
|
||||
LOG(ERROR) << "EVP_PKCS82PKEY returned NULL.";
|
||||
if (evp == nullptr) {
|
||||
LOG(ERROR) << "EVP_PKCS82PKEY returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
*private_key = EVP_PKEY_get1_RSA(evp);
|
||||
if (*private_key == NULL) {
|
||||
if (*private_key == nullptr) {
|
||||
LOG(ERROR) << "PrivateKeyInfo did not contain an RSA key.";
|
||||
goto cleanup;
|
||||
}
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
if (evp != NULL) {
|
||||
if (evp != nullptr) {
|
||||
EVP_PKEY_free(evp);
|
||||
}
|
||||
if (pkcs8_pki != NULL) {
|
||||
if (pkcs8_pki != nullptr) {
|
||||
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
|
||||
}
|
||||
BIO_free(bio);
|
||||
@@ -223,7 +236,7 @@ cleanup:
|
||||
|
||||
bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
std::string* private_key_info) {
|
||||
RSA* key = NULL;
|
||||
RSA* key = nullptr;
|
||||
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
|
||||
bool success = SerializePrivateKeyInfo(key, private_key_info);
|
||||
RSA_free(key);
|
||||
@@ -234,7 +247,7 @@ bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
|
||||
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
std::string* rsa_private_key) {
|
||||
RSA* key = NULL;
|
||||
RSA* key = nullptr;
|
||||
if (DeserializePrivateKeyInfo(private_key_info, &key)) {
|
||||
bool success = SerializeRsaPrivateKey(key, rsa_private_key);
|
||||
RSA_free(key);
|
||||
@@ -246,37 +259,38 @@ bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
|
||||
const std::string& passphrase,
|
||||
std::string* serialized_private_key) {
|
||||
if (private_key == NULL) {
|
||||
LOG(ERROR) << "Private RSA key is NULL.";
|
||||
if (private_key == nullptr) {
|
||||
LOG(ERROR) << "Private RSA key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (passphrase.empty()) {
|
||||
LOG(ERROR) << "Passphrase for RSA key encryption is empty.";
|
||||
return false;
|
||||
}
|
||||
if (serialized_private_key == NULL) {
|
||||
LOG(ERROR) << "Pointer to hold serialized EncryptedPrivateKeyInfo is NULL.";
|
||||
if (serialized_private_key == nullptr) {
|
||||
LOG(ERROR)
|
||||
<< "Pointer to hold serialized EncryptedPrivateKeyInfo is nullptr.";
|
||||
return false;
|
||||
}
|
||||
EVP_PKEY* evp = EVP_PKEY_new();
|
||||
if (evp == NULL) {
|
||||
LOG(ERROR) << "EVP_PKEY_new returned NULL.";
|
||||
if (evp == nullptr) {
|
||||
LOG(ERROR) << "EVP_PKEY_new returned nullptr.";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
BIO* bio = NULL;
|
||||
BIO* bio = nullptr;
|
||||
if (EVP_PKEY_set1_RSA(evp, const_cast<RSA*>(private_key)) == 0) {
|
||||
LOG(ERROR) << "EVP_PKEY_set1_RSA failed.";
|
||||
goto cleanup;
|
||||
}
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
if (bio == NULL) {
|
||||
LOG(ERROR) << "BIO_new returned NULL.";
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
if (i2d_PKCS8PrivateKey_bio(bio, evp, EVP_aes_256_cbc(),
|
||||
const_cast<char*>(passphrase.data()),
|
||||
passphrase.size(), NULL, NULL) == 0) {
|
||||
passphrase.size(), nullptr, nullptr) == 0) {
|
||||
LOG(ERROR) << "i2d_PKCS8PrivateKey_bio failed.";
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -292,7 +306,7 @@ bool SerializeEncryptedPrivateKeyInfo(const RSA* private_key,
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
if (bio != NULL) {
|
||||
if (bio != nullptr) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
EVP_PKEY_free(evp);
|
||||
@@ -301,7 +315,7 @@ cleanup:
|
||||
|
||||
namespace {
|
||||
// Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below.
|
||||
int get_password(char *buf, int size, int rwflag, void *u) {
|
||||
int get_password(char* buf, int size, int rwflag, void* u) {
|
||||
CHECK(buf);
|
||||
CHECK(u);
|
||||
const std::string* pass(static_cast<const std::string*>(u));
|
||||
@@ -324,32 +338,32 @@ bool DeserializeEncryptedPrivateKeyInfo(const std::string& serialized_private_ke
|
||||
LOG(ERROR) << "Passphrase for RSA key decryption is empty.";
|
||||
return false;
|
||||
}
|
||||
if (private_key == NULL) {
|
||||
LOG(ERROR) << "Pointer to hold new RSA private key is NULL.";
|
||||
if (private_key == nullptr) {
|
||||
LOG(ERROR) << "Pointer to hold new RSA private key is nullptr.";
|
||||
return false;
|
||||
}
|
||||
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_private_key.data()),
|
||||
serialized_private_key.size());
|
||||
if (bio == NULL) {
|
||||
LOG(ERROR) << "BIO_new_mem_buf returned NULL";
|
||||
if (bio == nullptr) {
|
||||
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, NULL, get_password,
|
||||
EVP_PKEY* evp = d2i_PKCS8PrivateKey_bio(bio, nullptr, get_password,
|
||||
const_cast<std::string*>(&passphrase));
|
||||
if (evp == NULL) {
|
||||
LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned NULL.";
|
||||
if (evp == nullptr) {
|
||||
LOG(ERROR) << "d2i_PKCS8PrivateKey_bio returned nullptr.";
|
||||
goto cleanup;
|
||||
}
|
||||
*private_key = EVP_PKEY_get1_RSA(evp);
|
||||
if (*private_key == NULL) {
|
||||
if (*private_key == nullptr) {
|
||||
LOG(ERROR) << "EncryptedPrivateKeyInfo did not contain an RSA key.";
|
||||
goto cleanup;
|
||||
}
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
if (evp != NULL) {
|
||||
if (evp != nullptr) {
|
||||
EVP_PKEY_free(evp);
|
||||
}
|
||||
BIO_free(bio);
|
||||
@@ -359,11 +373,10 @@ cleanup:
|
||||
bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
const std::string& passphrase,
|
||||
std::string* private_key_info) {
|
||||
RSA* key = NULL;
|
||||
RSA* key = nullptr;
|
||||
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
|
||||
bool success = SerializeEncryptedPrivateKeyInfo(key,
|
||||
passphrase,
|
||||
private_key_info);
|
||||
bool success =
|
||||
SerializeEncryptedPrivateKeyInfo(key, passphrase, private_key_info);
|
||||
RSA_free(key);
|
||||
return success;
|
||||
}
|
||||
@@ -373,7 +386,7 @@ bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
const std::string& passphrase,
|
||||
std::string* rsa_private_key) {
|
||||
RSA* key = NULL;
|
||||
RSA* key = nullptr;
|
||||
if (DeserializeEncryptedPrivateKeyInfo(private_key_info, passphrase, &key)) {
|
||||
bool success = SerializeRsaPrivateKey(key, rsa_private_key);
|
||||
RSA_free(key);
|
||||
@@ -382,5 +395,122 @@ bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConvertToEulerTotient(RSA* rsa) {
|
||||
// RSA key generation requires computing e (public exponent) and d (private
|
||||
// exponent), such that m^(e * d) = 1 (mod n) for all m coprime to n.
|
||||
// BoringSSL previously computed this by taking the inverse of e modulo the
|
||||
// Euler totient, (p - 1) * (q - 1). However, it now uses the Carmichael
|
||||
// totient, lcm(p - 1, q - 1). These two methods produce equivalent RSA keys.
|
||||
//
|
||||
// This breaks some vendors' RSA code which use a custom RSA format (rather
|
||||
// than the standard one in RFC 8017) which omits most of the required
|
||||
// parameters. They then attempt to recover those parameters, but their
|
||||
// recovery algorithm breaks when using the Carmichael totient. To work around
|
||||
// this bug, re-compute the private exponent against the Euler totient.
|
||||
const BIGNUM *e, *p, *q;
|
||||
RSA_get0_key(rsa, nullptr /* n */, &e, nullptr /* d */);
|
||||
RSA_get0_factors(rsa, &p, &q);
|
||||
|
||||
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
|
||||
bssl::UniquePtr<BIGNUM> pm1(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> qm1(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> totient(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> d(BN_new());
|
||||
if (!ctx || !pm1 || !qm1 || !totient || !d ||
|
||||
!BN_sub(pm1.get(), p, BN_value_one()) ||
|
||||
!BN_sub(qm1.get(), q, BN_value_one()) ||
|
||||
!BN_mul(totient.get(), pm1.get(), qm1.get(), ctx.get()) ||
|
||||
!BN_mod_inverse(d.get(), e, totient.get(), ctx.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform a sanity check that d is still valid after conversion.
|
||||
// d > 2 ^ (nlen / 2) per Appendix B 3.1 in FIPS 186/4.
|
||||
int prime_bits = (8 * RSA_size(rsa)) / 2;
|
||||
if (!BigNumGreaterThanPow2(d.get(), prime_bits)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RSA_set0_key(rsa, nullptr /* n */, nullptr /* e */, d.get())) {
|
||||
return false;
|
||||
}
|
||||
d.release(); // RSA_set0_key takes ownership on success.
|
||||
|
||||
if (!RSA_check_key(rsa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertToEulerTotient(const std::string& private_key,
|
||||
std::string* euler_private_key) {
|
||||
CHECK(euler_private_key);
|
||||
RSA* rsa_ptr;
|
||||
if (!rsa_util::DeserializeRsaPrivateKey(private_key, &rsa_ptr)) {
|
||||
return false;
|
||||
}
|
||||
bssl::UniquePtr<RSA> rsa(rsa_ptr);
|
||||
if (!rsa_util::ConvertToEulerTotient(rsa.get()) ||
|
||||
!rsa_util::SerializeRsaPrivateKey(rsa.get(), euler_private_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertToCarmichaelTotient(RSA* rsa) {
|
||||
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
|
||||
bssl::UniquePtr<BIGNUM> pm1(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> qm1(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> gcd(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> totient(BN_new());
|
||||
bssl::UniquePtr<BIGNUM> d(BN_new());
|
||||
// This calculates d = e^-1 (mod lcm(p-1, q-1)).
|
||||
// This is equivalent to what is used in RSA_generate_key in BoringSSL.
|
||||
if (!BN_sub(pm1.get(), rsa->p, BN_value_one()) ||
|
||||
!BN_sub(qm1.get(), rsa->q, BN_value_one()) ||
|
||||
!BN_mul(totient.get(), pm1.get(), qm1.get(), ctx.get()) ||
|
||||
!BN_gcd(gcd.get(), pm1.get(), qm1.get(), ctx.get()) ||
|
||||
!BN_div(totient.get(), nullptr, totient.get(), gcd.get(), ctx.get()) ||
|
||||
!BN_mod_inverse(d.get(), rsa->e, totient.get(), ctx.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform a sanity check that d is still valid after conversion.
|
||||
// d > 2 ^ (nlen / 2) per Appendix B 3.1 in FIPS 186/4.
|
||||
int prime_bits = (8 * RSA_size(rsa)) / 2;
|
||||
if (!BigNumGreaterThanPow2(d.get(), prime_bits)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(user): Replace this with |RSA_set0_key| once BoringSSL has
|
||||
// finished transitioning to the OpenSSL 1.1.0 API.
|
||||
BN_free(rsa->d);
|
||||
rsa->d = d.release();
|
||||
|
||||
if (!RSA_check_key(rsa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertToCarmichaelTotient(const std::string& private_key,
|
||||
std::string* carmichael_private_key) {
|
||||
CHECK(carmichael_private_key);
|
||||
RSA* rsa_ptr;
|
||||
if (!rsa_util::DeserializeRsaPrivateKey(private_key, &rsa_ptr)) {
|
||||
return false;
|
||||
}
|
||||
bssl::UniquePtr<RSA> rsa(rsa_ptr);
|
||||
if (!rsa_util::ConvertToCarmichaelTotient(rsa.get()) ||
|
||||
!rsa_util::SerializeRsaPrivateKey(rsa.get(), carmichael_private_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace rsa_util
|
||||
} // namespace widevine
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -99,7 +99,6 @@ bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
|
||||
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
std::string* rsa_private_key);
|
||||
|
||||
|
||||
// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo.
|
||||
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||
// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption.
|
||||
@@ -147,6 +146,55 @@ bool EncryptedPrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||
const std::string& passphrase,
|
||||
std::string* rsa_private_key);
|
||||
|
||||
// This method changes |rsa| to use the more common, but not FIPS 186-4
|
||||
// compliant, computation for the RSA private exponent (d). This used to be the
|
||||
// form produced by the BoringSSL method RSA_generate_key. This changed in this
|
||||
// CL: https://boringssl-review.googlesource.com/c/boringssl/+/15944
|
||||
//
|
||||
// This method is used to produce a "backward-compatible" version. Some vendor
|
||||
// RSA implementations do not handle the new computation properly.
|
||||
//
|
||||
// - rsa is the openssl RSA structure. Must not be null. Caller retains
|
||||
// ownership.
|
||||
//
|
||||
// Returns true if the key was successfully updated, false otherwise.
|
||||
bool ConvertToEulerTotient(RSA* rsa);
|
||||
|
||||
// This is wrapper to the other SwitchToEulerTotient that supports deserializing
|
||||
// and serializing from and to a DER encoded PKCS#1 RSAPrivateKey.
|
||||
//
|
||||
// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
|
||||
// deserialized and converted.
|
||||
// - euler_private_key is a pointer to the std::string to hold the converted and
|
||||
// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the
|
||||
// string. This parameter must not be NULL.
|
||||
// Returns true if the key was successfully updated, false otherwise.
|
||||
bool ConvertToEulerTotient(const std::string& private_key,
|
||||
std::string* euler_private_key);
|
||||
|
||||
// This method changes |rsa| to use the FIPS 186-4 compliant computation for d.
|
||||
// This uses the Carmichael totient. This is equivalent to the way the key
|
||||
// is generated in BoringSSL as of this change:
|
||||
// https://boringssl-review.googlesource.com/c/boringssl/+/15944
|
||||
//
|
||||
// This method is mostly used for testing. It allows tests to convert back and
|
||||
// forth between forms and verify the output.
|
||||
//
|
||||
// Returns true if the key can be successfully updated, false otherwise.
|
||||
bool ConvertToCarmichaelTotient(RSA* rsa);
|
||||
|
||||
// This is wrapper to the other SwitchToCarmichaelTotient that supports
|
||||
// deserializing and serializing from and to a DER encoded PKCS#1 RSAPrivateKey.
|
||||
//
|
||||
// - private_key is the DER-encoded PKCS#1 RSAPrivateKey to be
|
||||
// deserialized and converted.
|
||||
// - carmichael_private_key is a pointer to the std::string to hold the converted and
|
||||
// serialized PKCS#1 RSAPrivateKey object. Caller retains ownership of the
|
||||
// string. This parameter must not be NULL.
|
||||
// Returns true if the key was successfully updated, false otherwise.
|
||||
bool ConvertToCarmichaelTotient(const std::string& private_key,
|
||||
std::string* carmichael_private_key);
|
||||
|
||||
} // namespace rsa_util
|
||||
} // namespace widevine
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -10,11 +10,24 @@
|
||||
// Description:
|
||||
// Unit test for rsa_util RSA utilties library.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "testing/base/public/test_utils.h"
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "openssl/bn.h"
|
||||
#include "common/rsa_test_keys.h"
|
||||
#include "common/rsa_util.h"
|
||||
|
||||
using ::testing::NotNull;
|
||||
|
||||
namespace {
|
||||
const uint32_t kRsaPublicExponent = 65537;
|
||||
const int kTestRsaBits = 2048;
|
||||
} // anonymous namespace
|
||||
|
||||
namespace widevine {
|
||||
namespace rsa_util {
|
||||
|
||||
@@ -221,5 +234,120 @@ TEST_F(RsaUtilTest, Pkcs8FailOnInvalidPassword) {
|
||||
RSA_free(test_output_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToCarmichaelTotient_ExistingKey_Success) {
|
||||
bssl::UniquePtr<RSA> original_private_key;
|
||||
RSA* original_key_ptr = nullptr;
|
||||
|
||||
EXPECT_TRUE(DeserializeRsaPrivateKey(
|
||||
test_keys_.private_test_key_2_2048_bits(), &original_key_ptr));
|
||||
original_private_key.reset(original_key_ptr);
|
||||
ASSERT_THAT(original_private_key, NotNull());
|
||||
|
||||
bssl::UniquePtr<RSA> private_key(
|
||||
RSAPrivateKey_dup(original_private_key.get()));
|
||||
ASSERT_THAT(private_key, NotNull());
|
||||
EXPECT_TRUE(ConvertToCarmichaelTotient(private_key.get()));
|
||||
|
||||
// Confirm that the key is valid and has changed from the original.
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
std::string serialized_carmichael_private_key;
|
||||
EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(),
|
||||
&serialized_carmichael_private_key));
|
||||
EXPECT_NE(serialized_carmichael_private_key,
|
||||
test_keys_.private_test_key_2_2048_bits());
|
||||
|
||||
// Convert back and make sure the serialized key matches the original.
|
||||
EXPECT_TRUE(ConvertToEulerTotient(private_key.get()));
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
std::string serialized_euler_private_key;
|
||||
EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(),
|
||||
&serialized_euler_private_key));
|
||||
EXPECT_EQ(serialized_euler_private_key,
|
||||
test_keys_.private_test_key_2_2048_bits());
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToEulerTotient_NewKey_Success) {
|
||||
bssl::UniquePtr<RSA> rsa;
|
||||
bssl::UniquePtr<RSA> private_key;
|
||||
bssl::UniquePtr<BIGNUM> exponent(BN_new());
|
||||
ASSERT_TRUE(BN_set_word(exponent.get(), kRsaPublicExponent));
|
||||
|
||||
// It is possible that sometimes, the d value generated using carmichael
|
||||
// and euler is the same. For this test, find a key where they are not the
|
||||
// same (max 100 tries).
|
||||
bool found_distinct_keys = false;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
rsa.reset(RSA_new());
|
||||
ASSERT_TRUE(RSA_generate_key_ex(rsa.get(), kTestRsaBits,
|
||||
exponent.get(), nullptr));
|
||||
|
||||
private_key.reset(RSAPrivateKey_dup(rsa.get()));
|
||||
EXPECT_TRUE(ConvertToEulerTotient(private_key.get()));
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
|
||||
// If the values are different, break.
|
||||
if (BN_cmp(private_key->d, rsa->d) != 0) {
|
||||
found_distinct_keys = true;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Euler and Carmichael d values are the same. Count: " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(found_distinct_keys)
|
||||
<< "Reached maximum attempts, but did not generate distinct keys";
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
|
||||
// Sanity check that the serialized keys are distinct.
|
||||
std::string serialized_carmichael_private_key;
|
||||
std::string serialized_private_key;
|
||||
EXPECT_TRUE(SerializeRsaPrivateKey(rsa.get(),
|
||||
&serialized_carmichael_private_key));
|
||||
|
||||
EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(),
|
||||
&serialized_private_key));
|
||||
EXPECT_NE(serialized_carmichael_private_key, serialized_private_key);
|
||||
|
||||
// Convert back to Carmichael, validate, and confirm that the keys are the
|
||||
// same.
|
||||
EXPECT_TRUE(ConvertToCarmichaelTotient(private_key.get()));
|
||||
EXPECT_EQ(1, RSA_check_key(private_key.get()));
|
||||
EXPECT_TRUE(SerializeRsaPrivateKey(private_key.get(),
|
||||
&serialized_private_key));
|
||||
EXPECT_EQ(serialized_carmichael_private_key, serialized_private_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToSerializedCarmichaelTotient_Success) {
|
||||
std::string private_key;
|
||||
EXPECT_TRUE(ConvertToCarmichaelTotient(
|
||||
test_keys_.private_test_key_2_2048_bits(), &private_key));
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_carmichael_totient_2048_bits(),
|
||||
private_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToSerializedEulerTotient_Success) {
|
||||
std::string private_key;
|
||||
EXPECT_TRUE(ConvertToEulerTotient(
|
||||
test_keys_.private_test_key_2_carmichael_totient_2048_bits(),
|
||||
&private_key));
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), private_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToEulerTotient_Idempotent_Success) {
|
||||
std::string private_key;
|
||||
EXPECT_TRUE(ConvertToEulerTotient(test_keys_.private_test_key_2_2048_bits(),
|
||||
&private_key));
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_2048_bits(), private_key);
|
||||
}
|
||||
|
||||
TEST_F(RsaUtilTest, ConvertToCarmichaelTotient_Idempotent_Success) {
|
||||
std::string private_key;
|
||||
EXPECT_TRUE(ConvertToCarmichaelTotient(
|
||||
test_keys_.private_test_key_2_carmichael_totient_2048_bits(),
|
||||
&private_key));
|
||||
EXPECT_EQ(test_keys_.private_test_key_2_carmichael_totient_2048_bits(),
|
||||
private_key);
|
||||
}
|
||||
|
||||
} // namespace rsa_util
|
||||
} // namespace widevine
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -29,4 +29,43 @@ std::string Sha256_Hash(const std::string& message) {
|
||||
return digest;
|
||||
}
|
||||
|
||||
std::string Sha512_Hash(const std::string& message) {
|
||||
std::string digest;
|
||||
digest.resize(SHA512_DIGEST_LENGTH);
|
||||
SHA512(reinterpret_cast<const uint8_t*>(message.data()), message.size(),
|
||||
reinterpret_cast<uint8_t*>(&digest[0]));
|
||||
return digest;
|
||||
}
|
||||
|
||||
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name) {
|
||||
// X.667 14 Setting the fields of a name-based UUID.
|
||||
// - Allocate a UUID to use as a "name space identifier" for all UUIDs
|
||||
// generated from names in that name space.
|
||||
// - Compute the 16-octet hash value of the name space identifier concatenated
|
||||
// with the name.
|
||||
SHA_CTX ctx;
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, name_space.data(), name_space.length());
|
||||
SHA1_Update(&ctx, name.data(), name.length());
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
SHA1_Final(hash, &ctx);
|
||||
std::string hash_str =
|
||||
std::string(reinterpret_cast<const char*>(hash), SHA_DIGEST_LENGTH);
|
||||
|
||||
// - For a SHA-1 hash function, the "hash value" referenced in 14.1 shall be
|
||||
// octets zero to 15.
|
||||
std::string uuid = hash_str.substr(0, 16);
|
||||
|
||||
// - Overwrite the four most significant bits (bits 15 through 12) of the
|
||||
// "VersionAndTimeHigh" field with the four-bit version number from Table 3
|
||||
// of 12.2 for the hash function that was used. [Name-based SHA-1 is 5]
|
||||
(uuid[6] &= 0xF) |= 0x50;
|
||||
|
||||
// - Overwrite the two most significant bits (bits 7 and 6) of the
|
||||
// "VariantAndClockSeqHigh" field with 1 and 0, respectively.
|
||||
(uuid[8] &= 0x3F) |= 0x80;
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Calculates SHA1 hash.
|
||||
@@ -19,6 +21,14 @@ std::string Sha1_Hash(const std::string& message);
|
||||
// Calculates SHA256 hash.
|
||||
std::string Sha256_Hash(const std::string& message);
|
||||
|
||||
// Calculate SHA512 hash.
|
||||
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);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_SHA_UTIL_H_
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google Inc.
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
@@ -7,8 +7,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/sha_util.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -31,21 +31,41 @@ TEST(ShaUtilTest, Sha256Empty) {
|
||||
|
||||
TEST(ShaUtilTest, Sha1) {
|
||||
const uint8_t kExpected[] = {
|
||||
0x5f, 0xfd, 0x3b, 0x85, 0x2c, 0xcd, 0xd4, 0x48, 0x80, 0x9a,
|
||||
0xbb, 0x17, 0x2e, 0x19, 0xbb, 0xb9, 0xc0, 0x1a, 0x43, 0xa4,
|
||||
0x6b, 0x63, 0xa5, 0xe3, 0x6b, 0xfe, 0x54, 0x19, 0x4e, 0xfe,
|
||||
0x9f, 0xe0, 0x29, 0x7e, 0xd8, 0x11, 0x0f, 0x10, 0x0d, 0x1d,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha1_Hash("random std::string"));
|
||||
Sha1_Hash("random data"));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, Sha256) {
|
||||
const uint8_t kExpected[] = {
|
||||
0xa8, 0x2a, 0xf4, 0xe9, 0x6b, 0x5b, 0x8d, 0x82, 0x5a, 0x69, 0x10,
|
||||
0xb9, 0x08, 0xec, 0xcd, 0xc4, 0x40, 0x1a, 0xf1, 0xe6, 0x63, 0xdb,
|
||||
0x5e, 0xdf, 0x2d, 0x7d, 0xfb, 0x71, 0x2d, 0xb9, 0x65, 0x29,
|
||||
0x62, 0x4b, 0x56, 0xb3, 0x38, 0x1b, 0xbc, 0xe0, 0x4f, 0x58, 0x88,
|
||||
0x83, 0xb4, 0x2f, 0x4e, 0x27, 0xfe, 0xc0, 0x95, 0x56, 0xf8, 0x61,
|
||||
0xcf, 0x94, 0x49, 0xe6, 0x5f, 0x26, 0xea, 0x70, 0xad, 0x88,
|
||||
};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha256_Hash("random std::string"));
|
||||
Sha256_Hash("random data"));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, Sha512) {
|
||||
const uint8_t kExpected[] = {
|
||||
0x8f, 0x49, 0x93, 0x1f, 0x4d, 0x4a, 0x67, 0x6f, 0x9a, 0x7e, 0x62,
|
||||
0x60, 0xea, 0xe1, 0x54, 0xfe, 0xe2, 0x75, 0x3c, 0xec, 0x3b, 0xb2,
|
||||
0x2e, 0xd7, 0x51, 0xcc, 0x39, 0xf9, 0x89, 0x69, 0xad, 0xd0, 0x14,
|
||||
0xaa, 0xbe, 0x40, 0xce, 0xe3, 0xab, 0xef, 0x10, 0x2f, 0x24, 0x0e,
|
||||
0xd8, 0x26, 0x7b, 0xb5, 0x7d, 0x86, 0xce, 0xbd, 0xd7, 0x32, 0x86,
|
||||
0x3a, 0x5e, 0x9e, 0x8d, 0x23, 0x77, 0x10, 0x80, 0x0c};
|
||||
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
|
||||
Sha512_Hash("random data"));
|
||||
}
|
||||
|
||||
TEST(ShaUtilTest, GenerateSha1Uuid) {
|
||||
std::string name_space =
|
||||
absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774");
|
||||
std::string name = "some seed value";
|
||||
EXPECT_EQ("730621a55c675c4086be38e72aefa03e",
|
||||
absl::BytesToHexString(GenerateSha1Uuid(name_space, name)));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
61
common/signature_util.cc
Normal file
61
common/signature_util.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/signature_util.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/sha_util.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace signature_util {
|
||||
|
||||
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");
|
||||
}
|
||||
std::string hash = Sha1_Hash(message);
|
||||
if (hash.empty()) {
|
||||
return Status(error::INTERNAL, "Computed hash is empty");
|
||||
}
|
||||
std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash);
|
||||
if (sig.empty()) {
|
||||
return Status(error::INTERNAL, "Computed AES signature is empty");
|
||||
}
|
||||
*signature = sig;
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
std::unique_ptr<RsaPrivateKey> rsa_private_key(
|
||||
RsaPrivateKey::Create(private_key));
|
||||
if (rsa_private_key == nullptr) {
|
||||
return Status(error::INTERNAL, "Failed to construct a RsaPrivateKey");
|
||||
}
|
||||
std::string sig;
|
||||
if (!rsa_private_key->GenerateSignature(message, &sig)) {
|
||||
return Status(error::INTERNAL, "Failed to generate a RSA signature");
|
||||
}
|
||||
if (sig.empty()) {
|
||||
return Status(error::INTERNAL, "Computed RSA signature is empty");
|
||||
}
|
||||
*signature = sig;
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace signature_util
|
||||
} // namespace widevine
|
||||
34
common/signature_util.h
Normal file
34
common/signature_util.h
Normal file
@@ -0,0 +1,34 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_SIGNATURE_UTIL_H_
|
||||
#define COMMON_SIGNATURE_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace signature_util {
|
||||
|
||||
// Generates an AES signature of |message| using |aes_key| and |aes_iv|.
|
||||
// Signature is returned via |signature| if generation was successful.
|
||||
// Returns a Status that carries the details of error if generation failed.
|
||||
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,
|
||||
std::string* signature);
|
||||
|
||||
} // namespace signature_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_SIGNATURE_UTIL_H_
|
||||
44
common/signing_key_util.cc
Normal file
44
common/signing_key_util.cc
Normal file
@@ -0,0 +1,44 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/signing_key_util.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using crypto_util::kSigningKeySizeBits;
|
||||
|
||||
uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version) {
|
||||
if (protocol_version <= VERSION_2_0) {
|
||||
return kSigningKeySizeBits;
|
||||
} else {
|
||||
return kSigningKeySizeBits * 2;
|
||||
}
|
||||
}
|
||||
|
||||
using crypto_util::kSigningKeySizeBytes;
|
||||
std::string GetClientSigningKey(const std::string& derived_key,
|
||||
ProtocolVersion protocol_version) {
|
||||
if (protocol_version == VERSION_2_0) {
|
||||
DCHECK(derived_key.size() >= kSigningKeySizeBytes);
|
||||
return derived_key.substr(0, kSigningKeySizeBytes);
|
||||
} else {
|
||||
DCHECK(derived_key.size() >= kSigningKeySizeBytes * 2);
|
||||
return derived_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetServerSigningKey(const std::string& derived_key) {
|
||||
DCHECK(derived_key.size() >= kSigningKeySizeBytes);
|
||||
return derived_key.substr(0, kSigningKeySizeBytes);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
55
common/signing_key_util.h
Normal file
55
common/signing_key_util.h
Normal file
@@ -0,0 +1,55 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Contains functions that are used to create and parse derived keys created
|
||||
// using the NIST 800-108 KDF recommendation, using AES-CMAC PRF.
|
||||
// NIST 800-108:
|
||||
// http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf
|
||||
// AES-CMAC:
|
||||
// http://tools.ietf.org/html/rfc4493
|
||||
//
|
||||
// Example usage:
|
||||
// using video::widevine::common::crypto_util::DeriveKey;
|
||||
// using widevine_server::sdk::VERSION_2_1;
|
||||
//
|
||||
// std::string derived_key = DeriveKey(key_str,
|
||||
// label,
|
||||
// context,
|
||||
// SigningKeyMaterialSizeBits(VERSION_2_1));
|
||||
// std::string server_derived_key = GetServerSigningKey(derived_key);
|
||||
// std::string client_derived_key = GetClientSigninKey(derived_key);
|
||||
#ifndef COMMON_SIGNING_KEY_UTIL_H_
|
||||
#define COMMON_SIGNING_KEY_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Returns the size of the signing key based on the License Protocol
|
||||
// Version. Signing keys for version 2.0 have a length of 256. Signing
|
||||
// keys for version 2.1 have a length of 512.
|
||||
uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version);
|
||||
|
||||
// Returns the client portion of the derived_key. The client portion
|
||||
// depend on the size of the key. Keys that are 512 bits in length
|
||||
// are assumed to be version 2.1 keys. The last 256 bits of those
|
||||
// keys are returned. Keys that are 256 bits in length are returned
|
||||
// in there entirety, version 2.0 keys.
|
||||
std::string GetClientSigningKey(const std::string& derived_key,
|
||||
ProtocolVersion protocol_version);
|
||||
|
||||
// Returns the server portion of the derived_key. The server portion
|
||||
// is the first 256 bits of the key.
|
||||
std::string GetServerSigningKey(const std::string& derived_key);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_SIGNING_KEY_UTIL_H_
|
||||
64
common/signing_key_util_test.cc
Normal file
64
common/signing_key_util_test.cc
Normal file
@@ -0,0 +1,64 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/signing_key_util.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/crypto_util.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace signing_key_util {
|
||||
namespace {
|
||||
const char* kFrontKeyHex =
|
||||
"0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b0a1a2a3a4a5a6a7a8a9a0b1b2b3b4b5b";
|
||||
const char* kBackKeyHex =
|
||||
"0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d0c1c2c3c4c5c6c7c8c9c0d1d2d3d4d5d";
|
||||
|
||||
std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) {
|
||||
if (protocol_version == widevine::VERSION_2_0) {
|
||||
return absl::HexStringToBytes(kFrontKeyHex);
|
||||
} else {
|
||||
return absl::HexStringToBytes(kFrontKeyHex) +
|
||||
absl::HexStringToBytes(kBackKeyHex);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_0) {
|
||||
ASSERT_EQ(crypto_util::kSigningKeySizeBits,
|
||||
SigningKeyMaterialSizeBits(VERSION_2_0));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_1) {
|
||||
ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2,
|
||||
SigningKeyMaterialSizeBits(VERSION_2_1));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) {
|
||||
ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey(
|
||||
GenerateDerivedKey(VERSION_2_1))));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_1) {
|
||||
ASSERT_EQ(kBackKeyHex, absl::BytesToHexString(GetClientSigningKey(
|
||||
GenerateDerivedKey(VERSION_2_1), VERSION_2_1)));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_0) {
|
||||
ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetServerSigningKey(
|
||||
GenerateDerivedKey(VERSION_2_0))));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, GetClientSigningKeyProtocolVersion2_0) {
|
||||
ASSERT_EQ(kFrontKeyHex, absl::BytesToHexString(GetClientSigningKey(
|
||||
GenerateDerivedKey(VERSION_2_0), VERSION_2_0)));
|
||||
}
|
||||
|
||||
} // namespace signing_key_util
|
||||
} // namespace widevine
|
||||
74
common/status.cc
Normal file
74
common/status.cc
Normal file
@@ -0,0 +1,74 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/status.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "util/error_space.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
|
||||
const char* kGenericErrorStatusMessage[] = {"OK",
|
||||
"unknown_error",
|
||||
"unknown_error",
|
||||
"invalid_argument",
|
||||
"unknown_error",
|
||||
"not_found",
|
||||
"already_exists",
|
||||
"permission_denied",
|
||||
"unknown_error",
|
||||
"unknown_error",
|
||||
"unknown_error",
|
||||
"unknown_error",
|
||||
"unimplemented",
|
||||
"internal",
|
||||
"unavailable"};
|
||||
|
||||
} // namespace
|
||||
|
||||
class GenericErrorSpace : public util::ErrorSpaceImpl<GenericErrorSpace> {
|
||||
public:
|
||||
static std::string space_name();
|
||||
static std::string code_to_string(int code);
|
||||
};
|
||||
|
||||
std::string GenericErrorSpace::space_name() { return "generic"; }
|
||||
|
||||
std::string GenericErrorSpace::code_to_string(int code) {
|
||||
static_assert(
|
||||
ABSL_ARRAYSIZE(kGenericErrorStatusMessage) == error::NUM_ERRORS,
|
||||
"mismatching generic error status message and generic error status.");
|
||||
|
||||
if (code >= 0 && code < error::NUM_ERRORS)
|
||||
return kGenericErrorStatusMessage[code];
|
||||
return std::to_string(code);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const util::ErrorSpace* Status::canonical_space() {
|
||||
return GenericErrorSpace::Get();
|
||||
}
|
||||
|
||||
std::string Status::ToString() const {
|
||||
if (status_code_ == error::OK) return "OK";
|
||||
return absl::StrCat("Errors::", error_space_->String(status_code_), ": ",
|
||||
error_message_);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Status& x) {
|
||||
os << x.ToString();
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
108
common/status.h
Normal file
108
common/status.h
Normal file
@@ -0,0 +1,108 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_STATUS_H_
|
||||
#define COMMON_STATUS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/error_space.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace error {
|
||||
|
||||
enum StatusCode {
|
||||
// Success.
|
||||
OK = 0,
|
||||
|
||||
// Client specified an invalid argument.
|
||||
INVALID_ARGUMENT = 3,
|
||||
|
||||
// Some requested entity (e.g., file or directory) was not found.
|
||||
NOT_FOUND = 5,
|
||||
|
||||
// Some entity that we attempted to create (e.g., file or directory)
|
||||
// already exists.
|
||||
ALREADY_EXISTS = 6,
|
||||
|
||||
// The caller does not have permission to execute the specified
|
||||
// operation. PERMISSION_DENIED must not be used for rejections
|
||||
// caused by exhausting some resource (use RESOURCE_EXHAUSTED
|
||||
// instead for those errors).
|
||||
PERMISSION_DENIED = 7,
|
||||
|
||||
// Operation is not implemented or not supported/enabled in this service.
|
||||
UNIMPLEMENTED = 12,
|
||||
|
||||
// Internal errors. Means some invariants expected by underlying
|
||||
// system has been broken. If you see one of these errors,
|
||||
// something is very broken.
|
||||
INTERNAL = 13,
|
||||
|
||||
// Operation is not implemented or not supported/enabled in this service.
|
||||
UNAVAILABLE = 14,
|
||||
|
||||
// Number of generic (non license related) errors.
|
||||
NUM_ERRORS,
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
|
||||
class Status {
|
||||
public:
|
||||
|
||||
Status() = default;
|
||||
|
||||
~Status() = default;
|
||||
|
||||
explicit Status(error::StatusCode c) : status_code_(c) {}
|
||||
|
||||
Status(error::StatusCode c, const std::string& error_message)
|
||||
: status_code_(c), error_message_(error_message) {}
|
||||
|
||||
Status(const util::ErrorSpace* e, error::StatusCode c,
|
||||
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)
|
||||
: error_space_(e), status_code_(error), error_message_(error_message) {}
|
||||
|
||||
bool ok() const { return status_code_ == error::OK; }
|
||||
const util::ErrorSpace* error_space() const { return error_space_; }
|
||||
static const util::ErrorSpace* canonical_space();
|
||||
std::string ToString() const;
|
||||
std::string error_message() const { return error_message_; }
|
||||
int error_code() const { return status_code_; }
|
||||
|
||||
private:
|
||||
const util::ErrorSpace* error_space_ = canonical_space();
|
||||
int status_code_ = error::OK;
|
||||
std::string error_message_;
|
||||
};
|
||||
|
||||
inline Status OkStatus() { return Status(); }
|
||||
|
||||
inline bool operator==(const Status& s1, const Status& s2) {
|
||||
return s1.error_space() == s2.error_space() &&
|
||||
s1.error_code() == s2.error_code() &&
|
||||
s1.error_message() == s2.error_message();
|
||||
}
|
||||
inline bool operator!=(const Status& s1, const Status& s2) {
|
||||
return !(s1 == s2);
|
||||
}
|
||||
|
||||
|
||||
// Prints a human-readable representation of 'x' to 'os'.
|
||||
std::ostream& operator<<(std::ostream& os, const Status& x);
|
||||
|
||||
#define CHECK_OK(expression) CHECK(expression.ok()) << expression.ToString()
|
||||
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_STATUS_H_
|
||||
61
common/status_test.cc
Normal file
61
common/status_test.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2007 Google LLC.
|
||||
//
|
||||
// This software 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/status.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
TEST(StatusTest, OK_Status) {
|
||||
// test case for ok status.
|
||||
Status status(error::OK);
|
||||
EXPECT_EQ("OK", status.ToString());
|
||||
}
|
||||
|
||||
TEST(StatusTest, OK_Status2) {
|
||||
// test case for ok status.
|
||||
Status status;
|
||||
EXPECT_EQ("OK", status.ToString());
|
||||
}
|
||||
|
||||
TEST(StatusTest, ALREADY_EXISTS_Status) {
|
||||
Status status(error::ALREADY_EXISTS, "it is already exist");
|
||||
EXPECT_EQ("Errors::already_exists: it is already exist", status.ToString());
|
||||
}
|
||||
|
||||
// test case for status in boundary cases.
|
||||
TEST(StatusTest, UNAVAILABLE_Status) {
|
||||
Status status(error::UNAVAILABLE, "unavailable");
|
||||
EXPECT_EQ("Errors::unavailable: unavailable", status.ToString());
|
||||
}
|
||||
|
||||
TEST(StatusTest, NoNameCode) {
|
||||
Status status(static_cast<error::StatusCode>(101), "Unknown error");
|
||||
EXPECT_EQ("Errors::101: Unknown error", status.ToString());
|
||||
}
|
||||
|
||||
TEST(StatusTest, EQUAL_OPERATOR) {
|
||||
Status status1(error::ALREADY_EXISTS, "already exists 1");
|
||||
Status status2(error::ALREADY_EXISTS, "already exists 1");
|
||||
EXPECT_EQ(status1, status2);
|
||||
}
|
||||
|
||||
TEST(StatusTest, NOT_EQUAL_OPERATOR) {
|
||||
Status status1(error::ALREADY_EXISTS, "already exists");
|
||||
Status status2(error::UNAVAILABLE, "unavailable");
|
||||
EXPECT_NE(status1, status2);
|
||||
}
|
||||
|
||||
TEST(StatusTest, NOT_EQUAL_OPERATOR_NONE_MSG) {
|
||||
Status status1(error::ALREADY_EXISTS);
|
||||
Status status2(error::UNAVAILABLE);
|
||||
EXPECT_NE(status1, status2);
|
||||
}
|
||||
|
||||
|
||||
} // namespace widevine
|
||||
36
common/string_util.cc
Normal file
36
common/string_util.cc
Normal file
@@ -0,0 +1,36 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace string_util {
|
||||
|
||||
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) {
|
||||
if (output == nullptr) {
|
||||
return Status(error::INTERNAL, "output is nullptr.");
|
||||
}
|
||||
|
||||
std::stringstream sstream(bitset);
|
||||
for (size_t i = 0; i < bitset.size(); i += 8) {
|
||||
std::bitset<8> bits;
|
||||
sstream >> bits;
|
||||
char c = static_cast<char>(bits.to_ulong());
|
||||
*output += c;
|
||||
}
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace string_util
|
||||
} // namespace widevine
|
||||
25
common/string_util.h
Normal file
25
common/string_util.h
Normal file
@@ -0,0 +1,25 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_STRING_UTIL_H_
|
||||
#define COMMON_STRING_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace string_util {
|
||||
|
||||
// Converts std::string representation of a bitset to its binary equivalent string.
|
||||
// For example, converts "01110100011001010111001101110100" to "test".
|
||||
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output);
|
||||
|
||||
} // namespace string_util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_STRING_UTIL_H_
|
||||
26
common/string_util_test.cc
Normal file
26
common/string_util_test.cc
Normal file
@@ -0,0 +1,26 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace string_util {
|
||||
|
||||
TEST(StringUtilTest, BitsetStringToBinaryString) {
|
||||
std::string input = "01110100011001010111001101110100";
|
||||
std::string output;
|
||||
|
||||
EXPECT_OK(BitsetStringToBinaryString(input, &output));
|
||||
|
||||
EXPECT_EQ("test", output);
|
||||
}
|
||||
|
||||
} // namespace string_util
|
||||
} // namespace widevine
|
||||
328
common/test_drm_certificates.cc
Normal file
328
common/test_drm_certificates.cc
Normal file
@@ -0,0 +1,328 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
|
||||
#include "common/test_drm_certificates.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
static const unsigned char kTestRootCertificate[] = {
|
||||
0x0a, 0x99, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xb9, 0x60, 0x22,
|
||||
0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5,
|
||||
0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78,
|
||||
0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f,
|
||||
0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca,
|
||||
0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b,
|
||||
0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46,
|
||||
0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01,
|
||||
0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c,
|
||||
0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14,
|
||||
0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd,
|
||||
0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6,
|
||||
0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde,
|
||||
0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31,
|
||||
0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a,
|
||||
0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90,
|
||||
0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49,
|
||||
0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f,
|
||||
0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda,
|
||||
0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17,
|
||||
0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30,
|
||||
0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf,
|
||||
0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16,
|
||||
0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42,
|
||||
0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9,
|
||||
0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4,
|
||||
0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a,
|
||||
0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72,
|
||||
0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde,
|
||||
0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04,
|
||||
0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b,
|
||||
0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33,
|
||||
0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67,
|
||||
0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02,
|
||||
0x03, 0x01, 0x00, 0x01, 0x12, 0x80, 0x03, 0x7f, 0x83, 0xde, 0xf0, 0x6a,
|
||||
0x07, 0x2b, 0x8c, 0xd7, 0x0c, 0xb8, 0x75, 0x50, 0xce, 0xe8, 0xa9, 0x35,
|
||||
0xcb, 0x9d, 0xe3, 0x83, 0x89, 0xe6, 0x78, 0xb2, 0x12, 0x12, 0x16, 0xfe,
|
||||
0x62, 0xf9, 0xed, 0x1d, 0x1d, 0xda, 0x82, 0x67, 0x82, 0x30, 0xf8, 0x49,
|
||||
0xc2, 0x49, 0x65, 0x3b, 0xa3, 0x69, 0xaa, 0xd4, 0xaa, 0xfa, 0x74, 0xa6,
|
||||
0xf1, 0xc3, 0xd8, 0xd0, 0x84, 0x27, 0x00, 0xa2, 0xec, 0xbd, 0xcf, 0x58,
|
||||
0xf2, 0xf6, 0x60, 0x00, 0xeb, 0x50, 0xae, 0x06, 0x9e, 0x5c, 0xd2, 0xce,
|
||||
0xc0, 0xbc, 0x73, 0xdb, 0x66, 0xc4, 0x93, 0x39, 0x22, 0x92, 0x92, 0x27,
|
||||
0x71, 0x3c, 0x25, 0x66, 0x96, 0x2e, 0xda, 0x66, 0x65, 0xbc, 0x38, 0xf5,
|
||||
0x4e, 0x8e, 0x68, 0x4d, 0x5f, 0x8f, 0xf5, 0x90, 0xcc, 0xfb, 0xf3, 0x8c,
|
||||
0x63, 0x3f, 0xe2, 0xf9, 0x4a, 0x37, 0xec, 0x68, 0x0b, 0x00, 0xcd, 0x0e,
|
||||
0x13, 0x66, 0x06, 0x2f, 0x37, 0xc7, 0x3a, 0xa3, 0x7a, 0x1e, 0xb8, 0x12,
|
||||
0x1d, 0xf4, 0x09, 0xba, 0xfc, 0x55, 0x1d, 0xa8, 0x54, 0x4a, 0x4c, 0x54,
|
||||
0xda, 0x32, 0xe3, 0x4c, 0xa2, 0x03, 0xae, 0x65, 0xf0, 0x81, 0x4a, 0xe8,
|
||||
0xc7, 0x93, 0x78, 0xdf, 0xc0, 0x3d, 0xc5, 0x24, 0xdc, 0x45, 0x27, 0xe1,
|
||||
0xba, 0xc8, 0xe2, 0x1f, 0x27, 0x7c, 0x61, 0xba, 0x1b, 0x31, 0xc0, 0xf1,
|
||||
0xad, 0x13, 0xdd, 0x61, 0x31, 0xf4, 0xc0, 0xe9, 0x0e, 0x8c, 0x8e, 0xe8,
|
||||
0xd1, 0xf8, 0xdb, 0x76, 0xdf, 0x3f, 0x1a, 0x25, 0x28, 0x46, 0xc4, 0xf4,
|
||||
0xdb, 0x8a, 0x3b, 0x03, 0x16, 0x96, 0x6b, 0x28, 0x0f, 0x05, 0xe6, 0xa9,
|
||||
0xcb, 0x0d, 0x95, 0x57, 0x89, 0x3e, 0x4c, 0x70, 0xed, 0x84, 0x45, 0xdd,
|
||||
0x88, 0x43, 0x4b, 0xc1, 0x9e, 0x52, 0xb3, 0x3a, 0xa1, 0xd9, 0xd4, 0xf9,
|
||||
0x68, 0x08, 0x0b, 0x83, 0x35, 0x75, 0xf1, 0x2a, 0xa7, 0xce, 0xf6, 0x3f,
|
||||
0x4a, 0x84, 0xd0, 0x0c, 0xfa, 0xf2, 0x0f, 0x42, 0x28, 0x1a, 0x1a, 0x92,
|
||||
0xa7, 0x7d, 0x6f, 0xad, 0x57, 0x82, 0x44, 0x1a, 0x6d, 0x35, 0x85, 0x15,
|
||||
0x2c, 0xd4, 0x28, 0xb4, 0x7c, 0xde, 0x66, 0x3b, 0xeb, 0x6d, 0x32, 0xc0,
|
||||
0x30, 0xdf, 0x16, 0x99, 0x2e, 0xce, 0x8d, 0x23, 0x43, 0x06, 0x00, 0xe9,
|
||||
0xb1, 0x94, 0x20, 0x42, 0x2a, 0xf5, 0xf1, 0x79, 0x4f, 0x2c, 0xd9, 0xe1,
|
||||
0xc7, 0x2e, 0xd4, 0x8a, 0x31, 0x5a, 0x80, 0x27, 0x57, 0xa6, 0xfc, 0xb2,
|
||||
0x47, 0x4c, 0x5b, 0x05, 0x22, 0x82, 0x77, 0x76, 0xbe, 0xd4, 0x23, 0x8c,
|
||||
0xdf, 0xfc, 0xe9, 0xbc, 0x01, 0xc0, 0x16, 0x60, 0xff, 0x00, 0x45, 0x36,
|
||||
0x2f, 0x29, 0x5f, 0x5f, 0xa8, 0x83, 0x8a, 0x55, 0xc2, 0x39, 0x72, 0x35,
|
||||
0xc2, 0xb4, 0x81, 0xf7, 0xd7, 0x40, 0x15, 0x0c, 0xf1, 0xef, 0x58, 0xe7,
|
||||
0xc4, 0xc1, 0x23, 0x47, 0x92, 0x29, 0x44};
|
||||
|
||||
const unsigned char kTestIntermediateCertificate[] = {
|
||||
0x0a, 0xaf, 0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18,
|
||||
0xb2, 0x92, 0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
|
||||
0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a,
|
||||
0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd,
|
||||
0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67,
|
||||
0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d,
|
||||
0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f,
|
||||
0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f,
|
||||
0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf,
|
||||
0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73,
|
||||
0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5,
|
||||
0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13,
|
||||
0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad,
|
||||
0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57,
|
||||
0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24,
|
||||
0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06,
|
||||
0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61,
|
||||
0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8,
|
||||
0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb,
|
||||
0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b,
|
||||
0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b,
|
||||
0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d,
|
||||
0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb,
|
||||
0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01,
|
||||
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40,
|
||||
0xa8, 0xd0, 0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf,
|
||||
0xc9, 0x26, 0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4,
|
||||
0x33, 0x8e, 0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b,
|
||||
0xc3, 0x4f, 0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51,
|
||||
0xce, 0x45, 0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4,
|
||||
0x5a, 0x02, 0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9,
|
||||
0xee, 0xc9, 0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb,
|
||||
0x1f, 0x7a, 0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9,
|
||||
0x26, 0xbe, 0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25,
|
||||
0xdb, 0x85, 0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba,
|
||||
0xb9, 0x3d, 0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80,
|
||||
0x12, 0x72, 0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98,
|
||||
0xab, 0x73, 0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1,
|
||||
0x75, 0x79, 0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98,
|
||||
0xe8, 0x40, 0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f,
|
||||
0x6f, 0x23, 0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30,
|
||||
0xaa, 0xcf, 0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b,
|
||||
0x7f, 0x91, 0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a,
|
||||
0x98, 0xad, 0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e,
|
||||
0x6e, 0x9d, 0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04,
|
||||
0x4f, 0x32, 0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52,
|
||||
0xfc, 0xcc, 0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa,
|
||||
0xb3, 0x77, 0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d,
|
||||
0x3c, 0x74, 0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f,
|
||||
0x13, 0x97, 0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94,
|
||||
0x3b, 0x31, 0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b,
|
||||
0x65, 0xe9, 0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d,
|
||||
0x0a, 0xfd, 0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4,
|
||||
0x83, 0xcf, 0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16,
|
||||
0x7b, 0x66, 0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d,
|
||||
0x94, 0x5d, 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b,
|
||||
0x55, 0xf8, 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
|
||||
|
||||
const unsigned char kTestUserDrmCertificate[] = {
|
||||
0x0a, 0xc1, 0x02, 0x08, 0x02, 0x12, 0x10, 0x46, 0x45, 0x44, 0x43, 0x42,
|
||||
0x41, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x18,
|
||||
0x91, 0xab, 0x4b, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
|
||||
0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, 0x43, 0x51,
|
||||
0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d,
|
||||
0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, 0xa7, 0x5c,
|
||||
0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32,
|
||||
0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, 0x35, 0xb7,
|
||||
0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3,
|
||||
0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, 0xc2, 0xd7,
|
||||
0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc,
|
||||
0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, 0x56, 0x93,
|
||||
0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99,
|
||||
0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, 0x53, 0xfc,
|
||||
0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6,
|
||||
0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, 0xe2, 0x6a,
|
||||
0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44,
|
||||
0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, 0x20, 0xec,
|
||||
0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea,
|
||||
0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, 0x59, 0x6e,
|
||||
0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94,
|
||||
0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, 0xbc, 0xe8,
|
||||
0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16,
|
||||
0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, 0xa7, 0xdd,
|
||||
0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01,
|
||||
0x28, 0xd2, 0x85, 0xd8, 0xcc, 0x04, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65,
|
||||
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x12, 0x80, 0x02, 0x62, 0xd5, 0x8b, 0xb6, 0x52, 0x94, 0xcb, 0x25, 0xba,
|
||||
0x68, 0x44, 0xdc, 0x6f, 0x03, 0xf6, 0x24, 0xc5, 0xba, 0x46, 0xd1, 0xa1,
|
||||
0x83, 0xe7, 0xaf, 0x94, 0x96, 0x8b, 0x57, 0x89, 0xd6, 0xa0, 0x99, 0x6f,
|
||||
0xed, 0xac, 0xfe, 0x6c, 0x9d, 0x80, 0x1c, 0xae, 0x34, 0xda, 0x49, 0x4f,
|
||||
0x10, 0x22, 0x3c, 0xdd, 0x77, 0xa0, 0x9a, 0x79, 0x73, 0x68, 0x66, 0xa4,
|
||||
0x6d, 0x1e, 0x82, 0xbf, 0xce, 0x06, 0x1a, 0x83, 0xcd, 0xa3, 0xed, 0x91,
|
||||
0xbe, 0xb1, 0xfe, 0xf3, 0xde, 0x63, 0x96, 0xd5, 0x24, 0x44, 0x46, 0x94,
|
||||
0x7f, 0xc2, 0x14, 0x19, 0x42, 0x08, 0x64, 0xef, 0x93, 0x81, 0x7a, 0x54,
|
||||
0x8b, 0x6e, 0xd9, 0xf5, 0x14, 0x88, 0x6c, 0x39, 0x6f, 0x0f, 0x70, 0x91,
|
||||
0x97, 0xd4, 0x24, 0x73, 0x9d, 0x12, 0x7a, 0xc8, 0x83, 0xd7, 0x2b, 0xc7,
|
||||
0xb7, 0xe1, 0x20, 0x6c, 0x28, 0x11, 0x6f, 0x56, 0x82, 0xf6, 0x1c, 0x4f,
|
||||
0x2d, 0x51, 0x0f, 0xd6, 0xd4, 0x14, 0xea, 0xac, 0x28, 0x66, 0xeb, 0x37,
|
||||
0xca, 0x00, 0x49, 0xff, 0xed, 0x8e, 0x8c, 0x3e, 0x4b, 0x9b, 0x12, 0x0e,
|
||||
0xbf, 0xcd, 0xb7, 0xe6, 0xed, 0xd6, 0x1f, 0x88, 0xe8, 0x99, 0x68, 0x1a,
|
||||
0xf8, 0xbb, 0xa2, 0x33, 0xfa, 0xb6, 0x21, 0xdf, 0xba, 0x24, 0x5c, 0x19,
|
||||
0xa2, 0xe7, 0x6f, 0x61, 0x90, 0x78, 0x21, 0xca, 0x2f, 0x84, 0xab, 0x9f,
|
||||
0xff, 0x37, 0x14, 0x33, 0x83, 0x43, 0x98, 0xeb, 0xa9, 0x88, 0xde, 0xad,
|
||||
0x3a, 0xd9, 0xe2, 0x5c, 0x26, 0xd3, 0x95, 0x72, 0xba, 0x8c, 0x77, 0xdf,
|
||||
0x90, 0x67, 0x4e, 0xbc, 0xda, 0x83, 0x09, 0x22, 0x70, 0x51, 0x84, 0x70,
|
||||
0x31, 0x25, 0x8b, 0xae, 0x5e, 0x19, 0xba, 0x97, 0xd7, 0x1f, 0x6a, 0xd7,
|
||||
0x95, 0xcf, 0xde, 0x8f, 0x93, 0x69, 0x88, 0x11, 0xbe, 0x8c, 0x6a, 0xfb,
|
||||
0x3c, 0x13, 0x87, 0x0e, 0x6c, 0xa5, 0xa0, 0x1a, 0xb5, 0x05, 0x0a, 0xaf,
|
||||
0x02, 0x08, 0x01, 0x12, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
|
||||
0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x18, 0xb2, 0x92,
|
||||
0x04, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
|
||||
0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40,
|
||||
0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7,
|
||||
0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56,
|
||||
0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e,
|
||||
0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34,
|
||||
0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31,
|
||||
0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e,
|
||||
0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39,
|
||||
0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2,
|
||||
0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54,
|
||||
0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3,
|
||||
0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71,
|
||||
0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96,
|
||||
0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a,
|
||||
0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c,
|
||||
0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f,
|
||||
0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca,
|
||||
0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77,
|
||||
0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27,
|
||||
0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c,
|
||||
0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c,
|
||||
0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x28, 0xd2,
|
||||
0x85, 0xd8, 0xcc, 0x04, 0x12, 0x80, 0x03, 0x7b, 0xd3, 0x40, 0xa8, 0xd0,
|
||||
0x31, 0x1e, 0x95, 0x35, 0xdd, 0xb3, 0x20, 0xcf, 0xc2, 0xcf, 0xc9, 0x26,
|
||||
0x49, 0x53, 0xc8, 0x58, 0xd5, 0x12, 0xf0, 0x71, 0xf4, 0xd4, 0x33, 0x8e,
|
||||
0xd7, 0x6f, 0x79, 0xbe, 0x17, 0xeb, 0x36, 0x71, 0xf2, 0x3b, 0xc3, 0x4f,
|
||||
0x3a, 0xeb, 0xc7, 0xfb, 0xf6, 0x40, 0xf8, 0xe6, 0xe4, 0x51, 0xce, 0x45,
|
||||
0x5c, 0xf0, 0x66, 0xd1, 0x22, 0x55, 0x72, 0xcd, 0x50, 0xb4, 0x5a, 0x02,
|
||||
0x2f, 0xb7, 0x11, 0x24, 0x61, 0x12, 0x9f, 0x80, 0x5f, 0xc9, 0xee, 0xc9,
|
||||
0xd4, 0x7b, 0x62, 0x76, 0x34, 0xdd, 0x45, 0xae, 0x42, 0xbb, 0x1f, 0x7a,
|
||||
0x18, 0x85, 0xc7, 0xcf, 0xc9, 0x86, 0x47, 0xfd, 0x23, 0xd9, 0x26, 0xbe,
|
||||
0x47, 0x3e, 0x80, 0x45, 0x41, 0x39, 0x92, 0xe4, 0x0e, 0x25, 0xdb, 0x85,
|
||||
0x35, 0x77, 0x34, 0x3a, 0x67, 0xbf, 0xea, 0xfa, 0x84, 0xba, 0xb9, 0x3d,
|
||||
0x03, 0x89, 0xa8, 0x13, 0x9f, 0x35, 0xa1, 0x12, 0x0e, 0x80, 0x12, 0x72,
|
||||
0x24, 0x4e, 0xc2, 0x6d, 0x2b, 0x77, 0x19, 0xb8, 0xa1, 0x98, 0xab, 0x73,
|
||||
0x43, 0x79, 0xf6, 0x7b, 0x9e, 0xc9, 0x4f, 0xb8, 0xb5, 0xf1, 0x75, 0x79,
|
||||
0x7a, 0x48, 0x01, 0x0e, 0xb6, 0xb9, 0x3e, 0x46, 0xf0, 0x98, 0xe8, 0x40,
|
||||
0x6a, 0x60, 0xeb, 0x8f, 0x51, 0x78, 0x31, 0x5c, 0xe1, 0x0f, 0x6f, 0x23,
|
||||
0x36, 0xf3, 0xd4, 0x7a, 0x68, 0x74, 0x32, 0x3c, 0xf6, 0x30, 0xaa, 0xcf,
|
||||
0x4f, 0xb7, 0xdf, 0xc4, 0xe0, 0x1b, 0x8c, 0xa8, 0x2b, 0x1b, 0x7f, 0x91,
|
||||
0xf9, 0x98, 0xb9, 0xac, 0xf4, 0x50, 0x3e, 0xc1, 0x1c, 0x7a, 0x98, 0xad,
|
||||
0x88, 0x68, 0xe6, 0xe8, 0x4f, 0x8b, 0x5f, 0xf7, 0xf6, 0x0e, 0x6e, 0x9d,
|
||||
0xe1, 0x55, 0xe2, 0xf7, 0x5b, 0x2c, 0x73, 0x5e, 0x77, 0x04, 0x4f, 0x32,
|
||||
0x5d, 0x13, 0x51, 0x8f, 0x1a, 0x53, 0xad, 0xff, 0x1e, 0x52, 0xfc, 0xcc,
|
||||
0xa5, 0x80, 0x92, 0x9b, 0x89, 0x64, 0x18, 0x49, 0xd9, 0xaa, 0xb3, 0x77,
|
||||
0xf3, 0x60, 0x4c, 0x6e, 0x9f, 0x0d, 0xf0, 0xbc, 0x8e, 0x2d, 0x3c, 0x74,
|
||||
0xff, 0x3b, 0xc0, 0x3f, 0xc4, 0xa8, 0xf2, 0x4c, 0x40, 0x2f, 0x13, 0x97,
|
||||
0x01, 0xb8, 0x29, 0x1f, 0x8f, 0x04, 0xfb, 0xd7, 0xaa, 0x94, 0x3b, 0x31,
|
||||
0x54, 0xcc, 0x58, 0x19, 0x60, 0xb1, 0xe7, 0x16, 0x24, 0x0b, 0x65, 0xe9,
|
||||
0x19, 0x51, 0xb5, 0x14, 0x95, 0x66, 0x3f, 0x0b, 0x05, 0x3d, 0x0a, 0xfd,
|
||||
0x14, 0xb7, 0x1a, 0x90, 0xe8, 0xe6, 0xbc, 0xdf, 0x9f, 0xd4, 0x83, 0xcf,
|
||||
0xe7, 0xd4, 0x1c, 0x17, 0xe8, 0x13, 0xdb, 0x99, 0xb7, 0x16, 0x7b, 0x66,
|
||||
0x35, 0xf6, 0x56, 0x92, 0x9c, 0x35, 0xa0, 0xe0, 0x90, 0x4d, 0x94, 0x5d,
|
||||
0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, 0x55, 0xf8,
|
||||
0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a};
|
||||
|
||||
const unsigned char kTestDrmServiceCertificate[] = {
|
||||
0x0a, 0xbc, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32,
|
||||
0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18,
|
||||
0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02,
|
||||
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54,
|
||||
0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58,
|
||||
0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57,
|
||||
0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa,
|
||||
0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e,
|
||||
0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda,
|
||||
0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9,
|
||||
0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2,
|
||||
0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e,
|
||||
0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a,
|
||||
0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67,
|
||||
0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56,
|
||||
0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b,
|
||||
0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83,
|
||||
0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1,
|
||||
0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f,
|
||||
0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40,
|
||||
0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46,
|
||||
0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86,
|
||||
0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b,
|
||||
0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b,
|
||||
0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00,
|
||||
0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0x6e, 0xc3,
|
||||
0x5a, 0x17, 0xa8, 0xf9, 0xef, 0xee, 0x67, 0x4d, 0x0a, 0xef, 0x57, 0x5e,
|
||||
0xbc, 0x59, 0x3d, 0x22, 0x84, 0xa0, 0x0a, 0xf5, 0x84, 0x26, 0xb7, 0x8b,
|
||||
0xab, 0x91, 0x3e, 0x4b, 0xb9, 0x91, 0x3c, 0x50, 0xc9, 0x08, 0x2f, 0x97,
|
||||
0x0a, 0x91, 0xb5, 0x48, 0xe4, 0xba, 0xfd, 0x7b, 0xbd, 0xf0, 0xba, 0x08,
|
||||
0xb3, 0x29, 0xb4, 0x23, 0x74, 0xaf, 0x3f, 0xe9, 0x77, 0x78, 0x3f, 0xdc,
|
||||
0x3d, 0x8a, 0x37, 0xec, 0x1c, 0x3a, 0xff, 0x60, 0x8e, 0x10, 0x72, 0xaa,
|
||||
0x97, 0x98, 0x56, 0xa0, 0x35, 0xa9, 0xbf, 0x43, 0x21, 0x6a, 0x15, 0x88,
|
||||
0xba, 0xc0, 0x68, 0x01, 0x7b, 0xd7, 0x88, 0x2f, 0x1a, 0xc5, 0x1f, 0x54,
|
||||
0xf0, 0xea, 0x36, 0xb7, 0xed, 0x49, 0x78, 0x09, 0xb1, 0x07, 0x46, 0xfe,
|
||||
0xf4, 0xfa, 0x16, 0x0c, 0x46, 0x91, 0xe2, 0xa9, 0xe0, 0x8e, 0x97, 0xe5,
|
||||
0xea, 0x2f, 0xd9, 0x94, 0x1e, 0xe7, 0xba, 0x28, 0x98, 0x92, 0xae, 0xb8,
|
||||
0xb6, 0x6e, 0xf6, 0xd2, 0x50, 0xd3, 0x5b, 0x25, 0x12, 0x68, 0x5e, 0x07,
|
||||
0x82, 0x64, 0x27, 0xfe, 0x1a, 0xcd, 0x38, 0xa8, 0x00, 0x53, 0x8c, 0x69,
|
||||
0x51, 0x75, 0x71, 0xc2, 0x6a, 0x5f, 0x05, 0x13, 0x77, 0x2b, 0xc8, 0x6c,
|
||||
0xab, 0xd2, 0x64, 0x27, 0xbd, 0x21, 0xfc, 0x33, 0x0a, 0x3a, 0x53, 0xa6,
|
||||
0x28, 0x1c, 0x2a, 0xad, 0x23, 0x0a, 0x95, 0xe4, 0x38, 0x6b, 0x9b, 0x3e,
|
||||
0x77, 0x7d, 0x96, 0x20, 0x42, 0xf5, 0x18, 0xbe, 0xb0, 0x78, 0xe4, 0xf0,
|
||||
0x95, 0x6c, 0xd5, 0x30, 0xd6, 0xfc, 0x04, 0xe2, 0xf7, 0xff, 0x06, 0x6b,
|
||||
0xaf, 0xf1, 0x9c, 0x10, 0xa6, 0xdb, 0xed, 0x4a, 0x18, 0x68, 0x87, 0xda,
|
||||
0x43, 0x2c, 0x60, 0xc6, 0x0a, 0x72, 0x1e, 0x9f, 0x4b, 0x05, 0x80, 0x15,
|
||||
0x17, 0x84, 0xf1, 0xee, 0xcc, 0x80, 0x25, 0x33, 0x87, 0x74, 0x02, 0x8c,
|
||||
0xa1, 0xbb, 0xd9, 0x29, 0x33, 0x97, 0xbd, 0x5b, 0x1c, 0xed, 0xcc, 0x47,
|
||||
0xda, 0x73, 0xae, 0xb1, 0x75, 0xac, 0xf7, 0x39, 0xbe, 0x67, 0xc3, 0xaf,
|
||||
0x60, 0x07, 0xf5, 0xba, 0x81, 0xf4, 0x42, 0xad, 0x28, 0x8d, 0xe6, 0x63,
|
||||
0xea, 0x8a, 0x0e, 0x71, 0x53, 0x6e, 0x62, 0x8a, 0x23, 0x4f, 0xad, 0x2a,
|
||||
0x9a, 0xf6, 0xeb, 0xa8, 0x82, 0x83, 0xbb, 0x5f, 0xc9, 0x86, 0xd8, 0x76,
|
||||
0xb9, 0xf3, 0xe7, 0x32, 0xdd, 0xe0, 0x44, 0x6a, 0xab, 0x78, 0xa0, 0x8c,
|
||||
0xa4, 0x99, 0x6f, 0x71, 0x42, 0x8b, 0x31, 0x32, 0xbb, 0x80, 0x36, 0x61,
|
||||
0x1c, 0xe5, 0x6d, 0x87, 0xf2, 0x68, 0xca, 0xcd, 0xe0, 0x5f, 0xa2, 0x68,
|
||||
0x5b, 0xfc, 0x73, 0xc9, 0x26, 0x2b, 0x13, 0x05, 0x1c, 0xde, 0x19, 0xdf,
|
||||
0x34, 0xba, 0xf5, 0xec, 0xaf, 0x26, 0xfb, 0x64, 0xc4, 0x38, 0x7e, 0xdb,
|
||||
0x51, 0x28, 0x49, 0xa7, 0x12, 0x88, 0xa5, 0x6d, 0xa2, 0xfa};
|
||||
|
||||
TestDrmCertificates::TestDrmCertificates()
|
||||
: test_root_certificate_(
|
||||
kTestRootCertificate,
|
||||
kTestRootCertificate + sizeof(kTestRootCertificate)),
|
||||
test_intermediate_certificate_(
|
||||
kTestIntermediateCertificate,
|
||||
kTestIntermediateCertificate + sizeof(kTestIntermediateCertificate)),
|
||||
test_user_device_certificate_(
|
||||
kTestUserDrmCertificate,
|
||||
kTestUserDrmCertificate + sizeof(kTestUserDrmCertificate)),
|
||||
test_service_certificate_(
|
||||
kTestDrmServiceCertificate,
|
||||
kTestDrmServiceCertificate + sizeof(kTestDrmServiceCertificate)) {}
|
||||
|
||||
} // namespace widevine
|
||||
54
common/test_drm_certificates.h
Normal file
54
common/test_drm_certificates.h
Normal file
@@ -0,0 +1,54 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Class contains certificates that can be used for testing. Provides methods
|
||||
// to retrieve a test root certificate, a test intermediate certificate and a
|
||||
// test user device certificate.
|
||||
#ifndef COMMON_TEST_DRM_CERTIFICATES_H_
|
||||
#define COMMON_TEST_DRM_CERTIFICATES_H_
|
||||
|
||||
#include <string>
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class TestDrmCertificates {
|
||||
public:
|
||||
TestDrmCertificates();
|
||||
virtual ~TestDrmCertificates() {}
|
||||
|
||||
// returns a test root certificate
|
||||
const std::string& test_root_certificate() const { return test_root_certificate_; }
|
||||
|
||||
// returns a test intermediate certificate
|
||||
const std::string& test_intermediate_certificate() const {
|
||||
return test_intermediate_certificate_;
|
||||
}
|
||||
|
||||
// returns an user device certificate
|
||||
const std::string& test_user_device_certificate() const {
|
||||
return test_user_device_certificate_;
|
||||
}
|
||||
|
||||
// returns a service certificate
|
||||
const std::string& test_service_certificate() const {
|
||||
return test_service_certificate_;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string test_root_certificate_;
|
||||
const std::string test_intermediate_certificate_;
|
||||
const std::string test_user_device_certificate_;
|
||||
const std::string test_service_certificate_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestDrmCertificates);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
#endif // COMMON_TEST_DRM_CERTIFICATES_H_
|
||||
71
common/test_utils.cc
Normal file
71
common/test_utils.cc
Normal file
@@ -0,0 +1,71 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/test_utils.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/sha.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||
const std::string& message,
|
||||
std::string* signature) {
|
||||
CHECK(signature);
|
||||
if (pem_private_key.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT, "Empty PEM private key");
|
||||
}
|
||||
if (message.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT, "Empty message");
|
||||
}
|
||||
BIO* bio(NULL);
|
||||
bio = BIO_new_mem_buf(const_cast<char*>(pem_private_key.data()),
|
||||
pem_private_key.size());
|
||||
if (bio == NULL) {
|
||||
return Status(error::INTERNAL, "BIO allocation failed");
|
||||
}
|
||||
Status status;
|
||||
RSA* key(NULL);
|
||||
std::unique_ptr<char[]> sig_buffer;
|
||||
unsigned int sig_size;
|
||||
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||
key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
|
||||
if (key == NULL) {
|
||||
status = Status(Status::canonical_space(), error::INVALID_ARGUMENT,
|
||||
"PEM RSA private key load failed");
|
||||
goto cleanup;
|
||||
}
|
||||
SHA256(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
|
||||
digest);
|
||||
sig_size = RSA_size(key);
|
||||
sig_buffer.reset(new char[sig_size]);
|
||||
if (RSA_sign(NID_sha256, digest, sizeof(digest),
|
||||
reinterpret_cast<unsigned char*>(sig_buffer.get()), &sig_size,
|
||||
key) != 1) {
|
||||
status = Status(Status::canonical_space(), error::INTERNAL,
|
||||
"RSA private encrypt failed");
|
||||
goto cleanup;
|
||||
}
|
||||
signature->assign(sig_buffer.get(), sig_size);
|
||||
|
||||
cleanup:
|
||||
if (key != NULL) {
|
||||
RSA_free(key);
|
||||
}
|
||||
if (bio != NULL) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
32
common/test_utils.h
Normal file
32
common/test_utils.h
Normal file
@@ -0,0 +1,32 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2013 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Auxiliary functions for license server SDK testing.
|
||||
|
||||
#ifndef COMMON_TEST_UTILS_H_
|
||||
#define COMMON_TEST_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Generate RSA signature using the specified RSA private key, SHA256 digest,
|
||||
// and PKCS#1 1.5 padding. |pem_private_key| is a PEM-encoded private RSA key,
|
||||
// |message| is the message to be signed, and |signature| is a pointer to a
|
||||
// std::string where the signature will be stored. The caller returns ownership of
|
||||
// all paramters.
|
||||
Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||
const std::string& message,
|
||||
std::string* signature);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_TEST_UTILS_H_
|
||||
43
common/verified_media_pipeline.cc
Normal file
43
common/verified_media_pipeline.cc
Normal file
@@ -0,0 +1,43 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Helper methods for verifying VMP (Verified Media Pipeline) data.
|
||||
|
||||
#include "common/verified_media_pipeline.h"
|
||||
|
||||
#include "common/vmp_checker.h"
|
||||
|
||||
namespace widevine {
|
||||
Status VerifyVmpData(const std::string& vmp_data,
|
||||
PlatformVerificationStatus* platform_verification_status) {
|
||||
*platform_verification_status = PLATFORM_UNVERIFIED;
|
||||
VmpChecker::Result vmp_result;
|
||||
Status status = VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result);
|
||||
if (status.ok()) {
|
||||
switch (vmp_result) {
|
||||
case VmpChecker::kUnverified:
|
||||
*platform_verification_status = PLATFORM_UNVERIFIED;
|
||||
break;
|
||||
case VmpChecker::kVerified:
|
||||
*platform_verification_status = PLATFORM_SOFTWARE_VERIFIED;
|
||||
break;
|
||||
case VmpChecker::kSecureStorageVerified:
|
||||
*platform_verification_status =
|
||||
PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED;
|
||||
break;
|
||||
case VmpChecker::kTampered:
|
||||
*platform_verification_status = PLATFORM_TAMPERED;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
*platform_verification_status = PLATFORM_TAMPERED;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
} // namespace widevine
|
||||
27
common/verified_media_pipeline.h
Normal file
27
common/verified_media_pipeline.h
Normal file
@@ -0,0 +1,27 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Helper methods for verifying VMP (Verified Media Pipeline) data.
|
||||
|
||||
#ifndef COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
||||
#define COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
||||
|
||||
#include <string>
|
||||
#include "common/status.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Retrieve the PlatformVerificationStatus for |vmp_data|. The
|
||||
// PlatformVerificationStatus is defined at
|
||||
Status VerifyVmpData(const std::string& vmp_data,
|
||||
PlatformVerificationStatus* platform_verification_status);
|
||||
|
||||
} // namespace widevine
|
||||
#endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
||||
358
common/vmp_checker.cc
Normal file
358
common/vmp_checker.cc
Normal file
@@ -0,0 +1,358 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Singleton object which validates VMP (Verified Media Pipeline) data for
|
||||
// purposes of platform software verification. Thread safe.
|
||||
|
||||
#include "common/vmp_checker.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/x509_cert.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/verified_media_pipeline.pb.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kBlessedBinaryFlag = 0x00000001;
|
||||
|
||||
const uint8_t kDevVmpCodeSigningDrmRootCertificate[] = {
|
||||
0x30, 0x82, 0x04, 0xb8, 0x30, 0x82, 0x03, 0x20, 0x02, 0x09, 0x00, 0xc5,
|
||||
0xf8, 0x2f, 0x03, 0x8f, 0xac, 0xf1, 0x58, 0x30, 0x0d, 0x06, 0x09, 0x2a,
|
||||
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
|
||||
0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
|
||||
0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
|
||||
0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31,
|
||||
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69,
|
||||
0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
|
||||
0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31,
|
||||
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69,
|
||||
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03,
|
||||
0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e,
|
||||
0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69,
|
||||
0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
||||
0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73, 0x6b,
|
||||
0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x30, 0x32, 0x38, 0x30,
|
||||
0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x31, 0x36,
|
||||
0x31, 0x30, 0x30, 0x34, 0x30, 0x31, 0x30, 0x37, 0x34, 0x30, 0x5a, 0x30,
|
||||
0x81, 0x9c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
|
||||
0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
|
||||
0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e,
|
||||
0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b,
|
||||
0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06,
|
||||
0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||
0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57,
|
||||
0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x1e, 0x30, 0x1c, 0x06,
|
||||
0x03, 0x55, 0x04, 0x03, 0x0c, 0x15, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69,
|
||||
0x6e, 0x65, 0x2d, 0x64, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73,
|
||||
0x69, 0x67, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48,
|
||||
0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74, 0x69, 0x6e, 0x73,
|
||||
0x6b, 0x69, 0x70, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x30, 0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
|
||||
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
|
||||
0x8f, 0x00, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xd2,
|
||||
0x6f, 0x60, 0x7f, 0xff, 0x7c, 0xbd, 0xa4, 0xe5, 0x8c, 0xa6, 0xcf, 0xde,
|
||||
0x22, 0x6d, 0x3a, 0x5e, 0x83, 0xa9, 0x0e, 0x9b, 0xd4, 0x93, 0xb8, 0xb0,
|
||||
0xe0, 0x5d, 0x03, 0x3d, 0xc1, 0x00, 0xb8, 0x1a, 0xcc, 0x84, 0x31, 0xfb,
|
||||
0x9e, 0x97, 0x79, 0x17, 0x04, 0x48, 0xe7, 0x13, 0x98, 0x0a, 0x47, 0x41,
|
||||
0xac, 0x6c, 0x52, 0xb0, 0xca, 0x9e, 0xfb, 0xfd, 0x78, 0x65, 0xd0, 0xd6,
|
||||
0x12, 0x07, 0x7e, 0x24, 0x65, 0x46, 0x6c, 0xb9, 0x23, 0xbb, 0xdc, 0x02,
|
||||
0x03, 0xc7, 0xb0, 0x02, 0xc1, 0xd3, 0x10, 0x59, 0xe7, 0x0b, 0x45, 0x13,
|
||||
0x73, 0x5f, 0xae, 0x58, 0xcd, 0xbf, 0x42, 0x8a, 0xac, 0xf5, 0x6a, 0x1e,
|
||||
0x75, 0x26, 0xb1, 0x69, 0x07, 0xad, 0xf5, 0xfd, 0x4c, 0xaa, 0x66, 0x55,
|
||||
0x74, 0x56, 0x9a, 0x9e, 0x40, 0x78, 0x77, 0x9a, 0x39, 0x7a, 0x37, 0xe4,
|
||||
0x25, 0x6b, 0x07, 0x09, 0xbe, 0xe2, 0x0d, 0x23, 0x83, 0xfc, 0x94, 0x9f,
|
||||
0x26, 0x98, 0x0e, 0x49, 0x81, 0x7b, 0xf4, 0xe6, 0xd4, 0xda, 0x7a, 0xc9,
|
||||
0xa4, 0x14, 0x5a, 0xa9, 0xaf, 0x0c, 0xd9, 0xf1, 0xbc, 0xd8, 0x6c, 0xd2,
|
||||
0xd4, 0x1b, 0x82, 0x10, 0x3d, 0x87, 0xf1, 0x81, 0xe6, 0x1a, 0xb7, 0xfa,
|
||||
0xfa, 0x1f, 0x9c, 0xde, 0xa1, 0x3f, 0x11, 0xb1, 0xd8, 0x26, 0xd1, 0x86,
|
||||
0x21, 0xdc, 0x03, 0xcb, 0xd6, 0x40, 0xfb, 0x5f, 0xb0, 0x84, 0x7f, 0x69,
|
||||
0x9e, 0xa3, 0xbc, 0xfb, 0x03, 0xb6, 0xc1, 0xb9, 0x23, 0xb1, 0x20, 0x6f,
|
||||
0x71, 0xf5, 0x7a, 0x3b, 0x84, 0x30, 0xa8, 0x59, 0xc0, 0x8f, 0x75, 0xfd,
|
||||
0xcb, 0xe1, 0xc3, 0x5f, 0xe7, 0x8a, 0xb0, 0xe9, 0xf8, 0xef, 0x04, 0x4b,
|
||||
0x4a, 0xf6, 0xc3, 0x7d, 0x08, 0xfe, 0x08, 0x52, 0x2e, 0xbc, 0x1f, 0x65,
|
||||
0xf6, 0x51, 0xb7, 0xd8, 0x24, 0x21, 0x49, 0x1d, 0x7f, 0x16, 0x28, 0x14,
|
||||
0xd9, 0xc2, 0x19, 0xdb, 0xa2, 0xc6, 0xf0, 0x3a, 0x2d, 0x98, 0x70, 0x72,
|
||||
0x45, 0xf7, 0x80, 0x37, 0x56, 0x0b, 0x0c, 0x6f, 0x80, 0xf1, 0x8c, 0xe2,
|
||||
0xf3, 0x4d, 0x16, 0xc5, 0x74, 0x90, 0x34, 0x1e, 0x57, 0x3c, 0xde, 0xf1,
|
||||
0xc4, 0x8c, 0x17, 0x09, 0xd3, 0xc5, 0x92, 0x9d, 0xcf, 0xdc, 0x7b, 0x4f,
|
||||
0xae, 0x20, 0x10, 0xd7, 0x04, 0x56, 0x9d, 0x9a, 0xa9, 0xd8, 0x06, 0x4a,
|
||||
0xd6, 0x68, 0xd4, 0x83, 0xcb, 0x7d, 0xe2, 0x62, 0xd1, 0x99, 0xb8, 0x9d,
|
||||
0x81, 0xc7, 0xfc, 0x94, 0x69, 0x0d, 0x2a, 0x1c, 0x99, 0xcf, 0x40, 0xc3,
|
||||
0xfd, 0xe9, 0x64, 0x5b, 0xc3, 0x1d, 0xda, 0x1c, 0x89, 0xab, 0x34, 0x1b,
|
||||
0x53, 0x6e, 0xad, 0xf0, 0x6e, 0x97, 0x87, 0xe8, 0xfb, 0x0c, 0x96, 0x93,
|
||||
0x1b, 0x52, 0x82, 0x6a, 0xba, 0x0f, 0xe3, 0x5d, 0xc4, 0x17, 0xdc, 0xe4,
|
||||
0x31, 0x78, 0x12, 0x26, 0x10, 0x74, 0x14, 0x7c, 0x45, 0xb2, 0xb9, 0x02,
|
||||
0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
||||
0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00,
|
||||
0x8f, 0x5b, 0x2a, 0x40, 0xce, 0xae, 0xa2, 0x79, 0xfc, 0xc5, 0x5e, 0x94,
|
||||
0xc8, 0x10, 0x4b, 0xf1, 0x21, 0x7f, 0x4b, 0xeb, 0x81, 0x3d, 0xb8, 0x26,
|
||||
0x83, 0x84, 0x76, 0x79, 0xda, 0x35, 0xfc, 0xdf, 0xfe, 0x10, 0x7a, 0xd5,
|
||||
0x17, 0xc0, 0xad, 0x0d, 0xf9, 0x3f, 0xa6, 0xa1, 0xcd, 0x6c, 0x9c, 0x3b,
|
||||
0x52, 0xbd, 0x04, 0xf9, 0xe2, 0x9e, 0x86, 0x83, 0x98, 0x60, 0x01, 0x99,
|
||||
0xb7, 0xbd, 0x02, 0x87, 0xd8, 0xea, 0x65, 0xaa, 0x60, 0x6f, 0x33, 0x50,
|
||||
0x25, 0x84, 0xb7, 0x42, 0x63, 0x39, 0xfd, 0x17, 0x67, 0x74, 0x88, 0x66,
|
||||
0xe2, 0x38, 0x59, 0xf6, 0x9b, 0x98, 0x95, 0xdd, 0x54, 0x2c, 0x69, 0x6a,
|
||||
0x0a, 0x51, 0x66, 0x4d, 0x65, 0xc0, 0x58, 0x3e, 0xcf, 0x15, 0x63, 0x7a,
|
||||
0x32, 0xc8, 0xfb, 0xe7, 0x11, 0x2e, 0x25, 0x17, 0x52, 0x4d, 0x8e, 0x6b,
|
||||
0x6d, 0x58, 0x4e, 0xf6, 0xd6, 0xb0, 0xfa, 0x0d, 0x7a, 0xb1, 0x44, 0x52,
|
||||
0x9a, 0x6c, 0x90, 0x38, 0x68, 0xa5, 0xa9, 0x9b, 0xc5, 0x45, 0x09, 0xfa,
|
||||
0xaa, 0x8c, 0xfe, 0x91, 0x55, 0x93, 0x35, 0x52, 0x45, 0xbb, 0xaa, 0x5b,
|
||||
0xf0, 0x63, 0x53, 0x13, 0xcf, 0x48, 0x7b, 0xaa, 0x20, 0xa0, 0x07, 0x43,
|
||||
0x1d, 0xd7, 0xb3, 0x4a, 0x1b, 0x8c, 0x51, 0xb5, 0xf6, 0xb9, 0x5b, 0x13,
|
||||
0x02, 0x74, 0x3e, 0x48, 0xde, 0xec, 0xeb, 0x65, 0xfe, 0xf2, 0x61, 0xe5,
|
||||
0x68, 0xd5, 0xea, 0xd9, 0x79, 0xa6, 0x71, 0xb1, 0x57, 0x0f, 0xbc, 0xd0,
|
||||
0x31, 0x4f, 0xff, 0xc5, 0x95, 0xe8, 0xee, 0x70, 0x18, 0xb9, 0xbc, 0x19,
|
||||
0xcd, 0x3a, 0x06, 0x75, 0xe4, 0x57, 0xc1, 0x2e, 0x32, 0x19, 0xdd, 0x2e,
|
||||
0x45, 0xc0, 0x19, 0xe6, 0x72, 0x81, 0x2c, 0xb6, 0xed, 0x1c, 0xd4, 0xef,
|
||||
0x42, 0x18, 0x44, 0x44, 0x75, 0xd6, 0x29, 0x81, 0xe1, 0xf7, 0x5b, 0x48,
|
||||
0xa3, 0xf8, 0x92, 0x54, 0xd0, 0x79, 0xa1, 0xe1, 0x8e, 0xa8, 0x98, 0x2d,
|
||||
0x57, 0x5d, 0xb5, 0x5a, 0x01, 0x1b, 0xb3, 0xcf, 0x5f, 0x64, 0x2e, 0x70,
|
||||
0xba, 0xa0, 0x41, 0xbb, 0xd4, 0x82, 0x28, 0x3c, 0xb1, 0x81, 0x76, 0xd6,
|
||||
0x85, 0x2e, 0xc6, 0x01, 0x7f, 0xae, 0xc3, 0x17, 0x2f, 0xed, 0xbe, 0xad,
|
||||
0xa2, 0x7c, 0x53, 0xc6, 0x77, 0x73, 0x1d, 0x19, 0x90, 0x2a, 0xf8, 0xd5,
|
||||
0x50, 0x65, 0xc1, 0x22, 0x3c, 0x24, 0x96, 0xeb, 0x7b, 0x53, 0x8f, 0xbf,
|
||||
0xd9, 0xf7, 0x80, 0xa8, 0x76, 0x72, 0xea, 0xb7, 0x7f, 0xf7, 0xa2, 0x52,
|
||||
0xc7, 0xa7, 0xd6, 0x88, 0xa1, 0x38, 0x40, 0x5d, 0xcd, 0xdb, 0xe3, 0x8e,
|
||||
0xc7, 0xf9, 0x39, 0xbe, 0xfa, 0x27, 0x41, 0x73, 0x3a, 0x1c, 0xb3, 0x03,
|
||||
0xf1, 0x36, 0xea, 0xe8, 0xb3, 0xe1, 0x6e, 0x59, 0xcc, 0xe2, 0x75, 0x2b,
|
||||
0xf9, 0x55, 0xb9, 0xc2, 0xdf, 0x0a, 0x8d, 0x8d, 0xd1, 0x62, 0x3b, 0x86};
|
||||
|
||||
const uint8_t kProdVmpCodeSigningDrmRootCertificate[] = {
|
||||
0x30, 0x82, 0x04, 0xd7, 0x30, 0x82, 0x03, 0x3f, 0xa0, 0x03, 0x02, 0x01,
|
||||
0x02, 0x02, 0x11, 0x00, 0xca, 0xa4, 0xbd, 0x6b, 0x93, 0x56, 0x4a, 0xf1,
|
||||
0x84, 0xef, 0x5e, 0xed, 0xe8, 0xf7, 0xe2, 0x0b, 0x30, 0x0d, 0x06, 0x09,
|
||||
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
|
||||
0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
|
||||
0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
|
||||
0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31,
|
||||
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69,
|
||||
0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
|
||||
0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31,
|
||||
0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69,
|
||||
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
|
||||
0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e,
|
||||
0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x72,
|
||||
0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x37,
|
||||
0x30, 0x31, 0x32, 0x34, 0x32, 0x33, 0x33, 0x38, 0x31, 0x34, 0x5a, 0x18,
|
||||
0x0f, 0x33, 0x30, 0x31, 0x36, 0x30, 0x35, 0x32, 0x37, 0x32, 0x33, 0x33,
|
||||
0x38, 0x31, 0x34, 0x5a, 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
|
||||
0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06,
|
||||
0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e,
|
||||
0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
|
||||
0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31,
|
||||
0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
|
||||
0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31,
|
||||
0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x77, 0x69,
|
||||
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2d, 0x63, 0x6f, 0x64, 0x65, 0x73,
|
||||
0x69, 0x67, 0x6e, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x63, 0x61, 0x30,
|
||||
0x82, 0x01, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
||||
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30,
|
||||
0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xe0, 0xb5, 0xc5, 0x6d,
|
||||
0x04, 0xc1, 0x97, 0xaf, 0xe7, 0x62, 0xfb, 0x84, 0xdc, 0xd1, 0xf4, 0xb1,
|
||||
0xb5, 0xa2, 0x7c, 0xca, 0x31, 0xf8, 0xce, 0xa7, 0x7a, 0x92, 0xc2, 0xbe,
|
||||
0x14, 0xdc, 0x85, 0x9f, 0x18, 0x9e, 0x78, 0xba, 0x65, 0x05, 0x56, 0x88,
|
||||
0x88, 0xdc, 0x1f, 0x4f, 0x24, 0x7f, 0xf5, 0x26, 0x6f, 0x6e, 0xcc, 0x04,
|
||||
0x2f, 0x38, 0xb8, 0xcd, 0x27, 0xd7, 0x9e, 0x07, 0xd3, 0xa9, 0xd4, 0x6b,
|
||||
0x84, 0xfe, 0xf8, 0xac, 0x9c, 0x53, 0xdf, 0x7a, 0x45, 0x5e, 0x77, 0xf8,
|
||||
0x4e, 0x88, 0x00, 0x5c, 0x6f, 0xb6, 0xa7, 0x0b, 0x4b, 0x63, 0x57, 0x92,
|
||||
0x7a, 0x7b, 0x3d, 0x20, 0x88, 0x3e, 0x7b, 0xb4, 0x28, 0x2b, 0x63, 0x81,
|
||||
0xd4, 0x0c, 0x4c, 0xb7, 0x54, 0x68, 0x68, 0x2c, 0x0d, 0xf5, 0xa6, 0x9e,
|
||||
0x16, 0x93, 0x76, 0xdd, 0xc0, 0xd5, 0x93, 0x65, 0x99, 0x90, 0x17, 0x2d,
|
||||
0x2b, 0xdc, 0x6f, 0xaf, 0x58, 0xfd, 0x78, 0xe9, 0xf5, 0xde, 0x2e, 0x36,
|
||||
0x95, 0xf0, 0xcf, 0x25, 0x41, 0x3d, 0x4f, 0x37, 0xd1, 0x70, 0x5b, 0xb5,
|
||||
0xc0, 0xc8, 0xf3, 0x63, 0xa3, 0xda, 0x9f, 0x94, 0xdb, 0xf8, 0x51, 0xf2,
|
||||
0xa9, 0xe5, 0x67, 0x0e, 0x29, 0xb3, 0x45, 0x12, 0xc5, 0x42, 0xf9, 0x3b,
|
||||
0x38, 0xf9, 0xa5, 0x7b, 0x41, 0x88, 0x6f, 0x32, 0x62, 0x03, 0x5f, 0xfd,
|
||||
0x35, 0x97, 0xc2, 0x83, 0x15, 0xb6, 0x56, 0x4f, 0xbb, 0x81, 0x39, 0x37,
|
||||
0xf2, 0x9c, 0x2a, 0x61, 0xa8, 0x63, 0x5f, 0xa0, 0x27, 0x30, 0x06, 0xd4,
|
||||
0xcb, 0x9d, 0xb7, 0xe9, 0xf2, 0xae, 0xd6, 0xc9, 0xcd, 0x72, 0xa3, 0xe6,
|
||||
0xf8, 0x54, 0x03, 0x6e, 0xe1, 0x95, 0x03, 0xdd, 0x7a, 0x85, 0xb3, 0x5c,
|
||||
0xa7, 0xca, 0x99, 0xec, 0xa6, 0xe8, 0x1a, 0xb2, 0x72, 0xe1, 0x91, 0x2d,
|
||||
0x97, 0xe3, 0x2a, 0x9c, 0x42, 0xaa, 0x45, 0xf2, 0x8e, 0x51, 0xc0, 0xd8,
|
||||
0x21, 0x83, 0x66, 0x07, 0xb5, 0x20, 0xb8, 0x28, 0xa5, 0xde, 0xfb, 0x4e,
|
||||
0x2e, 0xc7, 0x70, 0x9b, 0x3d, 0x52, 0x66, 0x24, 0xc5, 0xa2, 0x2e, 0x49,
|
||||
0x54, 0x5c, 0xfd, 0xc0, 0xde, 0xf6, 0x9d, 0xb4, 0x70, 0x31, 0x2b, 0xac,
|
||||
0x14, 0xfb, 0x19, 0x9e, 0x89, 0xd5, 0x07, 0x87, 0xa0, 0xd8, 0x15, 0x96,
|
||||
0xe9, 0xf7, 0x91, 0x36, 0x52, 0x83, 0x3c, 0x2c, 0xfa, 0xb5, 0xc4, 0xc6,
|
||||
0x1a, 0x34, 0xf0, 0x53, 0x94, 0x15, 0x82, 0xa2, 0x2f, 0x98, 0xbb, 0x49,
|
||||
0xca, 0xf7, 0xe0, 0xcb, 0x9e, 0x3c, 0xa6, 0x64, 0x59, 0x77, 0x63, 0xd1,
|
||||
0x05, 0x03, 0x99, 0x6c, 0x50, 0x08, 0xec, 0x64, 0x86, 0xf7, 0x97, 0xaf,
|
||||
0xf8, 0xcc, 0xdf, 0x91, 0xc7, 0x2c, 0x15, 0x0f, 0xa7, 0x0e, 0x02, 0x33,
|
||||
0x63, 0x84, 0xb7, 0x7e, 0xd3, 0x10, 0x89, 0x05, 0x2d, 0xb4, 0x68, 0x38,
|
||||
0xe0, 0x00, 0x49, 0xda, 0xaa, 0xb9, 0xf9, 0xbf, 0x02, 0x03, 0x01, 0x00,
|
||||
0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
|
||||
0x04, 0x16, 0x04, 0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f,
|
||||
0xd0, 0x9a, 0xd9, 0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86,
|
||||
0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
|
||||
0x14, 0xca, 0x3d, 0xd8, 0x8e, 0x0f, 0x74, 0x57, 0x7f, 0xd0, 0x9a, 0xd9,
|
||||
0xe1, 0x21, 0xbf, 0x42, 0xfb, 0x23, 0x55, 0x29, 0x86, 0x30, 0x0c, 0x06,
|
||||
0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
|
||||
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
|
||||
0x05, 0x00, 0x03, 0x82, 0x01, 0x81, 0x00, 0xc1, 0x81, 0x17, 0x08, 0x0a,
|
||||
0xcb, 0xae, 0x54, 0x92, 0xed, 0x0f, 0xc0, 0xf0, 0x83, 0xb8, 0xe9, 0x2a,
|
||||
0xd5, 0x65, 0x18, 0xb5, 0x92, 0xfc, 0x67, 0x9c, 0x39, 0x9e, 0x3a, 0x93,
|
||||
0x1f, 0x91, 0x7b, 0x35, 0x6d, 0x09, 0x6c, 0x3c, 0x02, 0x85, 0xe8, 0xc6,
|
||||
0x0c, 0x6b, 0x35, 0xeb, 0x8c, 0xe4, 0x44, 0x80, 0xc5, 0x4e, 0x65, 0xa6,
|
||||
0xbc, 0x25, 0x93, 0x5a, 0xed, 0x5a, 0xd1, 0x5a, 0xcf, 0xf1, 0xbe, 0xd5,
|
||||
0x46, 0x7c, 0x78, 0xfe, 0xb6, 0xa6, 0x9c, 0x85, 0xa9, 0xe2, 0x13, 0x70,
|
||||
0x99, 0x03, 0x5a, 0x3a, 0xa8, 0x7b, 0xdd, 0x50, 0x76, 0x83, 0xfe, 0x49,
|
||||
0xc0, 0x5e, 0xc7, 0xf1, 0x18, 0xc0, 0xe6, 0xb7, 0xc7, 0xe4, 0x9d, 0x54,
|
||||
0xaf, 0x25, 0xdf, 0xe4, 0x81, 0xc1, 0xe9, 0xaa, 0x7c, 0x05, 0x20, 0xfa,
|
||||
0x91, 0x47, 0xd1, 0x4a, 0xe2, 0x24, 0x6f, 0x72, 0x22, 0x69, 0xd0, 0x89,
|
||||
0x78, 0x2c, 0x9a, 0x16, 0x7d, 0x92, 0xdd, 0x64, 0x6c, 0xc8, 0xbf, 0x33,
|
||||
0xe3, 0x91, 0xb9, 0xb5, 0x16, 0xfe, 0xfd, 0xa2, 0xdb, 0x82, 0xf0, 0xec,
|
||||
0xac, 0xe7, 0xf9, 0x2e, 0xac, 0x50, 0xf0, 0xcf, 0xba, 0xe1, 0x55, 0xa9,
|
||||
0xb0, 0xd9, 0x66, 0x3f, 0xb6, 0xee, 0x57, 0xff, 0x8d, 0x43, 0xb7, 0xc3,
|
||||
0xb4, 0x44, 0xa9, 0xcc, 0x99, 0xa2, 0xbf, 0xac, 0x4f, 0xec, 0xed, 0xb5,
|
||||
0xd0, 0x3f, 0xa9, 0x50, 0x3b, 0xc1, 0x24, 0xf2, 0xd0, 0xef, 0x5c, 0xbf,
|
||||
0x5c, 0xc2, 0x41, 0x29, 0xbe, 0xb6, 0x76, 0x5a, 0x19, 0xde, 0x67, 0x1c,
|
||||
0x2a, 0x67, 0xae, 0x07, 0xe8, 0xfa, 0x49, 0xf1, 0x81, 0xbc, 0x22, 0xa1,
|
||||
0xe6, 0x5d, 0x27, 0x76, 0x0f, 0x4d, 0x41, 0x57, 0xcf, 0x0f, 0x12, 0x2f,
|
||||
0xdd, 0x20, 0x88, 0xcf, 0xc8, 0xa7, 0xc0, 0xe5, 0xec, 0xcd, 0xb9, 0xa7,
|
||||
0x1c, 0x29, 0x55, 0xed, 0x67, 0xf9, 0x38, 0x33, 0xea, 0x85, 0xe9, 0x69,
|
||||
0x5a, 0x7c, 0xfe, 0x37, 0x3b, 0xdd, 0x61, 0x5f, 0xaa, 0xc3, 0x18, 0xbc,
|
||||
0x58, 0x95, 0x39, 0x61, 0x79, 0xa1, 0x46, 0xcc, 0xc0, 0xe7, 0xd6, 0x52,
|
||||
0x3c, 0xc7, 0xfa, 0xed, 0x89, 0x06, 0xeb, 0xd4, 0x5e, 0x9c, 0xa5, 0x55,
|
||||
0x8c, 0xe3, 0x5f, 0xe6, 0xb4, 0x0a, 0xf4, 0xf6, 0x7d, 0xeb, 0x64, 0x74,
|
||||
0xa9, 0x1a, 0x8d, 0x6e, 0xf1, 0x41, 0xc7, 0x7e, 0xc6, 0x26, 0x3a, 0x47,
|
||||
0x70, 0x49, 0x07, 0x27, 0xa2, 0xb9, 0xc6, 0x79, 0x9d, 0x94, 0xf5, 0x51,
|
||||
0x69, 0xdf, 0xbd, 0x84, 0xee, 0xaa, 0x46, 0xea, 0x4b, 0x27, 0xb6, 0x5c,
|
||||
0xac, 0xcf, 0x4a, 0x48, 0x12, 0x40, 0x86, 0x80, 0xd4, 0xb8, 0x0a, 0xb1,
|
||||
0x9f, 0xdc, 0x68, 0x60, 0x14, 0x33, 0x1d, 0x88, 0xfb, 0xa2, 0xfc, 0x49,
|
||||
0x0c, 0xa9, 0x76, 0x2d, 0xd7, 0x32, 0x3f, 0x77, 0xdb, 0x62, 0x8c, 0x35,
|
||||
0x88, 0x5d, 0x66, 0xc9, 0x8d, 0x07, 0xe5};
|
||||
|
||||
const char kCodeSigningDevelopmentFlagOid[] = "1.3.6.1.4.1.11129.4.1.2";
|
||||
const char kSecureStorageFlagOid[] = "1.3.6.1.4.1.11129.4.1.3";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
|
||||
VmpChecker::VmpChecker() : allow_development_vmp_(false) {}
|
||||
|
||||
VmpChecker::~VmpChecker() {}
|
||||
|
||||
Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
Status status = ca_cert->LoadDer(
|
||||
cert_type == kCertificateTypeProduction
|
||||
? std::string(reinterpret_cast<const char*>(
|
||||
kProdVmpCodeSigningDrmRootCertificate),
|
||||
sizeof(kProdVmpCodeSigningDrmRootCertificate))
|
||||
: std::string(reinterpret_cast<const char*>(
|
||||
kDevVmpCodeSigningDrmRootCertificate),
|
||||
sizeof(kDevVmpCodeSigningDrmRootCertificate)));
|
||||
if (!status.ok()) return status;
|
||||
|
||||
ca_.reset(new X509CA(ca_cert.release()));
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
VmpChecker* VmpChecker::Instance() {
|
||||
static VmpChecker instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
// Verify VMP data and return appropriate result.
|
||||
Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) {
|
||||
DCHECK(!vmp_data.empty());
|
||||
DCHECK(result);
|
||||
|
||||
if (!ca_) return Status(error_space, CERT_CHAIN_NOT_SELECTED, "");
|
||||
|
||||
vmp::VmpData vmp_data_obj;
|
||||
if (!vmp_data_obj.ParseFromString(vmp_data)) {
|
||||
LOG(INFO) << "Error deserializing VmpData.";
|
||||
return Status(error_space, INVALID_MESSAGE, "vmp-data-deserialize-failed");
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<X509Cert>> code_signing_certs;
|
||||
const std::string kDevelopmentFlagOid(kCodeSigningDevelopmentFlagOid);
|
||||
bool secure_storage_verified(true);
|
||||
for (int cert_idx = 0; cert_idx < vmp_data_obj.certificates_size();
|
||||
++cert_idx) {
|
||||
code_signing_certs.emplace_back(new X509Cert);
|
||||
Status status(code_signing_certs.back()->LoadDer(
|
||||
vmp_data_obj.certificates(cert_idx)));
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (!allow_development_vmp_) {
|
||||
bool dev_flag;
|
||||
if (code_signing_certs.back()->GetV3BooleanExtension(kDevelopmentFlagOid,
|
||||
&dev_flag) &&
|
||||
dev_flag) {
|
||||
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
"development-vmp-certificate-not-allowed");
|
||||
}
|
||||
}
|
||||
status = ca_->VerifyCert(*code_signing_certs.back());
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (secure_storage_verified) {
|
||||
bool secure_storage_flag;
|
||||
if (!code_signing_certs.back()->GetV3BooleanExtension(
|
||||
kSecureStorageFlagOid, &secure_storage_flag) ||
|
||||
!secure_storage_flag) {
|
||||
secure_storage_verified = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t num_blessed_binaries(0);
|
||||
for (int binary_idx = 0; binary_idx < vmp_data_obj.signed_binary_info_size();
|
||||
++binary_idx) {
|
||||
const vmp::VmpData::SignedBinaryInfo& binary_info(
|
||||
vmp_data_obj.signed_binary_info(binary_idx));
|
||||
if (binary_info.signature().empty()) {
|
||||
LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\".";
|
||||
*result = kTampered;
|
||||
return OkStatus();
|
||||
}
|
||||
if (binary_info.certificate_index() >= code_signing_certs.size()) {
|
||||
LOG(INFO) << "Invalid code signing certificate index.";
|
||||
*result = kTampered;
|
||||
return OkStatus();
|
||||
}
|
||||
X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get();
|
||||
std::unique_ptr<RsaPublicKey> key(cert->GetRsaPublicKey());
|
||||
std::string message(binary_info.binary_hash());
|
||||
message += binary_info.flags() & 0xff;
|
||||
if (!key->VerifySignature(message, binary_info.signature())) {
|
||||
LOG(INFO) << "Code signature verification failed for file \""
|
||||
<< binary_info.file_name() << "\".";
|
||||
*result = kTampered;
|
||||
return OkStatus();
|
||||
}
|
||||
if (binary_info.flags() & kBlessedBinaryFlag) ++num_blessed_binaries;
|
||||
}
|
||||
if (num_blessed_binaries != 1) {
|
||||
LOG(INFO) << "Invalid number of blessed binaries (" << num_blessed_binaries
|
||||
<< ").";
|
||||
*result = kTampered;
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
VLOG(2) << "VMP verification success. Secure storage: "
|
||||
<< secure_storage_verified;
|
||||
*result = secure_storage_verified ? kSecureStorageVerified : kVerified;
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
57
common/vmp_checker.h
Normal file
57
common/vmp_checker.h
Normal file
@@ -0,0 +1,57 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// Singleton object which validates VMP (Verified Media Pipeline) data for
|
||||
// purposes of platform software verification.
|
||||
|
||||
#ifndef COMMON_VMP_CHECKER_H_
|
||||
#define COMMON_VMP_CHECKER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
class X509CA;
|
||||
|
||||
class VmpChecker {
|
||||
public:
|
||||
enum Result {
|
||||
kUnverified = 0,
|
||||
kVerified = 1,
|
||||
kSecureStorageVerified = 2,
|
||||
kTampered = 3
|
||||
};
|
||||
|
||||
// Singleton accessor.
|
||||
static VmpChecker* Instance();
|
||||
|
||||
// Select the type of root to use. Not thread-safe.
|
||||
virtual Status SelectCertificateType(CertificateType cert_type);
|
||||
|
||||
// Verify VMP data and return appropriate result.
|
||||
virtual Status VerifyVmpData(const std::string& vmp_data, Result* result);
|
||||
|
||||
// Enable/disable development code signing certificates.
|
||||
void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; }
|
||||
bool allow_development_vmp() const { return allow_development_vmp_; }
|
||||
|
||||
private:
|
||||
VmpChecker();
|
||||
~VmpChecker();
|
||||
|
||||
std::unique_ptr<X509CA> ca_;
|
||||
bool allow_development_vmp_ = false;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_VMP_CHECKER_H_
|
||||
321
common/vmp_checker_test.cc
Normal file
321
common/vmp_checker_test.cc
Normal file
@@ -0,0 +1,321 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/vmp_checker.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/verified_media_pipeline.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kBlessedBinaryFlag = 0x00000001;
|
||||
|
||||
const char kDevVmpCodeSigningKey[] =
|
||||
"308204a50201000282010100b3a7da87309390688388d614a5a37e7ee73a"
|
||||
"c3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd"
|
||||
"8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6"
|
||||
"d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b"
|
||||
"b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51"
|
||||
"957dc4608fd34c999a7b35245bce2795715d8a5957a0872398473eed4860"
|
||||
"29286f51d4118927e88634ad040034e061fd3f58632961761b1fbb8faf45"
|
||||
"391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706"
|
||||
"424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203"
|
||||
"01000102820101008befeb1ff28e7ea56a0f63f1a133c08c48c0553efe86"
|
||||
"07cfd9d216d981aef81a81db226b47277a6d32d09207df88e03316247ae7"
|
||||
"39325456a00644bbfacd6dc2990851f047ccdc1226bec21afac9eacfb957"
|
||||
"1e51889cfb6dac853fcdd1bb1593bd5528cdd3cbbb32c69183ef018fd3f5"
|
||||
"3153f097fd3de9aca7998a6f4522e60c5e76ecc717033a2be16c233fe3e2"
|
||||
"94c0ae68179a1ae5d33d51f2c0bcda35cffa21e1814374af1c014c280cfb"
|
||||
"09e294a099f9a6f003d3da026af0f53029cad3d401178b0db53ca97fd166"
|
||||
"a5eddf1384ddd92c55ab31758e0651e41dd7b24f2c438d58de00f55343cc"
|
||||
"a461b94ff6358fc37f404ecbda2365d3e75391566248d3e102818100e4c8"
|
||||
"783343b43fc127f5077fb35b37145ab6ee5f9a802e8100161ce441cbdbdc"
|
||||
"1eb15f50bbef1f5088ba83ff914977862dffc63bd364172c9044e1131dbb"
|
||||
"ff98755daa49670a014163b3c121395d880ad874b9ebdf66ff72a245c2b5"
|
||||
"4f8fe1e233e72fd9cc253cbcd144f52537e9b7ed16b8a6d328de61c41f86"
|
||||
"ea1d9ef326fb02818100c90738b2cf61af785e53393d9e96bc2f4d79358b"
|
||||
"7bd487a242d170b8372af0a26af4980534a099647a4f38efb1a47d9dcdcf"
|
||||
"a4ea6036396ef333e4f1de62ec529c3122f0d3b6abc818918494a52b028b"
|
||||
"2cdf06bf2b450d5b99da7f203ff011318c030a01659c4c56244c2c312528"
|
||||
"e30dd3b7709022d8fd14a2dba23a855da02f0281804f8ebeede4cf5b9449"
|
||||
"d6d582bcd62d73309088984a5be4d00b3da55262e7074fa684bbc69173f8"
|
||||
"09c36248e0a89f49a7297bd66d9b7724efe4436f997c2f92146c4be4199e"
|
||||
"71463a7cf75763bc552027d559d2058a2c810c560db845e0a30243ed14a9"
|
||||
"f92d1a8de2834b5d8c51c33ea87dcc3c8715a12f9249fc5a916e62d3dd02"
|
||||
"8181008f74c7d1528cb35b82748174a7a789c377d5f790025e382c62e273"
|
||||
"3e02a071f875baf681407d1af9c90e9fe2ed322532679cb6634b2566f6f6"
|
||||
"37223a3828ffdc33fa1ca51f704c460ec2498a8a13974d1a484dd83e5898"
|
||||
"9fb5bb66dcecc3b4815719141acb182ea18a659163c0d0dcb7114ee6d4f5"
|
||||
"09441165e6b66e6c9dd3a102818100c449003eee6d9a0b724b66027a791e"
|
||||
"3b86a72b2098ab085c2e69c276627fd6beeee98918de14ce15d17e059906"
|
||||
"a178015d04e205918203ffa6c1324868d5c0a019f6fe82335403743322ee"
|
||||
"14b1159692194ac22d9056c0c39309d249b33abaddb0d08ee87cc053a12a"
|
||||
"83f12d261525e1b37df643c3e55770ef247d5d094816a0";
|
||||
|
||||
const char kDevVmpCodeSigningCert[] =
|
||||
"3082056a308203d2a003020102020f112233445566778899aabbccddeeff300d06092a86"
|
||||
"4886f70d01010b050030819c310b30090603550406130255533113301106035504080c0a"
|
||||
"57617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d060355"
|
||||
"040a0c06476f6f676c653111300f060355040b0c085769646576696e65311e301c060355"
|
||||
"04030c157769646576696e652d6465762d636f64657369676e3121301f06092a864886f7"
|
||||
"0d010901161274696e736b697040676f6f676c652e636f6d301e170d3137313030393231"
|
||||
"323835385a170d3237313030373231323835385a3081a0310b3009060355040613025553"
|
||||
"3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b"
|
||||
"6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465"
|
||||
"76696e653122302006035504030c197769646576696e652d6465762d766d702d636f6465"
|
||||
"7369676e3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e"
|
||||
"636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100"
|
||||
"b3a7da87309390688388d614a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5d"
|
||||
"e2f7b409b1eac2dc578b2bfd8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c"
|
||||
"62c71d894fe6d5524e1b28af1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646b"
|
||||
"b50d3a13d8687ad304005b0b0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd3"
|
||||
"4c999a7b35245bce2795715d8a5957a0872398473eed486029286f51d4118927e88634ad"
|
||||
"040034e061fd3f58632961761b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be"
|
||||
"2fd258331e7a86baa7394706424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3b"
|
||||
"b73008150203010001a38201213082011d301d0603551d0e041604147266b4ce84aafd02"
|
||||
"b1159cd2fa04c2553c6c02463081bb0603551d230481b33081b0a181a2a4819f30819c31"
|
||||
"0b30090603550406130255533113301106035504080c0a57617368696e67746f6e311130"
|
||||
"0f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c65311130"
|
||||
"0f060355040b0c085769646576696e65311e301c06035504030c157769646576696e652d"
|
||||
"6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b6970"
|
||||
"40676f6f676c652e636f6d820900c5f82f038facf15830090603551d1304023000300b06"
|
||||
"03551d0f04040302078030130603551d25040c300a06082b060105050703033011060a2b"
|
||||
"06010401d67904010204030101ff300d06092a864886f70d01010b05000382018100aa23"
|
||||
"6a5c0e23d5bf67c9f80f893d8347ba489541cf7f4ab7dfffda0ca21a3372e8ee8cfea863"
|
||||
"628b9e0795904bc0e7495517246143c7b8555884e82fe1c305f0f4c3575447d4e7ce3243"
|
||||
"4e1e0cf11712d537cd434c11d1328b814c94dbd0bab802e8fed5390da5f0cd719ce0e366"
|
||||
"47620bcf40ed3945c80ab19beb7728080a74d4ff5d62564f47b32c4915c1f14890d379c8"
|
||||
"8060f0bac73301defda06275a2e1a2024f92f0b4700d4e50d2d6f8d033715a362d7ca5ab"
|
||||
"6d2dae20d8cefa2d3fc4f61e1734802984e5078dd6d957719fa75ea10dd02d983f7e383b"
|
||||
"10fa92be7add70238388e63ec2c7ec49a37aa0c8a2566c46e9755cd9ce654c6d42053d1e"
|
||||
"1cd9555f8a3bc5e426857072a8e8b44a756543893b1d29dabf31a2301597df2666612f23"
|
||||
"a442613526e19f2aa9b2ea49f16f14b16794e053967a21b821c7b2495b2e02b01344a339"
|
||||
"9d7e31dd71982cf21a546b1947bbce236381d717070c27096b6a91413abc69c3c0759574"
|
||||
"4b7e91daf24b2a0acfd85924669f00292a4bd0c57b4c";
|
||||
|
||||
const char kDevSecureStorageVmpCodeSigningCert[] =
|
||||
"30820571308203d9a0030201020203112233300d06092a864886f70d01010b050030819c"
|
||||
"310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3111"
|
||||
"300f06035504070c084b69726b6c616e64310f300d060355040a0c06476f6f676c653111"
|
||||
"300f060355040b0c085769646576696e65311e301c06035504030c157769646576696e65"
|
||||
"2d6465762d636f64657369676e3121301f06092a864886f70d010901161274696e736b69"
|
||||
"7040676f6f676c652e636f6d301e170d3138303232323231323834395a170d3238303232"
|
||||
"303231323834395a3081a0310b30090603550406130255533113301106035504080c0a57"
|
||||
"617368696e67746f6e3111300f06035504070c084b69726b6c616e64310f300d06035504"
|
||||
"0a0c06476f6f676c653111300f060355040b0c085769646576696e653122302006035504"
|
||||
"030c197769646576696e652d6465762d766d702d636f64657369676e3121301f06092a86"
|
||||
"4886f70d010901161274696e736b697040676f6f676c652e636f6d30820122300d06092a"
|
||||
"864886f70d01010105000382010f003082010a0282010100b3a7da87309390688388d614"
|
||||
"a5a37e7ee73ac3b296caf4464bfeccbeedf2e3d802d62d5de2f7b409b1eac2dc578b2bfd"
|
||||
"8b5f20acef13d8ba6a4ccfd406f29a60e1af4151212a062c62c71d894fe6d5524e1b28af"
|
||||
"1d51d2b806aca778f77fd0e4ef278c4fbd5eb756398d646bb50d3a13d8687ad304005b0b"
|
||||
"0a054e5109d70dec9953b2f99768fca8fe51957dc4608fd34c999a7b35245bce2795715d"
|
||||
"8a5957a0872398473eed486029286f51d4118927e88634ad040034e061fd3f5863296176"
|
||||
"1b1fbb8faf45391d84f7ffe2bf5e27bd499eee14a17bc8be2fd258331e7a86baa7394706"
|
||||
"424420f6eaba0001bffe74976a5b4cc46380ceef9ce93b3bb73008150203010001a38201"
|
||||
"3430820130301d0603551d0e041604147266b4ce84aafd02b1159cd2fa04c2553c6c0246"
|
||||
"3081bb0603551d230481b33081b0a181a2a4819f30819c310b3009060355040613025553"
|
||||
"3113301106035504080c0a57617368696e67746f6e3111300f06035504070c084b69726b"
|
||||
"6c616e64310f300d060355040a0c06476f6f676c653111300f060355040b0c0857696465"
|
||||
"76696e65311e301c06035504030c157769646576696e652d6465762d636f64657369676e"
|
||||
"3121301f06092a864886f70d010901161274696e736b697040676f6f676c652e636f6d82"
|
||||
"0900c5f82f038facf15830090603551d1304023000300b0603551d0f0404030207803013"
|
||||
"0603551d25040c300a06082b060105050703033011060a2b06010401d679040102040301"
|
||||
"01ff3011060a2b06010401d67904010304030101ff300d06092a864886f70d01010b0500"
|
||||
"038201810069482a9bb125b17c76c77f86004e36ca4fc36bbde9c86901481c70d165e3cf"
|
||||
"1c8541264190b3e7106c33ff4920a99bc9f939298091dc72ff898d22f5017b04d213eb60"
|
||||
"626382656fdd66b9cd1830d98ef89d8e405b2d7ce9445db8de2ca365438b848f85d5266b"
|
||||
"c211c8934154bb88ddb9e6f9aaff761814c1c0da90dd499f174507b5f4e89339e8abd157"
|
||||
"8b3238d8f5c9dcb1f76e4d810679c2eca3583144f1ac0ce955b7c6a2cc9fc9f3d6c87069"
|
||||
"28e301ebc3844e53f4905ff60803110dfc3b4f74b50ae1baffd091daad3ea29925f8009e"
|
||||
"adc471a9ae673d9a9a003901d962f58fede85b2f65d9a470725459b19a69b06ae49179a3"
|
||||
"395286b7d7039b32985a6db5b30bcd5cbed975d9a68de44fbcd1dbc8b6fbf67746b87d30"
|
||||
"122c1cd8ed303a8bec7e23d284c0de35cfdccf261c317efe192efa84d2600bba3f9af846"
|
||||
"8f89953a98f92c97b13d320f484627790d5b7b407f29f343c41154efbcf06e98632a3f3d"
|
||||
"7138184a6f40af2435b3d98054ed9f4c3aa9ecf95c5c3014d3aa4d12f2";
|
||||
|
||||
const char kSameAsPrevious[] = "";
|
||||
|
||||
} // namespace
|
||||
|
||||
class VmpCheckerTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
ASSERT_OK(
|
||||
VmpChecker::Instance()->SelectCertificateType(kCertificateTypeTesting));
|
||||
vmp_data_.Clear();
|
||||
VmpChecker::Instance()->set_allow_development_vmp(true);
|
||||
signing_key_.reset(
|
||||
RsaPrivateKey::Create(absl::HexStringToBytes(kDevVmpCodeSigningKey)));
|
||||
ASSERT_TRUE(signing_key_);
|
||||
}
|
||||
|
||||
// Adds a binary to the VMP data to be verified. If |signing_cert| is
|
||||
// |kSameAsPrevious| (empty), then the binary is signed using the same
|
||||
// certificate as the previously added binary. This means that the first
|
||||
// call to this function should not use |kSameAsPrevious|.
|
||||
void AddVmpBinary(const std::string& signing_cert, const std::string& file_name,
|
||||
const std::string& binary_hash, uint32_t flags) {
|
||||
DCHECK(!signing_cert.empty() || !vmp_data_.certificates().empty());
|
||||
|
||||
if (!signing_cert.empty()) {
|
||||
*vmp_data_.add_certificates() = absl::HexStringToBytes(signing_cert);
|
||||
}
|
||||
vmp::VmpData::SignedBinaryInfo* new_binary =
|
||||
vmp_data_.add_signed_binary_info();
|
||||
new_binary->set_file_name(file_name);
|
||||
new_binary->set_certificate_index(vmp_data_.certificates_size() - 1);
|
||||
new_binary->set_binary_hash(binary_hash);
|
||||
new_binary->set_flags(flags);
|
||||
std::string message(binary_hash);
|
||||
message += flags & 0xff;
|
||||
std::string signature;
|
||||
ASSERT_TRUE(signing_key_->GenerateSignature(message, &signature));
|
||||
new_binary->set_signature(signature);
|
||||
}
|
||||
|
||||
const std::string GetVmpData() {
|
||||
std::string result;
|
||||
vmp_data_.SerializeToString(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
vmp::VmpData vmp_data_;
|
||||
std::unique_ptr<RsaPrivateKey> signing_key_;
|
||||
};
|
||||
|
||||
TEST_F(VmpCheckerTest, Success) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kVerified, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, SecureStorageSuccess) {
|
||||
AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevSecureStorageVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kSecureStorageVerified, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailDevelopmentCert) {
|
||||
VmpChecker::Instance()->set_allow_development_vmp(false);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
VmpChecker::Result result;
|
||||
EXPECT_EQ(DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
VmpChecker::Instance()
|
||||
->VerifyVmpData(GetVmpData(), &result)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailTwoBlessedBinaries) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
vmp_data_.mutable_signed_binary_info(0)->set_flags(kBlessedBinaryFlag);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailNoBlessedBinaries) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
vmp_data_.mutable_signed_binary_info(3)->set_flags(0);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailBadSignature) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
++((*vmp_data_.mutable_signed_binary_info(2)->mutable_signature())[10]);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailSignatureVerification) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
++((*vmp_data_.mutable_signed_binary_info(3)->mutable_binary_hash())[16]);
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
TEST_F(VmpCheckerTest, FailUnsignedBinary) {
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary1.exe",
|
||||
"0123456789abdef0123456789abdef", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary2.dll", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
0);
|
||||
AddVmpBinary(kDevVmpCodeSigningCert, "binary3.dll",
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 0);
|
||||
AddVmpBinary(kSameAsPrevious, "binary4.dll", "cccccccccccccccccccccccccccccc",
|
||||
kBlessedBinaryFlag);
|
||||
vmp_data_.mutable_signed_binary_info(2)->clear_binary_hash();
|
||||
VmpChecker::Result result;
|
||||
ASSERT_OK(VmpChecker::Instance()->VerifyVmpData(GetVmpData(), &result));
|
||||
EXPECT_EQ(VmpChecker::kTampered, result);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
19
common/widevine_system_id.cc
Normal file
19
common/widevine_system_id.cc
Normal file
@@ -0,0 +1,19 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Common Encryption (CENC) system ID for Widevine DRM.
|
||||
|
||||
#include "common/widevine_system_id.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
const uint8_t kWidevineSystemId[16] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
||||
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
||||
0xd5, 0x1d, 0x21, 0xed};
|
||||
|
||||
} // namespace widevine
|
||||
22
common/widevine_system_id.h
Normal file
22
common/widevine_system_id.h
Normal file
@@ -0,0 +1,22 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Common Encryption (CENC) system ID for Widevine DRM.
|
||||
|
||||
#ifndef COMMON_WIDEVINE_SYSTEM_ID_H_
|
||||
#define COMMON_WIDEVINE_SYSTEM_ID_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
extern const uint8_t kWidevineSystemId[16];
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_WIDEVINE_SYSTEM_ID_H_
|
||||
86
common/wvm_test_keys.cc
Normal file
86
common/wvm_test_keys.cc
Normal file
@@ -0,0 +1,86 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/wvm_test_keys.h"
|
||||
#include "common/wvm_token_handler.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace wvm_test_keys {
|
||||
|
||||
// Preprov key and keyboxes in this header use system ID 0x112.
|
||||
const char kTestPreprovKeyHex[] = "f7538b38acc78ec68c732ac665c55c65";
|
||||
const char kBadPreprovKey1Hex[] = "1badbadbadbadbadbadbadbadbadbad1";
|
||||
const char kBadPreprovKey2Hex[] = "2badbadbadbadbadbadbadbadbadbad2";
|
||||
|
||||
const char kTestToken1Hex[] =
|
||||
"00000002000001128e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e";
|
||||
|
||||
// The device key corresponding to kTestToken1.
|
||||
const char kTestDeviceKey1Hex[] = "4071197f1f8910d9bf10c6bc4c987638";
|
||||
|
||||
// Base64Encode(kTestToken1)
|
||||
const char kTestToken1Base64[] =
|
||||
"AAAAAgAAARKOHr/gN4KAlsplOLT29Ly1HCtxkc8Dfpi+qiSSSQfhKPn/SbVKFlzZ"
|
||||
"wz5lR1N+tNKft+jfPCwc2SUXoS9JIpU+";
|
||||
|
||||
const char kTestToken2Hex[] =
|
||||
"0000000200000112d906feebe1750c5886ff77c2dfa31bb40e002f3adbc0fa5b"
|
||||
"eb2486cf5f419549cdaa23230e5165ac2ffab56d53b692b7ba0c1857400c6add"
|
||||
"3af3ff3d5cb24985";
|
||||
|
||||
// The device key corresponding to kTestToken2.
|
||||
const char kTestDeviceKey2Hex[] = "42cfb1765201042302a404d1e0fac8ed";
|
||||
|
||||
// Base64Encode(kTestToken2)
|
||||
const char kTestToken2Base64[] =
|
||||
"AAAAAgAAARLZBv7r4XUMWIb/d8Lfoxu0DgAvOtvA+lvrJIbPX0GVSc2qIyMOUWWs"
|
||||
"L/q1bVO2kre6DBhXQAxq3Trz/z1cskmF";
|
||||
|
||||
const char kTestToken3DesHex[] =
|
||||
"00000002100000138e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e";
|
||||
|
||||
const char kTestDeviceKey3DesHex[] = "4071197f1f8910d9bf10c6bc4c987638";
|
||||
|
||||
std::vector<WvmTokenHandler::PreprovKey> GetPreprovKeyVector() {
|
||||
return {
|
||||
// Return multiple preprov keys for the same device.
|
||||
WvmTokenHandler::PreprovKey(kTestSystemId,
|
||||
absl::HexStringToBytes(kBadPreprovKey1Hex)),
|
||||
WvmTokenHandler::PreprovKey(kTestSystemId,
|
||||
absl::HexStringToBytes(kTestPreprovKeyHex)),
|
||||
WvmTokenHandler::PreprovKey(kTestSystemId,
|
||||
absl::HexStringToBytes(kBadPreprovKey2Hex)),
|
||||
|
||||
// Add another device that uses 3DES encryption for asset keys. Tokens
|
||||
// the same except for the system ID portion.
|
||||
WvmTokenHandler::PreprovKey(kTestSystemId3Des,
|
||||
absl::HexStringToBytes(kTestPreprovKeyHex),
|
||||
WvmTokenHandler::DES3),
|
||||
};
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::string> GetPreprovKeyTable() {
|
||||
return {{kTestSystemId, std::string(kTestPreprovKeyHex)}};
|
||||
}
|
||||
|
||||
std::multimap<uint32_t, std::string> GetPreprovKeyMultimap() {
|
||||
return {{kTestSystemId, kBadPreprovKey1Hex},
|
||||
{kTestSystemId, kTestPreprovKeyHex},
|
||||
{kTestSystemId, kBadPreprovKey2Hex}};
|
||||
}
|
||||
|
||||
} // namespace wvm_test_keys
|
||||
} // namespace widevine
|
||||
58
common/wvm_test_keys.h
Normal file
58
common/wvm_test_keys.h
Normal file
@@ -0,0 +1,58 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Sample keys for use in tests of Widevine WVM crypto code.
|
||||
|
||||
#ifndef COMMON_WVM_TEST_KEYS_H_
|
||||
#define COMMON_WVM_TEST_KEYS_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "common/wvm_token_handler.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace wvm_test_keys {
|
||||
|
||||
// Most preprov key and keyboxes in this header use system ID 0x112.
|
||||
const uint32_t kTestSystemId = 0x112;
|
||||
|
||||
// One oddball system that uses 3DES. (We'll re-use the system and device keys).
|
||||
const uint32_t kTestSystemId3Des = 0x10000013;
|
||||
|
||||
extern const char kTestPreprovKeyHex[];
|
||||
|
||||
extern const char kTestToken1Hex[];
|
||||
extern const char kTestDeviceKey1Hex[];
|
||||
|
||||
extern const char kTestToken2Hex[];
|
||||
extern const char kTestDeviceKey2Hex[];
|
||||
|
||||
extern const char kTestToken3DesHex[];
|
||||
extern const char kTestDeviceKey3DesHex[];
|
||||
|
||||
// Return a list of preprovisioning keys, suitable for initializing
|
||||
// the preprov key table for tests.
|
||||
std::vector<WvmTokenHandler::PreprovKey> GetPreprovKeyVector();
|
||||
|
||||
// Old version of GetPreprovKeyTable() which uses a simple int->std::string
|
||||
// map. Doesn't support multiple devices per system ID.
|
||||
// TODO(user): get rid of this once other code as been migrated off of it.
|
||||
std::map<uint32_t, std::string> GetPreprovKeyTable();
|
||||
|
||||
// Version of GetPreprovKeyTable() which uses a simple int->hex_string
|
||||
// multimap, useful for modular DRM code which doesn't care about the asset
|
||||
// key cipher.
|
||||
std::multimap<uint32_t, std::string> GetPreprovKeyMultimap();
|
||||
|
||||
} // namespace wvm_test_keys
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_WVM_TEST_KEYS_H_
|
||||
311
common/wvm_token_handler.cc
Normal file
311
common/wvm_token_handler.cc
Normal file
@@ -0,0 +1,311 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/wvm_token_handler.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/endian/endian.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/ecb_util.h"
|
||||
#include "common/sha_util.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
const int kKeyboxFlagInsecure = 1;
|
||||
const int kSystemIdSamsungTVFactory = 8;
|
||||
const int kPreProvisioningKeySizeBytes = 16;
|
||||
const int kKeyboxSizeBytes = 72;
|
||||
const std::string kZeroIV(16, '\0');
|
||||
|
||||
class PreprovKeysMap {
|
||||
public:
|
||||
void Update(const std::vector<WvmTokenHandler::PreprovKey>& key_vector);
|
||||
std::vector<WvmTokenHandler::PreprovKey> GetPreprovKeys(uint32_t system_id);
|
||||
bool IsSystemIdKnown(uint32_t system_id);
|
||||
bool IsEmpty();
|
||||
|
||||
static PreprovKeysMap* GetSingleton();
|
||||
|
||||
private:
|
||||
absl::Mutex mutex_;
|
||||
std::multimap<uint32_t, WvmTokenHandler::PreprovKey> preprov_keys_;
|
||||
};
|
||||
|
||||
void PreprovKeysMap::Update(
|
||||
const std::vector<WvmTokenHandler::PreprovKey>& key_vector) {
|
||||
absl::WriterMutexLock lock(&mutex_);
|
||||
preprov_keys_.clear();
|
||||
for (const WvmTokenHandler::PreprovKey& ppk : key_vector) {
|
||||
if (ppk.key_bytes.size() != kPreProvisioningKeySizeBytes) {
|
||||
LOG(WARNING) << "Invalid preprov key for system id: " << ppk.system_id;
|
||||
continue;
|
||||
}
|
||||
preprov_keys_.insert(std::make_pair(ppk.system_id, ppk));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<WvmTokenHandler::PreprovKey> PreprovKeysMap::GetPreprovKeys(
|
||||
uint32_t system_id) {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
std::vector<WvmTokenHandler::PreprovKey> key_vector;
|
||||
auto range = preprov_keys_.equal_range(system_id);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
key_vector.push_back(it->second);
|
||||
return key_vector;
|
||||
}
|
||||
|
||||
bool PreprovKeysMap::IsSystemIdKnown(uint32_t system_id) {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
return gtl::ContainsKey(preprov_keys_, system_id);
|
||||
}
|
||||
|
||||
bool PreprovKeysMap::IsEmpty() {
|
||||
absl::ReaderMutexLock lock(&mutex_);
|
||||
return preprov_keys_.empty();
|
||||
}
|
||||
|
||||
PreprovKeysMap* PreprovKeysMap::GetSingleton() {
|
||||
static auto* const kInstance = new PreprovKeysMap();
|
||||
return kInstance;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
||||
const std::string& key_bytes, Cipher cipher,
|
||||
const std::string& model_filter)
|
||||
: system_id(system_id),
|
||||
key_bytes(key_bytes),
|
||||
cipher(cipher),
|
||||
model_filter(model_filter) {}
|
||||
|
||||
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
||||
const std::string& key_bytes, Cipher cipher)
|
||||
: system_id(system_id), key_bytes(key_bytes), cipher(cipher) {}
|
||||
|
||||
WvmTokenHandler::PreprovKey::PreprovKey(uint32_t system_id,
|
||||
const std::string& key_bytes)
|
||||
: system_id(system_id), key_bytes(key_bytes), cipher(AES) {}
|
||||
|
||||
void WvmTokenHandler::SetPreprovKeys(const std::vector<PreprovKey>& keyvec) {
|
||||
PreprovKeysMap::GetSingleton()->Update(keyvec);
|
||||
}
|
||||
|
||||
bool WvmTokenHandler::IsSystemIdKnown(uint32_t system_id) {
|
||||
return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id);
|
||||
}
|
||||
|
||||
Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out,
|
||||
bool* insecure_out) {
|
||||
const std::string default_make_model;
|
||||
return DecryptDeviceKey(token, default_make_model, device_key_out, cipher_out,
|
||||
insecure_out);
|
||||
}
|
||||
|
||||
Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
const std::string& make_model,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out,
|
||||
bool* insecure_out) {
|
||||
DCHECK(device_key_out);
|
||||
// DCHECK below is commented out because preprov_keys_ being nullptr
|
||||
// is a valid test in wvm_token_handler_test.cc. If we have
|
||||
// DCHECK(preprov_keys_) here it would thrown an failure in Kokoro
|
||||
// presubmit because evidently Kokoro does debug build.
|
||||
// DCHECK(preprov_keys_);
|
||||
if (token.size() < kKeyboxSizeBytes) {
|
||||
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
|
||||
}
|
||||
if (PreprovKeysMap::GetSingleton()->IsEmpty()) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Pre-provisioning key map is nullptr.");
|
||||
}
|
||||
|
||||
uint32_t system_id = GetSystemId(token);
|
||||
|
||||
// There may be multiple preprov keys for a system ID; try them all.
|
||||
std::vector<PreprovKey> key_vector =
|
||||
PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id);
|
||||
|
||||
Status status;
|
||||
// First pass through the matching system Ids is an attempt to find an
|
||||
// alternate preprov key specific to this make/model.
|
||||
const PreprovKey* preferred_ppk = NULL;
|
||||
for (const PreprovKey& ppk : key_vector) {
|
||||
if (!ppk.model_filter.empty() && ppk.model_filter == make_model) {
|
||||
preferred_ppk = &ppk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const PreprovKey& ppk : key_vector) {
|
||||
if (preferred_ppk && preferred_ppk != &ppk) {
|
||||
continue;
|
||||
}
|
||||
uint32_t version;
|
||||
status = DecryptDeviceKeyWithPreprovKey(
|
||||
ppk.key_bytes, token, device_key_out, insecure_out, &version);
|
||||
if (version != 2) {
|
||||
// Only version 2 keyboxes supported.
|
||||
return Status(error::PERMISSION_DENIED,
|
||||
absl::StrCat("invalid-keybox-version ", version));
|
||||
}
|
||||
if (status.ok()) {
|
||||
if (cipher_out) {
|
||||
*cipher_out = ppk.cipher;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
}
|
||||
if (!status.ok()) {
|
||||
// Return error from last attempt.
|
||||
return status;
|
||||
}
|
||||
return Status(
|
||||
error::NOT_FOUND,
|
||||
absl::StrCat("Unknown system id: ", system_id).c_str()); // NOLINT
|
||||
}
|
||||
|
||||
// Decrypt a token using the preprov key for its system ID, and use the
|
||||
// decrypted device key to encrypt the given asset key. Returns the encrypted
|
||||
// asset key in |result|.
|
||||
// On failure, returns an error from the Widevine Server SDK error space.
|
||||
Status WvmTokenHandler::GetEncryptedAssetKey(absl::string_view token,
|
||||
absl::string_view raw_asset_key,
|
||||
const std::string& make_model,
|
||||
std::string* result) {
|
||||
std::string device_key;
|
||||
Cipher cipher = AES;
|
||||
Status status =
|
||||
DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
return EncryptAssetKey(device_key, raw_asset_key, cipher, result);
|
||||
}
|
||||
|
||||
uint32_t WvmTokenHandler::GetSystemId(absl::string_view token) {
|
||||
uint32_t system_id = 0;
|
||||
if (token.size() >= 8) {
|
||||
// Bytes 4-8 contain the little-endian system ID.
|
||||
system_id = BigEndian::Load32(token.data() + 4);
|
||||
}
|
||||
return system_id;
|
||||
}
|
||||
|
||||
std::string WvmTokenHandler::GetEncryptedUniqueId(absl::string_view token) {
|
||||
std::string encrypted_unique_id("");
|
||||
if (token.size() >= 24) {
|
||||
encrypted_unique_id = std::string(token.substr(8, 16));
|
||||
}
|
||||
return encrypted_unique_id;
|
||||
}
|
||||
|
||||
Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
absl::string_view preprov_key, absl::string_view token,
|
||||
std::string* device_key_out) {
|
||||
return DecryptDeviceKeyWithPreprovKey(preprov_key, token, device_key_out,
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
||||
Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
absl::string_view preprov_key, absl::string_view token,
|
||||
std::string* device_key_out, bool* insecure_out, uint32_t* version) {
|
||||
CHECK(device_key_out);
|
||||
if (token.size() < kKeyboxSizeBytes) {
|
||||
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
|
||||
}
|
||||
if (version) {
|
||||
*version = BigEndian::Load32(token.data());
|
||||
}
|
||||
// This was checked at initialization, so if it fails now something is wrong.
|
||||
CHECK_EQ(preprov_key.size(), kPreProvisioningKeySizeBytes);
|
||||
|
||||
absl::string_view encrypted_unique_id = token.substr(8, 16);
|
||||
std::string unique_id = crypto_util::DecryptAesCbcNoPad(
|
||||
std::string(preprov_key), kZeroIV, std::string(encrypted_unique_id));
|
||||
|
||||
if (unique_id.size() != 16) {
|
||||
// Decrypting 16 bytes should result in 16 bytes.
|
||||
LOG(WARNING) << "Internal error decrypting unique id from token.";
|
||||
return Status(error::INTERNAL, "Wrong size after decrypt/16.");
|
||||
}
|
||||
|
||||
absl::string_view encrypted_bits = token.substr(24, 48);
|
||||
std::string decrypted_bits = crypto_util::DecryptAesCbcNoPad(
|
||||
unique_id, kZeroIV, std::string(encrypted_bits));
|
||||
if (decrypted_bits.size() != 48) {
|
||||
// Decrypting 48 bytes should result in 48 bytes.
|
||||
LOG(WARNING) << "Internal error decrypting device key from token.";
|
||||
return Status(error::INTERNAL, "Wrong size after decrypt/48.");
|
||||
}
|
||||
uint8_t keybox_flags = decrypted_bits[36];
|
||||
absl::string_view device_key =
|
||||
absl::string_view(decrypted_bits).substr(0, 16);
|
||||
absl::string_view expected_hash =
|
||||
absl::string_view(decrypted_bits).substr(16, 20);
|
||||
std::string actual_hash = Sha1_Hash(std::string(device_key));
|
||||
|
||||
if (GetSystemId(token) == kSystemIdSamsungTVFactory) {
|
||||
// Keyboxes with this system ID have corrupted bytes starting after the
|
||||
// first 16 bytes of the hash, so we use only the uncorrupted part.
|
||||
expected_hash = expected_hash.substr(0, 16);
|
||||
actual_hash.resize(16);
|
||||
keybox_flags = 0;
|
||||
}
|
||||
if (expected_hash != actual_hash) {
|
||||
return Status(error::PERMISSION_DENIED, "Keybox validation failed.");
|
||||
}
|
||||
*device_key_out = std::string(device_key);
|
||||
if (insecure_out) {
|
||||
*insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0;
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key,
|
||||
absl::string_view raw_asset_key,
|
||||
Cipher cipher, std::string* result) {
|
||||
CHECK(result);
|
||||
if (device_key.size() != 16) {
|
||||
return Status(error::INVALID_ARGUMENT, "Invalid device key: size != 16");
|
||||
}
|
||||
if (raw_asset_key.size() < 16) {
|
||||
return Status(error::INVALID_ARGUMENT, "Invalid asset key: size < 16");
|
||||
}
|
||||
// Truncate extra characters in the key; wvm always uses 16.
|
||||
absl::string_view asset_key = raw_asset_key.substr(0, 16);
|
||||
switch (cipher) {
|
||||
case DES3:
|
||||
if (!crypto_util::Encrypt3DesEcb(device_key, asset_key, result)) {
|
||||
return Status(error::INTERNAL, "Error encrypting asset key with 3DES.");
|
||||
}
|
||||
return OkStatus();
|
||||
case AES:
|
||||
if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) {
|
||||
return Status(error::INTERNAL, "Error encrypting asset key with AES.");
|
||||
}
|
||||
return OkStatus();
|
||||
case PASS_THRU:
|
||||
result->assign(raw_asset_key.data(), raw_asset_key.size());
|
||||
return OkStatus();
|
||||
default:
|
||||
return Status(error::INVALID_ARGUMENT, "Unknown cipher type");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
125
common/wvm_token_handler.h
Normal file
125
common/wvm_token_handler.h
Normal file
@@ -0,0 +1,125 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_WVM_TOKEN_HANDLER_H_
|
||||
#define COMMON_WVM_TOKEN_HANDLER_H_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Class for decoding the encrypted token that comes from a WVM classic keybox.
|
||||
//
|
||||
// Internally, this class keeps a multimap from system ID to the preprov key(s)
|
||||
// for that system, so construction is relatively expensive; don't create an
|
||||
// instance of this class per-request.
|
||||
//
|
||||
// Errors in this file are returned in the canonical space, but are chosen so
|
||||
// that it's possible to map different failure modes to the appropriate codes
|
||||
// in the WVM or server SDK error spaces:
|
||||
// OK - success.
|
||||
// NOT_FOUND - system id from token wasn't in the preprov key table.
|
||||
// PERMISSION_DENIED - hash of device key didn't match.
|
||||
// INVALID_ARGUMENT - token or a key is wrong size or otherwise invalid.
|
||||
// INTERNAL_ERROR - something went wrong that shouldn't have been able to.
|
||||
class WvmTokenHandler {
|
||||
public:
|
||||
// Cipher type to use for encrypting asset keys. This matches the enum in
|
||||
// video/widevine/lockbox/public/key.proto.
|
||||
enum Cipher {
|
||||
DES3 = 0,
|
||||
AES = 1,
|
||||
PASS_THRU = 2,
|
||||
};
|
||||
|
||||
struct PreprovKey {
|
||||
// Utility constructor.
|
||||
PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher,
|
||||
const std::string& model_filter);
|
||||
PreprovKey(uint32_t system_id, const std::string& key_bytes, Cipher cipher);
|
||||
// Constructor if the cipher isn't needed (i.e. modular DRM).
|
||||
PreprovKey(uint32_t system_id, const std::string& key_bytes);
|
||||
uint32_t system_id;
|
||||
std::string key_bytes;
|
||||
Cipher cipher;
|
||||
// If set, the make/model in the license request must match this value.
|
||||
std::string model_filter;
|
||||
};
|
||||
|
||||
// Set pre-provisioning keys from the given vector. This may be called
|
||||
// concurrently with other methods.
|
||||
static void SetPreprovKeys(const std::vector<PreprovKey>& preprov_keys);
|
||||
|
||||
// Returns true if system_id is in the preprov key table.
|
||||
static bool IsSystemIdKnown(uint32_t system_id);
|
||||
|
||||
// Decrypt a token using the preprov key for its system ID, and
|
||||
// return the decrypted device key in result.
|
||||
// On failure, returns one of the errors listed above.
|
||||
// cipher_out may be null; if not, *cipher_out will be set to the cipher type
|
||||
// to use with the device key.
|
||||
// insecure_out may be null; if not, *insecure_out will be set to the
|
||||
// decrypted value of the 'insecure keybox' flag.
|
||||
static Status DecryptDeviceKey(absl::string_view token,
|
||||
std::string* device_key_out, Cipher* cipher_out,
|
||||
bool* insecure_out);
|
||||
// Same as above, except takes in the make/model from the license request.
|
||||
// For legacy WVM license, we have some special cases where we need to inspect
|
||||
// the make/model as we apply alternate keys.
|
||||
static Status DecryptDeviceKey(absl::string_view token,
|
||||
const std::string& make_model,
|
||||
std::string* device_key_out, Cipher* cipher_out,
|
||||
bool* insecure_out);
|
||||
|
||||
// Decrypt a token using the preprov key for its system ID, and use the
|
||||
// decrypted device key to encrypt the given asset key. Returns the encrypted
|
||||
// asset key in result.
|
||||
static Status GetEncryptedAssetKey(absl::string_view token,
|
||||
absl::string_view raw_asset_key,
|
||||
const std::string& make_model, std::string* result);
|
||||
|
||||
// Extract the system ID component of a token (bytes 4-8).
|
||||
static uint32_t GetSystemId(absl::string_view token);
|
||||
|
||||
// Extract the encrypted unique ID component of a token (bytes 8-24).
|
||||
static std::string GetEncryptedUniqueId(absl::string_view token);
|
||||
|
||||
// Try to decrypt a token using the provided preprov key, and return the
|
||||
// decrypted device key in result.
|
||||
//
|
||||
// Note that the if the input std::string lengths are correct (16 and 72 bytes),
|
||||
// the only possible cause of failure is the decrypted device key hash
|
||||
// being incorrect.
|
||||
static Status DecryptDeviceKeyWithPreprovKey(
|
||||
absl::string_view preprov_key_bytes, absl::string_view token,
|
||||
std::string* device_key_out);
|
||||
|
||||
// Same as above, but allows extracting the 'insecure keybox' flag and keybox
|
||||
// version.
|
||||
static Status DecryptDeviceKeyWithPreprovKey(
|
||||
absl::string_view preprov_key_bytes, absl::string_view token,
|
||||
std::string* device_key_out, bool* insecure_out, uint32_t* version);
|
||||
|
||||
// Given a decrypted device key as returned by DecryptToken(), use it to
|
||||
// encrypt an asset key with the given cipher.
|
||||
static Status EncryptAssetKey(absl::string_view device_key,
|
||||
absl::string_view raw_asset_key, Cipher cipher,
|
||||
std::string* result);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_WVM_TOKEN_HANDLER_H_
|
||||
229
common/wvm_token_handler_test.cc
Normal file
229
common/wvm_token_handler_test.cc
Normal file
@@ -0,0 +1,229 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/wvm_token_handler.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "common/wvm_test_keys.h"
|
||||
|
||||
using widevine::wvm_test_keys::kTestSystemId;
|
||||
using widevine::wvm_test_keys::kTestSystemId3Des;
|
||||
using widevine::wvm_test_keys::kTestPreprovKeyHex;
|
||||
using widevine::wvm_test_keys::kTestDeviceKey1Hex;
|
||||
using widevine::wvm_test_keys::kTestDeviceKey2Hex;
|
||||
using widevine::wvm_test_keys::kTestDeviceKey3DesHex;
|
||||
using widevine::wvm_test_keys::kTestToken1Hex;
|
||||
using widevine::wvm_test_keys::kTestToken2Hex;
|
||||
using widevine::wvm_test_keys::kTestToken3DesHex;
|
||||
using widevine::wvm_test_keys::GetPreprovKeyVector;
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using absl::BytesToHexString;
|
||||
using absl::HexStringToBytes;
|
||||
|
||||
TEST(WvmTokenHandlerTest, GetSystemId) {
|
||||
EXPECT_EQ(kTestSystemId,
|
||||
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex)));
|
||||
EXPECT_EQ(kTestSystemId,
|
||||
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken2Hex)));
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, GetEncryptedUniqueId) {
|
||||
EXPECT_EQ(
|
||||
HexStringToBytes("8e1ebfe037828096ca6538b4f6f4bcb5"),
|
||||
WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken1Hex)));
|
||||
EXPECT_EQ(
|
||||
HexStringToBytes("d906feebe1750c5886ff77c2dfa31bb4"),
|
||||
WvmTokenHandler::GetEncryptedUniqueId(HexStringToBytes(kTestToken2Hex)));
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
|
||||
Status status;
|
||||
std::string device_key;
|
||||
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken1Hex),
|
||||
&device_key);
|
||||
EXPECT_OK(status) << status;
|
||||
EXPECT_EQ(kTestDeviceKey1Hex, BytesToHexString(device_key));
|
||||
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(kTestPreprovKeyHex), HexStringToBytes(kTestToken2Hex),
|
||||
&device_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ(kTestDeviceKey2Hex, BytesToHexString(device_key));
|
||||
|
||||
// Test with invalid token. Hash failure should produce PERMISSION_DENIED.
|
||||
device_key.clear();
|
||||
std::string token = HexStringToBytes(
|
||||
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e");
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(kTestPreprovKeyHex), token, &device_key);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
}
|
||||
|
||||
// See b/68798704 for background of this test.
|
||||
// It is important to keep this test *before* all the DecryptDeviceKey* tests
|
||||
// below, in which WvmTokenHandler::SetPreprovKeys() would be called.
|
||||
TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) {
|
||||
// Not calling WvmTokenHandler::SetPreprovKeys()
|
||||
// So preprov_keys_ would be nullptr.
|
||||
Status status;
|
||||
std::string device_key;
|
||||
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
|
||||
&device_key, nullptr, nullptr);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
|
||||
}
|
||||
|
||||
// Same tests as DecryptDeviceKeyWithPreprovKey(), but we use the handler's
|
||||
// table of preprov keys instead of providing our own.
|
||||
TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
|
||||
Status status;
|
||||
std::string device_key;
|
||||
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
|
||||
|
||||
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
|
||||
&device_key, nullptr, nullptr);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ(HexStringToBytes(kTestDeviceKey1Hex), device_key);
|
||||
|
||||
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken2Hex),
|
||||
&device_key, nullptr, nullptr);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ(HexStringToBytes(kTestDeviceKey2Hex), device_key);
|
||||
|
||||
// Test with invalid token. Hash failure should produce PERMISSION_DENIED.
|
||||
device_key.clear();
|
||||
std::string token = HexStringToBytes(
|
||||
"00000002000001129e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e");
|
||||
status =
|
||||
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
|
||||
// Test with nonexistent system id. Should produce NOT_FOUND.
|
||||
device_key.clear();
|
||||
token = HexStringToBytes(
|
||||
"00000002555555559e1ebfe037828096ca6538b4f6f4bcb51c2b7191cf037e98"
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e");
|
||||
status =
|
||||
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(error::NOT_FOUND, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, GetEncryptedAssetKey) {
|
||||
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
|
||||
|
||||
std::string raw_asset_key = "asset-key-000000";
|
||||
std::string asset_key;
|
||||
std::string make_model;
|
||||
Status status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key));
|
||||
|
||||
status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken2Hex), raw_asset_key, make_model, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("091802159bf8da12aecfcdfb092075c8", BytesToHexString(asset_key));
|
||||
|
||||
// Check 3DES encryption of asset keys
|
||||
status = WvmTokenHandler::EncryptAssetKey(
|
||||
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
|
||||
WvmTokenHandler::DES3, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
|
||||
asset_key.clear();
|
||||
status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model,
|
||||
&asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
|
||||
// Test with pass-thru (Cipher=PASS_THRU).
|
||||
asset_key.clear();
|
||||
status = WvmTokenHandler::EncryptAssetKey(
|
||||
HexStringToBytes(kTestDeviceKey1Hex), raw_asset_key,
|
||||
WvmTokenHandler::PASS_THRU, &asset_key);
|
||||
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ(BytesToHexString(raw_asset_key), BytesToHexString(asset_key));
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, FilterOnMakeModel) {
|
||||
// Use all good keys, but only the key for LG:BD572 should work. It works
|
||||
// by setting only that one to DES3. All other AES ppks will encrypt the
|
||||
// asset key incorrectly.
|
||||
std::vector<WvmTokenHandler::PreprovKey> ppks;
|
||||
ppks.push_back(WvmTokenHandler::PreprovKey(
|
||||
kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex),
|
||||
WvmTokenHandler::AES, ""));
|
||||
ppks.push_back(WvmTokenHandler::PreprovKey(
|
||||
kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex),
|
||||
WvmTokenHandler::DES3, "LG:BD572"));
|
||||
ppks.push_back(WvmTokenHandler::PreprovKey(
|
||||
kTestSystemId3Des, HexStringToBytes(kTestPreprovKeyHex),
|
||||
WvmTokenHandler::AES, ""));
|
||||
WvmTokenHandler::SetPreprovKeys(ppks);
|
||||
std::string raw_asset_key = "asset-key-000000";
|
||||
std::string asset_key;
|
||||
// Check 3DES encryption of asset keys
|
||||
Status status = WvmTokenHandler::EncryptAssetKey(
|
||||
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
|
||||
WvmTokenHandler::DES3, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
|
||||
asset_key.clear();
|
||||
std::string make_model;
|
||||
status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model,
|
||||
&asset_key);
|
||||
EXPECT_OK(status);
|
||||
// Should fail because the asset key was encrypted with AES instead of DES3.
|
||||
EXPECT_NE("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
|
||||
// Set the make/model so we find and use the correct ppk.
|
||||
make_model = "LG:BD572";
|
||||
status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken3DesHex), raw_asset_key, make_model,
|
||||
&asset_key);
|
||||
EXPECT_OK(status);
|
||||
// Should work because the asset key was encrypted with DES3.
|
||||
EXPECT_EQ("3693a68bdeba192be0ea279e6c165197", BytesToHexString(asset_key));
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, AncientKeybox) {
|
||||
Status status;
|
||||
std::string device_key;
|
||||
|
||||
std::string v1_token(
|
||||
std::string(kTestPreprovKeyHex).replace(0, 16, "0000000100000001"));
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex),
|
||||
&device_key);
|
||||
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
458
common/x509_cert.cc
Normal file
458
common/x509_cert.cc
Normal file
@@ -0,0 +1,458 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "common/x509_cert.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <cstdint>
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/pkcs7.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "openssl/x509v3.h"
|
||||
#include "common/openssl_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
|
||||
namespace {
|
||||
// Serializes the X509 |certificate| into a PEM-encoded string. Returns true
|
||||
// on success, false otherwise. The caller retains ownership of
|
||||
// |certificate| and |serialized_certificate|. |serialized_certificate| must
|
||||
// not be NULL.
|
||||
bool PemEncodeX509Certificate(const X509& certificate,
|
||||
std::string* serialized_certificate) {
|
||||
CHECK(serialized_certificate) << "serialized_certificate can not be null.";
|
||||
|
||||
ScopedBIO bio(BIO_new(BIO_s_mem()));
|
||||
if (bio == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The const_cast is necessary for the openssl call.
|
||||
PEM_write_bio_X509(bio.get(), const_cast<X509*>(&certificate));
|
||||
int serialized_size = BIO_pending(bio.get());
|
||||
serialized_certificate->resize(serialized_size);
|
||||
if (BIO_read(bio.get(), &(*serialized_certificate)[0], serialized_size) !=
|
||||
serialized_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // anonymous namespace.
|
||||
|
||||
namespace widevine {
|
||||
|
||||
std::unique_ptr<X509Cert> X509Cert::FromOpenSslCert(ScopedX509 certificate) {
|
||||
return std::unique_ptr<X509Cert>(new X509Cert(certificate.release()));
|
||||
}
|
||||
|
||||
X509Cert::X509Cert() : openssl_cert_(NULL) {}
|
||||
|
||||
X509Cert::~X509Cert() {
|
||||
if (openssl_cert_ != NULL) {
|
||||
X509_free(openssl_cert_);
|
||||
}
|
||||
}
|
||||
|
||||
X509Cert::X509Cert(X509* openssl_cert) : openssl_cert_(openssl_cert) {}
|
||||
|
||||
Status X509Cert::LoadPem(const std::string& pem_cert) {
|
||||
if (pem_cert.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT, "Empty PEM certificate");
|
||||
}
|
||||
BIO* bio(NULL);
|
||||
X509* new_cert(NULL);
|
||||
bio = BIO_new_mem_buf(const_cast<char*>(pem_cert.data()), pem_cert.size());
|
||||
if (bio == NULL) {
|
||||
return Status(error::INTERNAL, "BIO allocation failed");
|
||||
}
|
||||
Status status;
|
||||
new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
|
||||
if (new_cert == NULL) {
|
||||
status = Status(error::INVALID_ARGUMENT, "PEM certificate load failed");
|
||||
goto cleanup;
|
||||
}
|
||||
if (openssl_cert_ != NULL) {
|
||||
X509_free(openssl_cert_);
|
||||
}
|
||||
openssl_cert_ = new_cert;
|
||||
|
||||
cleanup:
|
||||
if (bio != NULL) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status X509Cert::LoadDer(const std::string& der_cert) {
|
||||
if (der_cert.empty()) {
|
||||
return Status(error::INVALID_ARGUMENT, "Empty DER certificate");
|
||||
}
|
||||
const unsigned char* cert_data =
|
||||
reinterpret_cast<const unsigned char*>(der_cert.data());
|
||||
X509* new_cert = d2i_X509(NULL, &cert_data, der_cert.size());
|
||||
if (new_cert == NULL) {
|
||||
return Status(error::INVALID_ARGUMENT, "DER certificate load failed");
|
||||
}
|
||||
if (openssl_cert_ != NULL) {
|
||||
X509_free(openssl_cert_);
|
||||
}
|
||||
openssl_cert_ = new_cert;
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
std::string X509Cert::GetPem() const {
|
||||
std::string serialized_certificate;
|
||||
if (!PemEncodeX509Certificate(*openssl_cert_, &serialized_certificate)) {
|
||||
return "";
|
||||
}
|
||||
return serialized_certificate;
|
||||
}
|
||||
|
||||
std::unique_ptr<RsaPublicKey> X509Cert::GetRsaPublicKey() const {
|
||||
ScopedPKEY pkey(X509_get_pubkey(openssl_cert_));
|
||||
return std::unique_ptr<RsaPublicKey>(
|
||||
new RsaPublicKey(EVP_PKEY_get1_RSA(pkey.get())));
|
||||
}
|
||||
|
||||
const std::string& X509Cert::GetSubjectName() {
|
||||
if (subject_name_.empty() && (openssl_cert_ != NULL)) {
|
||||
X509_NAME* subject = X509_get_subject_name(openssl_cert_);
|
||||
if (subject != NULL) {
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
if (bio != NULL) {
|
||||
X509_NAME_print_ex(bio, subject, 0, 0);
|
||||
int size = BIO_pending(bio);
|
||||
std::unique_ptr<char[]> buffer(new char[size]);
|
||||
int bytes_read = BIO_read(bio, buffer.get(), size);
|
||||
if (bytes_read == size) {
|
||||
subject_name_.assign(buffer.get(), bytes_read);
|
||||
}
|
||||
BIO_free(bio);
|
||||
}
|
||||
}
|
||||
}
|
||||
return subject_name_;
|
||||
}
|
||||
|
||||
std::string X509Cert::GetSubjectNameField(const std::string& field) {
|
||||
if (field.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
const std::string& subject = GetSubjectName();
|
||||
size_t start_pos = subject.find(field + "=");
|
||||
if (start_pos == std::string::npos) {
|
||||
return std::string();
|
||||
}
|
||||
start_pos += field.size() + 1;
|
||||
size_t end_pos = subject.find(",", start_pos);
|
||||
if (end_pos == std::string::npos) {
|
||||
end_pos = subject.size();
|
||||
}
|
||||
return subject.substr(start_pos, end_pos - start_pos);
|
||||
}
|
||||
|
||||
std::string X509Cert::GetSerialNumber() const {
|
||||
if (openssl_cert_ == NULL) {
|
||||
return std::string();
|
||||
}
|
||||
BIGNUM* bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(openssl_cert_), NULL);
|
||||
if (bn == NULL) {
|
||||
return std::string();
|
||||
}
|
||||
std::string result;
|
||||
char* openssl_sn = BN_bn2hex(bn);
|
||||
if (openssl_sn != NULL) {
|
||||
result = absl::HexStringToBytes(openssl_sn);
|
||||
OPENSSL_free(openssl_sn);
|
||||
}
|
||||
BN_free(bn);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool X509Cert::GetNotBeforeSeconds(int64_t* valid_start_seconds) const {
|
||||
if (openssl_cert_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return Asn1TimeToEpochSeconds(X509_get0_notBefore(openssl_cert_),
|
||||
valid_start_seconds)
|
||||
.ok();
|
||||
}
|
||||
|
||||
bool X509Cert::GetNotAfterSeconds(int64_t* valid_end_seconds) const {
|
||||
if (openssl_cert_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return Asn1TimeToEpochSeconds(X509_get0_notAfter(openssl_cert_),
|
||||
valid_end_seconds)
|
||||
.ok();
|
||||
}
|
||||
|
||||
bool X509Cert::IsCaCertificate() const {
|
||||
return X509_check_ca(openssl_cert_) != 0;
|
||||
}
|
||||
|
||||
bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const {
|
||||
ScopedAsn1Object extension_name(OBJ_txt2obj(oid.c_str(), 1));
|
||||
int ext_pos = X509_get_ext_by_OBJ(openssl_cert_, extension_name.get(), -1);
|
||||
if (ext_pos < 0) return false;
|
||||
X509_EXTENSION* extension(X509_get_ext(openssl_cert_, ext_pos));
|
||||
if (!extension) return false;
|
||||
ASN1_OCTET_STRING* extension_data(X509_EXTENSION_get_data(extension));
|
||||
if (!extension_data) return false;
|
||||
if ((extension_data->length != 3) || (extension_data->data[0] != 1) ||
|
||||
(extension_data->data[1] != 1))
|
||||
return false;
|
||||
|
||||
*value = extension_data->data[2] != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
Status X509Cert::Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
|
||||
int64_t* epoch_seconds) const {
|
||||
if (asn1_time == nullptr) {
|
||||
// This code is exported to shared source. The exported code does not yet
|
||||
// support MakeStatus.
|
||||
// NOLINTNEXTLINE
|
||||
return Status(error::INVALID_ARGUMENT, "asn1_time cannot be null.");
|
||||
}
|
||||
|
||||
if (epoch_seconds == nullptr) {
|
||||
// NOLINTNEXTLINE
|
||||
return Status(error::INVALID_ARGUMENT, "epoch_seconds cannot be null.");
|
||||
}
|
||||
|
||||
ScopedAsn1Time epoch_time(ASN1_TIME_new());
|
||||
if (!ASN1_TIME_set(epoch_time.get(), 0)) {
|
||||
// NOLINTNEXTLINE
|
||||
return Status(error::INTERNAL, "Failed to set epoch time.");
|
||||
}
|
||||
|
||||
int day = 0;
|
||||
int seconds = 0;
|
||||
if (!ASN1_TIME_diff(&day, &seconds, epoch_time.get(), asn1_time)) {
|
||||
// NOLINTNEXTLINE
|
||||
return Status(error::INTERNAL,
|
||||
"Failed to convert asn1 time to epoch time.");
|
||||
}
|
||||
|
||||
*epoch_seconds = 24L * 3600L * day + seconds;
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
X509CertChain::~X509CertChain() { Reset(); }
|
||||
|
||||
void X509CertChain::Reset() {
|
||||
for (auto certp : cert_chain_) {
|
||||
delete certp;
|
||||
}
|
||||
cert_chain_.clear();
|
||||
}
|
||||
|
||||
Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
||||
static const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----";
|
||||
static const char kEndCertificate[] = "-----END CERTIFICATE-----";
|
||||
|
||||
Reset();
|
||||
size_t begin_pos = pem_cert_chain.find(kBeginCertificate);
|
||||
while (begin_pos != std::string::npos) {
|
||||
size_t end_pos = pem_cert_chain.find(
|
||||
kEndCertificate, begin_pos + sizeof(kBeginCertificate) - 1);
|
||||
if (end_pos != std::string::npos) {
|
||||
end_pos += sizeof(kEndCertificate) - 1;
|
||||
std::unique_ptr<X509Cert> new_cert(new X509Cert);
|
||||
Status status = new_cert->LoadPem(
|
||||
pem_cert_chain.substr(begin_pos, end_pos - begin_pos));
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
cert_chain_.push_back(new_cert.release());
|
||||
begin_pos = pem_cert_chain.find(kBeginCertificate, end_pos);
|
||||
}
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) {
|
||||
ScopedX509Stack cert_stack(sk_X509_new_null());
|
||||
CBS cbs;
|
||||
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pk7_cert_chain.data()),
|
||||
pk7_cert_chain.size());
|
||||
if (!PKCS7_get_certificates(cert_stack.get(), &cbs)) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Unable to load PKCS#7 certificate chain");
|
||||
}
|
||||
|
||||
while (sk_X509_num(cert_stack.get()) > 0) {
|
||||
cert_chain_.insert(cert_chain_.begin(),
|
||||
new X509Cert(sk_X509_pop(cert_stack.get())));
|
||||
}
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
std::string X509CertChain::GetPkcs7() {
|
||||
std::string pkcs7_cert;
|
||||
ScopedX509Stack cert_stack(sk_X509_new_null());
|
||||
for (X509Cert* cert : cert_chain_) {
|
||||
// X509 stack takes ownership of certificates. Copy certificates to retain
|
||||
// |cert_chain_|.
|
||||
X509Cert cert_copy;
|
||||
if (!cert_copy.LoadPem(cert->GetPem()).ok()) {
|
||||
LOG(WARNING) << "Certificate chain serialization failed";
|
||||
return "";
|
||||
}
|
||||
X509* openssl_cert_copy = const_cast<X509*>(cert_copy.openssl_cert());
|
||||
cert_copy.openssl_cert_ = nullptr;
|
||||
sk_X509_push(cert_stack.get(), openssl_cert_copy);
|
||||
}
|
||||
ScopedPKCS7 pkcs7(
|
||||
PKCS7_sign(nullptr, nullptr, cert_stack.get(), nullptr, PKCS7_DETACHED));
|
||||
if (!pkcs7) {
|
||||
LOG(WARNING) << "Could not convert certificate chain to PKCS7";
|
||||
return "";
|
||||
}
|
||||
ScopedBIO bio(BIO_new(BIO_s_mem()));
|
||||
if (bio.get() == nullptr || !i2d_PKCS7_bio(bio.get(), pkcs7.get())) {
|
||||
LOG(WARNING) << "Failed writing PKCS7 to bio";
|
||||
return "";
|
||||
}
|
||||
int cert_size = BIO_pending(bio.get());
|
||||
pkcs7_cert.resize(cert_size);
|
||||
if (BIO_read(bio.get(), &pkcs7_cert[0], cert_size) != cert_size) {
|
||||
LOG(WARNING) << "BIO_read failure";
|
||||
return "";
|
||||
}
|
||||
return pkcs7_cert;
|
||||
}
|
||||
|
||||
X509Cert* X509CertChain::GetCert(size_t cert_index) const {
|
||||
if (cert_index >= cert_chain_.size()) {
|
||||
return NULL;
|
||||
}
|
||||
return cert_chain_[cert_index];
|
||||
}
|
||||
|
||||
X509CA::X509CA(X509Cert* ca_cert) : ca_cert_(ca_cert), openssl_store_(NULL) {}
|
||||
|
||||
X509CA::~X509CA() {
|
||||
if (openssl_store_ != NULL) {
|
||||
X509_STORE_free(openssl_store_);
|
||||
}
|
||||
}
|
||||
|
||||
Status X509CA::InitializeStore() {
|
||||
absl::WriterMutexLock lock(&openssl_store_mutex_);
|
||||
if (openssl_store_ == NULL) {
|
||||
if (ca_cert_ == NULL) {
|
||||
return Status(error::INTERNAL, "CA X.509Cert is NULL");
|
||||
}
|
||||
openssl_store_ = X509_STORE_new();
|
||||
if (openssl_store_ == NULL) {
|
||||
return Status(error::INTERNAL, "Failed to allocate X.509 store");
|
||||
}
|
||||
if (X509_STORE_add_cert(openssl_store_,
|
||||
const_cast<X509*>(ca_cert_->openssl_cert())) == 0) {
|
||||
X509_STORE_free(openssl_store_);
|
||||
openssl_store_ = NULL;
|
||||
|
||||
return Status(error::INTERNAL,
|
||||
"Failed to add X.509 CA certificate to store");
|
||||
}
|
||||
}
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status X509CA::VerifyCert(const X509Cert& cert) {
|
||||
return OpenSslX509Verify(cert.openssl_cert(), nullptr);
|
||||
}
|
||||
|
||||
Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) {
|
||||
if (cert_chain.GetNumCerts() < 1) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Cannot verify empty certificate chain");
|
||||
}
|
||||
|
||||
ScopedX509StackOnly intermediates(sk_X509_new_null());
|
||||
if (!intermediates) {
|
||||
return Status(error::INTERNAL,
|
||||
"Failed to allocate X.509 intermediate certificate stack");
|
||||
}
|
||||
const X509Cert* leaf_cert(nullptr);
|
||||
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
|
||||
if (cert_chain.GetCert(idx)->IsCaCertificate()) {
|
||||
sk_X509_push(intermediates.get(),
|
||||
const_cast<X509*>(cert_chain.GetCert(idx)->openssl_cert()));
|
||||
} else {
|
||||
leaf_cert = cert_chain.GetCert(idx);
|
||||
}
|
||||
}
|
||||
if (!leaf_cert) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"X.509 certificate chain without leaf certificate.");
|
||||
}
|
||||
return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get());
|
||||
}
|
||||
|
||||
Status X509CA::VerifyCertWithChain(const X509Cert& cert,
|
||||
const X509CertChain& cert_chain) {
|
||||
ScopedX509StackOnly intermediates(sk_X509_new_null());
|
||||
if (!intermediates) {
|
||||
// MakeStatus is now preferred. But we don't support it in the exported
|
||||
// version, yet. So, ignore lint here.
|
||||
// NOLINTNEXTLINE
|
||||
return Status(error::INTERNAL,
|
||||
"Failed to allocate X.509 intermediate certificate stack");
|
||||
}
|
||||
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
|
||||
sk_X509_push(intermediates.get(),
|
||||
const_cast<X509*>(cert_chain.GetCert(idx)->openssl_cert()));
|
||||
}
|
||||
|
||||
return OpenSslX509Verify(cert.openssl_cert(), intermediates.get());
|
||||
}
|
||||
|
||||
Status X509CA::OpenSslX509Verify(const X509* cert,
|
||||
STACK_OF(X509) * intermediates) {
|
||||
DCHECK(cert);
|
||||
|
||||
absl::ReaderMutexLock lock(&openssl_store_mutex_);
|
||||
if (openssl_store_ == NULL) {
|
||||
openssl_store_mutex_.ReaderUnlock();
|
||||
Status status = InitializeStore();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
openssl_store_mutex_.ReaderLock();
|
||||
}
|
||||
ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new());
|
||||
if (!store_ctx) {
|
||||
return Status(error::INTERNAL, "Failed to allocate X.509 store context");
|
||||
}
|
||||
if (X509_STORE_CTX_init(store_ctx.get(), openssl_store_,
|
||||
const_cast<X509*>(cert), intermediates) == 0) {
|
||||
return Status(error::INTERNAL, "Failed to initialize X.509 store context");
|
||||
}
|
||||
int x509_status = X509_verify_cert(store_ctx.get());
|
||||
if (x509_status != 1) {
|
||||
return Status(error::INTERNAL,
|
||||
std::string("X.509 certificate chain validation failed: ") +
|
||||
X509_verify_cert_error_string(
|
||||
X509_STORE_CTX_get_error(store_ctx.get())));
|
||||
}
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
176
common/x509_cert.h
Normal file
176
common/x509_cert.h
Normal file
@@ -0,0 +1,176 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Description:
|
||||
// X.509 certificate classes used by the license server SDK.
|
||||
|
||||
#ifndef COMMON_X509_CERT_H_
|
||||
#define COMMON_X509_CERT_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/thread_annotations.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "openssl/x509v3.h"
|
||||
#include "common/openssl_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// NOTE: All Status codes are in the canonical error space.
|
||||
|
||||
// Class which holds a single X.509 certificates.
|
||||
class X509Cert {
|
||||
public:
|
||||
// Load the certificate from an openssl X509 certificate instance.
|
||||
static std::unique_ptr<X509Cert> FromOpenSslCert(ScopedX509 openssl_cert_);
|
||||
|
||||
X509Cert();
|
||||
virtual ~X509Cert();
|
||||
|
||||
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
||||
// a PEM-encoded certificate.
|
||||
Status LoadPem(const std::string& pem_cert);
|
||||
|
||||
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
||||
// a DER-encoded certificate.
|
||||
Status LoadDer(const std::string& der_cert);
|
||||
|
||||
// Return a std::string containing the PEM-encoded certificate.
|
||||
std::string GetPem() const;
|
||||
|
||||
// Returns certificate RSA public key. nullptr if not found, or if key type
|
||||
// is not RSA.
|
||||
std::unique_ptr<RsaPublicKey> GetRsaPublicKey() const;
|
||||
|
||||
// Returns the internal OpenSSL X509 certificate.
|
||||
const X509* openssl_cert() const { return openssl_cert_; }
|
||||
|
||||
// Returns the certificate subject name.
|
||||
const std::string& GetSubjectName();
|
||||
|
||||
// Returns a field within the certificate subject name, or an empty std::string
|
||||
// if the field is not found.
|
||||
std::string GetSubjectNameField(const std::string& field);
|
||||
|
||||
// Returns the certificate serial number, binary encoded, or an empty std::string
|
||||
// if an error occurs.
|
||||
std::string GetSerialNumber() const;
|
||||
|
||||
// Gets the start of the validity period for the certificate in seconds
|
||||
// since the epoch. |valid_start_seconds| must not be null. Returns true on
|
||||
// success, false otherwise.
|
||||
bool GetNotBeforeSeconds(int64_t* valid_start_seconds) const;
|
||||
|
||||
// Gets the end of the validity period for the certificate in seconds
|
||||
// since the epoch. |valid_end_seconds| must not be null. Returns true on
|
||||
// success, false otherwise.
|
||||
bool GetNotAfterSeconds(int64_t* valid_end_seconds) const;
|
||||
|
||||
// Returns true if the certificate is a CA (root or intermediate) certificate.
|
||||
bool IsCaCertificate() const;
|
||||
|
||||
// Gets the value of an X.509 V3 extension encoded as a boolean. |oid| is the
|
||||
// object identifier sequence for the extension, and |value| is a pointer to
|
||||
// an integer which will contain the extension value upon successful return.
|
||||
// Returns true if successful, or false otherwise.
|
||||
bool GetV3BooleanExtension(const std::string& oid, bool* value) const;
|
||||
|
||||
private:
|
||||
explicit X509Cert(X509* openssl_cert);
|
||||
Status Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
|
||||
int64_t* epoch_seconds) const;
|
||||
|
||||
X509* openssl_cert_;
|
||||
std::string subject_name_;
|
||||
|
||||
friend class X509CertChain;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(X509Cert);
|
||||
};
|
||||
|
||||
// Class which holds a chain of X.509 certificates.
|
||||
class X509CertChain {
|
||||
public:
|
||||
X509CertChain() {}
|
||||
virtual ~X509CertChain();
|
||||
|
||||
// Loads a chain of PEM-encoded X.509 certificates. Takes a single parameter,
|
||||
// |pem_cert_chain|, which is the concatenation of a number of PEM X.509
|
||||
// certificates, beginning with the leaf certificate, and ending with the
|
||||
// certificate signed by the root CA.
|
||||
Status LoadPem(const std::string& pem_cert_chain);
|
||||
|
||||
// Loads a chain of DER-encoded PKCS#7 certificates. Takes a single parameter,
|
||||
// |pk7_cert_chain|, which is a DER-encoded PKCS#7 X.509 certificate
|
||||
// container.
|
||||
Status LoadPkcs7(const std::string& pk7_cert_chain);
|
||||
|
||||
// Writes the |cert_chain_| to a DER-encoded PKCS#7 X.509 cryptographic
|
||||
// message. The final message does not include signed data.
|
||||
std::string GetPkcs7();
|
||||
|
||||
// Returns the number of certificates in the chain.
|
||||
size_t GetNumCerts() const { return cert_chain_.size(); }
|
||||
|
||||
// Returns the X509Cert at the specified chain index (0 start). Returns
|
||||
// NULL if cert_index is out of range. The X509CertChain retains ownwership
|
||||
// of the object returned.
|
||||
X509Cert* GetCert(size_t cert_index) const;
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
|
||||
std::vector<X509Cert*> cert_chain_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(X509CertChain);
|
||||
};
|
||||
|
||||
// CA class which holds the root CA cert, and verifies certificate chains.
|
||||
class X509CA {
|
||||
public:
|
||||
// New object assumes ownership of |ca_cert|.
|
||||
explicit X509CA(X509Cert* ca_cert);
|
||||
virtual ~X509CA();
|
||||
|
||||
// Does X.509 PKI validation of |cert| against the root CA certificate
|
||||
// used when constructing X509CA. This method is thread-safe.
|
||||
Status VerifyCert(const X509Cert& cert);
|
||||
|
||||
// Does X.509 PKI validation of |cert_chain| against the root CA certificate
|
||||
// used when constructing X509CA. This method is thread-safe.
|
||||
Status VerifyCertChain(const X509CertChain& cert_chain);
|
||||
|
||||
// Does X.509 PKI validation of |cert| using the |cert_chain|
|
||||
// certificates. This method allows |cert| to be an ICA. This method is
|
||||
// thread-safe.
|
||||
Status VerifyCertWithChain(const X509Cert& cert,
|
||||
const X509CertChain& cert_chain);
|
||||
|
||||
private:
|
||||
Status InitializeStore();
|
||||
Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * intermediates);
|
||||
|
||||
std::unique_ptr<X509Cert> ca_cert_;
|
||||
absl::Mutex openssl_store_mutex_;
|
||||
X509_STORE* openssl_store_ GUARDED_BY(openssl_store_mutex_);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(X509CA);
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_X509_CERT_H_
|
||||
531
common/x509_cert_test.cc
Normal file
531
common/x509_cert_test.cc
Normal file
@@ -0,0 +1,531 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/test_utils.h"
|
||||
#include "common/x509_cert.h"
|
||||
|
||||
namespace widevine {
|
||||
const char kTestRootCaDerCert[] =
|
||||
"30820403308202eba003020102020900a24f94af7ae6831f300d06092a86"
|
||||
"4886f70d0101050500308197310b30090603550406130255533113301106"
|
||||
"035504080c0a57617368696e67746f6e3111300f06035504070c084b6972"
|
||||
"6b6c616e6431133011060355040a0c0a476f6f676c6520496e633111300f"
|
||||
"060355040b0c085769646576696e653115301306035504030c0c54657374"
|
||||
"20526f6f742043413121301f06092a864886f70d010901161274696e736b"
|
||||
"697040676f6f676c652e636f6d301e170d3133303831363030353731305a"
|
||||
"170d3333303831353030353731305a308197310b30090603550406130255"
|
||||
"533113301106035504080c0a57617368696e67746f6e3111300f06035504"
|
||||
"070c084b69726b6c616e6431133011060355040a0c0a476f6f676c652049"
|
||||
"6e633111300f060355040b0c085769646576696e65311530130603550403"
|
||||
"0c0c5465737420526f6f742043413121301f06092a864886f70d01090116"
|
||||
"1274696e736b697040676f6f676c652e636f6d30820122300d06092a8648"
|
||||
"86f70d01010105000382010f003082010a0282010100c6eee629d99f7736"
|
||||
"2db5545ed1d6dfb3616c742c617d5fd48f2fbfcb3f2ec40a080bd04d551c"
|
||||
"e519471a8bb4ec5c2c75bf8a2d2caf3f85d90e9e39391dfbdaae68051319"
|
||||
"0da71b1b2ae4829a15c44bc1b19b17134844b94c6f06d9216333236574f3"
|
||||
"f11b0d10c3c621410e42630c57ce9e901057eda5c3c2203ee2ad805a0d93"
|
||||
"52fa91da45a6f4875b4524c193c42fd9048a10204e5b2c8203402ba760e7"
|
||||
"e1b4126c3e2ab4258f2bf28cd3170de8c738a6a1f4cfcc0649fa95f1414f"
|
||||
"d9d09dd4f511bc0a9bf3a5844a334d9e0a4b9525d2789be6abafe2d0cc20"
|
||||
"79dcf030ffa9be8ae3fe2cab4ebdfa494d48aa8c63264d31e2208a9c28f7"
|
||||
"3e0103ce164683bf0203010001a350304e301d0603551d0e041604144d30"
|
||||
"ff181ac4f10da99e6a12c01e02accadf840a301f0603551d230418301680"
|
||||
"144d30ff181ac4f10da99e6a12c01e02accadf840a300c0603551d130405"
|
||||
"30030101ff300d06092a864886f70d01010505000382010100779e9b98d3"
|
||||
"ec066f29862903a00e9c98259d987c04b9e6a2e6c3381ee59ec1dd0d7dee"
|
||||
"79da612e4dfaa3465c8916993ed7adebb27340de20ca101067f8342b2124"
|
||||
"ec0d5db531277b4653c3bc72b2a8daeae120e5348e1a338f6e68e7129436"
|
||||
"026e78024f04d766b132252ec152402dcec28174346aa0ba997d7f1af140"
|
||||
"ff025bec841f8039ba10d7cc098cf24554f8cbb2aa31875205c67df2f053"
|
||||
"0d8784faf63c4f945e62da374cad6155e6ae44f597bcff4566ea2aac4258"
|
||||
"e4ae81569c0eddd1df6929532b4538bd204b2ff5847cb46ac7383c96fe82"
|
||||
"d22de9a13c5092c92c297021c51a2a0a5250cf26c271ff262f25a7738ae4"
|
||||
"c270d87191c13aefdd177b";
|
||||
|
||||
const char kTestRootCaPemCert[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIEAzCCAuugAwIBAgIJAKJPlK965oMfMA0GCSqGSIb3DQEBBQUAMIGXMQswCQYD\n"
|
||||
"VQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQx\n"
|
||||
"EzARBgNVBAoMCkdvb2dsZSBJbmMxETAPBgNVBAsMCFdpZGV2aW5lMRUwEwYDVQQD\n"
|
||||
"DAxUZXN0IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNv\n"
|
||||
"bTAeFw0xMzA4MTYwMDU3MTBaFw0zMzA4MTUwMDU3MTBaMIGXMQswCQYDVQQGEwJV\n"
|
||||
"UzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxEzARBgNV\n"
|
||||
"BAoMCkdvb2dsZSBJbmMxETAPBgNVBAsMCFdpZGV2aW5lMRUwEwYDVQQDDAxUZXN0\n"
|
||||
"IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n"
|
||||
"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbu5inZn3c2LbVUXtHW37NhbHQs\n"
|
||||
"YX1f1I8vv8s/LsQKCAvQTVUc5RlHGou07Fwsdb+KLSyvP4XZDp45OR372q5oBRMZ\n"
|
||||
"DacbGyrkgpoVxEvBsZsXE0hEuUxvBtkhYzMjZXTz8RsNEMPGIUEOQmMMV86ekBBX\n"
|
||||
"7aXDwiA+4q2AWg2TUvqR2kWm9IdbRSTBk8Qv2QSKECBOWyyCA0Arp2Dn4bQSbD4q\n"
|
||||
"tCWPK/KM0xcN6Mc4pqH0z8wGSfqV8UFP2dCd1PURvAqb86WESjNNngpLlSXSeJvm\n"
|
||||
"q6/i0MwgedzwMP+pvorj/iyrTr36SU1IqoxjJk0x4iCKnCj3PgEDzhZGg78CAwEA\n"
|
||||
"AaNQME4wHQYDVR0OBBYEFE0w/xgaxPENqZ5qEsAeAqzK34QKMB8GA1UdIwQYMBaA\n"
|
||||
"FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n"
|
||||
"BQADggEBAHeem5jT7AZvKYYpA6AOnJglnZh8BLnmoubDOB7lnsHdDX3uedphLk36\n"
|
||||
"o0ZciRaZPtet67JzQN4gyhAQZ/g0KyEk7A1dtTEne0ZTw7xysqja6uEg5TSOGjOP\n"
|
||||
"bmjnEpQ2Am54Ak8E12axMiUuwVJALc7CgXQ0aqC6mX1/GvFA/wJb7IQfgDm6ENfM\n"
|
||||
"CYzyRVT4y7KqMYdSBcZ98vBTDYeE+vY8T5ReYto3TK1hVeauRPWXvP9FZuoqrEJY\n"
|
||||
"5K6BVpwO3dHfaSlTK0U4vSBLL/WEfLRqxzg8lv6C0i3poTxQksksKXAhxRoqClJQ\n"
|
||||
"zybCcf8mLyWnc4rkwnDYcZHBOu/dF3s=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
const char kTestPemCert[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n"
|
||||
"VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n"
|
||||
"Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n"
|
||||
"ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n"
|
||||
"MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n"
|
||||
"BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n"
|
||||
"aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n"
|
||||
"Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n"
|
||||
"aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n"
|
||||
"DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n"
|
||||
"l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n"
|
||||
"krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n"
|
||||
"wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n"
|
||||
"fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n"
|
||||
"FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n"
|
||||
"qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n"
|
||||
"EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n"
|
||||
"f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n"
|
||||
"QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n"
|
||||
"7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n"
|
||||
"3rDeUI+Odg==\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
const char kTestPemCertSubjectField_C[] = "US";
|
||||
const char kTestPemCertSubjectField_CN[] =
|
||||
"stable id/emailAddress=tinskip@google.com";
|
||||
const char kTestPemCertSerialNumber[] = "\002";
|
||||
|
||||
const int64_t kTestPemCertNotBeforeSeconds = 1376689440;
|
||||
const int64_t kTestPemCertNotAfterSeconds = 2007755040;
|
||||
|
||||
const char kTestPemCertChain[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIDwzCCAqsCAQIwDQYJKoZIhvcNAQEFBQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYD\n"
|
||||
"VQQIDApXYXNoaW5ndG9uMREwDwYDVQQHDAhLaXJrbGFuZDETMBEGA1UECgwKR29v\n"
|
||||
"Z2xlIEluYzERMA8GA1UECwwIV2lkZXZpbmUxHTAbBgNVBAMMFFRlc3QgSW50ZXJt\n"
|
||||
"ZWRpYXRlIENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcN\n"
|
||||
"MTMwODE2MjE0NDAwWhcNMzMwODE1MjE0NDAwWjCBrjELMAkGA1UEBhMCVVMxEzAR\n"
|
||||
"BgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMSkwJwYDVQQKDCBD\n"
|
||||
"aHJvbWUgRGV2aWNlIENvbnRlbnQgUHJvdGVjdGlvbjEVMBMGA1UECwwMdGVzdGlu\n"
|
||||
"Zy50ZXN0MRIwEAYDVQQDDAlzdGFibGUgaWQxITAfBgkqhkiG9w0BCQEWEnRpbnNr\n"
|
||||
"aXBAZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlb\n"
|
||||
"DqstOK0TlLtJZOGzysjD48ZXEnpwti0cQAK6JcN9htwpHemBlzAbIuOIjeY2tfvk\n"
|
||||
"l2uIOOnNMgAiKs/Dpu9VbedXAVCnuxE7/yrWIw/rg1ZmqdxQXFqTo+52ErteMru4\n"
|
||||
"krOaNgQ63SE934yR0MSFzuSbvTgTFLP7hHueaeg8+CUvQRU0WoC2akMXzY1G6AkV\n"
|
||||
"wyY/lufA/XEQXgPbhvP67YxR+exwCfzQGolB5hkliKux0rmzDfcIiHMM0IDaE6nu\n"
|
||||
"fbm8BKPxlZS/QrzTZAr9Q5GMyjcu0XTI1fknGVrE4pZMh8ge+ondcgIQxXBOhfJK\n"
|
||||
"FCofYSP7rBxtasK+4ncCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEATcNfaLpfLbX6\n"
|
||||
"qz1qKMLYaNe4OI0X8t8ZNXqEdqyNd4C7kSdaQkwNunVAqw1CadUzLRi8Of18cwlQ\n"
|
||||
"EXBN4bPTeODCobPjS71YcYPhDsvGQcQ3GQC6BOyHKCTYpqgcIIPEGFzI+FrACede\n"
|
||||
"f4tyIexq63iIx1IpmTBnpYnnfgc8v4anphNODHKMRBHy8BJRcKpTFFFo571c5OjE\n"
|
||||
"QjhKEOp9eD72GuEgtK0f7jXYH2bRT4lmSLxg2L1jbwg3qIjoX2gjeILyzUF+FTzO\n"
|
||||
"7G5JWQnyDjd/ZJuld7FRsJmuzAgISeqVeraYXU1p4utbqutATmmHBcYhkXJKBKkf\n"
|
||||
"3rDeUI+Odg==\n"
|
||||
"-----END CERTIFICATE-----\n"
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n"
|
||||
"EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n"
|
||||
"DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n"
|
||||
"b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n"
|
||||
"ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n"
|
||||
"BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n"
|
||||
"bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n"
|
||||
"ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n"
|
||||
"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n"
|
||||
"f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n"
|
||||
"oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n"
|
||||
"UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n"
|
||||
"gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n"
|
||||
"9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n"
|
||||
"AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n"
|
||||
"FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n"
|
||||
"BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n"
|
||||
"xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n"
|
||||
"HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n"
|
||||
"ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n"
|
||||
"qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n"
|
||||
"6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
const char kTestPemIca[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIEAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMx\n"
|
||||
"EzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQK\n"
|
||||
"DApHb29nbGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEVMBMGA1UEAwwMVGVzdCBS\n"
|
||||
"b290IENBMSEwHwYJKoZIhvcNAQkBFhJ0aW5za2lwQGdvb2dsZS5jb20wHhcNMTMw\n"
|
||||
"ODE2MjE0MTQ2WhcNMzMwODE1MjE0MTQ2WjCBnzELMAkGA1UEBhMCVVMxEzARBgNV\n"
|
||||
"BAgMCldhc2hpbmd0b24xETAPBgNVBAcMCEtpcmtsYW5kMRMwEQYDVQQKDApHb29n\n"
|
||||
"bGUgSW5jMREwDwYDVQQLDAhXaWRldmluZTEdMBsGA1UEAwwUVGVzdCBJbnRlcm1l\n"
|
||||
"ZGlhdGUgQ0ExITAfBgkqhkiG9w0BCQEWEnRpbnNraXBAZ29vZ2xlLmNvbTCCASIw\n"
|
||||
"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANooBi6x3I9Incs6ytlPjBu7yEy5\n"
|
||||
"f6BLf5NREE5nQm74Rt7PAA7YVDtxHP+pi1uyxsL3fUrx904s4tdXNRK85/2zn7+o\n"
|
||||
"oZPYb8fH6dgl7ocmYeyC0jSmg7++ZiaS6OsjPSUTE2aEbAe6Q+ZhYsAbdkL7Z2dN\n"
|
||||
"UJR9akhLEqlqfX4q5bWA0M3P/2/fqNYMS0w010Nwpd+KydbceT0rHQTmTGVsqCCL\n"
|
||||
"gmaP9a8aQRMSP0dn5IOcc/K1Qnnfw1gxnjGF4aBP7KbCMxNBrbgBOwiTxgEMIcKZ\n"
|
||||
"9IGszAcpftKX5ra3XePzFWCcnwilppaaE/2XWXkcAehc8d3xtkdAYZyVIBUCAwEA\n"
|
||||
"AaNQME4wHQYDVR0OBBYEFDm35gzM6ll13HhZUbW5uDw7BieTMB8GA1UdIwQYMBaA\n"
|
||||
"FE0w/xgaxPENqZ5qEsAeAqzK34QKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF\n"
|
||||
"BQADggEBALj+/Z8ygfWVNncV0N9UsAcwlGUe5ME+VoXUF/0SOmdrc8LtPc2Dkc8b\n"
|
||||
"xiQN1wHxE/OFsbsOdobPzwOBh67KyYyVWtxzzsLO0MHGxsbOmwa1AersoP4x8xoC\n"
|
||||
"HaBU90cviYqz5k6rZyBIlFIrM5lqG1JB3U0kTceG/1sqwRAAu94BYqMW1iWyr9Mq\n"
|
||||
"ASRCVBOrksWda4pZkCLp62vk7ItOcs2PrHf6UWbANTDH+8Q+pIw2wuJ5lf/imqKO\n"
|
||||
"qrYCJmAi6VBa2jyHqXVPMk6lL1Rmdk4UgOsRvsbmKzb2vYeWIwhsXY5Spo3WVTLv\n"
|
||||
"6kIkGZCFP/ws7ctk+fQyjjttncIdL2k=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
const char kTestPk7CertChain[] =
|
||||
"308207fb06092a864886f70d010702a08207ec308207e80201013100300b"
|
||||
"06092a864886f70d010701a08207ce308203c3308202ab020102300d0609"
|
||||
"2a864886f70d010105050030819f310b3009060355040613025553311330"
|
||||
"1106035504080c0a57617368696e67746f6e3111300f06035504070c084b"
|
||||
"69726b6c616e6431133011060355040a0c0a476f6f676c6520496e633111"
|
||||
"300f060355040b0c085769646576696e65311d301b06035504030c145465"
|
||||
"737420496e7465726d6564696174652043413121301f06092a864886f70d"
|
||||
"010901161274696e736b697040676f6f676c652e636f6d301e170d313330"
|
||||
"3831363231343430305a170d3333303831353231343430305a3081ae310b"
|
||||
"30090603550406130255533113301106035504080c0a57617368696e6774"
|
||||
"6f6e3111300f06035504070c084b69726b6c616e6431293027060355040a"
|
||||
"0c204368726f6d652044657669636520436f6e74656e742050726f746563"
|
||||
"74696f6e31153013060355040b0c0c74657374696e672e74657374311230"
|
||||
"1006035504030c09737461626c652069643121301f06092a864886f70d01"
|
||||
"0901161274696e736b697040676f6f676c652e636f6d30820122300d0609"
|
||||
"2a864886f70d01010105000382010f003082010a0282010100a95b0eab2d"
|
||||
"38ad1394bb4964e1b3cac8c3e3c657127a70b62d1c4002ba25c37d86dc29"
|
||||
"1de98197301b22e3888de636b5fbe4976b8838e9cd3200222acfc3a6ef55"
|
||||
"6de7570150a7bb113bff2ad6230feb835666a9dc505c5a93a3ee7612bb5e"
|
||||
"32bbb892b39a36043add213ddf8c91d0c485cee49bbd381314b3fb847b9e"
|
||||
"69e83cf8252f4115345a80b66a4317cd8d46e80915c3263f96e7c0fd7110"
|
||||
"5e03db86f3faed8c51f9ec7009fcd01a8941e6192588abb1d2b9b30df708"
|
||||
"88730cd080da13a9ee7db9bc04a3f19594bf42bcd3640afd43918cca372e"
|
||||
"d174c8d5f927195ac4e2964c87c81efa89dd720210c5704e85f24a142a1f"
|
||||
"6123fbac1c6d6ac2bee2770203010001300d06092a864886f70d01010505"
|
||||
"0003820101004dc35f68ba5f2db5faab3d6a28c2d868d7b8388d17f2df19"
|
||||
"357a8476ac8d7780bb91275a424c0dba7540ab0d4269d5332d18bc39fd7c"
|
||||
"73095011704de1b3d378e0c2a1b3e34bbd587183e10ecbc641c4371900ba"
|
||||
"04ec872824d8a6a81c2083c4185cc8f85ac009e75e7f8b7221ec6aeb7888"
|
||||
"c75229993067a589e77e073cbf86a7a6134e0c728c4411f2f0125170aa53"
|
||||
"145168e7bd5ce4e8c442384a10ea7d783ef61ae120b4ad1fee35d81f66d1"
|
||||
"4f896648bc60d8bd636f0837a888e85f68237882f2cd417e153cceec6e49"
|
||||
"5909f20e377f649ba577b151b099aecc080849ea957ab6985d4d69e2eb5b"
|
||||
"aaeb404e698705c62191724a04a91fdeb0de508f8e7630820403308202eb"
|
||||
"a003020102020101300d06092a864886f70d0101050500308197310b3009"
|
||||
"0603550406130255533113301106035504080c0a57617368696e67746f6e"
|
||||
"3111300f06035504070c084b69726b6c616e6431133011060355040a0c0a"
|
||||
"476f6f676c6520496e633111300f060355040b0c085769646576696e6531"
|
||||
"15301306035504030c0c5465737420526f6f742043413121301f06092a86"
|
||||
"4886f70d010901161274696e736b697040676f6f676c652e636f6d301e17"
|
||||
"0d3133303831363231343134365a170d3333303831353231343134365a30"
|
||||
"819f310b30090603550406130255533113301106035504080c0a57617368"
|
||||
"696e67746f6e3111300f06035504070c084b69726b6c616e643113301106"
|
||||
"0355040a0c0a476f6f676c6520496e633111300f060355040b0c08576964"
|
||||
"6576696e65311d301b06035504030c145465737420496e7465726d656469"
|
||||
"6174652043413121301f06092a864886f70d010901161274696e736b6970"
|
||||
"40676f6f676c652e636f6d30820122300d06092a864886f70d0101010500"
|
||||
"0382010f003082010a0282010100da28062eb1dc8f489dcb3acad94f8c1b"
|
||||
"bbc84cb97fa04b7f9351104e67426ef846decf000ed8543b711cffa98b5b"
|
||||
"b2c6c2f77d4af1f74e2ce2d7573512bce7fdb39fbfa8a193d86fc7c7e9d8"
|
||||
"25ee872661ec82d234a683bfbe662692e8eb233d25131366846c07ba43e6"
|
||||
"6162c01b7642fb67674d50947d6a484b12a96a7d7e2ae5b580d0cdcfff6f"
|
||||
"dfa8d60c4b4c34d74370a5df8ac9d6dc793d2b1d04e64c656ca8208b8266"
|
||||
"8ff5af1a4113123f4767e4839c73f2b54279dfc358319e3185e1a04feca6"
|
||||
"c2331341adb8013b0893c6010c21c299f481accc07297ed297e6b6b75de3"
|
||||
"f315609c9f08a5a6969a13fd9759791c01e85cf1ddf1b64740619c952015"
|
||||
"0203010001a350304e301d0603551d0e0416041439b7e60cccea5975dc78"
|
||||
"5951b5b9b83c3b062793301f0603551d230418301680144d30ff181ac4f1"
|
||||
"0da99e6a12c01e02accadf840a300c0603551d13040530030101ff300d06"
|
||||
"092a864886f70d01010505000382010100b8fefd9f3281f595367715d0df"
|
||||
"54b0073094651ee4c13e5685d417fd123a676b73c2ed3dcd8391cf1bc624"
|
||||
"0dd701f113f385b1bb0e7686cfcf038187aecac98c955adc73cec2ced0c1"
|
||||
"c6c6c6ce9b06b501eaeca0fe31f31a021da054f7472f898ab3e64eab6720"
|
||||
"4894522b33996a1b5241dd4d244dc786ff5b2ac11000bbde0162a316d625"
|
||||
"b2afd32a0124425413ab92c59d6b8a599022e9eb6be4ec8b4e72cd8fac77"
|
||||
"fa5166c03530c7fbc43ea48c36c2e27995ffe29aa28eaab602266022e950"
|
||||
"5ada3c87a9754f324ea52f5466764e1480eb11bec6e62b36f6bd87962308"
|
||||
"6c5d8e52a68dd65532efea42241990853ffc2cedcb64f9f4328e3b6d9dc2"
|
||||
"1d2f69a1003100";
|
||||
|
||||
const char kTestCertPrivateKey[] =
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"MIIEowIBAAKCAQEAqVsOqy04rROUu0lk4bPKyMPjxlcSenC2LRxAArolw32G3Ckd\n"
|
||||
"6YGXMBsi44iN5ja1++SXa4g46c0yACIqz8Om71Vt51cBUKe7ETv/KtYjD+uDVmap\n"
|
||||
"3FBcWpOj7nYSu14yu7iSs5o2BDrdIT3fjJHQxIXO5Ju9OBMUs/uEe55p6Dz4JS9B\n"
|
||||
"FTRagLZqQxfNjUboCRXDJj+W58D9cRBeA9uG8/rtjFH57HAJ/NAaiUHmGSWIq7HS\n"
|
||||
"ubMN9wiIcwzQgNoTqe59ubwEo/GVlL9CvNNkCv1DkYzKNy7RdMjV+ScZWsTilkyH\n"
|
||||
"yB76id1yAhDFcE6F8koUKh9hI/usHG1qwr7idwIDAQABAoIBADdwlZa30QvnkxLU\n"
|
||||
"be/s+X9LkS8GpgfrCdgunU3HPkGGwDUmSKJ+R835tCwkMb+hPWXeaStMhsUS5UFh\n"
|
||||
"7f3hoK5MmxPWSZnrrrNvnpKZUxUNFgucxBJZREJqfom7oVow9g6511xwKSqtUmJl\n"
|
||||
"bN8JhPwwiZAQ45qNtINO3QnSy/y4IGrUPgjMpmJa26a+JhduTRq+LMPu2wz+HxS1\n"
|
||||
"Vf2q0H1IOJr/kimMFMaBRYErNclFa8VIFjwjz5reH5lJyptajGhruor6EK1qqhNc\n"
|
||||
"zPSRY4TZH5QcjM46zui6l3tL9e32j6oUd4mAp4HhH0fws/pwawFYECI+M+7OCjgK\n"
|
||||
"y+qSJ1ECgYEA1g+L0yN4i+uScs7EpsYJfaRP1PMtGnUsof64Pg6i9IKcuf5mi5Kp\n"
|
||||
"aIgZdXAZIzsACH5XbfuC5Srs4565k/9XrHehLcuBzodulrzwmOUDbJAxIDw4uTUX\n"
|
||||
"95W0uK9UqyGLyM8wNYs/EzhveSFL8fnFWzOAL/+HshQpKCBzedSU+G0CgYEAyolH\n"
|
||||
"xws2mim7rSrYyRz1Vj02rLZuBUR7cPaHDxjjuuSUbI2nsDRsm6ZUCNlJtReHBkpH\n"
|
||||
"eW5iClBGkksVsJJYJBmyDw6a3mnj0mfxBnh9zGaHQi0RCuOwmYlu2L/XVQXiMFKT\n"
|
||||
"gffazuvysg7N/bz7CJjm8PRRx/cAxxFfAozdf/MCgYEAtBagLCHLaOvnaW9LQoOZ\n"
|
||||
"uHpkL2PmrjumMSN7HbpyngLEmDXPT90zaR4XTRXiECGzBXJFW+IdXW+fnGANANXx\n"
|
||||
"jMeYck6kBn0qLOcIA5moJ82nhtcjYa2pXEI2qKnZMaAnWen1RRbBGgqAvgelPQ5F\n"
|
||||
"W1UYo0j3gHo1peynOff+3IECgYAsP53M4KhHOgLkrE28cnUvKCR/y0NyJyoI3fNX\n"
|
||||
"2wo11KaQqMoP9wQbZVVKsZ4m0EMRnrzKzNDii/M/FuRgNTjIekyqeXhgSyYY29iO\n"
|
||||
"n1hshaHbVVk51dDJWns7I3559tUZ1ZCgfnPxbR8Sw6VBYD4//JfH4LjVRSOIWkU1\n"
|
||||
"m2zw/QKBgGE55o0xrCywF3wDUtFa6vgpsOfZu9IblsWktSbD/lk1YOqGpU//B0O4\n"
|
||||
"GqihOQT7E9kDNusspFUGpZrE0T0B+GW1T9iTR0zd+lC+qExv2ggDJoH063DnH5OU\n"
|
||||
"Qz2M8LESeFxf6ZlBxkcyrk6G1RAy7lUs9fHhfmpEJLVv4DTCuWDl\n"
|
||||
"-----END RSA PRIVATE KEY-----\n";
|
||||
|
||||
const char kTestMessage[] =
|
||||
"c8635a17ccc672c941d0cc287715411a0a0222613a04d47693a53eb7f32c"
|
||||
"1ebae1f5d916a815b880426362c42f5f18f694a380756e0452018c70b3e4"
|
||||
"f72ebb5269cb7233a3b8a2a1840e33ca9d473224d17ff91bae6b8d4ff2d1"
|
||||
"8e5c89b5fc8a52c4f791c2063ab1a29ffd3372db483e4975c1c9c7408bf6"
|
||||
"dfe5696e256e86b75313c501ab781175971b9411a73c444592afb1ec1667"
|
||||
"2bfb935715ef5302f3bef712d2296be4f64ef2dc861f0611b06c35d0a5c2"
|
||||
"5ff9f4a2563f265f109d2fa8f8165d7891b8a83c84520eaa284d49a4f76e"
|
||||
"ac158204a5bdf018edd9401ae6593092ba97970be9a58b10720a235c9158"
|
||||
"b9f235f9dda3de05990cef8c2fd04920a2a434bd5b6aa75767762d89b964"
|
||||
"90e42524855a7eab49a8f82ac593e4df01990206d3fa98329aa50e31db89"
|
||||
"b46b82ee0073851826f77aabb3779738a6f311b79f54d036a98dca4881ef"
|
||||
"88c3cbfc86ac358c7bd107dc234d3772fc707df01637354dcb9270c7aefa"
|
||||
"852dd21818ede33ab7154c32f25268b82f89b344e6469b81b6699df68c56"
|
||||
"a6e61f1dd8f140f3be4edce755ceee8ee7868f45a17f8b4b4b0988f45815"
|
||||
"1b43d07dcb0cd80b1ffa37b824e0abc25897cb41c242a3db845bedd37adf"
|
||||
"88a13c0b2f0b158464b02f9fd97ad6e87b92c13cbeee5e69d183cc898c4e"
|
||||
"0cfa9c59abde74a437d030cb966137ffe9abe6be71ed21ef751cdea73625"
|
||||
"7cff9e378718f7d7e9c4d567cbec8e0afdfab0585b8ed0d5f8de159b6524"
|
||||
"22c90737b44c84603ba1131f557604fe4e6b4d91e45363903b8db179cee0"
|
||||
"a50f2ae73394973c8671df7a7b2eeb8341a3417727cfe43290a67ac3ad02"
|
||||
"a52c3d1698c2c28a46268518aea66cecb40f43f50bb9cea4ed1d49ceb51d"
|
||||
"9967fabccccc7237a36b6cecda5916234730d7b3ca3295519d77b7516824"
|
||||
"10e8a238b6345e8d28132f60423a13fdf4b6a6cf272cef9a0833abb4b86d"
|
||||
"9828af45442a390e241b2b8c3290671da4a163d7e55fea7828098c0749ca"
|
||||
"ff65145dd6b4a6e4c65d214801bb8302d8914864e99c4d0b390b8126d4bc"
|
||||
"0353e376e69aba56cf71b9943a47dcffa07c6a24986a077f69b7bec6bd9c"
|
||||
"357e211875453bdadd9bfc4526f96c458e0052d27a903611c09a9c7b5f51"
|
||||
"83daad078aec0e79ef991d102d4af492773f1509a265c5644cbab3253e34"
|
||||
"3015e4305fffd17ce0261bcb232cfa0e1dcc71f83dc1aac490e526f6269f"
|
||||
"606d0e0e556bb30b774c2208ed3771474be23f39b7fc21dcbf304a923d9c";
|
||||
|
||||
const char kTestDevCodeSigningCert[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIFDjCCA3agAwIBAgIPESIzRFVmd4iZqrvM3e7/MA0GCSqGSIb3DQEBCwUAMIGc\n"
|
||||
"MQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2ly\n"
|
||||
"a2xhbmQxDzANBgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNV\n"
|
||||
"BAMMFXdpZGV2aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tp\n"
|
||||
"cEBnb29nbGUuY29tMB4XDTE3MTAwOTIwMjUwNloXDTI3MTAwNzIwMjUwNlowRTEL\n"
|
||||
"MAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy\n"
|
||||
"bmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
|
||||
"ggEBAObsg/w+dJedH3x5KEsXdA/5sunWc8G+iZl0wMcngh2DiwmOSkKf68uCK/iW\n"
|
||||
"T0a2XGgk13zl1HuKrjatgc7n6E1j/sqDZBGkr0q1wQgsdzm3qvGZoDG/+Z2U23WU\n"
|
||||
"kX6ZcyIYUbpO2VtQELEl6DgNwoUi/9Yp+vCb6lsItpSZ1WRD9NhbWh1MxZxj1s18\n"
|
||||
"OYcEzpEYg4/vHTVhocUR/1Rp9M9yn0nH1MUdtjhgBM3BmlRH7TA/nF111A4+GzMN\n"
|
||||
"qyqfb0/6yXE64Ca3+fGg1hstfUUXkpmjjNPhYJ6QTgA3Xfrz04a4uwB+pSliF3SD\n"
|
||||
"gip7O3rDyK0ES55lGpZ7B3s3TakCAwEAAaOCASEwggEdMB0GA1UdDgQWBBQ2jJme\n"
|
||||
"0BuaGrhgFGJR2i59HR+DizCBuwYDVR0jBIGzMIGwoYGipIGfMIGcMQswCQYDVQQG\n"
|
||||
"EwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQxDzAN\n"
|
||||
"BgNVBAoMBkdvb2dsZTERMA8GA1UECwwIV2lkZXZpbmUxHjAcBgNVBAMMFXdpZGV2\n"
|
||||
"aW5lLWRldi1jb2Rlc2lnbjEhMB8GCSqGSIb3DQEJARYSdGluc2tpcEBnb29nbGUu\n"
|
||||
"Y29tggkAxfgvA4+s8VgwCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAww\n"
|
||||
"CgYIKwYBBQUHAwMwEQYKKwYBBAHWeQQBAgQDAQH/MA0GCSqGSIb3DQEBCwUAA4IB\n"
|
||||
"gQAtan04ZGie7rRsKpb1F6t7xs48KE6cj6L99B5dgl37fZaZIQ3XE2vbmmmY5YTx\n"
|
||||
"wofCkvOZMXHeQfJEK5GIK49TW/lAR+3kJUJzSh+N67f0X8O1pUl97IUFsbi6PTw/\n"
|
||||
"mjhu197Kdy/OxPu/csOkEChuOfJLagRxXtIXeIyaeVOmn6fkFTOMOL2BusWOPuIs\n"
|
||||
"9OmOQ+UHXpMuX4c2x9iO4NzZwwI/MgULLCrd/c73q199H+ttdPFoNs8+xGdodqA/\n"
|
||||
"NFlHtMHMLMKVGpazAf+JW1/c3nb8L3S0nw4q7vPWi216RdZTfKfSIs/f/IW3CYJh\n"
|
||||
"/IAuHOYvlD0GdSOFZHfhrnAvKhJ2iRu32psN87L9rL5EL22LT8csV/gLMc3SZ35n\n"
|
||||
"/viuYcTDnMbe9S/Mge3mMJ9XHD5XBhN3hzmGDQEUdRS5MXrYdY32viPE7f+GAO9s\n"
|
||||
"5MXS+h+FxQ6QUar2q1zHc/0Gr1hLzA6HYBmI0/AF8LsHs799XjrMKHkSBN6UQkC1\n"
|
||||
"hRk=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
const char kDevCertFlagOid[] = "1.3.6.1.4.1.11129.4.1.2";
|
||||
const bool kTestDevCodeSigningCertFlagValue = true;
|
||||
|
||||
|
||||
TEST(X509CertTest, LoadCert) {
|
||||
X509Cert test_cert;
|
||||
EXPECT_EQ(OkStatus(),
|
||||
test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||
EXPECT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
// TODO(user): Add more specific status checks to failure tests.
|
||||
EXPECT_NE(OkStatus(), test_cert.LoadDer("bad cert"));
|
||||
EXPECT_NE(OkStatus(), test_cert.LoadPem("bad cert"));
|
||||
EXPECT_NE(OkStatus(), test_cert.LoadDer(""));
|
||||
EXPECT_NE(OkStatus(), test_cert.LoadPem(""));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, VerifySignature) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
std::string message(absl::HexStringToBytes(kTestMessage));
|
||||
std::string signature;
|
||||
ASSERT_EQ(OkStatus(), GenerateRsaSignatureSha256Pkcs1(kTestCertPrivateKey,
|
||||
message, &signature));
|
||||
std::unique_ptr<RsaPublicKey> pub_key(test_cert.GetRsaPublicKey());
|
||||
ASSERT_TRUE(pub_key);
|
||||
EXPECT_TRUE(pub_key->VerifySignatureSha256Pkcs7(message, signature));
|
||||
|
||||
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, "bad signature"));
|
||||
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("bad digest", signature));
|
||||
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7(message, ""));
|
||||
EXPECT_FALSE(pub_key->VerifySignatureSha256Pkcs7("", signature));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, GetSubjectNameField) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_EQ(kTestPemCertSubjectField_C, test_cert.GetSubjectNameField("C"));
|
||||
EXPECT_EQ(kTestPemCertSubjectField_CN, test_cert.GetSubjectNameField("CN"));
|
||||
EXPECT_EQ("", test_cert.GetSubjectNameField("invalid_field"));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, GetSerialNumber) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber());
|
||||
}
|
||||
|
||||
TEST(X509CertTest, GetNotBeforeSeconds) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
int64_t not_before_seconds = 0;
|
||||
ASSERT_TRUE(test_cert.GetNotBeforeSeconds(¬_before_seconds));
|
||||
EXPECT_EQ(kTestPemCertNotBeforeSeconds, not_before_seconds);
|
||||
}
|
||||
|
||||
TEST(X509CertTest, GetNotAfterSeconds) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
int64_t not_after_seconds = 0;
|
||||
ASSERT_TRUE(test_cert.GetNotAfterSeconds(¬_after_seconds));
|
||||
EXPECT_EQ(kTestPemCertNotAfterSeconds, not_after_seconds);
|
||||
}
|
||||
|
||||
TEST(X509CertTest, CertChain) {
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
ASSERT_EQ(2, test_chain.GetNumCerts());
|
||||
EXPECT_FALSE(test_chain.GetCert(0) == NULL);
|
||||
EXPECT_FALSE(test_chain.GetCert(1) == NULL);
|
||||
EXPECT_TRUE(test_chain.GetCert(2) == NULL);
|
||||
}
|
||||
|
||||
TEST(X509CertTest, IsCaCertificate) {
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
ASSERT_EQ(2, test_chain.GetNumCerts());
|
||||
EXPECT_FALSE(test_chain.GetCert(0)->IsCaCertificate());
|
||||
EXPECT_TRUE(test_chain.GetCert(1)->IsCaCertificate());
|
||||
}
|
||||
|
||||
TEST(X509CertTest, ChainVerificationPem) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
ASSERT_EQ(OkStatus(),
|
||||
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||
X509CA ca(ca_cert.release());
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, ChainVerificationPkcs7) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
ASSERT_EQ(OkStatus(),
|
||||
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||
X509CA ca(ca_cert.release());
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(OkStatus(),
|
||||
test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain)));
|
||||
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, VerifyCertWithChainIca) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||
X509CA ca(ca_cert.release());
|
||||
|
||||
// Verify the ICA with the root succeeds.
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
X509Cert ica_cert;
|
||||
ASSERT_EQ(OkStatus(), ica_cert.LoadPem(kTestPemIca));
|
||||
EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(ica_cert, test_chain));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, VerifyCertWithChainLeaf) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||
X509CA ca(ca_cert.release());
|
||||
|
||||
// Verify the leaf with the root and ICA succeeds.
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemIca));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
X509Cert leaf_cert;
|
||||
ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, VerifyCertWithChainLeafMissincIca) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||
X509CA ca(ca_cert.release());
|
||||
|
||||
// Verify the leaf with only the root fails (ICA missing).
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
X509Cert leaf_cert;
|
||||
ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_NE(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, GetPkcs7) {
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
std::string pkcs7_certificate = test_chain.GetPkcs7();
|
||||
ASSERT_NE(pkcs7_certificate.size(), 0);
|
||||
X509CertChain new_test_chain;
|
||||
ASSERT_EQ(OkStatus(), new_test_chain.LoadPkcs7(pkcs7_certificate));
|
||||
ASSERT_EQ(test_chain.GetNumCerts(), new_test_chain.GetNumCerts());
|
||||
for (int i = 0; i < test_chain.GetNumCerts(); i++) {
|
||||
ASSERT_EQ(test_chain.GetCert(i)->GetPem(),
|
||||
new_test_chain.GetCert(i)->GetPem());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(X509CertTest, BooleanExtension) {
|
||||
std::unique_ptr<X509Cert> cert1(new X509Cert);
|
||||
ASSERT_EQ(OkStatus(), cert1->LoadPem(kTestPemCert));
|
||||
bool extension_value;
|
||||
EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
|
||||
|
||||
std::unique_ptr<X509Cert> cert2(new X509Cert);
|
||||
ASSERT_EQ(OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert));
|
||||
ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
|
||||
EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
Reference in New Issue
Block a user