Minimal implementation of Widevine MediaCAS ECMG.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=226515998
This commit is contained in:
110
common/BUILD
110
common/BUILD
@@ -16,6 +16,7 @@ filegroup(
|
||||
name = "binary_release_files",
|
||||
srcs = [
|
||||
"certificate_type.h",
|
||||
"status.h",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -37,9 +38,9 @@ cc_library(
|
||||
hdrs = ["status.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//util:error_space",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//util:error_space",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -62,20 +63,21 @@ cc_library(
|
||||
":error_space",
|
||||
":random_util",
|
||||
":rsa_key",
|
||||
":sha_util",
|
||||
":signing_key_util",
|
||||
":status",
|
||||
":wvm_token_handler",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"//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",
|
||||
"//strings",
|
||||
"//util/gtl:map_util",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"@abseil_repo//absl/time",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -86,18 +88,20 @@ cc_test(
|
||||
":client_cert",
|
||||
":drm_root_certificate",
|
||||
":error_space",
|
||||
":sha_util",
|
||||
":test_drm_certificates",
|
||||
":wvm_test_keys",
|
||||
"//base",
|
||||
"//common:rsa_key",
|
||||
"//common:rsa_test_keys",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
"//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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -114,15 +118,15 @@ cc_library(
|
||||
":random_util",
|
||||
":rsa_key",
|
||||
":signing_key_util",
|
||||
":status",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"@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",
|
||||
"//util/gtl:map_util",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -134,14 +138,14 @@ cc_test(
|
||||
":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",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -154,15 +158,15 @@ cc_library(
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":sha_util",
|
||||
":status",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"@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",
|
||||
"@abseil_repo//absl/memory",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -178,10 +182,10 @@ cc_test(
|
||||
":test_drm_certificates",
|
||||
"//base",
|
||||
"//external:protobuf",
|
||||
"//testing:gunit_main",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -193,11 +197,11 @@ cc_library(
|
||||
":aes_cbc_util",
|
||||
":drm_service_certificate",
|
||||
":error_space",
|
||||
":status",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -220,9 +224,9 @@ cc_test(
|
||||
":rsa_test_keys",
|
||||
":rsa_util",
|
||||
"//base",
|
||||
"//external:openssl",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -307,9 +311,9 @@ cc_library(
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//external:openssl",
|
||||
"//util/endian",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -332,8 +336,8 @@ cc_library(
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//external:openssl",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -415,8 +419,8 @@ cc_library(
|
||||
":aes_cbc_util",
|
||||
":rsa_key",
|
||||
":sha_util",
|
||||
":status",
|
||||
"//base",
|
||||
"//common:status",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -438,10 +442,10 @@ cc_test(
|
||||
deps = [
|
||||
":crypto_util",
|
||||
":signing_key_util",
|
||||
"//protos/public:license_protocol_proto",
|
||||
"//testing:gunit",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:license_protocol_proto",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -464,12 +468,12 @@ cc_library(
|
||||
":aes_cbc_util",
|
||||
":ecb_util",
|
||||
":sha_util",
|
||||
":status",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"//util/endian",
|
||||
"//util/gtl:map_util",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//util/endian",
|
||||
"//util/gtl:map_util",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -503,9 +507,9 @@ cc_library(
|
||||
srcs = ["error_space.cc"],
|
||||
hdrs = ["error_space.h"],
|
||||
deps = [
|
||||
"//common:status",
|
||||
"//protos/public:errors_proto",
|
||||
"//util:error_space",
|
||||
"//util:proto_status",
|
||||
"//protos/public:errors_proto",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -518,14 +522,14 @@ cc_library(
|
||||
":drm_service_certificate",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":status",
|
||||
":x509_cert",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:remote_attestation_proto",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -540,15 +544,15 @@ cc_library(
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":rsa_util",
|
||||
":status",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"@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",
|
||||
"//util/gtl:map_util",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -566,13 +570,13 @@ cc_test(
|
||||
":test_drm_certificates",
|
||||
"//base",
|
||||
"//external:protobuf",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:client_identification_proto",
|
||||
"//protos/public:drm_certificate_proto",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:license_server_sdk_proto",
|
||||
"//protos/public:signed_drm_certificate_proto",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -581,11 +585,11 @@ cc_library(
|
||||
srcs = ["verified_media_pipeline.cc"],
|
||||
hdrs = ["verified_media_pipeline.h"],
|
||||
deps = [
|
||||
":status",
|
||||
":vmp_checker",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"//protos/public:license_protocol_proto",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:license_protocol_proto",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -597,11 +601,11 @@ cc_library(
|
||||
":error_space",
|
||||
":openssl_util",
|
||||
":rsa_key",
|
||||
":status",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"//external:openssl",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/synchronization",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -611,8 +615,8 @@ cc_library(
|
||||
srcs = ["test_utils.cc"],
|
||||
hdrs = ["test_utils.h"],
|
||||
deps = [
|
||||
":status",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"//external:openssl",
|
||||
],
|
||||
)
|
||||
@@ -639,9 +643,9 @@ cc_library(
|
||||
":certificate_type",
|
||||
":error_space",
|
||||
":rsa_key",
|
||||
":status",
|
||||
":x509_cert",
|
||||
"//base",
|
||||
"//common:status",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:verified_media_pipeline_proto",
|
||||
],
|
||||
@@ -655,10 +659,10 @@ cc_test(
|
||||
":rsa_key",
|
||||
":vmp_checker",
|
||||
"//base",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:verified_media_pipeline_proto",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//protos/public:errors_proto",
|
||||
"//protos/public:verified_media_pipeline_proto",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -667,8 +671,8 @@ cc_library(
|
||||
srcs = ["string_util.cc"],
|
||||
hdrs = ["string_util.h"],
|
||||
deps = [
|
||||
":status",
|
||||
"//base",
|
||||
"//common:status",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -17,19 +17,18 @@
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "common/status.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"
|
||||
|
||||
// TODO(user): Get rid of this horror.
|
||||
|
||||
namespace widevine {
|
||||
namespace {
|
||||
|
||||
@@ -37,45 +36,44 @@ const int kKeyboxSizeBytes = 72;
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(user): change to util::StatusOr<std::unique_ptr<ClientCert>>
|
||||
// instead of ClientCert** to explicitly assigning ownership of the created
|
||||
// object to the caller.
|
||||
|
||||
util::Status ClientCert::Create(const DrmRootCertificate* root_certificate,
|
||||
ClientIdentification::TokenType token_type,
|
||||
const std::string& token, ClientCert** client_cert) {
|
||||
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 util::Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||
"keybox-token-is-too-short");
|
||||
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 util::Status(error_space, util::error::UNIMPLEMENTED,
|
||||
"client-type-not-implemented");
|
||||
return Status(error_space, error::UNIMPLEMENTED,
|
||||
"client-type-not-implemented");
|
||||
}
|
||||
}
|
||||
|
||||
util::Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
|
||||
ClientCert** client_cert) {
|
||||
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);
|
||||
util::Status status = new_client_cert->Initialize(keybox_token);
|
||||
Status status = new_client_cert->Initialize(keybox_token);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
*client_cert = new_client_cert.release();
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status ClientCert::CreateWithDrmCertificate(
|
||||
Status ClientCert::CreateWithDrmCertificate(
|
||||
const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
|
||||
ClientCert** client_cert) {
|
||||
CHECK(client_cert);
|
||||
@@ -83,14 +81,14 @@ util::Status ClientCert::CreateWithDrmCertificate(
|
||||
|
||||
std::unique_ptr<CertificateClientCert> new_client_cert(
|
||||
new CertificateClientCert);
|
||||
util::Status status =
|
||||
Status status =
|
||||
new_client_cert->Initialize(root_certificate, drm_certificate);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
*client_cert = new_client_cert.release();
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
void ClientCert::CreateSignature(const std::string& message, std::string* signature) {
|
||||
@@ -110,8 +108,10 @@ void ClientCert::GenerateSigningKey(const std::string& message,
|
||||
DCHECK(!key().empty());
|
||||
using crypto_util::DeriveKey;
|
||||
using crypto_util::kSigningKeyLabel;
|
||||
set_signing_key(DeriveKey(key(), kSigningKeyLabel, message,
|
||||
SigningKeyMaterialSize(protocol_version)));
|
||||
set_signing_key(
|
||||
DeriveKey(key(), kSigningKeyLabel,
|
||||
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||
SigningKeyMaterialSizeBits(protocol_version)));
|
||||
}
|
||||
|
||||
KeyboxClientCert::KeyboxClientCert() {}
|
||||
@@ -139,51 +139,51 @@ uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
|
||||
return WvmTokenHandler::GetSystemId(keybox_bytes);
|
||||
}
|
||||
|
||||
util::Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) {
|
||||
Status KeyboxClientCert::Initialize(const std::string& keybox_bytes) {
|
||||
if (keybox_bytes.size() < kKeyboxSizeBytes) {
|
||||
return util::Status(error_space, INVALID_KEYBOX_TOKEN,
|
||||
"keybox-token-is-too-short");
|
||||
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;
|
||||
util::Status status = WvmTokenHandler::DecryptDeviceKey(
|
||||
keybox_bytes, &device_key_, nullptr, &insecure_keybox);
|
||||
Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &device_key_,
|
||||
nullptr, &insecure_keybox);
|
||||
if (!status.ok()) {
|
||||
Errors new_code = status.error_code() == util::error::NOT_FOUND
|
||||
Errors new_code = status.error_code() == error::NOT_FOUND
|
||||
? MISSING_PRE_PROV_KEY
|
||||
: KEYBOX_DECRYPT_ERROR;
|
||||
return util::Status(error_space, new_code, status.error_message());
|
||||
return Status(error_space, new_code, status.error_message());
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status KeyboxClientCert::VerifySignature(
|
||||
const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) {
|
||||
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 util::Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
|
||||
return Status(error_space, INVALID_SIGNATURE, "invalid-keybox-mac");
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
CertificateClientCert::CertificateClientCert() {}
|
||||
|
||||
CertificateClientCert::~CertificateClientCert() {}
|
||||
|
||||
util::Status CertificateClientCert::Initialize(
|
||||
Status CertificateClientCert::Initialize(
|
||||
const DrmRootCertificate* drm_root_certificate,
|
||||
const std::string& serialized_certificate) {
|
||||
CHECK(drm_root_certificate);
|
||||
|
||||
SignedDrmCertificate signed_device_cert;
|
||||
DrmCertificate device_cert;
|
||||
util::Status status = drm_root_certificate->VerifyCertificate(
|
||||
Status status = drm_root_certificate->VerifyCertificate(
|
||||
serialized_certificate, &signed_device_cert, &device_cert);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -192,12 +192,12 @@ util::Status CertificateClientCert::Initialize(
|
||||
const SignedDrmCertificate& signer = signed_device_cert.signer();
|
||||
DrmCertificate model_certificate;
|
||||
if (!model_certificate.ParseFromString(signer.drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-invalid-signer");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-invalid-signer");
|
||||
}
|
||||
if (!model_certificate.has_serial_number()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-signer-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).
|
||||
@@ -205,56 +205,58 @@ util::Status CertificateClientCert::Initialize(
|
||||
DrmCertificate provisioner_certificate;
|
||||
if (!provisioner_certificate.ParseFromString(
|
||||
signer.signer().drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-invalid-signer");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-invalid-signer");
|
||||
}
|
||||
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
|
||||
set_signed_by_provisioner(true);
|
||||
} else {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"expected-provisioning-provider-certificate-type");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"expected-provisioning-provider-certificate-type");
|
||||
}
|
||||
if (!provisioner_certificate.has_provider_id() ||
|
||||
provisioner_certificate.provider_id().empty()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-provisioning-service-id");
|
||||
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 util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-missing-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 util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-public-key-failed");
|
||||
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 util::Status(error_space, ENCRYPT_ERROR,
|
||||
"drm-certificate-failed-encrypt-session-key");
|
||||
return Status(error_space, ENCRYPT_ERROR,
|
||||
"drm-certificate-failed-encrypt-session-key");
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status CertificateClientCert::VerifySignature(
|
||||
Status CertificateClientCert::VerifySignature(
|
||||
const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) {
|
||||
CHECK(rsa_public_key_);
|
||||
|
||||
if (!rsa_public_key_->VerifySignature(message, signature)) {
|
||||
return util::Status(error_space, INVALID_SIGNATURE, "");
|
||||
if (!rsa_public_key_->VerifySignature(
|
||||
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
|
||||
signature)) {
|
||||
return Status(error_space, INVALID_SIGNATURE, "");
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/status.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/status.h"
|
||||
#include "protos/public/client_identification.pb.h"
|
||||
#include "protos/public/license_protocol.pb.h"
|
||||
|
||||
@@ -29,15 +29,15 @@ class SignedDrmCertificate;
|
||||
class ClientCert {
|
||||
public:
|
||||
virtual ~ClientCert() {}
|
||||
static util::Status Create(
|
||||
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 util::Status CreateWithKeybox(const std::string& keybox_token,
|
||||
ClientCert** client_cert);
|
||||
static Status CreateWithKeybox(const std::string& keybox_token,
|
||||
ClientCert** client_cert);
|
||||
// Creates a Device Certificate based ClientCert.
|
||||
static util::Status CreateWithDrmCertificate(
|
||||
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().
|
||||
@@ -46,9 +46,8 @@ class ClientCert {
|
||||
// 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 util::Status VerifySignature(const std::string& message,
|
||||
const std::string& signature,
|
||||
ProtocolVersion protocol_version) = 0;
|
||||
virtual Status VerifySignature(const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) = 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,
|
||||
@@ -118,10 +117,10 @@ class KeyboxClientCert : public ClientCert {
|
||||
static bool IsSystemIdKnown(const uint32_t system_id);
|
||||
static uint32_t GetSystemId(const std::string& keybox_bytes);
|
||||
|
||||
util::Status Initialize(const std::string& keybox_bytes);
|
||||
Status Initialize(const std::string& keybox_bytes);
|
||||
|
||||
util::Status VerifySignature(const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) override;
|
||||
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_; }
|
||||
@@ -148,8 +147,8 @@ class CertificateClientCert : public ClientCert {
|
||||
public:
|
||||
~CertificateClientCert() override;
|
||||
|
||||
util::Status VerifySignature(const std::string& message, const std::string& signature,
|
||||
ProtocolVersion protocol_version) 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 {
|
||||
@@ -162,8 +161,8 @@ class CertificateClientCert : public ClientCert {
|
||||
protected:
|
||||
friend class ClientCert;
|
||||
friend class MockCertificateClientCert;
|
||||
util::Status Initialize(const DrmRootCertificate* drm_root_certificate,
|
||||
const std::string& serialized_certificate);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#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"
|
||||
@@ -31,7 +33,7 @@
|
||||
|
||||
// TODO(user): Change these tests to use on-the-fly generated intermediate
|
||||
// and device certificates based on RsaTestKeys.
|
||||
// TODO(user): Add testcase(s) VerifySignature, CreateSignature,
|
||||
// TODO(user): Add testcase(s) CreateSignature,
|
||||
// and GenerateSigningKey.
|
||||
|
||||
namespace widevine {
|
||||
@@ -73,11 +75,10 @@ class ClientCertTest : public ::testing::Test {
|
||||
const std::string certificate_;
|
||||
const std::string expected_serial_number_;
|
||||
uint32_t expected_system_id_;
|
||||
util::Status expected_status_;
|
||||
Status expected_status_;
|
||||
TestCertificateAndData(const std::string& certificate,
|
||||
const std::string& expected_serial_number,
|
||||
uint32_t expected_system_id,
|
||||
util::Status expected_status)
|
||||
uint32_t expected_system_id, Status expected_status)
|
||||
: certificate_(certificate),
|
||||
expected_serial_number_(expected_serial_number),
|
||||
expected_system_id_(expected_system_id),
|
||||
@@ -111,7 +112,8 @@ class ClientCertTest : public ::testing::Test {
|
||||
SignedDrmCertificate* signer, uint32_t system_id,
|
||||
const std::string& serial_number);
|
||||
|
||||
RsaTestKeys test_keys_;
|
||||
RsaTestKeys test_rsa_keys_;
|
||||
TestDrmCertificates test_drm_certs_;
|
||||
std::unique_ptr<DrmRootCertificate> root_cert_;
|
||||
static bool setup_preprov_keys_;
|
||||
};
|
||||
@@ -121,7 +123,7 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||
const bool expect_success,
|
||||
const bool compare_device_key) {
|
||||
// Test validation of a valid request.
|
||||
util::Status status;
|
||||
Status status;
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
|
||||
// Two ways to create a client cert object, test both.
|
||||
@@ -136,7 +138,7 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||
}
|
||||
std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
|
||||
if (expect_success) {
|
||||
ASSERT_EQ(util::OkStatus(), status);
|
||||
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_,
|
||||
@@ -145,7 +147,7 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
|
||||
EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
|
||||
}
|
||||
} else {
|
||||
EXPECT_NE(util::OkStatus(), status);
|
||||
EXPECT_NE(OkStatus(), status);
|
||||
EXPECT_FALSE(keybox_cert);
|
||||
}
|
||||
}
|
||||
@@ -159,7 +161,7 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
|
||||
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
|
||||
|
||||
// Test validation of a valid request.
|
||||
util::Status status;
|
||||
Status status;
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
status = ClientCert::Create(root_cert_.get(),
|
||||
ClientIdentification::DRM_DEVICE_CERTIFICATE,
|
||||
@@ -209,7 +211,7 @@ DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
|
||||
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
|
||||
intermediate_certificate->set_serial_number(serial_number);
|
||||
intermediate_certificate->set_public_key(
|
||||
test_keys_.public_test_key_2_2048_bits());
|
||||
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();
|
||||
@@ -221,7 +223,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
|
||||
std::unique_ptr<DrmCertificate> intermediate_certificate(
|
||||
GenerateIntermediateCertificate(system_id, serial_number));
|
||||
return SignCertificate(*intermediate_certificate, signer,
|
||||
test_keys_.private_test_key_1_3072_bits());
|
||||
test_rsa_keys_.private_test_key_1_3072_bits());
|
||||
}
|
||||
|
||||
DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
||||
@@ -230,7 +232,7 @@ DrmCertificate* ClientCertTest::GenerateDrmCertificate(
|
||||
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_keys_.public_test_key_3_2048_bits());
|
||||
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();
|
||||
}
|
||||
@@ -241,7 +243,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
|
||||
std::unique_ptr<DrmCertificate> drm_certificate(
|
||||
GenerateDrmCertificate(system_id, serial_number));
|
||||
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate(
|
||||
*drm_certificate, signer, test_keys_.private_test_key_2_2048_bits()));
|
||||
*drm_certificate, signer, test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
return signed_drm_certificate.release();
|
||||
}
|
||||
|
||||
@@ -252,7 +254,7 @@ DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
|
||||
provisioner_certificate->set_serial_number(serial_number);
|
||||
// TODO(user): Need to generate 3072 bit test for provisioner certificates.
|
||||
provisioner_certificate->set_public_key(
|
||||
test_keys_.public_test_key_1_3072_bits());
|
||||
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);
|
||||
@@ -264,7 +266,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
|
||||
std::unique_ptr<DrmCertificate> provisioner_certificate(
|
||||
GenerateProvisionerCertificate(system_id, serial_number, service_id));
|
||||
return SignCertificate(*provisioner_certificate, nullptr,
|
||||
test_keys_.private_test_key_1_3072_bits());
|
||||
test_rsa_keys_.private_test_key_1_3072_bits());
|
||||
}
|
||||
|
||||
TEST_F(ClientCertTest, BasicValidation) {
|
||||
@@ -302,8 +304,7 @@ TEST_F(ClientCertTest, BasicCertValidation) {
|
||||
nullptr, system_id, serial_number),
|
||||
system_id, serial_number + "-device"));
|
||||
const TestCertificateAndData kValidCertificateAndExpectedData(
|
||||
signed_cert->SerializeAsString(), serial_number, system_id,
|
||||
util::OkStatus());
|
||||
signed_cert->SerializeAsString(), serial_number, system_id, OkStatus());
|
||||
const bool compare_data = true;
|
||||
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
|
||||
compare_data);
|
||||
@@ -347,7 +348,7 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
||||
new SignedDrmCertificate);
|
||||
invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
|
||||
GenerateSignature(invalid_drm_cert->drm_certificate(),
|
||||
test_keys_.private_test_key_2_2048_bits(),
|
||||
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));
|
||||
@@ -357,18 +358,18 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
||||
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
|
||||
*dev_cert,
|
||||
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
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_keys_.private_test_key_1_3072_bits(),
|
||||
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_keys_.private_test_key_2_2048_bits()));
|
||||
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));
|
||||
@@ -376,8 +377,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
||||
std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_keys_.private_test_key_1_3072_bits()),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
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(
|
||||
@@ -391,8 +392,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
||||
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_keys_.private_test_key_1_3072_bits()),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
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));
|
||||
@@ -400,8 +401,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
||||
std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
|
||||
*dev_cert,
|
||||
SignCertificate(*signer_cert, nullptr,
|
||||
test_keys_.private_test_key_1_3072_bits()),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
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(
|
||||
@@ -409,37 +410,36 @@ TEST_F(ClientCertTest, InvalidCertificate) {
|
||||
signed_signer->set_signature("bad-signature");
|
||||
std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
|
||||
SignCertificate(*dev_cert, signed_signer.release(),
|
||||
test_keys_.private_test_key_2_2048_bits()));
|
||||
test_rsa_keys_.private_test_key_2_2048_bits()));
|
||||
|
||||
const TestCertificateAndData kInvalidCertificate[] = {
|
||||
TestCertificateAndData("f", "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signed-drm-certificate")),
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signed-drm-certificate")),
|
||||
TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-drm-certificate")),
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-drm-certificate")),
|
||||
TestCertificateAndData(bad_device_public_key->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-public-key-failed")),
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"drm-certificate-public-key-failed")),
|
||||
TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate")),
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate")),
|
||||
TestCertificateAndData(bad_signer_public_key->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-leaf-signer-public-key")),
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-leaf-signer-public-key")),
|
||||
TestCertificateAndData(bad_device_signature->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature")),
|
||||
TestCertificateAndData(
|
||||
missing_model_sn->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"model-certificate-missing-system-id")),
|
||||
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,
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-signer-serial-number")),
|
||||
Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-signer-serial-number")),
|
||||
TestCertificateAndData(bad_signer_signature->SerializeAsString(), "", 0,
|
||||
util::Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature")),
|
||||
Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature")),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) {
|
||||
@@ -454,7 +454,7 @@ TEST_F(ClientCertTest, MissingPreProvKey) {
|
||||
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
|
||||
"2517a12f4922953e"));
|
||||
ClientCert* client_cert_ptr = nullptr;
|
||||
util::Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr);
|
||||
Status status = ClientCert::CreateWithKeybox(token, &client_cert_ptr);
|
||||
ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
|
||||
}
|
||||
|
||||
@@ -563,4 +563,55 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
|
||||
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
|
||||
|
||||
@@ -51,39 +51,39 @@ std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
return default_value;
|
||||
}
|
||||
|
||||
util::Status DecryptEncryptedClientIdentification(
|
||||
Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id) {
|
||||
return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id,
|
||||
client_id);
|
||||
}
|
||||
|
||||
util::Status DecryptEncryptedClientIdentification(
|
||||
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 util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id");
|
||||
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 util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id-iv");
|
||||
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 util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-decryption-failed");
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-decryption-failed");
|
||||
}
|
||||
if (!client_id->ParseFromString(serialized_client_id)) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-parse-failed");
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-parse-failed");
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -43,16 +43,16 @@ std::string GetClientInfo(const ClientIdentification& client_id,
|
||||
// |client_id| using the private key for the service certificate which was
|
||||
// used to encrypt the information.
|
||||
// |client_id| is owned by caller.
|
||||
// Returns util::Status::OK, if successful, else an error.
|
||||
util::Status DecryptEncryptedClientIdentification(
|
||||
// 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 util::Status::OK, if successful, else an error.
|
||||
util::Status DecryptEncryptedClientIdentification(
|
||||
// Returns Status::OK, if successful, else an error.
|
||||
Status DecryptEncryptedClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
const std::string& privacy_key, ClientIdentification* client_id);
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
const char kEncryptionKeyLabel[] = "ENCRYPTION";
|
||||
const int kEncryptionKeySizeBits = 128;
|
||||
const char kWrappingKeyLabel[] = "ENCRYPTION";
|
||||
const int kWrappingKeySizeBits = 128;
|
||||
const char kSigningKeyLabel[] = "AUTHENTICATION";
|
||||
const int kSigningKeySizeBits = 256;
|
||||
const size_t kSigningKeySizeBytes = 32;
|
||||
|
||||
@@ -22,8 +22,8 @@ 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 kEncryptionKeyLabel[];
|
||||
extern const int kEncryptionKeySizeBits;
|
||||
extern const char kWrappingKeyLabel[];
|
||||
extern const int kWrappingKeySizeBits;
|
||||
extern const char kSigningKeyLabel[];
|
||||
extern const int kSigningKeySizeBits;
|
||||
extern const size_t kSigningKeySizeBytes;
|
||||
|
||||
@@ -52,47 +52,47 @@ DeviceStatusList::DeviceStatusList()
|
||||
|
||||
DeviceStatusList::~DeviceStatusList() {}
|
||||
|
||||
util::Status DeviceStatusList::UpdateStatusList(
|
||||
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 util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"signed-certificate-status-list-parse-error");
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"signed-certificate-status-list-parse-error");
|
||||
}
|
||||
if (!signed_certificate_status_list.has_certificate_status_list()) {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-status-list");
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-status-list");
|
||||
}
|
||||
if (!signed_certificate_status_list.has_signature()) {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-status-list-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 util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-public-key");
|
||||
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 util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"invalid-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 util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-parse-error");
|
||||
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 util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-expired");
|
||||
return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-expired");
|
||||
}
|
||||
|
||||
absl::WriterMutexLock lock(&status_map_lock_);
|
||||
@@ -106,44 +106,44 @@ util::Status DeviceStatusList::UpdateStatusList(
|
||||
if (device_info.has_system_id()) {
|
||||
device_status_map_[device_info.system_id()] = cert_status;
|
||||
} else {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"device-info-missing-system-id");
|
||||
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 util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status DeviceStatusList::GetCertStatus(
|
||||
const ClientCert& client_cert, ProvisionedDeviceInfo* device_info) {
|
||||
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 util::Status(error_space, UNSUPPORTED_SYSTEM_ID,
|
||||
"keybox-unsupported-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 util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
// DRM certificate checks.
|
||||
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"device-certificate-unsupported-token-type");
|
||||
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 util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-expired");
|
||||
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());
|
||||
@@ -155,15 +155,15 @@ util::Status DeviceStatusList::GetCertStatus(
|
||||
LOG(WARNING) << "Allowing REVOKED device: "
|
||||
<< device_info->ShortDebugString();
|
||||
} else {
|
||||
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
|
||||
"device-certificate-revoked");
|
||||
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 util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
"test-only-drm-certificate-not-allowed");
|
||||
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() !=
|
||||
@@ -175,21 +175,21 @@ util::Status DeviceStatusList::GetCertStatus(
|
||||
// list is older than the certificate, the certificate is for all purposes
|
||||
// unknown.
|
||||
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"intermediate-certificate-serial-number-mismatch");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"intermediate-certificate-serial-number-mismatch");
|
||||
}
|
||||
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
"device-certificate-status-unknown");
|
||||
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
"device-certificate-status-unknown");
|
||||
}
|
||||
} else {
|
||||
if (!allow_unknown_devices_) {
|
||||
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
"device-certificate-status-unknown");
|
||||
return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
|
||||
"device-certificate-status-unknown");
|
||||
}
|
||||
device_info->Clear();
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
|
||||
@@ -247,18 +247,18 @@ bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
|
||||
return it;
|
||||
}
|
||||
|
||||
util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
||||
Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
||||
const std::string& certificate_provisioning_service_response,
|
||||
std::string* signed_certificate_status_list, std::string* certificate_status_list) {
|
||||
util::Status status = util::OkStatus();
|
||||
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 util::Status(
|
||||
error_space, util::error::INVALID_ARGUMENT,
|
||||
return Status(
|
||||
error_space, error::INVALID_ARGUMENT,
|
||||
"Unable to parse the certificate_provisioning_service_response. "
|
||||
"SignedList not terminated.");
|
||||
}
|
||||
@@ -284,8 +284,8 @@ util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
||||
if (!absl::WebSafeBase64Unescape(signed_list,
|
||||
signed_certificate_status_list)) {
|
||||
if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) {
|
||||
return util::Status(error_space, util::error::INVALID_ARGUMENT,
|
||||
"Base64 decode of signedlist failed.");
|
||||
return Status(error_space, error::INVALID_ARGUMENT,
|
||||
"Base64 decode of signedlist failed.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -295,42 +295,40 @@ util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
|
||||
signed_certificate_status_list)) {
|
||||
if (!absl::Base64Unescape(certificate_provisioning_service_response,
|
||||
signed_certificate_status_list)) {
|
||||
return util::Status(error_space, util::error::INVALID_ARGUMENT,
|
||||
"Base64 decode of certList failed.");
|
||||
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 util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"signed-certificate-status-list-parse-error");
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"signed-certificate-status-list-parse-error");
|
||||
}
|
||||
if (!signed_status_list.has_certificate_status_list()) {
|
||||
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"missing-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 util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-parse-error");
|
||||
return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
|
||||
"certificate-status-list-parse-error");
|
||||
}
|
||||
*certificate_status_list = signed_status_list.certificate_status_list();
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
||||
Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
||||
const std::string& version,
|
||||
std::string* signed_device_certificate_status_list_request) {
|
||||
if (version.empty()) {
|
||||
return util::Status(error_space, util::error::INVALID_ARGUMENT,
|
||||
"SDK version is 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 util::Status(
|
||||
error_space, util::error::INVALID_ARGUMENT,
|
||||
"Signed_device_certificate_status_list_request is empty");
|
||||
return Status(error_space, error::INVALID_ARGUMENT,
|
||||
"Signed_device_certificate_status_list_request is empty");
|
||||
}
|
||||
// Construct SignedDeviceCertificateStatusListRequest.
|
||||
DeviceCertificateStatusListRequest request;
|
||||
@@ -345,15 +343,13 @@ util::Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
||||
DrmServiceCertificate::GetDefaultDrmServiceCertificate();
|
||||
if (sc == nullptr) {
|
||||
signed_device_certificate_status_list_request->clear();
|
||||
return util::Status(error_space,
|
||||
widevine::INVALID_SERVICE_CERTIFICATE,
|
||||
"Drm service certificate is not loaded.");
|
||||
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 util::Status(error_space,
|
||||
widevine::INVALID_SERVICE_CERTIFICATE,
|
||||
"Private key in the service certificate is null.");
|
||||
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,
|
||||
@@ -361,6 +357,6 @@ util::Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
|
||||
signed_request.set_signature(signature);
|
||||
signed_request.SerializeToString(
|
||||
signed_device_certificate_status_list_request);
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
} // namespace widevine
|
||||
|
||||
@@ -37,12 +37,12 @@ class DeviceStatusList {
|
||||
DeviceStatusList();
|
||||
virtual ~DeviceStatusList();
|
||||
|
||||
// Takes |signed_certificate_status_list| and copies to an internal map of
|
||||
// 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.
|
||||
util::Status UpdateStatusList(const std::string& root_certificate_public_key,
|
||||
const std::string& signed_certificate_status_list,
|
||||
uint32_t expiration_period_seconds);
|
||||
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) {
|
||||
@@ -58,9 +58,8 @@ class DeviceStatusList {
|
||||
// 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.
|
||||
util::Status GetCertStatus(
|
||||
const ClientCert& client_cert,
|
||||
widevine::ProvisionedDeviceInfo* device_info);
|
||||
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);
|
||||
@@ -86,7 +85,7 @@ class DeviceStatusList {
|
||||
* @param certificate_status_list
|
||||
* @return WvPLStatus - Status::OK if success, else error.
|
||||
*/
|
||||
static util::Status ExtractFromProvisioningServiceResponse(
|
||||
static Status ExtractFromProvisioningServiceResponse(
|
||||
const std::string& certificate_provisioning_service_response,
|
||||
std::string* signed_certificate_status_list, std::string* certificate_status_list);
|
||||
/**
|
||||
@@ -94,9 +93,9 @@ class DeviceStatusList {
|
||||
*
|
||||
* @param signed_device_certificate_status_list_request
|
||||
* @param version
|
||||
* @return util::Status - Status::OK if success, else error.
|
||||
* @return Status - Status::OK if success, else error.
|
||||
*/
|
||||
static util::Status GenerateSignedDeviceCertificateStatusListRequest(
|
||||
static Status GenerateSignedDeviceCertificateStatusListRequest(
|
||||
const std::string& version,
|
||||
std::string* signed_device_certificate_status_list_request);
|
||||
|
||||
|
||||
@@ -114,10 +114,9 @@ class DeviceStatusListTest : public ::testing::Test {
|
||||
ASSERT_TRUE(
|
||||
signed_cert_status_list_.SerializeToString(&serialized_status_list_));
|
||||
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
device_status_list_.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, kDefaultExpirePeriod));
|
||||
ASSERT_EQ(OkStatus(), device_status_list_.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, kDefaultExpirePeriod));
|
||||
}
|
||||
|
||||
DeviceStatusList device_status_list_;
|
||||
@@ -140,7 +139,7 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_CALL(valid_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
EXPECT_EQ(OkStatus(),
|
||||
device_status_list_.GetCertStatus(valid_client_cert, &device_info));
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
@@ -191,8 +190,8 @@ TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
|
||||
.WillRepeatedly(Return(kTestOnlyCertSystemId));
|
||||
EXPECT_CALL(test_only_client_cert, signer_serial_number())
|
||||
.WillRepeatedly(ReturnRef(test_only_drm_serial_number));
|
||||
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus(
|
||||
test_only_client_cert, &device_info));
|
||||
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(test_only_client_cert,
|
||||
&device_info));
|
||||
}
|
||||
|
||||
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
||||
@@ -208,8 +207,8 @@ TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
|
||||
.WillRepeatedly(Return(ClientIdentification::KEYBOX));
|
||||
EXPECT_CALL(valid_client_keybox, system_id())
|
||||
.WillRepeatedly(Return(kValidCertSystemId));
|
||||
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus(
|
||||
valid_client_keybox, &device_info));
|
||||
EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(valid_client_keybox,
|
||||
&device_info));
|
||||
EXPECT_TRUE(device_info.has_model());
|
||||
EXPECT_EQ(kDeviceModel, device_info.model());
|
||||
|
||||
@@ -249,7 +248,7 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
|
||||
// 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(util::OkStatus(),
|
||||
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());
|
||||
@@ -314,9 +313,9 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
|
||||
.Times(2)
|
||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 100));
|
||||
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(),
|
||||
@@ -331,9 +330,9 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||
.WillOnce(Return(kStatusListCreationTime + 100))
|
||||
.WillOnce(Return(kStatusListCreationTime + 101));
|
||||
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList(
|
||||
test_keys_.public_test_key_1_3072_bits(),
|
||||
serialized_status_list_, 100));
|
||||
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;
|
||||
@@ -346,8 +345,8 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
|
||||
.WillRepeatedly(ReturnRef(valid_drm_serial_number));
|
||||
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
|
||||
.WillRepeatedly(Return(kStatusListCreationTime - 1));
|
||||
EXPECT_EQ(util::OkStatus(), mock_device_status_list.GetCertStatus(
|
||||
valid_client_cert, &device_info));
|
||||
EXPECT_EQ(OkStatus(), mock_device_status_list.GetCertStatus(valid_client_cert,
|
||||
&device_info));
|
||||
|
||||
EXPECT_EQ(
|
||||
EXPIRED_CERTIFICATE_STATUS_LIST,
|
||||
|
||||
@@ -265,10 +265,10 @@ class VerifiedCertSignatureCache {
|
||||
|
||||
// Checks cache, on miss, uses public key. If successful, adds to
|
||||
// cache.
|
||||
util::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) {
|
||||
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_);
|
||||
@@ -279,11 +279,11 @@ class VerifiedCertSignatureCache {
|
||||
(signature != cached_signature->second.signature) ||
|
||||
(signer_serial_number != cached_signature->second.signer_serial)) {
|
||||
// Cached signature mismatch.
|
||||
return util::Status(error_space, INVALID_SIGNATURE,
|
||||
"cached-signature-mismatch");
|
||||
return Status(error_space, INVALID_SIGNATURE,
|
||||
"cached-signature-mismatch");
|
||||
}
|
||||
// Cached signature match.
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,12 +291,12 @@ class VerifiedCertSignatureCache {
|
||||
std::unique_ptr<RsaPublicKey> signer_key(
|
||||
key_factory_->CreateFromPkcs1PublicKey(signer_public_key));
|
||||
if (!signer_key) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-public-key");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-public-key");
|
||||
}
|
||||
if (!signer_key->VerifySignature(cert, signature)) {
|
||||
return util::Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature");
|
||||
return Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature");
|
||||
}
|
||||
|
||||
// Add signature to cache.
|
||||
@@ -304,7 +304,7 @@ class VerifiedCertSignatureCache {
|
||||
signature_cache_.emplace(
|
||||
serial_number,
|
||||
VerifiedCertSignature(cert, signature, signer_serial_number));
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -313,7 +313,7 @@ class VerifiedCertSignatureCache {
|
||||
const RsaKeyFactory* key_factory_;
|
||||
};
|
||||
|
||||
util::Status DrmRootCertificate::CreateByType(
|
||||
Status DrmRootCertificate::CreateByType(
|
||||
CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
CHECK(cert);
|
||||
|
||||
@@ -321,7 +321,7 @@ util::Status DrmRootCertificate::CreateByType(
|
||||
}
|
||||
|
||||
std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
|
||||
CertificateType cert_type, util::Status* status) {
|
||||
CertificateType cert_type, Status* status) {
|
||||
CHECK(status);
|
||||
|
||||
std::unique_ptr<DrmRootCertificate> new_root_cert;
|
||||
@@ -329,7 +329,7 @@ std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
|
||||
return new_root_cert;
|
||||
}
|
||||
|
||||
util::Status DrmRootCertificate::CreateByTypeString(
|
||||
Status DrmRootCertificate::CreateByTypeString(
|
||||
const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
CHECK(cert);
|
||||
|
||||
@@ -341,17 +341,16 @@ util::Status DrmRootCertificate::CreateByTypeString(
|
||||
} else if (cert_type_string == kTestingString) {
|
||||
cert_type = kCertificateTypeTesting;
|
||||
} else {
|
||||
return util::Status(
|
||||
error_space, INVALID_PARAMETER,
|
||||
absl::StrCat("invalid-certificate-type ", cert_type_string));
|
||||
return Status(error_space, INVALID_PARAMETER,
|
||||
absl::StrCat("invalid-certificate-type ", cert_type_string));
|
||||
}
|
||||
|
||||
return CreateByType(cert_type, cert);
|
||||
}
|
||||
|
||||
util::Status DrmRootCertificate::Create(
|
||||
CertificateType cert_type, std::unique_ptr<RsaKeyFactory> key_factory,
|
||||
std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
Status DrmRootCertificate::Create(CertificateType cert_type,
|
||||
std::unique_ptr<RsaKeyFactory> key_factory,
|
||||
std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
DCHECK(cert);
|
||||
|
||||
std::string serialized_certificate;
|
||||
@@ -375,49 +374,48 @@ util::Status DrmRootCertificate::Create(
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return util::Status(error_space, INVALID_PARAMETER,
|
||||
"invalid-certificate-type");
|
||||
return Status(error_space, INVALID_PARAMETER, "invalid-certificate-type");
|
||||
}
|
||||
|
||||
SignedDrmCertificate signed_root_cert;
|
||||
if (!signed_root_cert.ParseFromString(serialized_certificate)) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signed-root-cert-deserialize-fail");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signed-root-cert-deserialize-fail");
|
||||
}
|
||||
DrmCertificate root_cert;
|
||||
if (!signed_root_cert.has_drm_certificate()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-device-certificate");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-device-certificate");
|
||||
}
|
||||
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"root-cert-deserialize-fail");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"root-cert-deserialize-fail");
|
||||
}
|
||||
if (!root_cert.has_public_key()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-cert-public-key");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-cert-public-key");
|
||||
}
|
||||
if (!signed_root_cert.has_signature()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-certificate-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 util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-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 util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-certificate-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 util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
DrmRootCertificate::DrmRootCertificate(
|
||||
@@ -437,7 +435,7 @@ std::string DrmRootCertificate::GetDigest() const {
|
||||
return absl::BytesToHexString(Sha256_Hash(serialized_certificate_));
|
||||
}
|
||||
|
||||
util::Status DrmRootCertificate::VerifyCertificate(
|
||||
Status DrmRootCertificate::VerifyCertificate(
|
||||
const std::string& serialized_certificate,
|
||||
SignedDrmCertificate* signed_certificate,
|
||||
DrmCertificate* certificate) const {
|
||||
@@ -447,8 +445,8 @@ util::Status DrmRootCertificate::VerifyCertificate(
|
||||
signed_certificate = local_signed_certificate.get();
|
||||
}
|
||||
if (!signed_certificate->ParseFromString(serialized_certificate)) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signed-drm-certificate");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signed-drm-certificate");
|
||||
}
|
||||
|
||||
std::unique_ptr<DrmCertificate> local_certificate;
|
||||
@@ -458,20 +456,19 @@ util::Status DrmRootCertificate::VerifyCertificate(
|
||||
}
|
||||
if (signed_certificate->drm_certificate().empty() ||
|
||||
!certificate->ParseFromString(signed_certificate->drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-drm-certificate");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-drm-certificate");
|
||||
}
|
||||
if (certificate->serial_number().empty()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-serial-number");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-serial-number");
|
||||
}
|
||||
if (!certificate->has_creation_time_seconds()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-creation-time");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-creation-time");
|
||||
}
|
||||
if (certificate->public_key().empty()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-public-key");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key");
|
||||
}
|
||||
|
||||
// Verify signature chain, but do not use cache for leaf certificates.
|
||||
@@ -485,7 +482,7 @@ util::Status DrmRootCertificate::VerifyCertificate(
|
||||
// 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.
|
||||
util::Status DrmRootCertificate::VerifySignatures(
|
||||
Status DrmRootCertificate::VerifySignatures(
|
||||
const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number,
|
||||
bool use_cache) const {
|
||||
if (!signed_cert.has_signer()) {
|
||||
@@ -497,12 +494,12 @@ util::Status DrmRootCertificate::VerifySignatures(
|
||||
|
||||
DrmCertificate signer;
|
||||
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate");
|
||||
}
|
||||
|
||||
// Verify the signer before verifying signed_cert.
|
||||
util::Status status =
|
||||
Status status =
|
||||
VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -519,17 +516,17 @@ util::Status DrmRootCertificate::VerifySignatures(
|
||||
std::unique_ptr<RsaPublicKey> signer_public_key(
|
||||
key_factory_->CreateFromPkcs1PublicKey(signer.public_key()));
|
||||
if (!signer_public_key) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-leaf-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 util::Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature");
|
||||
return Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature");
|
||||
}
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -42,13 +42,13 @@ class DrmRootCertificate {
|
||||
// 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
|
||||
// util::Status::OK on success, or appropriate error status otherwise.
|
||||
static util::Status CreateByType(CertificateType cert_type,
|
||||
std::unique_ptr<DrmRootCertificate>* cert);
|
||||
// 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, util::Status* status);
|
||||
CertificateType cert_type, Status* status);
|
||||
|
||||
// Creates a DrmRootCertificate object given a certificate type std::string, which
|
||||
// must be one of "prod", "qa", or "test".
|
||||
@@ -56,19 +56,16 @@ class DrmRootCertificate {
|
||||
// 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
|
||||
// util::Status::OK on success, or appropriate error status otherwise.
|
||||
static util::Status CreateByTypeString(
|
||||
const std::string& cert_type_string,
|
||||
std::unique_ptr<DrmRootCertificate>* cert);
|
||||
// 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 util::Status::OK if successful, or an appropriate error code
|
||||
// otherwise.
|
||||
virtual util::Status VerifyCertificate(
|
||||
const std::string& serialized_certificate,
|
||||
SignedDrmCertificate* signed_certificate,
|
||||
DrmCertificate* certificate) const;
|
||||
// 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;
|
||||
@@ -86,13 +83,13 @@ class DrmRootCertificate {
|
||||
private:
|
||||
friend class DrmRootCertificateTest;
|
||||
|
||||
static util::Status Create(CertificateType cert_type,
|
||||
std::unique_ptr<RsaKeyFactory> key_factory,
|
||||
std::unique_ptr<DrmRootCertificate>* cert);
|
||||
static Status Create(CertificateType cert_type,
|
||||
std::unique_ptr<RsaKeyFactory> key_factory,
|
||||
std::unique_ptr<DrmRootCertificate>* cert);
|
||||
|
||||
util::Status VerifySignatures(const SignedDrmCertificate& signed_cert,
|
||||
const std::string& cert_serial_number,
|
||||
bool use_cache) const;
|
||||
Status VerifySignatures(const SignedDrmCertificate& signed_cert,
|
||||
const std::string& cert_serial_number,
|
||||
bool use_cache) const;
|
||||
|
||||
CertificateType type_;
|
||||
std::string serialized_certificate_;
|
||||
|
||||
@@ -33,8 +33,8 @@ TEST(DrmRootCertificateCreateTest, TestCertificate) {
|
||||
"49f917b1bdfed78002a58e799a58e940"
|
||||
"1fffaaed9d8d80752782b066757e2c8c");
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeTesting, &root_cert));
|
||||
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeTesting, &root_cert));
|
||||
ASSERT_TRUE(root_cert != nullptr);
|
||||
EXPECT_EQ(kTestCertificateHash, root_cert->GetDigest());
|
||||
}
|
||||
@@ -44,8 +44,8 @@ TEST(DrmRootCertificateCreateTest, DevCertificate) {
|
||||
"0e25ee95476a770f30b98ac5ef778b3f"
|
||||
"137b66c29385b84f547a361b4724b17d");
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeDevelopment, &root_cert));
|
||||
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeDevelopment, &root_cert));
|
||||
ASSERT_TRUE(root_cert != nullptr);
|
||||
EXPECT_EQ(kDevelopmentCertificateHash, root_cert->GetDigest());
|
||||
}
|
||||
@@ -55,8 +55,8 @@ TEST(DrmRootCertificateCreateTest, ProdCertificate) {
|
||||
"d62fdabc9286648a81f7d3bedaf2f5a5"
|
||||
"27bbad39bc38da034ba98a21569adb9b");
|
||||
std::unique_ptr<DrmRootCertificate> root_cert;
|
||||
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeProduction, &root_cert));
|
||||
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeProduction, &root_cert));
|
||||
ASSERT_TRUE(root_cert != nullptr);
|
||||
EXPECT_EQ(kProductionCertificateHash, root_cert->GetDigest());
|
||||
}
|
||||
@@ -111,8 +111,8 @@ class DrmRootCertificateTest : public testing::Test {
|
||||
drm_certificates_[2].set_public_key(
|
||||
test_keys_.public_test_key_3_2048_bits());
|
||||
|
||||
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeTesting, &root_cert_));
|
||||
ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
|
||||
kCertificateTypeTesting, &root_cert_));
|
||||
}
|
||||
|
||||
void GenerateSignedDrmCertificate() {
|
||||
@@ -144,7 +144,7 @@ class DrmRootCertificateTest : public testing::Test {
|
||||
|
||||
TEST_F(DrmRootCertificateTest, SuccessNoOutput) {
|
||||
GenerateSignedDrmCertificate();
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
ASSERT_EQ(OkStatus(),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
}
|
||||
@@ -153,25 +153,25 @@ TEST_F(DrmRootCertificateTest, SuccessWithOutput) {
|
||||
GenerateSignedDrmCertificate();
|
||||
SignedDrmCertificate out_signed_cert;
|
||||
DrmCertificate out_cert;
|
||||
ASSERT_EQ(util::OkStatus(), root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(),
|
||||
&out_signed_cert, &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(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signed-drm-certificate"),
|
||||
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(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate"),
|
||||
EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
}
|
||||
@@ -179,86 +179,84 @@ TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
|
||||
TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
|
||||
GenerateSignedDrmCertificate();
|
||||
signed_drm_certificate_.clear_drm_certificate();
|
||||
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-drm-certificate"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
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(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-drm-certificate"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
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(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-public-key"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
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(
|
||||
util::Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"),
|
||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||
nullptr, nullptr));
|
||||
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(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-creation-time"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
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(util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-serial-number"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
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(util::Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
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(util::OkStatus(),
|
||||
ASSERT_EQ(OkStatus(),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
|
||||
// Verify success using cache.
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
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(
|
||||
util::Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"),
|
||||
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
|
||||
nullptr, nullptr));
|
||||
EXPECT_EQ(Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"),
|
||||
root_cert_->VerifyCertificate(
|
||||
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -107,41 +107,41 @@ DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
|
||||
|
||||
} // namespace
|
||||
|
||||
util::Status DrmServiceCertificate::AddDrmServiceCertificate(
|
||||
const DrmRootCertificate* root_cert, const std::string& service_certificate,
|
||||
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;
|
||||
util::Status status =
|
||||
root_cert->VerifyCertificate(service_certificate, nullptr, &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 util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"not-service-certificate");
|
||||
return Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"not-service-certificate");
|
||||
}
|
||||
if (drm_cert.provider_id().empty()) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"missing-certificate-service-id");
|
||||
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 util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"invalid-certificate-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 util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
||||
"key-decryption-failed");
|
||||
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 util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
||||
"invalid-private-key");
|
||||
return Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
|
||||
"invalid-private-key");
|
||||
}
|
||||
|
||||
std::unique_ptr<DrmServiceCertificate> new_cert(new DrmServiceCertificate(
|
||||
@@ -150,7 +150,7 @@ util::Status DrmServiceCertificate::AddDrmServiceCertificate(
|
||||
std::move(private_key)));
|
||||
DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert));
|
||||
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
const DrmServiceCertificate*
|
||||
@@ -171,7 +171,7 @@ const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate(
|
||||
return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number);
|
||||
}
|
||||
|
||||
util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||
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) {
|
||||
@@ -181,36 +181,36 @@ util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||
service_private_key_passphrase);
|
||||
}
|
||||
|
||||
util::Status DrmServiceCertificate::DecryptClientIdentification(
|
||||
Status DrmServiceCertificate::DecryptClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id) {
|
||||
DCHECK(client_id);
|
||||
if (encrypted_client_id.service_certificate_serial_number().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-service-certificate-serial-number");
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-service-certificate-serial-number");
|
||||
}
|
||||
if (encrypted_client_id.provider_id().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-service-id");
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-service-id");
|
||||
}
|
||||
if (encrypted_client_id.encrypted_client_id().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id");
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id");
|
||||
}
|
||||
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id-iv");
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-client-id-iv");
|
||||
}
|
||||
if (encrypted_client_id.encrypted_privacy_key().empty()) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"missing-encrypted-privacy-key");
|
||||
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 util::Status(
|
||||
return Status(
|
||||
error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
"service-certificate-not-found (SN " +
|
||||
absl::BytesToHexString(
|
||||
@@ -219,56 +219,56 @@ util::Status DrmServiceCertificate::DecryptClientIdentification(
|
||||
}
|
||||
if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(),
|
||||
&privacy_key)) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"privacy-key-decryption-failed");
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"privacy-key-decryption-failed");
|
||||
}
|
||||
if (cert->provider_id() != encrypted_client_id.provider_id()) {
|
||||
return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
std::string("provider-id-mismatch (") + 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 util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-decryption-failed");
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-decryption-failed");
|
||||
}
|
||||
if (!client_id->ParseFromString(serialized_client_id)) {
|
||||
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-parse-failed");
|
||||
return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
|
||||
"client-id-parse-failed");
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
void DrmServiceCertificate::ResetServiceCertificates() {
|
||||
DrmServiceCertificateMap::GetInstance()->Reset();
|
||||
}
|
||||
|
||||
util::Status DrmServiceCertificate::ValidateDrmServiceCertificate() {
|
||||
Status DrmServiceCertificate::ValidateDrmServiceCertificate() {
|
||||
const DrmServiceCertificate* service_certificate =
|
||||
GetDefaultDrmServiceCertificate();
|
||||
if (!service_certificate) {
|
||||
return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
|
||||
"drm service certificate is not found.");
|
||||
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 util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signed drm service certificate is failed to parse.");
|
||||
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 util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"Drm service certificate is failed to parse.");
|
||||
return Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"Drm service certificate is failed to parse.");
|
||||
}
|
||||
if (!drm_cert.has_creation_time_seconds()) {
|
||||
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE,
|
||||
"missing certificate creation time");
|
||||
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 util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
DrmServiceCertificate::DrmServiceCertificate(
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/macros.h"
|
||||
#include "common/status.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
class RequestInspectorTest;
|
||||
@@ -48,7 +48,7 @@ class DrmServiceCertificate {
|
||||
// If the default service certificate is not set, this certificate will be
|
||||
// used as the default service certificate.
|
||||
// This method is thread-safe.
|
||||
static util::Status AddDrmServiceCertificate(
|
||||
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);
|
||||
@@ -56,7 +56,7 @@ class DrmServiceCertificate {
|
||||
// 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 util::Status SetDefaultDrmServiceCertificate(
|
||||
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);
|
||||
@@ -79,7 +79,7 @@ class DrmServiceCertificate {
|
||||
// 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 util::Status DecryptClientIdentification(
|
||||
static Status DecryptClientIdentification(
|
||||
const EncryptedClientIdentification& encrypted_client_id,
|
||||
ClientIdentification* client_id);
|
||||
|
||||
@@ -93,18 +93,18 @@ class DrmServiceCertificate {
|
||||
// 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 util::Status ValidateDrmServiceCertificate();
|
||||
static Status ValidateDrmServiceCertificate();
|
||||
|
||||
private:
|
||||
friend class DrmServiceCertificateTest;
|
||||
friend class widevine::RequestInspectorTest;
|
||||
|
||||
static util::Status AddDrmServiceCertificate(
|
||||
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 util::Status SetDefaultDrmServiceCertificate(
|
||||
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);
|
||||
|
||||
@@ -69,9 +69,9 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
||||
return serialized_cert;
|
||||
}
|
||||
|
||||
util::Status SetDefaultDrmServiceCertificate(const std::string& serial_number,
|
||||
const std::string& provider_id,
|
||||
uint32_t creation_time_seconds) {
|
||||
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()));
|
||||
@@ -79,15 +79,15 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
||||
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
||||
&encrypted_private_key)) {
|
||||
return util::Status(util::error::INTERNAL, "");
|
||||
return Status(error::INTERNAL, "");
|
||||
}
|
||||
return DrmServiceCertificate::SetDefaultDrmServiceCertificate(
|
||||
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
|
||||
}
|
||||
|
||||
util::Status AddDrmServiceCertificate(const std::string& serial_number,
|
||||
const std::string& provider_id,
|
||||
uint32_t creation_time_seconds) {
|
||||
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()));
|
||||
@@ -95,7 +95,7 @@ class DrmServiceCertificateTest : public ::testing::Test {
|
||||
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
|
||||
test_keys_.private_test_key_2_2048_bits(), kPassphrase,
|
||||
&encrypted_private_key)) {
|
||||
return util::Status(util::error::INTERNAL, "");
|
||||
return Status(error::INTERNAL, "");
|
||||
}
|
||||
return DrmServiceCertificate::AddDrmServiceCertificate(
|
||||
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
|
||||
@@ -140,9 +140,8 @@ TEST_F(DrmServiceCertificateTest, BasicClientIdDecrypt) {
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &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));
|
||||
}
|
||||
@@ -220,27 +219,24 @@ TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) {
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &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(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &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_number3, provider_id,
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &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));
|
||||
|
||||
@@ -292,9 +288,8 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &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));
|
||||
|
||||
@@ -310,9 +305,8 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
||||
|
||||
invalid = encrypted_client_id;
|
||||
++(*invalid.mutable_encrypted_client_id_iv())[4];
|
||||
EXPECT_NE(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
invalid, &decrypted_client_id));
|
||||
EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
invalid, &decrypted_client_id));
|
||||
|
||||
invalid.clear_encrypted_client_id_iv();
|
||||
EXPECT_EQ(
|
||||
@@ -324,9 +318,8 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
|
||||
|
||||
invalid = encrypted_client_id;
|
||||
++(*invalid.mutable_encrypted_client_id())[0];
|
||||
EXPECT_NE(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
invalid, &decrypted_client_id));
|
||||
EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
|
||||
invalid, &decrypted_client_id));
|
||||
|
||||
invalid.clear_encrypted_client_id();
|
||||
EXPECT_EQ(
|
||||
@@ -349,9 +342,8 @@ TEST_F(DrmServiceCertificateTest, PrivateKeyDecryptError) {
|
||||
test_keys_.public_test_key_2_2048_bits(),
|
||||
&encrypted_client_id);
|
||||
ClientIdentification decrypted_client_id;
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
DrmServiceCertificate::DecryptClientIdentification(
|
||||
encrypted_client_id, &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));
|
||||
|
||||
|
||||
@@ -113,99 +113,99 @@ void RemoteAttestationVerifier::EnableTestDrmCertificates(bool enable) {
|
||||
ca_.reset();
|
||||
}
|
||||
|
||||
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
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 (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-certificate-missing"));
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-certificate-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_salt()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-salt-missing"));
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-salt-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_signature()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-signature-missing"));
|
||||
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;
|
||||
util::Status status = DrmServiceCertificate::DecryptClientIdentification(
|
||||
Status status = DrmServiceCertificate::DecryptClientIdentification(
|
||||
remote_attestation.certificate(), &client_id);
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (client_id.type() !=
|
||||
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
std::string("remote-attestation-invalid-client-id-type (") +
|
||||
absl::StrCat(client_id.type()) + ")"));
|
||||
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);
|
||||
}
|
||||
|
||||
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
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 (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-certificate-missing"));
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-certificate-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_salt()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-salt-missing"));
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-salt-missing"));
|
||||
}
|
||||
if (!remote_attestation.has_signature()) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-signature-missing"));
|
||||
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;
|
||||
util::Status status = DecryptEncryptedClientIdentification(
|
||||
Status status = DecryptEncryptedClientIdentification(
|
||||
remote_attestation.certificate(), privacy_key, &client_id);
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (client_id.type() !=
|
||||
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
std::string("remote-attestation-invalid-client-id-type (") +
|
||||
absl::StrCat(client_id.type()) + ")"));
|
||||
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);
|
||||
}
|
||||
|
||||
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
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 (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-token-missing"));
|
||||
return (Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-token-missing"));
|
||||
}
|
||||
// Load and verify the certificate chain.
|
||||
std::unique_ptr<X509CertChain> cert_chain(new X509CertChain);
|
||||
util::Status status = cert_chain->LoadPem(client_id.token());
|
||||
Status status = cert_chain->LoadPem(client_id.token());
|
||||
if (!status.ok()) return status;
|
||||
|
||||
if (cert_chain->GetNumCerts() < 1) {
|
||||
return (util::Status(error_space, INVALID_MESSAGE,
|
||||
"remote-attestation-empty-certificate-chain"));
|
||||
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 (util::Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
std::string("remote-attestation-device-not-verified (") +
|
||||
device_mode_string + " / " + kDeviceModeFieldName +
|
||||
")"));
|
||||
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
std::string("remote-attestation-device-not-verified (") +
|
||||
device_mode_string + " / " + kDeviceModeFieldName +
|
||||
")"));
|
||||
}
|
||||
ca_mutex_.ReaderLock();
|
||||
if (ca_ == NULL) {
|
||||
@@ -217,10 +217,9 @@ util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
status = ca_->VerifyCertChain(*cert_chain);
|
||||
ca_mutex_.ReaderUnlock();
|
||||
if (!status.ok()) {
|
||||
return (util::Status(
|
||||
error_space, REMOTE_ATTESTATION_FAILED,
|
||||
std::string("remote-attestation-cert-chain-validation-failed: ") +
|
||||
status.error_message()));
|
||||
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;
|
||||
@@ -232,30 +231,30 @@ util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
|
||||
}
|
||||
}
|
||||
if (!leaf_key) {
|
||||
return util::Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
"remote-attestation-cert-chain-no-leaf");
|
||||
return Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
"remote-attestation-cert-chain-no-leaf");
|
||||
}
|
||||
|
||||
if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt,
|
||||
remote_attestation.signature())) {
|
||||
return (util::Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
"remote-attestation-signature-verification-failed: "));
|
||||
return (Status(error_space, REMOTE_ATTESTATION_FAILED,
|
||||
"remote-attestation-signature-verification-failed: "));
|
||||
}
|
||||
|
||||
*remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber();
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status RemoteAttestationVerifier::LoadCa() {
|
||||
Status RemoteAttestationVerifier::LoadCa() {
|
||||
absl::WriterMutexLock lock(&ca_mutex_);
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
util::Status status = ca_cert->LoadDer(absl::HexStringToBytes(
|
||||
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 util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -50,9 +50,9 @@ class RemoteAttestationVerifier {
|
||||
// return will contain the serial number for the client's remote attestation
|
||||
// certificate.
|
||||
// This method is thread-safe.
|
||||
util::Status VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
std::string* remote_attestation_cert_sn);
|
||||
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.
|
||||
@@ -61,9 +61,9 @@ class RemoteAttestationVerifier {
|
||||
// |privacy_key| is used to decrypt the EncryptedClientIdentification within
|
||||
// the |remote_attestation| message.
|
||||
// This method is thread-safe.
|
||||
util::Status VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
const std::string& privacy_key);
|
||||
Status VerifyRemoteAttestation(const std::string& message,
|
||||
const RemoteAttestation& remote_attestation,
|
||||
const std::string& privacy_key);
|
||||
|
||||
private:
|
||||
// Common subroutine to perform the verification.
|
||||
@@ -73,12 +73,12 @@ class RemoteAttestationVerifier {
|
||||
// |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.
|
||||
util::Status VerifyRemoteAttestation(
|
||||
const std::string& message, const RemoteAttestation& remote_attestation,
|
||||
const ClientIdentification& client_id,
|
||||
std::string* remote_attestation_cert_sn);
|
||||
Status VerifyRemoteAttestation(const std::string& message,
|
||||
const RemoteAttestation& remote_attestation,
|
||||
const ClientIdentification& client_id,
|
||||
std::string* remote_attestation_cert_sn);
|
||||
|
||||
util::Status LoadCa();
|
||||
Status LoadCa();
|
||||
|
||||
bool enable_test_certificates_;
|
||||
absl::Mutex ca_mutex_;
|
||||
|
||||
@@ -29,6 +29,14 @@ 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
|
||||
|
||||
@@ -21,6 +21,9 @@ 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.
|
||||
|
||||
@@ -48,6 +48,18 @@ TEST(ShaUtilTest, Sha256) {
|
||||
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");
|
||||
|
||||
@@ -11,55 +11,50 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/status.h"
|
||||
#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 {
|
||||
|
||||
util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
||||
const std::string& aes_iv, std::string* signature) {
|
||||
Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
||||
const std::string& aes_iv, std::string* signature) {
|
||||
if (signature == nullptr) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "signature is nullptr");
|
||||
return Status(error::INVALID_ARGUMENT, "signature is nullptr");
|
||||
}
|
||||
std::string hash = Sha1_Hash(message);
|
||||
if (hash.empty()) {
|
||||
return util::Status(util::error::INTERNAL, "Computed hash is empty");
|
||||
return Status(error::INTERNAL, "Computed hash is empty");
|
||||
}
|
||||
std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash);
|
||||
if (sig.empty()) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Computed AES signature is empty");
|
||||
return Status(error::INTERNAL, "Computed AES signature is empty");
|
||||
}
|
||||
*signature = sig;
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status GenerateRsaSignature(const std::string& message,
|
||||
const std::string& private_key,
|
||||
std::string* signature) {
|
||||
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
|
||||
std::string* signature) {
|
||||
if (signature == nullptr) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "signature is 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 util::Status(util::error::INTERNAL,
|
||||
"Failed to construct a RsaPrivateKey");
|
||||
return Status(error::INTERNAL, "Failed to construct a RsaPrivateKey");
|
||||
}
|
||||
std::string sig;
|
||||
if (!rsa_private_key->GenerateSignature(message, &sig)) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Failed to generate a RSA signature");
|
||||
return Status(error::INTERNAL, "Failed to generate a RSA signature");
|
||||
}
|
||||
if (sig.empty()) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Computed RSA signature is empty");
|
||||
return Status(error::INTERNAL, "Computed RSA signature is empty");
|
||||
}
|
||||
*signature = sig;
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace signature_util
|
||||
|
||||
@@ -19,14 +19,14 @@ 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.
|
||||
util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
|
||||
const std::string& aes_iv, std::string* signature);
|
||||
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.
|
||||
util::Status GenerateRsaSignature(const std::string& message,
|
||||
const std::string& private_key, std::string* signature);
|
||||
Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
|
||||
std::string* signature);
|
||||
|
||||
} // namespace signature_util
|
||||
} // namespace widevine
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace widevine {
|
||||
|
||||
using crypto_util::kSigningKeySizeBits;
|
||||
|
||||
uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version) {
|
||||
uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version) {
|
||||
if (protocol_version <= VERSION_2_0) {
|
||||
return kSigningKeySizeBits;
|
||||
} else {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// std::string derived_key = DeriveKey(key_str,
|
||||
// label,
|
||||
// context,
|
||||
// SigningKeyMaterialSize(VERSION_2_1));
|
||||
// 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_
|
||||
@@ -36,7 +36,7 @@ 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 SigningKeyMaterialSize(ProtocolVersion protocol_version);
|
||||
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
|
||||
|
||||
@@ -30,14 +30,14 @@ std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_0) {
|
||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_0) {
|
||||
ASSERT_EQ(crypto_util::kSigningKeySizeBits,
|
||||
SigningKeyMaterialSize(VERSION_2_0));
|
||||
SigningKeyMaterialSizeBits(VERSION_2_0));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_1) {
|
||||
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_1) {
|
||||
ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2,
|
||||
SigningKeyMaterialSize(VERSION_2_1));
|
||||
SigningKeyMaterialSizeBits(VERSION_2_1));
|
||||
}
|
||||
|
||||
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) {
|
||||
|
||||
@@ -12,10 +12,9 @@
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "common/status.h"
|
||||
#include "util/error_space.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace util {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -72,5 +71,4 @@ std::ostream& operator<<(std::ostream& os, const Status& x) {
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace widevine
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "util/error_space.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace util {
|
||||
namespace error {
|
||||
|
||||
enum StatusCode {
|
||||
@@ -104,7 +103,6 @@ std::ostream& operator<<(std::ostream& os, const Status& x);
|
||||
#define CHECK_OK(expression) CHECK(expression.ok()) << expression.ToString()
|
||||
|
||||
|
||||
} // namespace util
|
||||
} // namespace widevine
|
||||
|
||||
#endif // COMMON_STATUS_H_
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "testing/gunit.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace util {
|
||||
|
||||
TEST(StatusTest, OK_Status) {
|
||||
// test case for ok status.
|
||||
@@ -59,5 +58,4 @@ TEST(StatusTest, NOT_EQUAL_OPERATOR_NONE_MSG) {
|
||||
}
|
||||
|
||||
|
||||
} // namespace util
|
||||
} // namespace widevine
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
namespace widevine {
|
||||
namespace string_util {
|
||||
|
||||
util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) {
|
||||
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output) {
|
||||
if (output == nullptr) {
|
||||
return util::Status(util::error::INTERNAL, "output is nullptr.");
|
||||
return Status(error::INTERNAL, "output is nullptr.");
|
||||
}
|
||||
|
||||
std::stringstream sstream(bitset);
|
||||
@@ -29,7 +29,7 @@ util::Status BitsetStringToBinaryString(const std::string& bitset, std::string*
|
||||
*output += c;
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace string_util
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace string_util {
|
||||
|
||||
// Converts std::string representation of a bitset to its binary equivalent string.
|
||||
// For example, converts "01110100011001010111001101110100" to "test".
|
||||
util::Status BitsetStringToBinaryString(const std::string& bitset, std::string* output);
|
||||
Status BitsetStringToBinaryString(const std::string& bitset, std::string* output);
|
||||
|
||||
} // namespace string_util
|
||||
} // namespace widevine
|
||||
|
||||
@@ -18,32 +18,31 @@
|
||||
|
||||
namespace widevine {
|
||||
|
||||
util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||
const std::string& message,
|
||||
std::string* signature) {
|
||||
Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||
const std::string& message,
|
||||
std::string* signature) {
|
||||
CHECK(signature);
|
||||
if (pem_private_key.empty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM private key");
|
||||
return Status(error::INVALID_ARGUMENT, "Empty PEM private key");
|
||||
}
|
||||
if (message.empty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty message");
|
||||
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 util::Status(util::error::INTERNAL, "BIO allocation failed");
|
||||
return Status(error::INTERNAL, "BIO allocation failed");
|
||||
}
|
||||
util::Status status;
|
||||
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 = util::Status(util::Status::canonical_space(),
|
||||
util::error::INVALID_ARGUMENT,
|
||||
"PEM RSA private key load failed");
|
||||
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(),
|
||||
@@ -53,9 +52,8 @@ util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||
if (RSA_sign(NID_sha256, digest, sizeof(digest),
|
||||
reinterpret_cast<unsigned char*>(sig_buffer.get()), &sig_size,
|
||||
key) != 1) {
|
||||
status =
|
||||
util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
||||
"RSA private encrypt failed");
|
||||
status = Status(Status::canonical_space(), error::INTERNAL,
|
||||
"RSA private encrypt failed");
|
||||
goto cleanup;
|
||||
}
|
||||
signature->assign(sig_buffer.get(), sig_size);
|
||||
|
||||
@@ -23,9 +23,9 @@ namespace widevine {
|
||||
// |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.
|
||||
util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||
const std::string& message,
|
||||
std::string* signature);
|
||||
Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
||||
const std::string& message,
|
||||
std::string* signature);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
|
||||
@@ -14,13 +14,11 @@
|
||||
#include "common/vmp_checker.h"
|
||||
|
||||
namespace widevine {
|
||||
util::Status VerifyVmpData(
|
||||
const std::string& vmp_data,
|
||||
PlatformVerificationStatus* platform_verification_status) {
|
||||
Status VerifyVmpData(const std::string& vmp_data,
|
||||
PlatformVerificationStatus* platform_verification_status) {
|
||||
*platform_verification_status = PLATFORM_UNVERIFIED;
|
||||
VmpChecker::Result vmp_result;
|
||||
util::Status status =
|
||||
VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result);
|
||||
Status status = VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result);
|
||||
if (status.ok()) {
|
||||
switch (vmp_result) {
|
||||
case VmpChecker::kUnverified:
|
||||
|
||||
@@ -20,9 +20,8 @@ namespace widevine {
|
||||
|
||||
// Retrieve the PlatformVerificationStatus for |vmp_data|. The
|
||||
// PlatformVerificationStatus is defined at
|
||||
util::Status VerifyVmpData(
|
||||
const std::string& vmp_data,
|
||||
PlatformVerificationStatus* platform_verification_status);
|
||||
Status VerifyVmpData(const std::string& vmp_data,
|
||||
PlatformVerificationStatus* platform_verification_status);
|
||||
|
||||
} // namespace widevine
|
||||
#endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_
|
||||
|
||||
@@ -248,9 +248,9 @@ VmpChecker::VmpChecker() : allow_development_vmp_(false) {}
|
||||
|
||||
VmpChecker::~VmpChecker() {}
|
||||
|
||||
util::Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
|
||||
Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
util::Status status = ca_cert->LoadDer(
|
||||
Status status = ca_cert->LoadDer(
|
||||
cert_type == kCertificateTypeProduction
|
||||
? std::string(reinterpret_cast<const char*>(
|
||||
kProdVmpCodeSigningDrmRootCertificate),
|
||||
@@ -262,7 +262,7 @@ util::Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
|
||||
|
||||
ca_.reset(new X509CA(ca_cert.release()));
|
||||
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
VmpChecker* VmpChecker::Instance() {
|
||||
@@ -271,17 +271,16 @@ VmpChecker* VmpChecker::Instance() {
|
||||
}
|
||||
|
||||
// Verify VMP data and return appropriate result.
|
||||
util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) {
|
||||
Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* result) {
|
||||
DCHECK(!vmp_data.empty());
|
||||
DCHECK(result);
|
||||
|
||||
if (!ca_) return util::Status(error_space, CERT_CHAIN_NOT_SELECTED, "");
|
||||
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 util::Status(error_space, INVALID_MESSAGE,
|
||||
"vmp-data-deserialize-failed");
|
||||
return Status(error_space, INVALID_MESSAGE, "vmp-data-deserialize-failed");
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<X509Cert>> code_signing_certs;
|
||||
@@ -290,7 +289,7 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
||||
for (int cert_idx = 0; cert_idx < vmp_data_obj.certificates_size();
|
||||
++cert_idx) {
|
||||
code_signing_certs.emplace_back(new X509Cert);
|
||||
util::Status status(code_signing_certs.back()->LoadDer(
|
||||
Status status(code_signing_certs.back()->LoadDer(
|
||||
vmp_data_obj.certificates(cert_idx)));
|
||||
if (!status.ok()) return status;
|
||||
|
||||
@@ -299,8 +298,8 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
||||
if (code_signing_certs.back()->GetV3BooleanExtension(kDevelopmentFlagOid,
|
||||
&dev_flag) &&
|
||||
dev_flag) {
|
||||
return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
"development-vmp-certificate-not-allowed");
|
||||
return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
|
||||
"development-vmp-certificate-not-allowed");
|
||||
}
|
||||
}
|
||||
status = ca_->VerifyCert(*code_signing_certs.back());
|
||||
@@ -324,12 +323,12 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
||||
if (binary_info.signature().empty()) {
|
||||
LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\".";
|
||||
*result = kTampered;
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
if (binary_info.certificate_index() >= code_signing_certs.size()) {
|
||||
LOG(INFO) << "Invalid code signing certificate index.";
|
||||
*result = kTampered;
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get();
|
||||
std::unique_ptr<RsaPublicKey> key(cert->GetRsaPublicKey());
|
||||
@@ -339,7 +338,7 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
||||
LOG(INFO) << "Code signature verification failed for file \""
|
||||
<< binary_info.file_name() << "\".";
|
||||
*result = kTampered;
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
if (binary_info.flags() & kBlessedBinaryFlag) ++num_blessed_binaries;
|
||||
}
|
||||
@@ -347,13 +346,13 @@ util::Status VmpChecker::VerifyVmpData(const std::string& vmp_data, Result* resu
|
||||
LOG(INFO) << "Invalid number of blessed binaries (" << num_blessed_binaries
|
||||
<< ").";
|
||||
*result = kTampered;
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
VLOG(2) << "VMP verification success. Secure storage: "
|
||||
<< secure_storage_verified;
|
||||
*result = secure_storage_verified ? kSecureStorageVerified : kVerified;
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/status.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
class X509CA;
|
||||
@@ -35,10 +35,10 @@ class VmpChecker {
|
||||
static VmpChecker* Instance();
|
||||
|
||||
// Select the type of root to use. Not thread-safe.
|
||||
virtual util::Status SelectCertificateType(CertificateType cert_type);
|
||||
virtual Status SelectCertificateType(CertificateType cert_type);
|
||||
|
||||
// Verify VMP data and return appropriate result.
|
||||
virtual util::Status VerifyVmpData(const std::string& vmp_data, Result* 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; }
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "util/endian/endian.h"
|
||||
#include "util/gtl/map_util.h"
|
||||
#include "common/status.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "common/ecb_util.h"
|
||||
#include "common/sha_util.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
@@ -108,20 +108,20 @@ bool WvmTokenHandler::IsSystemIdKnown(uint32_t system_id) {
|
||||
return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id);
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out,
|
||||
bool* insecure_out) {
|
||||
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);
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
const std::string& make_model,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out,
|
||||
bool* 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
|
||||
@@ -129,12 +129,11 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
// presubmit because evidently Kokoro does debug build.
|
||||
// DCHECK(preprov_keys_);
|
||||
if (token.size() < kKeyboxSizeBytes) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Keybox token is too short.");
|
||||
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
|
||||
}
|
||||
if (PreprovKeysMap::GetSingleton()->IsEmpty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Pre-provisioning key map is nullptr.");
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Pre-provisioning key map is nullptr.");
|
||||
}
|
||||
|
||||
uint32_t system_id = GetSystemId(token);
|
||||
@@ -143,7 +142,7 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
std::vector<PreprovKey> key_vector =
|
||||
PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id);
|
||||
|
||||
util::Status status;
|
||||
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;
|
||||
@@ -162,8 +161,8 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
ppk.key_bytes, token, device_key_out, insecure_out, &version);
|
||||
if (version != 2) {
|
||||
// Only version 2 keyboxes supported.
|
||||
return util::Status(util::error::PERMISSION_DENIED,
|
||||
absl::StrCat("invalid-keybox-version ", version));
|
||||
return Status(error::PERMISSION_DENIED,
|
||||
absl::StrCat("invalid-keybox-version ", version));
|
||||
}
|
||||
if (status.ok()) {
|
||||
if (cipher_out) {
|
||||
@@ -176,8 +175,8 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
// Return error from last attempt.
|
||||
return status;
|
||||
}
|
||||
return util::Status(
|
||||
util::error::NOT_FOUND,
|
||||
return Status(
|
||||
error::NOT_FOUND,
|
||||
absl::StrCat("Unknown system id: ", system_id).c_str()); // NOLINT
|
||||
}
|
||||
|
||||
@@ -185,12 +184,13 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
|
||||
// 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.
|
||||
util::Status WvmTokenHandler::GetEncryptedAssetKey(
|
||||
absl::string_view token, absl::string_view raw_asset_key,
|
||||
const std::string& make_model, std::string* result) {
|
||||
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;
|
||||
util::Status status =
|
||||
Status status =
|
||||
DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -215,20 +215,19 @@ std::string WvmTokenHandler::GetEncryptedUniqueId(absl::string_view token) {
|
||||
return encrypted_unique_id;
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
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);
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
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 util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Keybox token is too short.");
|
||||
return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
|
||||
}
|
||||
if (version) {
|
||||
*version = BigEndian::Load32(token.data());
|
||||
@@ -243,7 +242,7 @@ util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
if (unique_id.size() != 16) {
|
||||
// Decrypting 16 bytes should result in 16 bytes.
|
||||
LOG(WARNING) << "Internal error decrypting unique id from token.";
|
||||
return util::Status(util::error::INTERNAL, "Wrong size after decrypt/16.");
|
||||
return Status(error::INTERNAL, "Wrong size after decrypt/16.");
|
||||
}
|
||||
|
||||
absl::string_view encrypted_bits = token.substr(24, 48);
|
||||
@@ -252,7 +251,7 @@ util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
if (decrypted_bits.size() != 48) {
|
||||
// Decrypting 48 bytes should result in 48 bytes.
|
||||
LOG(WARNING) << "Internal error decrypting device key from token.";
|
||||
return util::Status(util::error::INTERNAL, "Wrong size after decrypt/48.");
|
||||
return Status(error::INTERNAL, "Wrong size after decrypt/48.");
|
||||
}
|
||||
uint8_t keybox_flags = decrypted_bits[36];
|
||||
absl::string_view device_key =
|
||||
@@ -269,48 +268,43 @@ util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
keybox_flags = 0;
|
||||
}
|
||||
if (expected_hash != actual_hash) {
|
||||
return util::Status(util::error::PERMISSION_DENIED,
|
||||
"Keybox validation failed.");
|
||||
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 util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key,
|
||||
absl::string_view raw_asset_key,
|
||||
Cipher cipher, std::string* result) {
|
||||
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 util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Invalid device key: size != 16");
|
||||
return Status(error::INVALID_ARGUMENT, "Invalid device key: size != 16");
|
||||
}
|
||||
if (raw_asset_key.size() < 16) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Invalid 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 util::Status(util::error::INTERNAL,
|
||||
"Error encrypting asset key with 3DES.");
|
||||
return Status(error::INTERNAL, "Error encrypting asset key with 3DES.");
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
case AES:
|
||||
if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Error encrypting asset key with AES.");
|
||||
return Status(error::INTERNAL, "Error encrypting asset key with AES.");
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
case PASS_THRU:
|
||||
result->assign(raw_asset_key.data(), raw_asset_key.size());
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
default:
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Unknown cipher type");
|
||||
return Status(error::INVALID_ARGUMENT, "Unknown cipher type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,24 +70,23 @@ class WvmTokenHandler {
|
||||
// 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 util::Status DecryptDeviceKey(absl::string_view token,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out, bool* insecure_out);
|
||||
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 util::Status DecryptDeviceKey(absl::string_view token,
|
||||
const std::string& make_model,
|
||||
std::string* device_key_out,
|
||||
Cipher* cipher_out, bool* insecure_out);
|
||||
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 util::Status GetEncryptedAssetKey(absl::string_view token,
|
||||
absl::string_view raw_asset_key,
|
||||
const std::string& make_model,
|
||||
std::string* 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);
|
||||
@@ -101,21 +100,21 @@ class WvmTokenHandler {
|
||||
// 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 util::Status DecryptDeviceKeyWithPreprovKey(
|
||||
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 util::Status DecryptDeviceKeyWithPreprovKey(
|
||||
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 util::Status EncryptAssetKey(absl::string_view device_key,
|
||||
absl::string_view raw_asset_key,
|
||||
Cipher cipher, std::string* result);
|
||||
static Status EncryptAssetKey(absl::string_view device_key,
|
||||
absl::string_view raw_asset_key, Cipher cipher,
|
||||
std::string* result);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler);
|
||||
|
||||
@@ -29,12 +29,6 @@ namespace widevine {
|
||||
using absl::BytesToHexString;
|
||||
using absl::HexStringToBytes;
|
||||
|
||||
// TODO(user): Add EXPECT_OK macro to testing/gmock.h.
|
||||
// (b/37545268).
|
||||
#define EXPECT_OK(expression) \
|
||||
EXPECT_EQ(util::error::OK, expression.error_code())
|
||||
|
||||
|
||||
TEST(WvmTokenHandlerTest, GetSystemId) {
|
||||
EXPECT_EQ(kTestSystemId,
|
||||
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex)));
|
||||
@@ -52,7 +46,7 @@ TEST(WvmTokenHandlerTest, GetEncryptedUniqueId) {
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
|
||||
util::Status status;
|
||||
Status status;
|
||||
std::string device_key;
|
||||
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
@@ -76,7 +70,7 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(kTestPreprovKeyHex), token, &device_key);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
}
|
||||
|
||||
@@ -86,18 +80,18 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
|
||||
TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) {
|
||||
// Not calling WvmTokenHandler::SetPreprovKeys()
|
||||
// So preprov_keys_ would be nullptr.
|
||||
util::Status status;
|
||||
Status status;
|
||||
std::string device_key;
|
||||
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
|
||||
&device_key, nullptr, nullptr);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(util::error::INVALID_ARGUMENT, status.error_code());
|
||||
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) {
|
||||
util::Status status;
|
||||
Status status;
|
||||
std::string device_key;
|
||||
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
|
||||
|
||||
@@ -120,7 +114,7 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
|
||||
status =
|
||||
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
|
||||
// Test with nonexistent system id. Should produce NOT_FOUND.
|
||||
@@ -132,7 +126,7 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
|
||||
status =
|
||||
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(util::error::NOT_FOUND, status.error_code());
|
||||
EXPECT_EQ(error::NOT_FOUND, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
}
|
||||
|
||||
@@ -142,7 +136,7 @@ TEST(WvmTokenHandlerTest, GetEncryptedAssetKey) {
|
||||
std::string raw_asset_key = "asset-key-000000";
|
||||
std::string asset_key;
|
||||
std::string make_model;
|
||||
util::Status status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
Status status = WvmTokenHandler::GetEncryptedAssetKey(
|
||||
HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key));
|
||||
@@ -194,7 +188,7 @@ TEST(WvmTokenHandlerTest, FilterOnMakeModel) {
|
||||
std::string raw_asset_key = "asset-key-000000";
|
||||
std::string asset_key;
|
||||
// Check 3DES encryption of asset keys
|
||||
util::Status status = WvmTokenHandler::EncryptAssetKey(
|
||||
Status status = WvmTokenHandler::EncryptAssetKey(
|
||||
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
|
||||
WvmTokenHandler::DES3, &asset_key);
|
||||
EXPECT_OK(status);
|
||||
@@ -220,7 +214,7 @@ TEST(WvmTokenHandlerTest, FilterOnMakeModel) {
|
||||
}
|
||||
|
||||
TEST(WvmTokenHandlerTest, AncientKeybox) {
|
||||
util::Status status;
|
||||
Status status;
|
||||
std::string device_key;
|
||||
|
||||
std::string v1_token(
|
||||
@@ -228,7 +222,7 @@ TEST(WvmTokenHandlerTest, AncientKeybox) {
|
||||
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
|
||||
HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex),
|
||||
&device_key);
|
||||
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
|
||||
EXPECT_TRUE(device_key.empty());
|
||||
}
|
||||
|
||||
|
||||
@@ -67,22 +67,20 @@ X509Cert::~X509Cert() {
|
||||
|
||||
X509Cert::X509Cert(X509* openssl_cert) : openssl_cert_(openssl_cert) {}
|
||||
|
||||
util::Status X509Cert::LoadPem(const std::string& pem_cert) {
|
||||
Status X509Cert::LoadPem(const std::string& pem_cert) {
|
||||
if (pem_cert.empty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty PEM certificate");
|
||||
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 util::Status(util::error::INTERNAL, "BIO allocation failed");
|
||||
return Status(error::INTERNAL, "BIO allocation failed");
|
||||
}
|
||||
util::Status status;
|
||||
Status status;
|
||||
new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
|
||||
if (new_cert == NULL) {
|
||||
status = util::Status(util::Status::canonical_space(),
|
||||
util::error::INVALID_ARGUMENT,
|
||||
"PEM certificate load failed");
|
||||
status = Status(error::INVALID_ARGUMENT, "PEM certificate load failed");
|
||||
goto cleanup;
|
||||
}
|
||||
if (openssl_cert_ != NULL) {
|
||||
@@ -97,22 +95,21 @@ cleanup:
|
||||
return status;
|
||||
}
|
||||
|
||||
util::Status X509Cert::LoadDer(const std::string& der_cert) {
|
||||
Status X509Cert::LoadDer(const std::string& der_cert) {
|
||||
if (der_cert.empty()) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT, "Empty DER certificate");
|
||||
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 util::Status(util::error::INVALID_ARGUMENT,
|
||||
"DER certificate load failed");
|
||||
return Status(error::INVALID_ARGUMENT, "DER certificate load failed");
|
||||
}
|
||||
if (openssl_cert_ != NULL) {
|
||||
X509_free(openssl_cert_);
|
||||
}
|
||||
openssl_cert_ = new_cert;
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
std::string X509Cert::GetPem() const {
|
||||
@@ -222,38 +219,36 @@ bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const
|
||||
return true;
|
||||
}
|
||||
|
||||
util::Status X509Cert::Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
|
||||
int64_t* epoch_seconds) const {
|
||||
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 util::Status(util::error::INVALID_ARGUMENT,
|
||||
"asn1_time cannot be null.");
|
||||
return Status(error::INVALID_ARGUMENT, "asn1_time cannot be null.");
|
||||
}
|
||||
|
||||
if (epoch_seconds == nullptr) {
|
||||
// NOLINTNEXTLINE
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"epoch_seconds cannot be null.");
|
||||
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 util::Status(util::error::INTERNAL, "Failed to set epoch time.");
|
||||
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 util::Status(util::error::INTERNAL,
|
||||
"Failed to convert asn1 time to epoch time.");
|
||||
return Status(error::INTERNAL,
|
||||
"Failed to convert asn1 time to epoch time.");
|
||||
}
|
||||
|
||||
*epoch_seconds = 24L * 3600L * day + seconds;
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
X509CertChain::~X509CertChain() { Reset(); }
|
||||
@@ -265,7 +260,7 @@ void X509CertChain::Reset() {
|
||||
cert_chain_.clear();
|
||||
}
|
||||
|
||||
util::Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
||||
Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
||||
static const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----";
|
||||
static const char kEndCertificate[] = "-----END CERTIFICATE-----";
|
||||
|
||||
@@ -277,7 +272,7 @@ util::Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
||||
if (end_pos != std::string::npos) {
|
||||
end_pos += sizeof(kEndCertificate) - 1;
|
||||
std::unique_ptr<X509Cert> new_cert(new X509Cert);
|
||||
util::Status status = new_cert->LoadPem(
|
||||
Status status = new_cert->LoadPem(
|
||||
pem_cert_chain.substr(begin_pos, end_pos - begin_pos));
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -286,17 +281,17 @@ util::Status X509CertChain::LoadPem(const std::string& pem_cert_chain) {
|
||||
begin_pos = pem_cert_chain.find(kBeginCertificate, end_pos);
|
||||
}
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) {
|
||||
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 util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Unable to load PKCS#7 certificate chain");
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Unable to load PKCS#7 certificate chain");
|
||||
}
|
||||
|
||||
while (sk_X509_num(cert_stack.get()) > 0) {
|
||||
@@ -304,7 +299,7 @@ util::Status X509CertChain::LoadPkcs7(const std::string& pk7_cert_chain) {
|
||||
new X509Cert(sk_X509_pop(cert_stack.get())));
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
std::string X509CertChain::GetPkcs7() {
|
||||
@@ -357,44 +352,42 @@ X509CA::~X509CA() {
|
||||
}
|
||||
}
|
||||
|
||||
util::Status X509CA::InitializeStore() {
|
||||
Status X509CA::InitializeStore() {
|
||||
absl::WriterMutexLock lock(&openssl_store_mutex_);
|
||||
if (openssl_store_ == NULL) {
|
||||
if (ca_cert_ == NULL) {
|
||||
return util::Status(util::error::INTERNAL, "CA X.509Cert is NULL");
|
||||
return Status(error::INTERNAL, "CA X.509Cert is NULL");
|
||||
}
|
||||
openssl_store_ = X509_STORE_new();
|
||||
if (openssl_store_ == NULL) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Failed to allocate X.509 store");
|
||||
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 util::Status(util::error::INTERNAL,
|
||||
"Failed to add X.509 CA certificate to store");
|
||||
return Status(error::INTERNAL,
|
||||
"Failed to add X.509 CA certificate to store");
|
||||
}
|
||||
}
|
||||
return util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
util::Status X509CA::VerifyCert(const X509Cert& cert) {
|
||||
Status X509CA::VerifyCert(const X509Cert& cert) {
|
||||
return OpenSslX509Verify(cert.openssl_cert(), nullptr);
|
||||
}
|
||||
|
||||
util::Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) {
|
||||
Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) {
|
||||
if (cert_chain.GetNumCerts() < 1) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Cannot verify empty certificate chain");
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Cannot verify empty certificate chain");
|
||||
}
|
||||
|
||||
ScopedX509StackOnly intermediates(sk_X509_new_null());
|
||||
if (!intermediates) {
|
||||
return util::Status(
|
||||
util::Status::canonical_space(), util::error::INTERNAL,
|
||||
"Failed to allocate X.509 intermediate certificate stack");
|
||||
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) {
|
||||
@@ -406,23 +399,21 @@ util::Status X509CA::VerifyCertChain(const X509CertChain& cert_chain) {
|
||||
}
|
||||
}
|
||||
if (!leaf_cert) {
|
||||
return util::Status(util::Status::canonical_space(),
|
||||
util::error::INVALID_ARGUMENT,
|
||||
"X.509 certificate chain without leaf certificate.");
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"X.509 certificate chain without leaf certificate.");
|
||||
}
|
||||
return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get());
|
||||
}
|
||||
|
||||
util::Status X509CA::VerifyCertWithChain(const X509Cert& cert,
|
||||
const X509CertChain& cert_chain) {
|
||||
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 util::Status(
|
||||
util::Status::canonical_space(), util::error::INTERNAL,
|
||||
"Failed to allocate X.509 intermediate certificate stack");
|
||||
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(),
|
||||
@@ -432,14 +423,14 @@ util::Status X509CA::VerifyCertWithChain(const X509Cert& cert,
|
||||
return OpenSslX509Verify(cert.openssl_cert(), intermediates.get());
|
||||
}
|
||||
|
||||
util::Status X509CA::OpenSslX509Verify(const X509* cert,
|
||||
STACK_OF(X509) * intermediates) {
|
||||
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();
|
||||
util::Status status = InitializeStore();
|
||||
Status status = InitializeStore();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
@@ -447,23 +438,21 @@ util::Status X509CA::OpenSslX509Verify(const X509* cert,
|
||||
}
|
||||
ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new());
|
||||
if (!store_ctx) {
|
||||
return util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
||||
"Failed to allocate X.509 store context");
|
||||
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 util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
||||
"Failed to initialize X.509 store context");
|
||||
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 util::Status(util::Status::canonical_space(), util::error::INTERNAL,
|
||||
std::string("X.509 certificate chain validation failed: ") +
|
||||
X509_verify_cert_error_string(
|
||||
X509_STORE_CTX_get_error(store_ctx.get())));
|
||||
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 util::OkStatus();
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "openssl/x509v3.h"
|
||||
#include "common/status.h"
|
||||
#include "common/openssl_util.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/status.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// NOTE: All util::Status codes are in the canonical error space.
|
||||
// NOTE: All Status codes are in the canonical error space.
|
||||
|
||||
// Class which holds a single X.509 certificates.
|
||||
class X509Cert {
|
||||
@@ -43,11 +43,11 @@ class X509Cert {
|
||||
|
||||
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
||||
// a PEM-encoded certificate.
|
||||
util::Status LoadPem(const std::string& pem_cert);
|
||||
Status LoadPem(const std::string& pem_cert);
|
||||
|
||||
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
|
||||
// a DER-encoded certificate.
|
||||
util::Status LoadDer(const std::string& der_cert);
|
||||
Status LoadDer(const std::string& der_cert);
|
||||
|
||||
// Return a std::string containing the PEM-encoded certificate.
|
||||
std::string GetPem() const;
|
||||
@@ -91,8 +91,8 @@ class X509Cert {
|
||||
|
||||
private:
|
||||
explicit X509Cert(X509* openssl_cert);
|
||||
util::Status Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
|
||||
int64_t* epoch_seconds) const;
|
||||
Status Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
|
||||
int64_t* epoch_seconds) const;
|
||||
|
||||
X509* openssl_cert_;
|
||||
std::string subject_name_;
|
||||
@@ -112,12 +112,12 @@ class X509CertChain {
|
||||
// |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.
|
||||
util::Status LoadPem(const std::string& pem_cert_chain);
|
||||
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.
|
||||
util::Status LoadPkcs7(const std::string& pk7_cert_chain);
|
||||
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.
|
||||
@@ -148,21 +148,21 @@ class X509CA {
|
||||
|
||||
// Does X.509 PKI validation of |cert| against the root CA certificate
|
||||
// used when constructing X509CA. This method is thread-safe.
|
||||
util::Status VerifyCert(const X509Cert& cert);
|
||||
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.
|
||||
util::Status VerifyCertChain(const X509CertChain& cert_chain);
|
||||
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.
|
||||
util::Status VerifyCertWithChain(const X509Cert& cert,
|
||||
const X509CertChain& cert_chain);
|
||||
Status VerifyCertWithChain(const X509Cert& cert,
|
||||
const X509CertChain& cert_chain);
|
||||
|
||||
private:
|
||||
util::Status InitializeStore();
|
||||
util::Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * stack);
|
||||
Status InitializeStore();
|
||||
Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * intermediates);
|
||||
|
||||
std::unique_ptr<X509Cert> ca_cert_;
|
||||
absl::Mutex openssl_store_mutex_;
|
||||
|
||||
@@ -355,23 +355,23 @@ const bool kTestDevCodeSigningCertFlagValue = true;
|
||||
|
||||
TEST(X509CertTest, LoadCert) {
|
||||
X509Cert test_cert;
|
||||
EXPECT_EQ(util::OkStatus(),
|
||||
EXPECT_EQ(OkStatus(),
|
||||
test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||
EXPECT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
// TODO(user): Add more specific status checks to failure tests.
|
||||
EXPECT_NE(util::OkStatus(), test_cert.LoadDer("bad cert"));
|
||||
EXPECT_NE(util::OkStatus(), test_cert.LoadPem("bad cert"));
|
||||
EXPECT_NE(util::OkStatus(), test_cert.LoadDer(""));
|
||||
EXPECT_NE(util::OkStatus(), test_cert.LoadPem(""));
|
||||
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(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
std::string message(absl::HexStringToBytes(kTestMessage));
|
||||
std::string signature;
|
||||
ASSERT_EQ(util::OkStatus(), GenerateRsaSignatureSha256Pkcs1(
|
||||
kTestCertPrivateKey, message, &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));
|
||||
@@ -384,7 +384,7 @@ TEST(X509CertTest, VerifySignature) {
|
||||
|
||||
TEST(X509CertTest, GetSubjectNameField) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
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"));
|
||||
@@ -392,13 +392,13 @@ TEST(X509CertTest, GetSubjectNameField) {
|
||||
|
||||
TEST(X509CertTest, GetSerialNumber) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber());
|
||||
}
|
||||
|
||||
TEST(X509CertTest, GetNotBeforeSeconds) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
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);
|
||||
@@ -406,7 +406,7 @@ TEST(X509CertTest, GetNotBeforeSeconds) {
|
||||
|
||||
TEST(X509CertTest, GetNotAfterSeconds) {
|
||||
X509Cert test_cert;
|
||||
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert));
|
||||
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);
|
||||
@@ -414,7 +414,7 @@ TEST(X509CertTest, GetNotAfterSeconds) {
|
||||
|
||||
TEST(X509CertTest, CertChain) {
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
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);
|
||||
@@ -423,7 +423,7 @@ TEST(X509CertTest, CertChain) {
|
||||
|
||||
TEST(X509CertTest, IsCaCertificate) {
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
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());
|
||||
@@ -431,84 +431,84 @@ TEST(X509CertTest, IsCaCertificate) {
|
||||
|
||||
TEST(X509CertTest, ChainVerificationPem) {
|
||||
std::unique_ptr<X509Cert> ca_cert(new X509Cert);
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
ASSERT_EQ(OkStatus(),
|
||||
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||
X509CA ca(ca_cert.release());
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert));
|
||||
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(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
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(util::OkStatus(),
|
||||
ASSERT_EQ(OkStatus(),
|
||||
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
|
||||
X509CA ca(ca_cert.release());
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(),
|
||||
ASSERT_EQ(OkStatus(),
|
||||
test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain)));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert));
|
||||
EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain));
|
||||
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(util::OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||
ASSERT_EQ(OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||
X509CA ca(ca_cert.release());
|
||||
|
||||
// Verify the ICA with the root succeeds.
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
X509Cert ica_cert;
|
||||
ASSERT_EQ(util::OkStatus(), ica_cert.LoadPem(kTestPemIca));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertWithChain(ica_cert, test_chain));
|
||||
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(util::OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||
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(util::OkStatus(), test_chain.LoadPem(kTestPemIca));
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemIca));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
X509Cert leaf_cert;
|
||||
ASSERT_EQ(util::OkStatus(), leaf_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_EQ(util::OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
|
||||
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(util::OkStatus(), ca_cert->LoadPem(kTestRootCaPemCert));
|
||||
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(util::OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
||||
ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
|
||||
ASSERT_EQ(1, test_chain.GetNumCerts());
|
||||
X509Cert leaf_cert;
|
||||
ASSERT_EQ(util::OkStatus(), leaf_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_NE(util::OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
|
||||
ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert));
|
||||
EXPECT_NE(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
|
||||
}
|
||||
|
||||
TEST(X509CertTest, GetPkcs7) {
|
||||
X509CertChain test_chain;
|
||||
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain));
|
||||
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(util::OkStatus(), new_test_chain.LoadPkcs7(pkcs7_certificate));
|
||||
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(),
|
||||
@@ -518,12 +518,12 @@ TEST(X509CertTest, GetPkcs7) {
|
||||
|
||||
TEST(X509CertTest, BooleanExtension) {
|
||||
std::unique_ptr<X509Cert> cert1(new X509Cert);
|
||||
ASSERT_EQ(util::OkStatus(), cert1->LoadPem(kTestPemCert));
|
||||
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(util::OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert));
|
||||
ASSERT_EQ(OkStatus(), cert2->LoadPem(kTestDevCodeSigningCert));
|
||||
ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
|
||||
EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user