Minimal implementation of Widevine MediaCAS ECMG.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=226515998
This commit is contained in:
Fang Yu
2018-12-21 11:17:37 -08:00
parent 7487ce5aa8
commit bc68878bdf
88 changed files with 2456 additions and 2774 deletions

View File

@@ -16,6 +16,7 @@ filegroup(
name = "binary_release_files", name = "binary_release_files",
srcs = [ srcs = [
"certificate_type.h", "certificate_type.h",
"status.h",
], ],
) )
@@ -37,9 +38,9 @@ cc_library(
hdrs = ["status.h"], hdrs = ["status.h"],
deps = [ deps = [
"//base", "//base",
"//util:error_space",
"@abseil_repo//absl/base:core_headers", "@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//util:error_space",
], ],
) )
@@ -62,20 +63,21 @@ cc_library(
":error_space", ":error_space",
":random_util", ":random_util",
":rsa_key", ":rsa_key",
":sha_util",
":signing_key_util", ":signing_key_util",
":status",
":wvm_token_handler", ":wvm_token_handler",
"//base", "//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:client_identification_proto",
"//protos/public:drm_certificate_proto", "//protos/public:drm_certificate_proto",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"//protos/public:license_protocol_proto", "//protos/public:license_protocol_proto",
"//protos/public:signed_drm_certificate_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", ":client_cert",
":drm_root_certificate", ":drm_root_certificate",
":error_space", ":error_space",
":sha_util",
":test_drm_certificates",
":wvm_test_keys", ":wvm_test_keys",
"//base", "//base",
"//common:rsa_key",
"//common:rsa_test_keys",
"//protos/public:drm_certificate_proto",
"//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto",
"//strings", "//strings",
"//testing:gunit_main", "//testing:gunit_main",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization", "@abseil_repo//absl/synchronization",
"@abseil_repo//absl/time", "@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", ":random_util",
":rsa_key", ":rsa_key",
":signing_key_util", ":signing_key_util",
":status",
"//base", "//base",
"//common:status", "@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//util/gtl:map_util",
"//protos/public:client_identification_proto", "//protos/public:client_identification_proto",
"//protos/public:device_certificate_status_proto", "//protos/public:device_certificate_status_proto",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"//protos/public:provisioned_device_info_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", ":client_cert",
":device_status_list", ":device_status_list",
"//base", "//base",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//common:rsa_key", "//common:rsa_key",
"//common:rsa_test_keys", "//common:rsa_test_keys",
"//protos/public:client_identification_proto", "//protos/public:client_identification_proto",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"//protos/public:provisioned_device_info_proto", "//protos/public:provisioned_device_info_proto",
"//protos/public:signed_drm_certificate_proto", "//protos/public:signed_drm_certificate_proto",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
], ],
) )
@@ -154,15 +158,15 @@ cc_library(
":error_space", ":error_space",
":rsa_key", ":rsa_key",
":sha_util", ":sha_util",
":status",
"//base", "//base",
"//common:status", "@abseil_repo//absl/memory",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//external:openssl", "//external:openssl",
"//protos/public:drm_certificate_proto", "//protos/public:drm_certificate_proto",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_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", ":test_drm_certificates",
"//base", "//base",
"//external:protobuf", "//external:protobuf",
"//testing:gunit_main",
"//protos/public:drm_certificate_proto", "//protos/public:drm_certificate_proto",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_proto", "//protos/public:signed_drm_certificate_proto",
"//testing:gunit_main",
], ],
) )
@@ -193,11 +197,11 @@ cc_library(
":aes_cbc_util", ":aes_cbc_util",
":drm_service_certificate", ":drm_service_certificate",
":error_space", ":error_space",
":status",
"//base", "//base",
"//common:status", "@abseil_repo//absl/strings",
"//protos/public:client_identification_proto", "//protos/public:client_identification_proto",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"@abseil_repo//absl/strings",
], ],
) )
@@ -220,9 +224,9 @@ cc_test(
":rsa_test_keys", ":rsa_test_keys",
":rsa_util", ":rsa_util",
"//base", "//base",
"//external:openssl",
"//testing:gunit", "//testing:gunit",
"//testing:gunit_main", "//testing:gunit_main",
"//external:openssl",
], ],
) )
@@ -307,9 +311,9 @@ cc_library(
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//base", "//base",
"@abseil_repo//absl/strings",
"//external:openssl", "//external:openssl",
"//util/endian", "//util/endian",
"@abseil_repo//absl/strings",
], ],
) )
@@ -332,8 +336,8 @@ cc_library(
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//base", "//base",
"//external:openssl",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//external:openssl",
], ],
) )
@@ -415,8 +419,8 @@ cc_library(
":aes_cbc_util", ":aes_cbc_util",
":rsa_key", ":rsa_key",
":sha_util", ":sha_util",
":status",
"//base", "//base",
"//common:status",
], ],
) )
@@ -438,10 +442,10 @@ cc_test(
deps = [ deps = [
":crypto_util", ":crypto_util",
":signing_key_util", ":signing_key_util",
"//protos/public:license_protocol_proto",
"//testing:gunit", "//testing:gunit",
"//testing:gunit_main", "//testing:gunit_main",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//protos/public:license_protocol_proto",
], ],
) )
@@ -464,12 +468,12 @@ cc_library(
":aes_cbc_util", ":aes_cbc_util",
":ecb_util", ":ecb_util",
":sha_util", ":sha_util",
":status",
"//base", "//base",
"//common:status",
"//util/endian",
"//util/gtl:map_util",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization", "@abseil_repo//absl/synchronization",
"//util/endian",
"//util/gtl:map_util",
], ],
) )
@@ -503,9 +507,9 @@ cc_library(
srcs = ["error_space.cc"], srcs = ["error_space.cc"],
hdrs = ["error_space.h"], hdrs = ["error_space.h"],
deps = [ deps = [
"//common:status", "//util:error_space",
"//protos/public:errors_proto",
"//util:proto_status", "//util:proto_status",
"//protos/public:errors_proto",
], ],
) )
@@ -518,14 +522,14 @@ cc_library(
":drm_service_certificate", ":drm_service_certificate",
":error_space", ":error_space",
":rsa_key", ":rsa_key",
":status",
":x509_cert", ":x509_cert",
"//base", "//base",
"//common:status", "@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//protos/public:client_identification_proto", "//protos/public:client_identification_proto",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"//protos/public:remote_attestation_proto", "//protos/public:remote_attestation_proto",
"@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
], ],
) )
@@ -540,15 +544,15 @@ cc_library(
":error_space", ":error_space",
":rsa_key", ":rsa_key",
":rsa_util", ":rsa_util",
":status",
"//base", "//base",
"//common:status", "@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization",
"//util/gtl:map_util",
"//protos/public:client_identification_proto", "//protos/public:client_identification_proto",
"//protos/public:drm_certificate_proto", "//protos/public:drm_certificate_proto",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"//protos/public:signed_drm_certificate_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", ":test_drm_certificates",
"//base", "//base",
"//external:protobuf", "//external:protobuf",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//protos/public:client_identification_proto", "//protos/public:client_identification_proto",
"//protos/public:drm_certificate_proto", "//protos/public:drm_certificate_proto",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"//protos/public:license_server_sdk_proto", "//protos/public:license_server_sdk_proto",
"//protos/public:signed_drm_certificate_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"], srcs = ["verified_media_pipeline.cc"],
hdrs = ["verified_media_pipeline.h"], hdrs = ["verified_media_pipeline.h"],
deps = [ deps = [
":status",
":vmp_checker", ":vmp_checker",
"//base", "//base",
"//common:status",
"//protos/public:license_protocol_proto",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//protos/public:license_protocol_proto",
], ],
) )
@@ -597,11 +601,11 @@ cc_library(
":error_space", ":error_space",
":openssl_util", ":openssl_util",
":rsa_key", ":rsa_key",
":status",
"//base", "//base",
"//common:status",
"//external:openssl",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@abseil_repo//absl/synchronization", "@abseil_repo//absl/synchronization",
"//external:openssl",
], ],
) )
@@ -611,8 +615,8 @@ cc_library(
srcs = ["test_utils.cc"], srcs = ["test_utils.cc"],
hdrs = ["test_utils.h"], hdrs = ["test_utils.h"],
deps = [ deps = [
":status",
"//base", "//base",
"//common:status",
"//external:openssl", "//external:openssl",
], ],
) )
@@ -639,9 +643,9 @@ cc_library(
":certificate_type", ":certificate_type",
":error_space", ":error_space",
":rsa_key", ":rsa_key",
":status",
":x509_cert", ":x509_cert",
"//base", "//base",
"//common:status",
"//protos/public:errors_proto", "//protos/public:errors_proto",
"//protos/public:verified_media_pipeline_proto", "//protos/public:verified_media_pipeline_proto",
], ],
@@ -655,10 +659,10 @@ cc_test(
":rsa_key", ":rsa_key",
":vmp_checker", ":vmp_checker",
"//base", "//base",
"//protos/public:errors_proto",
"//protos/public:verified_media_pipeline_proto",
"//testing:gunit_main", "//testing:gunit_main",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//protos/public:errors_proto",
"//protos/public:verified_media_pipeline_proto",
], ],
) )
@@ -667,8 +671,8 @@ cc_library(
srcs = ["string_util.cc"], srcs = ["string_util.cc"],
hdrs = ["string_util.h"], hdrs = ["string_util.h"],
deps = [ deps = [
":status",
"//base", "//base",
"//common:status",
], ],
) )

View File

@@ -17,19 +17,18 @@
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "util/gtl/map_util.h" #include "util/gtl/map_util.h"
#include "common/status.h"
#include "common/crypto_util.h" #include "common/crypto_util.h"
#include "common/drm_root_certificate.h" #include "common/drm_root_certificate.h"
#include "common/error_space.h" #include "common/error_space.h"
#include "common/random_util.h" #include "common/random_util.h"
#include "common/sha_util.h"
#include "common/signing_key_util.h" #include "common/signing_key_util.h"
#include "common/status.h"
#include "common/wvm_token_handler.h" #include "common/wvm_token_handler.h"
#include "protos/public/drm_certificate.pb.h" #include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h" #include "protos/public/errors.pb.h"
#include "protos/public/signed_drm_certificate.pb.h" #include "protos/public/signed_drm_certificate.pb.h"
// TODO(user): Get rid of this horror.
namespace widevine { namespace widevine {
namespace { namespace {
@@ -37,45 +36,44 @@ const int kKeyboxSizeBytes = 72;
} // namespace } // namespace
// TODO(user): change to util::StatusOr<std::unique_ptr<ClientCert>>
// instead of ClientCert** to explicitly assigning ownership of the created // instead of ClientCert** to explicitly assigning ownership of the created
// object to the caller. // object to the caller.
util::Status ClientCert::Create(const DrmRootCertificate* root_certificate, Status ClientCert::Create(const DrmRootCertificate* root_certificate,
ClientIdentification::TokenType token_type, ClientIdentification::TokenType token_type,
const std::string& token, ClientCert** client_cert) { const std::string& token, ClientCert** client_cert) {
DCHECK(client_cert); DCHECK(client_cert);
if (token_type == ClientIdentification::KEYBOX) { if (token_type == ClientIdentification::KEYBOX) {
*client_cert = nullptr; *client_cert = nullptr;
if (token.size() < kKeyboxSizeBytes) { if (token.size() < kKeyboxSizeBytes) {
return util::Status(error_space, INVALID_KEYBOX_TOKEN, return Status(error_space, INVALID_KEYBOX_TOKEN,
"keybox-token-is-too-short"); "keybox-token-is-too-short");
} }
return ClientCert::CreateWithKeybox(token, client_cert); return ClientCert::CreateWithKeybox(token, client_cert);
} else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) { } else if (token_type == ClientIdentification::DRM_DEVICE_CERTIFICATE) {
return CreateWithDrmCertificate(root_certificate, token, client_cert); return CreateWithDrmCertificate(root_certificate, token, client_cert);
} else { } else {
return util::Status(error_space, util::error::UNIMPLEMENTED, return Status(error_space, error::UNIMPLEMENTED,
"client-type-not-implemented"); "client-type-not-implemented");
} }
} }
util::Status ClientCert::CreateWithKeybox(const std::string& keybox_token, Status ClientCert::CreateWithKeybox(const std::string& keybox_token,
ClientCert** client_cert) { ClientCert** client_cert) {
CHECK(client_cert); CHECK(client_cert);
*client_cert = nullptr; *client_cert = nullptr;
std::unique_ptr<KeyboxClientCert> new_client_cert(new KeyboxClientCert); 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()) { if (!status.ok()) {
return status; return status;
} }
*client_cert = new_client_cert.release(); *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, const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
ClientCert** client_cert) { ClientCert** client_cert) {
CHECK(client_cert); CHECK(client_cert);
@@ -83,14 +81,14 @@ util::Status ClientCert::CreateWithDrmCertificate(
std::unique_ptr<CertificateClientCert> new_client_cert( std::unique_ptr<CertificateClientCert> new_client_cert(
new CertificateClientCert); new CertificateClientCert);
util::Status status = Status status =
new_client_cert->Initialize(root_certificate, drm_certificate); new_client_cert->Initialize(root_certificate, drm_certificate);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
*client_cert = new_client_cert.release(); *client_cert = new_client_cert.release();
return util::OkStatus(); return OkStatus();
} }
void ClientCert::CreateSignature(const std::string& message, std::string* signature) { void ClientCert::CreateSignature(const std::string& message, std::string* signature) {
@@ -110,8 +108,10 @@ void ClientCert::GenerateSigningKey(const std::string& message,
DCHECK(!key().empty()); DCHECK(!key().empty());
using crypto_util::DeriveKey; using crypto_util::DeriveKey;
using crypto_util::kSigningKeyLabel; using crypto_util::kSigningKeyLabel;
set_signing_key(DeriveKey(key(), kSigningKeyLabel, message, set_signing_key(
SigningKeyMaterialSize(protocol_version))); DeriveKey(key(), kSigningKeyLabel,
protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
SigningKeyMaterialSizeBits(protocol_version)));
} }
KeyboxClientCert::KeyboxClientCert() {} KeyboxClientCert::KeyboxClientCert() {}
@@ -139,51 +139,51 @@ uint32_t KeyboxClientCert::GetSystemId(const std::string& keybox_bytes) {
return WvmTokenHandler::GetSystemId(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) { if (keybox_bytes.size() < kKeyboxSizeBytes) {
return util::Status(error_space, INVALID_KEYBOX_TOKEN, return Status(error_space, INVALID_KEYBOX_TOKEN,
"keybox-token-is-too-short"); "keybox-token-is-too-short");
} }
set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes)); set_system_id(WvmTokenHandler::GetSystemId(keybox_bytes));
set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes)); set_serial_number(WvmTokenHandler::GetEncryptedUniqueId(keybox_bytes));
bool insecure_keybox = false; bool insecure_keybox = false;
util::Status status = WvmTokenHandler::DecryptDeviceKey( Status status = WvmTokenHandler::DecryptDeviceKey(keybox_bytes, &device_key_,
keybox_bytes, &device_key_, nullptr, &insecure_keybox); nullptr, &insecure_keybox);
if (!status.ok()) { 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 ? MISSING_PRE_PROV_KEY
: KEYBOX_DECRYPT_ERROR; : 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( Status KeyboxClientCert::VerifySignature(const std::string& message,
const std::string& message, const std::string& signature, const std::string& signature,
ProtocolVersion protocol_version) { ProtocolVersion protocol_version) {
DCHECK(!signing_key().empty()); DCHECK(!signing_key().empty());
using crypto_util::VerifySignatureHmacSha256; using crypto_util::VerifySignatureHmacSha256;
if (!VerifySignatureHmacSha256( if (!VerifySignatureHmacSha256(
GetClientSigningKey(signing_key(), protocol_version), signature, GetClientSigningKey(signing_key(), protocol_version), signature,
message)) { 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() {}
CertificateClientCert::~CertificateClientCert() {} CertificateClientCert::~CertificateClientCert() {}
util::Status CertificateClientCert::Initialize( Status CertificateClientCert::Initialize(
const DrmRootCertificate* drm_root_certificate, const DrmRootCertificate* drm_root_certificate,
const std::string& serialized_certificate) { const std::string& serialized_certificate) {
CHECK(drm_root_certificate); CHECK(drm_root_certificate);
SignedDrmCertificate signed_device_cert; SignedDrmCertificate signed_device_cert;
DrmCertificate 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); serialized_certificate, &signed_device_cert, &device_cert);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
@@ -192,12 +192,12 @@ util::Status CertificateClientCert::Initialize(
const SignedDrmCertificate& signer = signed_device_cert.signer(); const SignedDrmCertificate& signer = signed_device_cert.signer();
DrmCertificate model_certificate; DrmCertificate model_certificate;
if (!model_certificate.ParseFromString(signer.drm_certificate())) { if (!model_certificate.ParseFromString(signer.drm_certificate())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-invalid-signer"); "drm-certificate-invalid-signer");
} }
if (!model_certificate.has_serial_number()) { if (!model_certificate.has_serial_number()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-signer-serial-number"); "missing-signer-serial-number");
} }
// Check to see if this model certificate is signed by a // Check to see if this model certificate is signed by a
// provisioner (entity using Widevine Provisioning Server SDK). // provisioner (entity using Widevine Provisioning Server SDK).
@@ -205,56 +205,58 @@ util::Status CertificateClientCert::Initialize(
DrmCertificate provisioner_certificate; DrmCertificate provisioner_certificate;
if (!provisioner_certificate.ParseFromString( if (!provisioner_certificate.ParseFromString(
signer.signer().drm_certificate())) { signer.signer().drm_certificate())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"model-certificate-invalid-signer"); "model-certificate-invalid-signer");
} }
if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) { if (provisioner_certificate.type() == DrmCertificate::PROVISIONER) {
set_signed_by_provisioner(true); set_signed_by_provisioner(true);
} else { } else {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"expected-provisioning-provider-certificate-type"); "expected-provisioning-provider-certificate-type");
} }
if (!provisioner_certificate.has_provider_id() || if (!provisioner_certificate.has_provider_id() ||
provisioner_certificate.provider_id().empty()) { provisioner_certificate.provider_id().empty()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-provisioning-service-id"); "missing-provisioning-service-id");
} }
set_service_id(provisioner_certificate.provider_id()); set_service_id(provisioner_certificate.provider_id());
} }
set_signer_serial_number(model_certificate.serial_number()); set_signer_serial_number(model_certificate.serial_number());
set_signer_creation_time_seconds(model_certificate.creation_time_seconds()); set_signer_creation_time_seconds(model_certificate.creation_time_seconds());
if (!model_certificate.has_system_id()) { if (!model_certificate.has_system_id()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"model-certificate-missing-system-id"); "model-certificate-missing-system-id");
} }
set_system_id(model_certificate.system_id()); set_system_id(model_certificate.system_id());
set_serial_number(device_cert.serial_number()); set_serial_number(device_cert.serial_number());
set_public_key(device_cert.public_key()); set_public_key(device_cert.public_key());
rsa_public_key_.reset(RsaPublicKey::Create(public_key())); rsa_public_key_.reset(RsaPublicKey::Create(public_key()));
if (rsa_public_key_ == nullptr) { if (rsa_public_key_ == nullptr) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-public-key-failed"); "drm-certificate-public-key-failed");
} }
// TODO(user): Move this somewhere else. It is license protocol. // TODO(user): Move this somewhere else. It is license protocol.
set_key(Random16Bytes()); set_key(Random16Bytes());
if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) { if (!rsa_public_key_->Encrypt(key(), &encrypted_session_key_)) {
return util::Status(error_space, ENCRYPT_ERROR, return Status(error_space, ENCRYPT_ERROR,
"drm-certificate-failed-encrypt-session-key"); "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, const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) { ProtocolVersion protocol_version) {
CHECK(rsa_public_key_); CHECK(rsa_public_key_);
if (!rsa_public_key_->VerifySignature(message, signature)) { if (!rsa_public_key_->VerifySignature(
return util::Status(error_space, INVALID_SIGNATURE, ""); protocol_version < VERSION_2_2 ? message : Sha512_Hash(message),
signature)) {
return Status(error_space, INVALID_SIGNATURE, "");
} }
return util::OkStatus(); return OkStatus();
} }
} // namespace widevine } // namespace widevine

View File

@@ -13,8 +13,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "common/status.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/status.h"
#include "protos/public/client_identification.pb.h" #include "protos/public/client_identification.pb.h"
#include "protos/public/license_protocol.pb.h" #include "protos/public/license_protocol.pb.h"
@@ -29,15 +29,15 @@ class SignedDrmCertificate;
class ClientCert { class ClientCert {
public: public:
virtual ~ClientCert() {} virtual ~ClientCert() {}
static util::Status Create( static Status Create(
const DrmRootCertificate* root_certificate, const DrmRootCertificate* root_certificate,
widevine::ClientIdentification::TokenType token_type, widevine::ClientIdentification::TokenType token_type,
const std::string& token, ClientCert** client_cert); const std::string& token, ClientCert** client_cert);
// Creates a Keybox based ClientCert. // Creates a Keybox based ClientCert.
static util::Status CreateWithKeybox(const std::string& keybox_token, static Status CreateWithKeybox(const std::string& keybox_token,
ClientCert** client_cert); ClientCert** client_cert);
// Creates a Device Certificate based ClientCert. // Creates a Device Certificate based ClientCert.
static util::Status CreateWithDrmCertificate( static Status CreateWithDrmCertificate(
const DrmRootCertificate* root_certificate, const std::string& drm_certificate, const DrmRootCertificate* root_certificate, const std::string& drm_certificate,
ClientCert** client_cert); ClientCert** client_cert);
// Creates a HMAC SHA256 signature based on the message and the key(). // 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 // Checks the passed in signature against a signature created used the
// classes information and the passed in message. Returns OK if signature // classes information and the passed in message. Returns OK if signature
// is valid. // is valid.
virtual util::Status VerifySignature(const std::string& message, virtual Status VerifySignature(const std::string& message, const std::string& signature,
const std::string& signature, ProtocolVersion protocol_version) = 0;
ProtocolVersion protocol_version) = 0;
// Creates a signing_key that is accessible using signing_key(). Signing_key // Creates a signing_key that is accessible using signing_key(). Signing_key
// is constructed by doing a key derivation using the key() and message. // is constructed by doing a key derivation using the key() and message.
virtual void GenerateSigningKey(const std::string& message, virtual void GenerateSigningKey(const std::string& message,
@@ -118,10 +117,10 @@ class KeyboxClientCert : public ClientCert {
static bool IsSystemIdKnown(const uint32_t system_id); static bool IsSystemIdKnown(const uint32_t system_id);
static uint32_t GetSystemId(const std::string& keybox_bytes); 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, Status VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) override; ProtocolVersion protocol_version) override;
const std::string& key() const override { return device_key_; } const std::string& key() const override { return device_key_; }
void set_key(const std::string& key) override { device_key_ = key; } void set_key(const std::string& key) override { device_key_ = key; }
const std::string& encrypted_key() const override { return encrypted_device_key_; } const std::string& encrypted_key() const override { return encrypted_device_key_; }
@@ -148,8 +147,8 @@ class CertificateClientCert : public ClientCert {
public: public:
~CertificateClientCert() override; ~CertificateClientCert() override;
util::Status VerifySignature(const std::string& message, const std::string& signature, Status VerifySignature(const std::string& message, const std::string& signature,
ProtocolVersion protocol_version) override; ProtocolVersion protocol_version) override;
const std::string& key() const override { return session_key_; } const std::string& key() const override { return session_key_; }
void set_key(const std::string& key) override { session_key_ = key; } void set_key(const std::string& key) override { session_key_ = key; }
const std::string& encrypted_key() const override { const std::string& encrypted_key() const override {
@@ -162,8 +161,8 @@ class CertificateClientCert : public ClientCert {
protected: protected:
friend class ClientCert; friend class ClientCert;
friend class MockCertificateClientCert; friend class MockCertificateClientCert;
util::Status Initialize(const DrmRootCertificate* drm_root_certificate, Status Initialize(const DrmRootCertificate* drm_root_certificate,
const std::string& serialized_certificate); const std::string& serialized_certificate);
virtual void set_public_key(const std::string& public_key) { virtual void set_public_key(const std::string& public_key) {
public_key_ = public_key; public_key_ = public_key;
} }

View File

@@ -24,6 +24,8 @@
#include "common/drm_root_certificate.h" #include "common/drm_root_certificate.h"
#include "common/error_space.h" #include "common/error_space.h"
#include "common/rsa_test_keys.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 "common/wvm_test_keys.h"
#include "protos/public/drm_certificate.pb.h" #include "protos/public/drm_certificate.pb.h"
#include "protos/public/errors.pb.h" #include "protos/public/errors.pb.h"
@@ -31,7 +33,7 @@
// TODO(user): Change these tests to use on-the-fly generated intermediate // TODO(user): Change these tests to use on-the-fly generated intermediate
// and device certificates based on RsaTestKeys. // and device certificates based on RsaTestKeys.
// TODO(user): Add testcase(s) VerifySignature, CreateSignature, // TODO(user): Add testcase(s) CreateSignature,
// and GenerateSigningKey. // and GenerateSigningKey.
namespace widevine { namespace widevine {
@@ -73,11 +75,10 @@ class ClientCertTest : public ::testing::Test {
const std::string certificate_; const std::string certificate_;
const std::string expected_serial_number_; const std::string expected_serial_number_;
uint32_t expected_system_id_; uint32_t expected_system_id_;
util::Status expected_status_; Status expected_status_;
TestCertificateAndData(const std::string& certificate, TestCertificateAndData(const std::string& certificate,
const std::string& expected_serial_number, const std::string& expected_serial_number,
uint32_t expected_system_id, uint32_t expected_system_id, Status expected_status)
util::Status expected_status)
: certificate_(certificate), : certificate_(certificate),
expected_serial_number_(expected_serial_number), expected_serial_number_(expected_serial_number),
expected_system_id_(expected_system_id), expected_system_id_(expected_system_id),
@@ -111,7 +112,8 @@ class ClientCertTest : public ::testing::Test {
SignedDrmCertificate* signer, uint32_t system_id, SignedDrmCertificate* signer, uint32_t system_id,
const std::string& serial_number); const std::string& serial_number);
RsaTestKeys test_keys_; RsaTestKeys test_rsa_keys_;
TestDrmCertificates test_drm_certs_;
std::unique_ptr<DrmRootCertificate> root_cert_; std::unique_ptr<DrmRootCertificate> root_cert_;
static bool setup_preprov_keys_; static bool setup_preprov_keys_;
}; };
@@ -121,7 +123,7 @@ void ClientCertTest::TestBasicValidation(const TestTokenAndKeys& expectation,
const bool expect_success, const bool expect_success,
const bool compare_device_key) { const bool compare_device_key) {
// Test validation of a valid request. // Test validation of a valid request.
util::Status status; Status status;
ClientCert* client_cert_ptr = nullptr; ClientCert* client_cert_ptr = nullptr;
// Two ways to create a client cert object, test both. // 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); std::unique_ptr<ClientCert> keybox_cert(client_cert_ptr);
if (expect_success) { if (expect_success) {
ASSERT_EQ(util::OkStatus(), status); ASSERT_EQ(OkStatus(), status);
ASSERT_TRUE(keybox_cert.get()); ASSERT_TRUE(keybox_cert.get());
EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id()); EXPECT_EQ(expectation.expected_system_id_, keybox_cert->system_id());
EXPECT_EQ(expectation.expected_serial_number_, 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()); EXPECT_EQ(expectation.expected_device_key_, keybox_cert->key());
} }
} else { } else {
EXPECT_NE(util::OkStatus(), status); EXPECT_NE(OkStatus(), status);
EXPECT_FALSE(keybox_cert); EXPECT_FALSE(keybox_cert);
} }
} }
@@ -159,7 +161,7 @@ void ClientCertTest::TestBasicValidationDrmCertificate(
DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_)); DrmRootCertificate::CreateByType(kCertificateTypeTesting, &root_cert_));
// Test validation of a valid request. // Test validation of a valid request.
util::Status status; Status status;
ClientCert* client_cert_ptr = nullptr; ClientCert* client_cert_ptr = nullptr;
status = ClientCert::Create(root_cert_.get(), status = ClientCert::Create(root_cert_.get(),
ClientIdentification::DRM_DEVICE_CERTIFICATE, ClientIdentification::DRM_DEVICE_CERTIFICATE,
@@ -209,7 +211,7 @@ DrmCertificate* ClientCertTest::GenerateIntermediateCertificate(
intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL); intermediate_certificate->set_type(DrmCertificate::DEVICE_MODEL);
intermediate_certificate->set_serial_number(serial_number); intermediate_certificate->set_serial_number(serial_number);
intermediate_certificate->set_public_key( intermediate_certificate->set_public_key(
test_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_system_id(system_id);
intermediate_certificate->set_creation_time_seconds(1234); intermediate_certificate->set_creation_time_seconds(1234);
return intermediate_certificate.release(); return intermediate_certificate.release();
@@ -221,7 +223,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedIntermediateCertificate(
std::unique_ptr<DrmCertificate> intermediate_certificate( std::unique_ptr<DrmCertificate> intermediate_certificate(
GenerateIntermediateCertificate(system_id, serial_number)); GenerateIntermediateCertificate(system_id, serial_number));
return SignCertificate(*intermediate_certificate, signer, 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( DrmCertificate* ClientCertTest::GenerateDrmCertificate(
@@ -230,7 +232,7 @@ DrmCertificate* ClientCertTest::GenerateDrmCertificate(
drm_certificate->set_type(DrmCertificate::DEVICE); drm_certificate->set_type(DrmCertificate::DEVICE);
drm_certificate->set_serial_number(serial_number); drm_certificate->set_serial_number(serial_number);
drm_certificate->set_system_id(system_id); drm_certificate->set_system_id(system_id);
drm_certificate->set_public_key(test_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); drm_certificate->set_creation_time_seconds(4321);
return drm_certificate.release(); return drm_certificate.release();
} }
@@ -241,7 +243,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedDrmCertificate(
std::unique_ptr<DrmCertificate> drm_certificate( std::unique_ptr<DrmCertificate> drm_certificate(
GenerateDrmCertificate(system_id, serial_number)); GenerateDrmCertificate(system_id, serial_number));
std::unique_ptr<SignedDrmCertificate> signed_drm_certificate(SignCertificate( 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(); return signed_drm_certificate.release();
} }
@@ -252,7 +254,7 @@ DrmCertificate* ClientCertTest::GenerateProvisionerCertificate(
provisioner_certificate->set_serial_number(serial_number); provisioner_certificate->set_serial_number(serial_number);
// TODO(user): Need to generate 3072 bit test for provisioner certificates. // TODO(user): Need to generate 3072 bit test for provisioner certificates.
provisioner_certificate->set_public_key( 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_system_id(system_id);
provisioner_certificate->set_provider_id(provider_id); provisioner_certificate->set_provider_id(provider_id);
provisioner_certificate->set_creation_time_seconds(1234); provisioner_certificate->set_creation_time_seconds(1234);
@@ -264,7 +266,7 @@ SignedDrmCertificate* ClientCertTest::GenerateSignedProvisionerCertificate(
std::unique_ptr<DrmCertificate> provisioner_certificate( std::unique_ptr<DrmCertificate> provisioner_certificate(
GenerateProvisionerCertificate(system_id, serial_number, service_id)); GenerateProvisionerCertificate(system_id, serial_number, service_id));
return SignCertificate(*provisioner_certificate, nullptr, return SignCertificate(*provisioner_certificate, nullptr,
test_keys_.private_test_key_1_3072_bits()); test_rsa_keys_.private_test_key_1_3072_bits());
} }
TEST_F(ClientCertTest, BasicValidation) { TEST_F(ClientCertTest, BasicValidation) {
@@ -302,8 +304,7 @@ TEST_F(ClientCertTest, BasicCertValidation) {
nullptr, system_id, serial_number), nullptr, system_id, serial_number),
system_id, serial_number + "-device")); system_id, serial_number + "-device"));
const TestCertificateAndData kValidCertificateAndExpectedData( const TestCertificateAndData kValidCertificateAndExpectedData(
signed_cert->SerializeAsString(), serial_number, system_id, signed_cert->SerializeAsString(), serial_number, system_id, OkStatus());
util::OkStatus());
const bool compare_data = true; const bool compare_data = true;
TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData, TestBasicValidationDrmCertificate(kValidCertificateAndExpectedData,
compare_data); compare_data);
@@ -347,7 +348,7 @@ TEST_F(ClientCertTest, InvalidCertificate) {
new SignedDrmCertificate); new SignedDrmCertificate);
invalid_drm_cert->set_drm_certificate("bad-serialized-cert"); invalid_drm_cert->set_drm_certificate("bad-serialized-cert");
GenerateSignature(invalid_drm_cert->drm_certificate(), GenerateSignature(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->mutable_signature());
invalid_drm_cert->set_allocated_signer( invalid_drm_cert->set_allocated_signer(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn)); GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
@@ -357,18 +358,18 @@ TEST_F(ClientCertTest, InvalidCertificate) {
std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate( std::unique_ptr<SignedDrmCertificate> bad_device_public_key(SignCertificate(
*dev_cert, *dev_cert,
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn), 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. // Invalid serialized intermediate certificate.
signed_signer.reset( signed_signer.reset(
GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn)); GenerateSignedIntermediateCertificate(nullptr, system_id, signer_sn));
signed_signer->set_drm_certificate("bad-serialized-cert"); signed_signer->set_drm_certificate("bad-serialized-cert");
GenerateSignature(signed_signer->drm_certificate(), GenerateSignature(signed_signer->drm_certificate(),
test_keys_.private_test_key_1_3072_bits(), test_rsa_keys_.private_test_key_1_3072_bits(),
signed_signer->mutable_signature()); signed_signer->mutable_signature());
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
std::unique_ptr<SignedDrmCertificate> invalid_signer( std::unique_ptr<SignedDrmCertificate> invalid_signer(
SignCertificate(*dev_cert, signed_signer.release(), SignCertificate(*dev_cert, signed_signer.release(),
test_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid signer public key. // Invalid signer public key.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_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( std::unique_ptr<SignedDrmCertificate> bad_signer_public_key(SignCertificate(
*dev_cert, *dev_cert,
SignCertificate(*signer_cert, nullptr, SignCertificate(*signer_cert, nullptr,
test_keys_.private_test_key_1_3072_bits()), test_rsa_keys_.private_test_key_1_3072_bits()),
test_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid device certificate signature. // Invalid device certificate signature.
std::unique_ptr<SignedDrmCertificate> bad_device_signature( std::unique_ptr<SignedDrmCertificate> bad_device_signature(
GenerateSignedDrmCertificate( GenerateSignedDrmCertificate(
@@ -391,8 +392,8 @@ TEST_F(ClientCertTest, InvalidCertificate) {
std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate( std::unique_ptr<SignedDrmCertificate> missing_model_sn(SignCertificate(
*dev_cert, *dev_cert,
SignCertificate(*signer_cert, nullptr, SignCertificate(*signer_cert, nullptr,
test_keys_.private_test_key_1_3072_bits()), test_rsa_keys_.private_test_key_1_3072_bits()),
test_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
// Missing signer serial number. // Missing signer serial number.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signer_cert.reset(GenerateIntermediateCertificate(system_id, signer_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( std::unique_ptr<SignedDrmCertificate> missing_signer_sn(SignCertificate(
*dev_cert, *dev_cert,
SignCertificate(*signer_cert, nullptr, SignCertificate(*signer_cert, nullptr,
test_keys_.private_test_key_1_3072_bits()), test_rsa_keys_.private_test_key_1_3072_bits()),
test_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
// Invalid serialized intermediate certificate. // Invalid serialized intermediate certificate.
dev_cert.reset(GenerateDrmCertificate(system_id, device_sn)); dev_cert.reset(GenerateDrmCertificate(system_id, device_sn));
signed_signer.reset( signed_signer.reset(
@@ -409,37 +410,36 @@ TEST_F(ClientCertTest, InvalidCertificate) {
signed_signer->set_signature("bad-signature"); signed_signer->set_signature("bad-signature");
std::unique_ptr<SignedDrmCertificate> bad_signer_signature( std::unique_ptr<SignedDrmCertificate> bad_signer_signature(
SignCertificate(*dev_cert, signed_signer.release(), SignCertificate(*dev_cert, signed_signer.release(),
test_keys_.private_test_key_2_2048_bits())); test_rsa_keys_.private_test_key_2_2048_bits()));
const TestCertificateAndData kInvalidCertificate[] = { const TestCertificateAndData kInvalidCertificate[] = {
TestCertificateAndData("f", "", 0, TestCertificateAndData("f", "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE, Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signed-drm-certificate")), "invalid-signed-drm-certificate")),
TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0, TestCertificateAndData(invalid_drm_cert->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE, Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-drm-certificate")), "invalid-drm-certificate")),
TestCertificateAndData(bad_device_public_key->SerializeAsString(), "", 0, TestCertificateAndData(bad_device_public_key->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE, Status(error_space, INVALID_DRM_CERTIFICATE,
"drm-certificate-public-key-failed")), "drm-certificate-public-key-failed")),
TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0, TestCertificateAndData(invalid_signer->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE, Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-certificate")), "invalid-signer-certificate")),
TestCertificateAndData(bad_signer_public_key->SerializeAsString(), "", 0, TestCertificateAndData(bad_signer_public_key->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE, Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-leaf-signer-public-key")), "invalid-leaf-signer-public-key")),
TestCertificateAndData(bad_device_signature->SerializeAsString(), "", 0, TestCertificateAndData(bad_device_signature->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_SIGNATURE, Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature")), "cache-miss-invalid-signature")),
TestCertificateAndData( TestCertificateAndData(missing_model_sn->SerializeAsString(), "", 0,
missing_model_sn->SerializeAsString(), "", 0, Status(error_space, INVALID_DRM_CERTIFICATE,
util::Status(error_space, INVALID_DRM_CERTIFICATE, "model-certificate-missing-system-id")),
"model-certificate-missing-system-id")),
TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0, TestCertificateAndData(missing_signer_sn->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_DRM_CERTIFICATE, Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-signer-serial-number")), "missing-signer-serial-number")),
TestCertificateAndData(bad_signer_signature->SerializeAsString(), "", 0, TestCertificateAndData(bad_signer_signature->SerializeAsString(), "", 0,
util::Status(error_space, INVALID_SIGNATURE, Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature")), "cache-miss-invalid-signature")),
}; };
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) { for (size_t i = 0; i < ABSL_ARRAYSIZE(kInvalidCertificate); ++i) {
@@ -454,7 +454,7 @@ TEST_F(ClientCertTest, MissingPreProvKey) {
"beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9" "beaa24924907e128f9ff49b54a165cd9c33e6547537eb4d29fb7e8df3c2c1cd9"
"2517a12f4922953e")); "2517a12f4922953e"));
ClientCert* client_cert_ptr = nullptr; 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()); ASSERT_EQ(MISSING_PRE_PROV_KEY, status.error_code());
} }
@@ -563,4 +563,55 @@ TEST_F(ClientCertTest, InvalidProvisionerDeviceCertChain) {
EXPECT_FALSE(client_cert_ptr); 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 } // namespace widevine

View File

@@ -51,39 +51,39 @@ std::string GetClientInfo(const ClientIdentification& client_id,
return default_value; return default_value;
} }
util::Status DecryptEncryptedClientIdentification( Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id, const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id) { ClientIdentification* client_id) {
return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id, return DrmServiceCertificate::DecryptClientIdentification(encrypted_client_id,
client_id); client_id);
} }
util::Status DecryptEncryptedClientIdentification( Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id, const EncryptedClientIdentification& encrypted_client_id,
const std::string& privacy_key, ClientIdentification* client_id) { const std::string& privacy_key, ClientIdentification* client_id) {
DCHECK(client_id); DCHECK(client_id);
if (!encrypted_client_id.has_encrypted_client_id() || if (!encrypted_client_id.has_encrypted_client_id() ||
encrypted_client_id.encrypted_client_id().empty()) { encrypted_client_id.encrypted_client_id().empty()) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-client-id"); "missing-encrypted-client-id");
} }
if (!encrypted_client_id.has_encrypted_client_id_iv() || if (!encrypted_client_id.has_encrypted_client_id_iv() ||
encrypted_client_id.encrypted_client_id_iv().empty()) { encrypted_client_id.encrypted_client_id_iv().empty()) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-client-id-iv"); "missing-encrypted-client-id-iv");
} }
std::string serialized_client_id(crypto_util::DecryptAesCbc( std::string serialized_client_id(crypto_util::DecryptAesCbc(
privacy_key, encrypted_client_id.encrypted_client_id_iv(), privacy_key, encrypted_client_id.encrypted_client_id_iv(),
encrypted_client_id.encrypted_client_id())); encrypted_client_id.encrypted_client_id()));
if (serialized_client_id.empty()) { if (serialized_client_id.empty()) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"client-id-decryption-failed"); "client-id-decryption-failed");
} }
if (!client_id->ParseFromString(serialized_client_id)) { if (!client_id->ParseFromString(serialized_client_id)) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"client-id-parse-failed"); "client-id-parse-failed");
} }
return util::OkStatus(); return OkStatus();
} }
} // namespace widevine } // namespace widevine

View File

@@ -43,16 +43,16 @@ std::string GetClientInfo(const ClientIdentification& client_id,
// |client_id| using the private key for the service certificate which was // |client_id| using the private key for the service certificate which was
// used to encrypt the information. // used to encrypt the information.
// |client_id| is owned by caller. // |client_id| is owned by caller.
// Returns util::Status::OK, if successful, else an error. // Returns Status::OK, if successful, else an error.
util::Status DecryptEncryptedClientIdentification( Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id, const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id); ClientIdentification* client_id);
// Decrypts the encrypted client identification in |encrypted_client_id| into // Decrypts the encrypted client identification in |encrypted_client_id| into
// |client_id| using |privacy_key|. // |client_id| using |privacy_key|.
// |client_id| is owned by caller. // |client_id| is owned by caller.
// Returns util::Status::OK, if successful, else an error. // Returns Status::OK, if successful, else an error.
util::Status DecryptEncryptedClientIdentification( Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id, const EncryptedClientIdentification& encrypted_client_id,
const std::string& privacy_key, ClientIdentification* client_id); const std::string& privacy_key, ClientIdentification* client_id);

View File

@@ -22,8 +22,8 @@
namespace widevine { namespace widevine {
namespace crypto_util { namespace crypto_util {
const char kEncryptionKeyLabel[] = "ENCRYPTION"; const char kWrappingKeyLabel[] = "ENCRYPTION";
const int kEncryptionKeySizeBits = 128; const int kWrappingKeySizeBits = 128;
const char kSigningKeyLabel[] = "AUTHENTICATION"; const char kSigningKeyLabel[] = "AUTHENTICATION";
const int kSigningKeySizeBits = 256; const int kSigningKeySizeBits = 256;
const size_t kSigningKeySizeBytes = 32; const size_t kSigningKeySizeBytes = 32;

View File

@@ -22,8 +22,8 @@ namespace crypto_util {
// Default constants used for key derivation for encryption and signing. // Default constants used for key derivation for encryption and signing.
// TODO(user): These are duplicated in session.cc in the sdk. de-dup. // TODO(user): These are duplicated in session.cc in the sdk. de-dup.
extern const char kEncryptionKeyLabel[]; extern const char kWrappingKeyLabel[];
extern const int kEncryptionKeySizeBits; extern const int kWrappingKeySizeBits;
extern const char kSigningKeyLabel[]; extern const char kSigningKeyLabel[];
extern const int kSigningKeySizeBits; extern const int kSigningKeySizeBits;
extern const size_t kSigningKeySizeBytes; extern const size_t kSigningKeySizeBytes;

View File

@@ -52,47 +52,47 @@ DeviceStatusList::DeviceStatusList()
DeviceStatusList::~DeviceStatusList() {} DeviceStatusList::~DeviceStatusList() {}
util::Status DeviceStatusList::UpdateStatusList( Status DeviceStatusList::UpdateStatusList(
const std::string& root_certificate_public_key, const std::string& root_certificate_public_key,
const std::string& serialized_certificate_status_list, const std::string& serialized_certificate_status_list,
uint32_t expiration_period_seconds) { uint32_t expiration_period_seconds) {
SignedDeviceCertificateStatusList signed_certificate_status_list; SignedDeviceCertificateStatusList signed_certificate_status_list;
if (!signed_certificate_status_list.ParseFromString( if (!signed_certificate_status_list.ParseFromString(
serialized_certificate_status_list)) { serialized_certificate_status_list)) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"signed-certificate-status-list-parse-error"); "signed-certificate-status-list-parse-error");
} }
if (!signed_certificate_status_list.has_certificate_status_list()) { if (!signed_certificate_status_list.has_certificate_status_list()) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list"); "missing-status-list");
} }
if (!signed_certificate_status_list.has_signature()) { if (!signed_certificate_status_list.has_signature()) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list-signature"); "missing-status-list-signature");
} }
std::unique_ptr<RsaPublicKey> root_key( std::unique_ptr<RsaPublicKey> root_key(
RsaPublicKey::Create(root_certificate_public_key)); RsaPublicKey::Create(root_certificate_public_key));
if (root_key == nullptr) { if (root_key == nullptr) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-public-key"); "invalid-root-public-key");
} }
if (!root_key->VerifySignature( if (!root_key->VerifySignature(
signed_certificate_status_list.certificate_status_list(), signed_certificate_status_list.certificate_status_list(),
signed_certificate_status_list.signature())) { signed_certificate_status_list.signature())) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"invalid-status-list-signature"); "invalid-status-list-signature");
} }
DeviceCertificateStatusList certificate_status_list; DeviceCertificateStatusList certificate_status_list;
if (!certificate_status_list.ParseFromString( if (!certificate_status_list.ParseFromString(
signed_certificate_status_list.certificate_status_list())) { signed_certificate_status_list.certificate_status_list())) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"certificate-status-list-parse-error"); "certificate-status-list-parse-error");
} }
if (expiration_period_seconds && if (expiration_period_seconds &&
(GetCurrentTime() > (certificate_status_list.creation_time_seconds() + (GetCurrentTime() > (certificate_status_list.creation_time_seconds() +
expiration_period_seconds))) { expiration_period_seconds))) {
return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST, return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
"certificate-status-list-expired"); "certificate-status-list-expired");
} }
absl::WriterMutexLock lock(&status_map_lock_); absl::WriterMutexLock lock(&status_map_lock_);
@@ -106,44 +106,44 @@ util::Status DeviceStatusList::UpdateStatusList(
if (device_info.has_system_id()) { if (device_info.has_system_id()) {
device_status_map_[device_info.system_id()] = cert_status; device_status_map_[device_info.system_id()] = cert_status;
} else { } else {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"device-info-missing-system-id"); "device-info-missing-system-id");
} }
} }
} }
creation_time_seconds_ = certificate_status_list.creation_time_seconds(); creation_time_seconds_ = certificate_status_list.creation_time_seconds();
expiration_period_seconds_ = expiration_period_seconds; expiration_period_seconds_ = expiration_period_seconds;
return util::OkStatus(); return OkStatus();
} }
util::Status DeviceStatusList::GetCertStatus( Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert,
const ClientCert& client_cert, ProvisionedDeviceInfo* device_info) { ProvisionedDeviceInfo* device_info) {
CHECK(device_info); CHECK(device_info);
// Keybox checks. // Keybox checks.
if (client_cert.type() == ClientIdentification::KEYBOX) { if (client_cert.type() == ClientIdentification::KEYBOX) {
if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) { if (!KeyboxClientCert::IsSystemIdKnown(client_cert.system_id())) {
return util::Status(error_space, UNSUPPORTED_SYSTEM_ID, return Status(error_space, UNSUPPORTED_SYSTEM_ID,
"keybox-unsupported-system-id"); "keybox-unsupported-system-id");
} }
// Get device information from certificate status list if available. // Get device information from certificate status list if available.
if (!GetDeviceInfo(client_cert, device_info)) { if (!GetDeviceInfo(client_cert, device_info)) {
device_info->Clear(); device_info->Clear();
} }
return util::OkStatus(); return OkStatus();
} }
// DRM certificate checks. // DRM certificate checks.
if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) { if (client_cert.type() != ClientIdentification::DRM_DEVICE_CERTIFICATE) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"device-certificate-unsupported-token-type"); "device-certificate-unsupported-token-type");
} }
absl::ReaderMutexLock lock(&status_map_lock_); absl::ReaderMutexLock lock(&status_map_lock_);
if (expiration_period_seconds_ && if (expiration_period_seconds_ &&
(GetCurrentTime() > (GetCurrentTime() >
(creation_time_seconds_ + expiration_period_seconds_))) { (creation_time_seconds_ + expiration_period_seconds_))) {
return util::Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST, return Status(error_space, EXPIRED_CERTIFICATE_STATUS_LIST,
"certificate-status-list-expired"); "certificate-status-list-expired");
} }
DeviceCertificateStatus* device_cert_status = DeviceCertificateStatus* device_cert_status =
gtl::FindOrNull(device_status_map_, client_cert.system_id()); gtl::FindOrNull(device_status_map_, client_cert.system_id());
@@ -155,15 +155,15 @@ util::Status DeviceStatusList::GetCertStatus(
LOG(WARNING) << "Allowing REVOKED device: " LOG(WARNING) << "Allowing REVOKED device: "
<< device_info->ShortDebugString(); << device_info->ShortDebugString();
} else { } else {
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED, return Status(error_space, DRM_DEVICE_CERTIFICATE_REVOKED,
"device-certificate-revoked"); "device-certificate-revoked");
} }
} }
if ((device_cert_status->status() == if ((device_cert_status->status() ==
DeviceCertificateStatus::STATUS_TEST_ONLY) && DeviceCertificateStatus::STATUS_TEST_ONLY) &&
!allow_test_only_devices_) { !allow_test_only_devices_) {
return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
"test-only-drm-certificate-not-allowed"); "test-only-drm-certificate-not-allowed");
} }
if (!client_cert.signed_by_provisioner() && if (!client_cert.signed_by_provisioner() &&
(client_cert.signer_serial_number() != (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 // list is older than the certificate, the certificate is for all purposes
// unknown. // unknown.
if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) { if (client_cert.signer_creation_time_seconds() < creation_time_seconds_) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"intermediate-certificate-serial-number-mismatch"); "intermediate-certificate-serial-number-mismatch");
} }
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown"); "device-certificate-status-unknown");
} }
} else { } else {
if (!allow_unknown_devices_) { if (!allow_unknown_devices_) {
return util::Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN, return Status(error_space, DRM_DEVICE_CERTIFICATE_UNKNOWN,
"device-certificate-status-unknown"); "device-certificate-status-unknown");
} }
device_info->Clear(); device_info->Clear();
} }
return util::OkStatus(); return OkStatus();
} }
bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert, bool DeviceStatusList::GetDeviceInfo(const ClientCert& client_cert,
@@ -247,18 +247,18 @@ bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) {
return it; return it;
} }
util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse( Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
const std::string& certificate_provisioning_service_response, const std::string& certificate_provisioning_service_response,
std::string* signed_certificate_status_list, std::string* certificate_status_list) { std::string* signed_certificate_status_list, std::string* certificate_status_list) {
util::Status status = util::OkStatus(); Status status = OkStatus();
size_t signed_list_start = size_t signed_list_start =
certificate_provisioning_service_response.find(kSignedList); certificate_provisioning_service_response.find(kSignedList);
if (signed_list_start != std::string::npos) { if (signed_list_start != std::string::npos) {
size_t signed_list_end = certificate_provisioning_service_response.find( size_t signed_list_end = certificate_provisioning_service_response.find(
kSignedListTerminator, signed_list_start); kSignedListTerminator, signed_list_start);
if (signed_list_end == std::string::npos) { if (signed_list_end == std::string::npos) {
return util::Status( return Status(
error_space, util::error::INVALID_ARGUMENT, error_space, error::INVALID_ARGUMENT,
"Unable to parse the certificate_provisioning_service_response. " "Unable to parse the certificate_provisioning_service_response. "
"SignedList not terminated."); "SignedList not terminated.");
} }
@@ -284,8 +284,8 @@ util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
if (!absl::WebSafeBase64Unescape(signed_list, if (!absl::WebSafeBase64Unescape(signed_list,
signed_certificate_status_list)) { signed_certificate_status_list)) {
if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) { if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) {
return util::Status(error_space, util::error::INVALID_ARGUMENT, return Status(error_space, error::INVALID_ARGUMENT,
"Base64 decode of signedlist failed."); "Base64 decode of signedlist failed.");
} }
} }
} else { } else {
@@ -295,42 +295,40 @@ util::Status DeviceStatusList::ExtractFromProvisioningServiceResponse(
signed_certificate_status_list)) { signed_certificate_status_list)) {
if (!absl::Base64Unescape(certificate_provisioning_service_response, if (!absl::Base64Unescape(certificate_provisioning_service_response,
signed_certificate_status_list)) { signed_certificate_status_list)) {
return util::Status(error_space, util::error::INVALID_ARGUMENT, return Status(error_space, error::INVALID_ARGUMENT,
"Base64 decode of certList failed."); "Base64 decode of certList failed.");
} }
} }
} }
SignedDeviceCertificateStatusList signed_status_list; SignedDeviceCertificateStatusList signed_status_list;
if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) { if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"signed-certificate-status-list-parse-error"); "signed-certificate-status-list-parse-error");
} }
if (!signed_status_list.has_certificate_status_list()) { if (!signed_status_list.has_certificate_status_list()) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"missing-status-list"); "missing-status-list");
} }
DeviceCertificateStatusList device_certificate_status_list; DeviceCertificateStatusList device_certificate_status_list;
if (!device_certificate_status_list.ParseFromString( if (!device_certificate_status_list.ParseFromString(
signed_status_list.certificate_status_list())) { signed_status_list.certificate_status_list())) {
return util::Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST,
"certificate-status-list-parse-error"); "certificate-status-list-parse-error");
} }
*certificate_status_list = signed_status_list.certificate_status_list(); *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, const std::string& version,
std::string* signed_device_certificate_status_list_request) { std::string* signed_device_certificate_status_list_request) {
if (version.empty()) { if (version.empty()) {
return util::Status(error_space, util::error::INVALID_ARGUMENT, return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty");
"SDK version is empty");
} }
DCHECK(signed_device_certificate_status_list_request); DCHECK(signed_device_certificate_status_list_request);
if (signed_device_certificate_status_list_request == nullptr) { if (signed_device_certificate_status_list_request == nullptr) {
return util::Status( return Status(error_space, error::INVALID_ARGUMENT,
error_space, util::error::INVALID_ARGUMENT, "Signed_device_certificate_status_list_request is empty");
"Signed_device_certificate_status_list_request is empty");
} }
// Construct SignedDeviceCertificateStatusListRequest. // Construct SignedDeviceCertificateStatusListRequest.
DeviceCertificateStatusListRequest request; DeviceCertificateStatusListRequest request;
@@ -345,15 +343,13 @@ util::Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
DrmServiceCertificate::GetDefaultDrmServiceCertificate(); DrmServiceCertificate::GetDefaultDrmServiceCertificate();
if (sc == nullptr) { if (sc == nullptr) {
signed_device_certificate_status_list_request->clear(); signed_device_certificate_status_list_request->clear();
return util::Status(error_space, return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
widevine::INVALID_SERVICE_CERTIFICATE, "Drm service certificate is not loaded.");
"Drm service certificate is not loaded.");
} }
const RsaPrivateKey* private_key = sc->private_key(); const RsaPrivateKey* private_key = sc->private_key();
if (private_key == nullptr) { if (private_key == nullptr) {
return util::Status(error_space, return Status(error_space, widevine::INVALID_SERVICE_CERTIFICATE,
widevine::INVALID_SERVICE_CERTIFICATE, "Private key in the service certificate is null.");
"Private key in the service certificate is null.");
} }
std::string signature; std::string signature;
private_key->GenerateSignature(device_certificate_status_list_request, private_key->GenerateSignature(device_certificate_status_list_request,
@@ -361,6 +357,6 @@ util::Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest(
signed_request.set_signature(signature); signed_request.set_signature(signature);
signed_request.SerializeToString( signed_request.SerializeToString(
signed_device_certificate_status_list_request); signed_device_certificate_status_list_request);
return util::OkStatus(); return OkStatus();
} }
} // namespace widevine } // namespace widevine

View File

@@ -37,12 +37,12 @@ class DeviceStatusList {
DeviceStatusList(); DeviceStatusList();
virtual ~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 // device certifcate status list. The internal map is used to verify
// a device was not revoked. Returns true is the list was successfully parsed. // a device was not revoked. Returns true is the list was successfully parsed.
util::Status UpdateStatusList(const std::string& root_certificate_public_key, Status UpdateStatusList(const std::string& root_certificate_public_key,
const std::string& signed_certificate_status_list, const std::string& serialized_certificate_status_list,
uint32_t expiration_period_seconds); uint32_t expiration_period_seconds);
void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; } void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; }
bool allow_unknown_devices() const { return allow_unknown_devices_; } bool allow_unknown_devices() const { return allow_unknown_devices_; }
void set_allow_test_only_devices(bool allow) { void set_allow_test_only_devices(bool allow) {
@@ -58,9 +58,8 @@ class DeviceStatusList {
// DRM_DEVICE_CERTIFICATE_UNKNOWN // DRM_DEVICE_CERTIFICATE_UNKNOWN
// If status is OK, a copy of the provisioned device info is copied // 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. // into |device_info|. Caller owns |device_info| and it must not be null.
util::Status GetCertStatus( Status GetCertStatus(const ClientCert& client_cert,
const ClientCert& client_cert, widevine::ProvisionedDeviceInfo* device_info);
widevine::ProvisionedDeviceInfo* device_info);
// Returns true if the pre-provisioning key or certificate for the specified // Returns true if the pre-provisioning key or certificate for the specified
// system ID are active (not disallowed or revoked). // system ID are active (not disallowed or revoked).
bool IsSystemIdActive(uint32_t system_id); bool IsSystemIdActive(uint32_t system_id);
@@ -86,7 +85,7 @@ class DeviceStatusList {
* @param certificate_status_list * @param certificate_status_list
* @return WvPLStatus - Status::OK if success, else error. * @return WvPLStatus - Status::OK if success, else error.
*/ */
static util::Status ExtractFromProvisioningServiceResponse( static Status ExtractFromProvisioningServiceResponse(
const std::string& certificate_provisioning_service_response, const std::string& certificate_provisioning_service_response,
std::string* signed_certificate_status_list, std::string* certificate_status_list); 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 signed_device_certificate_status_list_request
* @param version * @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, const std::string& version,
std::string* signed_device_certificate_status_list_request); std::string* signed_device_certificate_status_list_request);

View File

@@ -114,10 +114,9 @@ class DeviceStatusListTest : public ::testing::Test {
ASSERT_TRUE( ASSERT_TRUE(
signed_cert_status_list_.SerializeToString(&serialized_status_list_)); signed_cert_status_list_.SerializeToString(&serialized_status_list_));
ASSERT_EQ(util::OkStatus(), ASSERT_EQ(OkStatus(), device_status_list_.UpdateStatusList(
device_status_list_.UpdateStatusList( test_keys_.public_test_key_1_3072_bits(),
test_keys_.public_test_key_1_3072_bits(), serialized_status_list_, kDefaultExpirePeriod));
serialized_status_list_, kDefaultExpirePeriod));
} }
DeviceStatusList device_status_list_; DeviceStatusList device_status_list_;
@@ -140,7 +139,7 @@ TEST_F(DeviceStatusListTest, CheckForValidAndRevokedCert) {
.WillRepeatedly(Return(kValidCertSystemId)); .WillRepeatedly(Return(kValidCertSystemId));
EXPECT_CALL(valid_client_cert, signer_serial_number()) EXPECT_CALL(valid_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(valid_drm_serial_number)); .WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_EQ(util::OkStatus(), EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(valid_client_cert, &device_info)); device_status_list_.GetCertStatus(valid_client_cert, &device_info));
EXPECT_TRUE(device_info.has_model()); EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model()); EXPECT_EQ(kDeviceModel, device_info.model());
@@ -191,8 +190,8 @@ TEST_F(DeviceStatusListTest, TestOnlyCertNotAllowed) {
.WillRepeatedly(Return(kTestOnlyCertSystemId)); .WillRepeatedly(Return(kTestOnlyCertSystemId));
EXPECT_CALL(test_only_client_cert, signer_serial_number()) EXPECT_CALL(test_only_client_cert, signer_serial_number())
.WillRepeatedly(ReturnRef(test_only_drm_serial_number)); .WillRepeatedly(ReturnRef(test_only_drm_serial_number));
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus( EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(test_only_client_cert,
test_only_client_cert, &device_info)); &device_info));
} }
TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) { TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
@@ -208,8 +207,8 @@ TEST_F(DeviceStatusListTest, ValidAndUnknownKeybox) {
.WillRepeatedly(Return(ClientIdentification::KEYBOX)); .WillRepeatedly(Return(ClientIdentification::KEYBOX));
EXPECT_CALL(valid_client_keybox, system_id()) EXPECT_CALL(valid_client_keybox, system_id())
.WillRepeatedly(Return(kValidCertSystemId)); .WillRepeatedly(Return(kValidCertSystemId));
EXPECT_EQ(util::OkStatus(), device_status_list_.GetCertStatus( EXPECT_EQ(OkStatus(), device_status_list_.GetCertStatus(valid_client_keybox,
valid_client_keybox, &device_info)); &device_info));
EXPECT_TRUE(device_info.has_model()); EXPECT_TRUE(device_info.has_model());
EXPECT_EQ(kDeviceModel, device_info.model()); EXPECT_EQ(kDeviceModel, device_info.model());
@@ -249,7 +248,7 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) {
// We allow this case only for certs signed by a provisioner cert. // We allow this case only for certs signed by a provisioner cert.
EXPECT_CALL(older_client_cert, signed_by_provisioner()) EXPECT_CALL(older_client_cert, signed_by_provisioner())
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_EQ(util::OkStatus(), EXPECT_EQ(OkStatus(),
device_status_list_.GetCertStatus(older_client_cert, &device_info)); device_status_list_.GetCertStatus(older_client_cert, &device_info));
EXPECT_TRUE(device_info.has_system_id()); EXPECT_TRUE(device_info.has_system_id());
EXPECT_EQ(kValidCertSystemId, device_info.system_id()); EXPECT_EQ(kValidCertSystemId, device_info.system_id());
@@ -314,9 +313,9 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) {
.Times(2) .Times(2)
.WillOnce(Return(kStatusListCreationTime + 100)) .WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 101)); .WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList( EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(), test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100)); serialized_status_list_, 100));
EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST, EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST,
mock_device_status_list mock_device_status_list
.UpdateStatusList(test_keys_.public_test_key_1_3072_bits(), .UpdateStatusList(test_keys_.public_test_key_1_3072_bits(),
@@ -331,9 +330,9 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
.WillOnce(Return(kStatusListCreationTime + 100)) .WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 100)) .WillOnce(Return(kStatusListCreationTime + 100))
.WillOnce(Return(kStatusListCreationTime + 101)); .WillOnce(Return(kStatusListCreationTime + 101));
EXPECT_EQ(util::OkStatus(), mock_device_status_list.UpdateStatusList( EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList(
test_keys_.public_test_key_1_3072_bits(), test_keys_.public_test_key_1_3072_bits(),
serialized_status_list_, 100)); serialized_status_list_, 100));
ProvisionedDeviceInfo device_info; ProvisionedDeviceInfo device_info;
MockCertificateClientCert valid_client_cert; MockCertificateClientCert valid_client_cert;
@@ -346,8 +345,8 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) {
.WillRepeatedly(ReturnRef(valid_drm_serial_number)); .WillRepeatedly(ReturnRef(valid_drm_serial_number));
EXPECT_CALL(valid_client_cert, signer_creation_time_seconds()) EXPECT_CALL(valid_client_cert, signer_creation_time_seconds())
.WillRepeatedly(Return(kStatusListCreationTime - 1)); .WillRepeatedly(Return(kStatusListCreationTime - 1));
EXPECT_EQ(util::OkStatus(), mock_device_status_list.GetCertStatus( EXPECT_EQ(OkStatus(), mock_device_status_list.GetCertStatus(valid_client_cert,
valid_client_cert, &device_info)); &device_info));
EXPECT_EQ( EXPECT_EQ(
EXPIRED_CERTIFICATE_STATUS_LIST, EXPIRED_CERTIFICATE_STATUS_LIST,

View File

@@ -265,10 +265,10 @@ class VerifiedCertSignatureCache {
// Checks cache, on miss, uses public key. If successful, adds to // Checks cache, on miss, uses public key. If successful, adds to
// cache. // cache.
util::Status VerifySignature(const std::string& cert, const std::string& serial_number, Status VerifySignature(const std::string& cert, const std::string& serial_number,
const std::string& signature, const std::string& signature,
const std::string& signer_public_key, const std::string& signer_public_key,
const std::string& signer_serial_number) { const std::string& signer_serial_number) {
{ {
VerifiedCertSignatures::iterator cached_signature; VerifiedCertSignatures::iterator cached_signature;
absl::ReaderMutexLock read_lock(&signature_cache_mutex_); absl::ReaderMutexLock read_lock(&signature_cache_mutex_);
@@ -279,11 +279,11 @@ class VerifiedCertSignatureCache {
(signature != cached_signature->second.signature) || (signature != cached_signature->second.signature) ||
(signer_serial_number != cached_signature->second.signer_serial)) { (signer_serial_number != cached_signature->second.signer_serial)) {
// Cached signature mismatch. // Cached signature mismatch.
return util::Status(error_space, INVALID_SIGNATURE, return Status(error_space, INVALID_SIGNATURE,
"cached-signature-mismatch"); "cached-signature-mismatch");
} }
// Cached signature match. // Cached signature match.
return util::OkStatus(); return OkStatus();
} }
} }
@@ -291,12 +291,12 @@ class VerifiedCertSignatureCache {
std::unique_ptr<RsaPublicKey> signer_key( std::unique_ptr<RsaPublicKey> signer_key(
key_factory_->CreateFromPkcs1PublicKey(signer_public_key)); key_factory_->CreateFromPkcs1PublicKey(signer_public_key));
if (!signer_key) { if (!signer_key) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-public-key"); "invalid-signer-public-key");
} }
if (!signer_key->VerifySignature(cert, signature)) { if (!signer_key->VerifySignature(cert, signature)) {
return util::Status(error_space, INVALID_SIGNATURE, return Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature"); "cache-miss-invalid-signature");
} }
// Add signature to cache. // Add signature to cache.
@@ -304,7 +304,7 @@ class VerifiedCertSignatureCache {
signature_cache_.emplace( signature_cache_.emplace(
serial_number, serial_number,
VerifiedCertSignature(cert, signature, signer_serial_number)); VerifiedCertSignature(cert, signature, signer_serial_number));
return util::OkStatus(); return OkStatus();
} }
private: private:
@@ -313,7 +313,7 @@ class VerifiedCertSignatureCache {
const RsaKeyFactory* key_factory_; const RsaKeyFactory* key_factory_;
}; };
util::Status DrmRootCertificate::CreateByType( Status DrmRootCertificate::CreateByType(
CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) { CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) {
CHECK(cert); CHECK(cert);
@@ -321,7 +321,7 @@ util::Status DrmRootCertificate::CreateByType(
} }
std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType( std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
CertificateType cert_type, util::Status* status) { CertificateType cert_type, Status* status) {
CHECK(status); CHECK(status);
std::unique_ptr<DrmRootCertificate> new_root_cert; std::unique_ptr<DrmRootCertificate> new_root_cert;
@@ -329,7 +329,7 @@ std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
return new_root_cert; return new_root_cert;
} }
util::Status DrmRootCertificate::CreateByTypeString( Status DrmRootCertificate::CreateByTypeString(
const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert) { const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert) {
CHECK(cert); CHECK(cert);
@@ -341,17 +341,16 @@ util::Status DrmRootCertificate::CreateByTypeString(
} else if (cert_type_string == kTestingString) { } else if (cert_type_string == kTestingString) {
cert_type = kCertificateTypeTesting; cert_type = kCertificateTypeTesting;
} else { } else {
return util::Status( return Status(error_space, INVALID_PARAMETER,
error_space, INVALID_PARAMETER, absl::StrCat("invalid-certificate-type ", cert_type_string));
absl::StrCat("invalid-certificate-type ", cert_type_string));
} }
return CreateByType(cert_type, cert); return CreateByType(cert_type, cert);
} }
util::Status DrmRootCertificate::Create( Status DrmRootCertificate::Create(CertificateType cert_type,
CertificateType cert_type, std::unique_ptr<RsaKeyFactory> key_factory, std::unique_ptr<RsaKeyFactory> key_factory,
std::unique_ptr<DrmRootCertificate>* cert) { std::unique_ptr<DrmRootCertificate>* cert) {
DCHECK(cert); DCHECK(cert);
std::string serialized_certificate; std::string serialized_certificate;
@@ -375,49 +374,48 @@ util::Status DrmRootCertificate::Create(
break; break;
} }
default: default:
return util::Status(error_space, INVALID_PARAMETER, return Status(error_space, INVALID_PARAMETER, "invalid-certificate-type");
"invalid-certificate-type");
} }
SignedDrmCertificate signed_root_cert; SignedDrmCertificate signed_root_cert;
if (!signed_root_cert.ParseFromString(serialized_certificate)) { if (!signed_root_cert.ParseFromString(serialized_certificate)) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"signed-root-cert-deserialize-fail"); "signed-root-cert-deserialize-fail");
} }
DrmCertificate root_cert; DrmCertificate root_cert;
if (!signed_root_cert.has_drm_certificate()) { if (!signed_root_cert.has_drm_certificate()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-root-device-certificate"); "missing-root-device-certificate");
} }
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) { if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"root-cert-deserialize-fail"); "root-cert-deserialize-fail");
} }
if (!root_cert.has_public_key()) { if (!root_cert.has_public_key()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-root-cert-public-key"); "missing-root-cert-public-key");
} }
if (!signed_root_cert.has_signature()) { if (!signed_root_cert.has_signature()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-root-certificate-signature"); "missing-root-certificate-signature");
} }
std::unique_ptr<RsaPublicKey> public_key( std::unique_ptr<RsaPublicKey> public_key(
key_factory->CreateFromPkcs1PublicKey(root_cert.public_key())); key_factory->CreateFromPkcs1PublicKey(root_cert.public_key()));
if (!public_key) { if (!public_key) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-public-key"); "invalid-root-public-key");
} }
if (!public_key->VerifySignature(signed_root_cert.drm_certificate(), if (!public_key->VerifySignature(signed_root_cert.drm_certificate(),
signed_root_cert.signature())) { signed_root_cert.signature())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-root-certificate-signature"); "invalid-root-certificate-signature");
} }
cert->reset(new DrmRootCertificate( cert->reset(new DrmRootCertificate(
cert_type, serialized_certificate, root_cert.serial_number(), cert_type, serialized_certificate, root_cert.serial_number(),
root_cert.public_key(), std::move(key_factory))); root_cert.public_key(), std::move(key_factory)));
return util::OkStatus(); return OkStatus();
} }
DrmRootCertificate::DrmRootCertificate( DrmRootCertificate::DrmRootCertificate(
@@ -437,7 +435,7 @@ std::string DrmRootCertificate::GetDigest() const {
return absl::BytesToHexString(Sha256_Hash(serialized_certificate_)); return absl::BytesToHexString(Sha256_Hash(serialized_certificate_));
} }
util::Status DrmRootCertificate::VerifyCertificate( Status DrmRootCertificate::VerifyCertificate(
const std::string& serialized_certificate, const std::string& serialized_certificate,
SignedDrmCertificate* signed_certificate, SignedDrmCertificate* signed_certificate,
DrmCertificate* certificate) const { DrmCertificate* certificate) const {
@@ -447,8 +445,8 @@ util::Status DrmRootCertificate::VerifyCertificate(
signed_certificate = local_signed_certificate.get(); signed_certificate = local_signed_certificate.get();
} }
if (!signed_certificate->ParseFromString(serialized_certificate)) { if (!signed_certificate->ParseFromString(serialized_certificate)) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signed-drm-certificate"); "invalid-signed-drm-certificate");
} }
std::unique_ptr<DrmCertificate> local_certificate; std::unique_ptr<DrmCertificate> local_certificate;
@@ -458,20 +456,19 @@ util::Status DrmRootCertificate::VerifyCertificate(
} }
if (signed_certificate->drm_certificate().empty() || if (signed_certificate->drm_certificate().empty() ||
!certificate->ParseFromString(signed_certificate->drm_certificate())) { !certificate->ParseFromString(signed_certificate->drm_certificate())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-drm-certificate"); "invalid-drm-certificate");
} }
if (certificate->serial_number().empty()) { if (certificate->serial_number().empty()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-serial-number"); "missing-serial-number");
} }
if (!certificate->has_creation_time_seconds()) { if (!certificate->has_creation_time_seconds()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"missing-creation-time"); "missing-creation-time");
} }
if (certificate->public_key().empty()) { if (certificate->public_key().empty()) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key");
"missing-public-key");
} }
// Verify signature chain, but do not use cache for leaf certificates. // 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. // the case of device-unique device certificates.
// Signatures for root-signed certificates are always cached, even if they are // Signatures for root-signed certificates are always cached, even if they are
// leaf certificates. For example service, and provisioner certificates. // leaf certificates. For example service, and provisioner certificates.
util::Status DrmRootCertificate::VerifySignatures( Status DrmRootCertificate::VerifySignatures(
const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number, const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number,
bool use_cache) const { bool use_cache) const {
if (!signed_cert.has_signer()) { if (!signed_cert.has_signer()) {
@@ -497,12 +494,12 @@ util::Status DrmRootCertificate::VerifySignatures(
DrmCertificate signer; DrmCertificate signer;
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) { if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-certificate"); "invalid-signer-certificate");
} }
// Verify the signer before verifying signed_cert. // Verify the signer before verifying signed_cert.
util::Status status = Status status =
VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache); VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
@@ -519,17 +516,17 @@ util::Status DrmRootCertificate::VerifySignatures(
std::unique_ptr<RsaPublicKey> signer_public_key( std::unique_ptr<RsaPublicKey> signer_public_key(
key_factory_->CreateFromPkcs1PublicKey(signer.public_key())); key_factory_->CreateFromPkcs1PublicKey(signer.public_key()));
if (!signer_public_key) { if (!signer_public_key) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-leaf-signer-public-key"); "invalid-leaf-signer-public-key");
} }
if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(), if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(),
signed_cert.signature())) { signed_cert.signature())) {
return util::Status(error_space, INVALID_SIGNATURE, return Status(error_space, INVALID_SIGNATURE,
"cache-miss-invalid-signature"); "cache-miss-invalid-signature");
} }
} }
return util::OkStatus(); return OkStatus();
} }
} // namespace widevine } // namespace widevine

View File

@@ -42,13 +42,13 @@ class DrmRootCertificate {
// std::unique_ptr<DrmRootCertificate> which will be used to return a newly // std::unique_ptr<DrmRootCertificate> which will be used to return a newly
// created const DrmRootCertificate* if successful. The caller assumes // created const DrmRootCertificate* if successful. The caller assumes
// ownership of the new DrmRootCertificate. This method returns // ownership of the new DrmRootCertificate. This method returns
// util::Status::OK on success, or appropriate error status otherwise. // Status::OK on success, or appropriate error status otherwise.
static util::Status CreateByType(CertificateType cert_type, static Status CreateByType(CertificateType cert_type,
std::unique_ptr<DrmRootCertificate>* cert); std::unique_ptr<DrmRootCertificate>* cert);
// Variant on the method above to make CLIF happy until b/110539622 is fixed. // Variant on the method above to make CLIF happy until b/110539622 is fixed.
static std::unique_ptr<DrmRootCertificate> CreateByType( 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 // Creates a DrmRootCertificate object given a certificate type std::string, which
// must be one of "prod", "qa", or "test". // 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 // std::unique_ptr<DrmRootCertificate> which will be used to return a newly
// created const DrmRootCertificate* if successful. The caller assumes // created const DrmRootCertificate* if successful. The caller assumes
// ownership of the new DrmRootCertificate. This method returns // ownership of the new DrmRootCertificate. This method returns
// util::Status::OK on success, or appropriate error status otherwise. // Status::OK on success, or appropriate error status otherwise.
static util::Status CreateByTypeString( static Status CreateByTypeString(const std::string& cert_type_string,
const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert);
std::unique_ptr<DrmRootCertificate>* cert);
// |certificate| will contgain the DRM certificate upon successful return. // |certificate| will contgain the DRM certificate upon successful return.
// May be null. // May be null.
// Returns util::Status::OK if successful, or an appropriate error code // Returns Status::OK if successful, or an appropriate error code otherwise.
// otherwise. virtual Status VerifyCertificate(const std::string& serialized_certificate,
virtual util::Status VerifyCertificate( SignedDrmCertificate* signed_certificate,
const std::string& serialized_certificate, DrmCertificate* certificate) const;
SignedDrmCertificate* signed_certificate,
DrmCertificate* certificate) const;
// Returns the hex-encoded SHA-256 digest for this certificate. // Returns the hex-encoded SHA-256 digest for this certificate.
virtual std::string GetDigest() const; virtual std::string GetDigest() const;
@@ -86,13 +83,13 @@ class DrmRootCertificate {
private: private:
friend class DrmRootCertificateTest; friend class DrmRootCertificateTest;
static util::Status Create(CertificateType cert_type, static Status Create(CertificateType cert_type,
std::unique_ptr<RsaKeyFactory> key_factory, std::unique_ptr<RsaKeyFactory> key_factory,
std::unique_ptr<DrmRootCertificate>* cert); std::unique_ptr<DrmRootCertificate>* cert);
util::Status VerifySignatures(const SignedDrmCertificate& signed_cert, Status VerifySignatures(const SignedDrmCertificate& signed_cert,
const std::string& cert_serial_number, const std::string& cert_serial_number,
bool use_cache) const; bool use_cache) const;
CertificateType type_; CertificateType type_;
std::string serialized_certificate_; std::string serialized_certificate_;

View File

@@ -33,8 +33,8 @@ TEST(DrmRootCertificateCreateTest, TestCertificate) {
"49f917b1bdfed78002a58e799a58e940" "49f917b1bdfed78002a58e799a58e940"
"1fffaaed9d8d80752782b066757e2c8c"); "1fffaaed9d8d80752782b066757e2c8c");
std::unique_ptr<DrmRootCertificate> root_cert; std::unique_ptr<DrmRootCertificate> root_cert;
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
kCertificateTypeTesting, &root_cert)); kCertificateTypeTesting, &root_cert));
ASSERT_TRUE(root_cert != nullptr); ASSERT_TRUE(root_cert != nullptr);
EXPECT_EQ(kTestCertificateHash, root_cert->GetDigest()); EXPECT_EQ(kTestCertificateHash, root_cert->GetDigest());
} }
@@ -44,8 +44,8 @@ TEST(DrmRootCertificateCreateTest, DevCertificate) {
"0e25ee95476a770f30b98ac5ef778b3f" "0e25ee95476a770f30b98ac5ef778b3f"
"137b66c29385b84f547a361b4724b17d"); "137b66c29385b84f547a361b4724b17d");
std::unique_ptr<DrmRootCertificate> root_cert; std::unique_ptr<DrmRootCertificate> root_cert;
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
kCertificateTypeDevelopment, &root_cert)); kCertificateTypeDevelopment, &root_cert));
ASSERT_TRUE(root_cert != nullptr); ASSERT_TRUE(root_cert != nullptr);
EXPECT_EQ(kDevelopmentCertificateHash, root_cert->GetDigest()); EXPECT_EQ(kDevelopmentCertificateHash, root_cert->GetDigest());
} }
@@ -55,8 +55,8 @@ TEST(DrmRootCertificateCreateTest, ProdCertificate) {
"d62fdabc9286648a81f7d3bedaf2f5a5" "d62fdabc9286648a81f7d3bedaf2f5a5"
"27bbad39bc38da034ba98a21569adb9b"); "27bbad39bc38da034ba98a21569adb9b");
std::unique_ptr<DrmRootCertificate> root_cert; std::unique_ptr<DrmRootCertificate> root_cert;
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
kCertificateTypeProduction, &root_cert)); kCertificateTypeProduction, &root_cert));
ASSERT_TRUE(root_cert != nullptr); ASSERT_TRUE(root_cert != nullptr);
EXPECT_EQ(kProductionCertificateHash, root_cert->GetDigest()); EXPECT_EQ(kProductionCertificateHash, root_cert->GetDigest());
} }
@@ -111,8 +111,8 @@ class DrmRootCertificateTest : public testing::Test {
drm_certificates_[2].set_public_key( drm_certificates_[2].set_public_key(
test_keys_.public_test_key_3_2048_bits()); test_keys_.public_test_key_3_2048_bits());
ASSERT_EQ(util::OkStatus(), DrmRootCertificate::CreateByType( ASSERT_EQ(OkStatus(), DrmRootCertificate::CreateByType(
kCertificateTypeTesting, &root_cert_)); kCertificateTypeTesting, &root_cert_));
} }
void GenerateSignedDrmCertificate() { void GenerateSignedDrmCertificate() {
@@ -144,7 +144,7 @@ class DrmRootCertificateTest : public testing::Test {
TEST_F(DrmRootCertificateTest, SuccessNoOutput) { TEST_F(DrmRootCertificateTest, SuccessNoOutput) {
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
ASSERT_EQ(util::OkStatus(), ASSERT_EQ(OkStatus(),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
} }
@@ -153,25 +153,25 @@ TEST_F(DrmRootCertificateTest, SuccessWithOutput) {
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
SignedDrmCertificate out_signed_cert; SignedDrmCertificate out_signed_cert;
DrmCertificate out_cert; DrmCertificate out_cert;
ASSERT_EQ(util::OkStatus(), root_cert_->VerifyCertificate( ASSERT_EQ(OkStatus(), root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), signed_drm_certificate_.SerializeAsString(),
&out_signed_cert, &out_cert)); &out_signed_cert, &out_cert));
EXPECT_TRUE( EXPECT_TRUE(
MessageDifferencer::Equals(out_signed_cert, signed_drm_certificate_)); MessageDifferencer::Equals(out_signed_cert, signed_drm_certificate_));
EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2])); EXPECT_TRUE(MessageDifferencer::Equals(out_cert, drm_certificates_[2]));
} }
TEST_F(DrmRootCertificateTest, InvalidSignedDrmCertificate) { TEST_F(DrmRootCertificateTest, InvalidSignedDrmCertificate) {
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signed-drm-certificate"), "invalid-signed-drm-certificate"),
root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr)); root_cert_->VerifyCertificate("pure garbage", nullptr, nullptr));
} }
TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) { TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage"); signed_drm_certificate_.mutable_signer()->set_drm_certificate("more garbage");
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE,
"invalid-signer-certificate"), "invalid-signer-certificate"),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
} }
@@ -179,86 +179,84 @@ TEST_F(DrmRootCertificateTest, InvalidSignerCertificate) {
TEST_F(DrmRootCertificateTest, MissingDrmCertificate) { TEST_F(DrmRootCertificateTest, MissingDrmCertificate) {
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
signed_drm_certificate_.clear_drm_certificate(); signed_drm_certificate_.clear_drm_certificate();
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, EXPECT_EQ(
"invalid-drm-certificate"), Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); nullptr, nullptr));
} }
TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) { TEST_F(DrmRootCertificateTest, InvalidDrmCertificate) {
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
signed_drm_certificate_.set_drm_certificate("junk"); signed_drm_certificate_.set_drm_certificate("junk");
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, EXPECT_EQ(
"invalid-drm-certificate"), Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-drm-certificate"),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); nullptr, nullptr));
} }
TEST_F(DrmRootCertificateTest, InvalidPublicKey) { TEST_F(DrmRootCertificateTest, InvalidPublicKey) {
drm_certificates_[0].set_public_key("rubbish"); drm_certificates_[0].set_public_key("rubbish");
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, EXPECT_EQ(
"invalid-signer-public-key"), Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-signer-public-key"),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); nullptr, nullptr));
} }
TEST_F(DrmRootCertificateTest, MissingPublicKey) { TEST_F(DrmRootCertificateTest, MissingPublicKey) {
drm_certificates_[2].clear_public_key(); drm_certificates_[2].clear_public_key();
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
EXPECT_EQ( EXPECT_EQ(Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"),
util::Status(error_space, INVALID_DRM_CERTIFICATE, "missing-public-key"), root_cert_->VerifyCertificate(
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
nullptr, nullptr));
} }
TEST_F(DrmRootCertificateTest, MissingCreationTime) { TEST_F(DrmRootCertificateTest, MissingCreationTime) {
drm_certificates_[2].clear_creation_time_seconds(); drm_certificates_[2].clear_creation_time_seconds();
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, EXPECT_EQ(
"missing-creation-time"), Status(error_space, INVALID_DRM_CERTIFICATE, "missing-creation-time"),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); nullptr, nullptr));
} }
TEST_F(DrmRootCertificateTest, MissingSerialNumber) { TEST_F(DrmRootCertificateTest, MissingSerialNumber) {
drm_certificates_[2].set_serial_number(""); drm_certificates_[2].set_serial_number("");
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
EXPECT_EQ(util::Status(error_space, INVALID_DRM_CERTIFICATE, EXPECT_EQ(
"missing-serial-number"), Status(error_space, INVALID_DRM_CERTIFICATE, "missing-serial-number"),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); nullptr, nullptr));
} }
TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) { TEST_F(DrmRootCertificateTest, InvalidSignatureWithNoCache) {
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
signed_drm_certificate_.mutable_signer()->set_signature( signed_drm_certificate_.mutable_signer()->set_signature(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
EXPECT_EQ(util::Status(error_space, INVALID_SIGNATURE, EXPECT_EQ(
"cache-miss-invalid-signature"), Status(error_space, INVALID_SIGNATURE, "cache-miss-invalid-signature"),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(),
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); nullptr, nullptr));
} }
TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) { TEST_F(DrmRootCertificateTest, InvalidSignatureWithCache) {
GenerateSignedDrmCertificate(); GenerateSignedDrmCertificate();
// Verify and cache. // Verify and cache.
ASSERT_EQ(util::OkStatus(), ASSERT_EQ(OkStatus(),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
// Verify success using cache. // Verify success using cache.
ASSERT_EQ(util::OkStatus(), ASSERT_EQ(OkStatus(),
root_cert_->VerifyCertificate( root_cert_->VerifyCertificate(
signed_drm_certificate_.SerializeAsString(), nullptr, nullptr)); signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
// Verify failure using cache. // Verify failure using cache.
signed_drm_certificate_.mutable_signer()->set_signature( signed_drm_certificate_.mutable_signer()->set_signature(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
EXPECT_EQ( EXPECT_EQ(Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"),
util::Status(error_space, INVALID_SIGNATURE, "cached-signature-mismatch"), root_cert_->VerifyCertificate(
root_cert_->VerifyCertificate(signed_drm_certificate_.SerializeAsString(), signed_drm_certificate_.SerializeAsString(), nullptr, nullptr));
nullptr, nullptr));
} }
} // namespace widevine } // namespace widevine

View File

@@ -107,41 +107,41 @@ DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() {
} // namespace } // namespace
util::Status DrmServiceCertificate::AddDrmServiceCertificate( Status DrmServiceCertificate::AddDrmServiceCertificate(
const DrmRootCertificate* root_cert, const std::string& service_certificate, const DrmRootCertificate* root_drm_cert, const std::string& service_certificate,
const std::string& service_private_key, const std::string& service_private_key,
const std::string& service_private_key_passphrase) { const std::string& service_private_key_passphrase) {
DrmCertificate drm_cert; DrmCertificate drm_cert;
util::Status status = Status status =
root_cert->VerifyCertificate(service_certificate, nullptr, &drm_cert); root_drm_cert->VerifyCertificate(service_certificate, nullptr, &drm_cert);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
if (drm_cert.type() != DrmCertificate::SERVICE) { if (drm_cert.type() != DrmCertificate::SERVICE) {
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, return Status(error_space, INVALID_SERVICE_CERTIFICATE,
"not-service-certificate"); "not-service-certificate");
} }
if (drm_cert.provider_id().empty()) { if (drm_cert.provider_id().empty()) {
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, return Status(error_space, INVALID_SERVICE_CERTIFICATE,
"missing-certificate-service-id"); "missing-certificate-service-id");
} }
std::unique_ptr<RsaPublicKey> public_key( std::unique_ptr<RsaPublicKey> public_key(
RsaPublicKey::Create(drm_cert.public_key())); RsaPublicKey::Create(drm_cert.public_key()));
if (!public_key) { if (!public_key) {
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, return Status(error_space, INVALID_SERVICE_CERTIFICATE,
"invalid-certificate-public-key"); "invalid-certificate-public-key");
} }
std::string pkcs1_key; std::string pkcs1_key;
if (!rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey( if (!rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey(
service_private_key, service_private_key_passphrase, &pkcs1_key)) { service_private_key, service_private_key_passphrase, &pkcs1_key)) {
return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY, return Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
"key-decryption-failed"); "key-decryption-failed");
} }
std::unique_ptr<RsaPrivateKey> private_key(RsaPrivateKey::Create(pkcs1_key)); std::unique_ptr<RsaPrivateKey> private_key(RsaPrivateKey::Create(pkcs1_key));
if (private_key == nullptr) { if (private_key == nullptr) {
return util::Status(error_space, INVALID_SERVICE_PRIVATE_KEY, return Status(error_space, INVALID_SERVICE_PRIVATE_KEY,
"invalid-private-key"); "invalid-private-key");
} }
std::unique_ptr<DrmServiceCertificate> new_cert(new DrmServiceCertificate( std::unique_ptr<DrmServiceCertificate> new_cert(new DrmServiceCertificate(
@@ -150,7 +150,7 @@ util::Status DrmServiceCertificate::AddDrmServiceCertificate(
std::move(private_key))); std::move(private_key)));
DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert)); DrmServiceCertificateMap::GetInstance()->AddCert(std::move(new_cert));
return util::OkStatus(); return OkStatus();
} }
const DrmServiceCertificate* const DrmServiceCertificate*
@@ -171,7 +171,7 @@ const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate(
return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number); return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number);
} }
util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate( Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
const DrmRootCertificate* root_drm_cert, const std::string& service_certificate, const DrmRootCertificate* root_drm_cert, const std::string& service_certificate,
const std::string& service_private_key, const std::string& service_private_key,
const std::string& service_private_key_passphrase) { const std::string& service_private_key_passphrase) {
@@ -181,36 +181,36 @@ util::Status DrmServiceCertificate::SetDefaultDrmServiceCertificate(
service_private_key_passphrase); service_private_key_passphrase);
} }
util::Status DrmServiceCertificate::DecryptClientIdentification( Status DrmServiceCertificate::DecryptClientIdentification(
const EncryptedClientIdentification& encrypted_client_id, const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id) { ClientIdentification* client_id) {
DCHECK(client_id); DCHECK(client_id);
if (encrypted_client_id.service_certificate_serial_number().empty()) { if (encrypted_client_id.service_certificate_serial_number().empty()) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-service-certificate-serial-number"); "missing-service-certificate-serial-number");
} }
if (encrypted_client_id.provider_id().empty()) { if (encrypted_client_id.provider_id().empty()) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-service-id"); "missing-service-id");
} }
if (encrypted_client_id.encrypted_client_id().empty()) { if (encrypted_client_id.encrypted_client_id().empty()) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-client-id"); "missing-encrypted-client-id");
} }
if (encrypted_client_id.encrypted_client_id_iv().empty()) { if (encrypted_client_id.encrypted_client_id_iv().empty()) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-client-id-iv"); "missing-encrypted-client-id-iv");
} }
if (encrypted_client_id.encrypted_privacy_key().empty()) { if (encrypted_client_id.encrypted_privacy_key().empty()) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"missing-encrypted-privacy-key"); "missing-encrypted-privacy-key");
} }
std::string privacy_key; std::string privacy_key;
std::string provider_id; std::string provider_id;
const DrmServiceCertificate* cert = GetDrmServiceCertificate( const DrmServiceCertificate* cert = GetDrmServiceCertificate(
encrypted_client_id.service_certificate_serial_number()); encrypted_client_id.service_certificate_serial_number());
if (!cert) { if (!cert) {
return util::Status( return Status(
error_space, SERVICE_CERTIFICATE_NOT_FOUND, error_space, SERVICE_CERTIFICATE_NOT_FOUND,
"service-certificate-not-found (SN " + "service-certificate-not-found (SN " +
absl::BytesToHexString( absl::BytesToHexString(
@@ -219,56 +219,56 @@ util::Status DrmServiceCertificate::DecryptClientIdentification(
} }
if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(), if (!cert->private_key()->Decrypt(encrypted_client_id.encrypted_privacy_key(),
&privacy_key)) { &privacy_key)) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"privacy-key-decryption-failed"); "privacy-key-decryption-failed");
} }
if (cert->provider_id() != encrypted_client_id.provider_id()) { if (cert->provider_id() != encrypted_client_id.provider_id()) {
return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND, return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
std::string("provider-id-mismatch (") + cert->provider_id() + std::string("provider-id-mismatch (") + cert->provider_id() +
" / " + encrypted_client_id.provider_id() + ")"); " / " + encrypted_client_id.provider_id() + ")");
} }
std::string serialized_client_id(crypto_util::DecryptAesCbc( std::string serialized_client_id(crypto_util::DecryptAesCbc(
privacy_key, encrypted_client_id.encrypted_client_id_iv(), privacy_key, encrypted_client_id.encrypted_client_id_iv(),
encrypted_client_id.encrypted_client_id())); encrypted_client_id.encrypted_client_id()));
if (serialized_client_id.empty()) { if (serialized_client_id.empty()) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"client-id-decryption-failed"); "client-id-decryption-failed");
} }
if (!client_id->ParseFromString(serialized_client_id)) { if (!client_id->ParseFromString(serialized_client_id)) {
return util::Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION, return Status(error_space, INVALID_ENCRYPTED_CLIENT_IDENTIFICATION,
"client-id-parse-failed"); "client-id-parse-failed");
} }
return util::OkStatus(); return OkStatus();
} }
void DrmServiceCertificate::ResetServiceCertificates() { void DrmServiceCertificate::ResetServiceCertificates() {
DrmServiceCertificateMap::GetInstance()->Reset(); DrmServiceCertificateMap::GetInstance()->Reset();
} }
util::Status DrmServiceCertificate::ValidateDrmServiceCertificate() { Status DrmServiceCertificate::ValidateDrmServiceCertificate() {
const DrmServiceCertificate* service_certificate = const DrmServiceCertificate* service_certificate =
GetDefaultDrmServiceCertificate(); GetDefaultDrmServiceCertificate();
if (!service_certificate) { if (!service_certificate) {
return util::Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND, return Status(error_space, SERVICE_CERTIFICATE_NOT_FOUND,
"drm service certificate is not found."); "drm service certificate is not found.");
} }
SignedDrmCertificate signed_cert; SignedDrmCertificate signed_cert;
if (!signed_cert.ParseFromString(service_certificate->certificate())) { if (!signed_cert.ParseFromString(service_certificate->certificate())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"signed drm service certificate is failed to parse."); "signed drm service certificate is failed to parse.");
} }
DrmCertificate drm_cert; DrmCertificate drm_cert;
if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) { if (!drm_cert.ParseFromString(signed_cert.drm_certificate())) {
return util::Status(error_space, INVALID_DRM_CERTIFICATE, return Status(error_space, INVALID_DRM_CERTIFICATE,
"Drm service certificate is failed to parse."); "Drm service certificate is failed to parse.");
} }
if (!drm_cert.has_creation_time_seconds()) { if (!drm_cert.has_creation_time_seconds()) {
return util::Status(error_space, INVALID_SERVICE_CERTIFICATE, return Status(error_space, INVALID_SERVICE_CERTIFICATE,
"missing certificate creation time"); "missing certificate creation time");
} }
// TODO(user): Check creation_time_seconds field in DrmCertificate and also // TODO(user): Check creation_time_seconds field in DrmCertificate and also
// export the absl/time dependency through moe. // export the absl/time dependency through moe.
return util::OkStatus(); return OkStatus();
} }
DrmServiceCertificate::DrmServiceCertificate( DrmServiceCertificate::DrmServiceCertificate(

View File

@@ -18,9 +18,9 @@
#include <cstdint> #include <cstdint>
#include "base/macros.h" #include "base/macros.h"
#include "common/status.h"
#include "common/certificate_type.h" #include "common/certificate_type.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/status.h"
namespace widevine { namespace widevine {
class RequestInspectorTest; class RequestInspectorTest;
@@ -48,7 +48,7 @@ class DrmServiceCertificate {
// If the default service certificate is not set, this certificate will be // If the default service certificate is not set, this certificate will be
// used as the default service certificate. // used as the default service certificate.
// This method is thread-safe. // This method is thread-safe.
static util::Status AddDrmServiceCertificate( static Status AddDrmServiceCertificate(
const DrmRootCertificate* root_drm_cert, const DrmRootCertificate* root_drm_cert,
const std::string& service_certificate, const std::string& service_private_key, const std::string& service_certificate, const std::string& service_private_key,
const std::string& service_private_key_passphrase); const std::string& service_private_key_passphrase);
@@ -56,7 +56,7 @@ class DrmServiceCertificate {
// Same as AddDrmServiceCertificate(), but will clear the default service // Same as AddDrmServiceCertificate(), but will clear the default service
// certificate if it's set. This will result in this service certificate // certificate if it's set. This will result in this service certificate
// being set as the default service certificate. // being set as the default service certificate.
static util::Status SetDefaultDrmServiceCertificate( static Status SetDefaultDrmServiceCertificate(
const DrmRootCertificate* root_drm_cert, const DrmRootCertificate* root_drm_cert,
const std::string& service_certificate, const std::string& service_private_key, const std::string& service_certificate, const std::string& service_private_key,
const std::string& service_private_key_passphrase); const std::string& service_private_key_passphrase);
@@ -79,7 +79,7 @@ class DrmServiceCertificate {
// certificate which was used to encrypt the information. |client_id| must // certificate which was used to encrypt the information. |client_id| must
// not be NULL. Returns status::OK if successful, or an appropriate error // not be NULL. Returns status::OK if successful, or an appropriate error
// otherwise. This method is thread-safe. // otherwise. This method is thread-safe.
static util::Status DecryptClientIdentification( static Status DecryptClientIdentification(
const EncryptedClientIdentification& encrypted_client_id, const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id); ClientIdentification* client_id);
@@ -93,18 +93,18 @@ class DrmServiceCertificate {
// status::OK if successful, or in case of error, contact // status::OK if successful, or in case of error, contact
// widevine-tam@google.com to get the next valid service certificate renewed // widevine-tam@google.com to get the next valid service certificate renewed
// via get deviceCertificate StatusList. // via get deviceCertificate StatusList.
static util::Status ValidateDrmServiceCertificate(); static Status ValidateDrmServiceCertificate();
private: private:
friend class DrmServiceCertificateTest; friend class DrmServiceCertificateTest;
friend class widevine::RequestInspectorTest; 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& root_public_key, const std::string& service_certificate,
const std::string& service_private_key, const std::string& service_private_key,
const std::string& service_private_key_passphrase); const std::string& service_private_key_passphrase);
static util::Status SetDefaultDrmServiceCertificate( static Status SetDefaultDrmServiceCertificate(
const std::string& root_public_key, const std::string& service_certificate, const std::string& root_public_key, const std::string& service_certificate,
const std::string& service_private_key, const std::string& service_private_key,
const std::string& service_private_key_passphrase); const std::string& service_private_key_passphrase);

View File

@@ -69,9 +69,9 @@ class DrmServiceCertificateTest : public ::testing::Test {
return serialized_cert; return serialized_cert;
} }
util::Status SetDefaultDrmServiceCertificate(const std::string& serial_number, Status SetDefaultDrmServiceCertificate(const std::string& serial_number,
const std::string& provider_id, const std::string& provider_id,
uint32_t creation_time_seconds) { uint32_t creation_time_seconds) {
std::string signed_cert(GenerateDrmServiceCertificate( std::string signed_cert(GenerateDrmServiceCertificate(
serial_number, provider_id, creation_time_seconds, serial_number, provider_id, creation_time_seconds,
test_keys_.public_test_key_2_2048_bits())); test_keys_.public_test_key_2_2048_bits()));
@@ -79,15 +79,15 @@ class DrmServiceCertificateTest : public ::testing::Test {
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
test_keys_.private_test_key_2_2048_bits(), kPassphrase, test_keys_.private_test_key_2_2048_bits(), kPassphrase,
&encrypted_private_key)) { &encrypted_private_key)) {
return util::Status(util::error::INTERNAL, ""); return Status(error::INTERNAL, "");
} }
return DrmServiceCertificate::SetDefaultDrmServiceCertificate( return DrmServiceCertificate::SetDefaultDrmServiceCertificate(
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase); root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase);
} }
util::Status AddDrmServiceCertificate(const std::string& serial_number, Status AddDrmServiceCertificate(const std::string& serial_number,
const std::string& provider_id, const std::string& provider_id,
uint32_t creation_time_seconds) { uint32_t creation_time_seconds) {
std::string signed_cert(GenerateDrmServiceCertificate( std::string signed_cert(GenerateDrmServiceCertificate(
serial_number, provider_id, creation_time_seconds, serial_number, provider_id, creation_time_seconds,
test_keys_.public_test_key_2_2048_bits())); test_keys_.public_test_key_2_2048_bits()));
@@ -95,7 +95,7 @@ class DrmServiceCertificateTest : public ::testing::Test {
if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo( if (!rsa_util::RsaPrivateKeyToEncryptedPrivateKeyInfo(
test_keys_.private_test_key_2_2048_bits(), kPassphrase, test_keys_.private_test_key_2_2048_bits(), kPassphrase,
&encrypted_private_key)) { &encrypted_private_key)) {
return util::Status(util::error::INTERNAL, ""); return Status(error::INTERNAL, "");
} }
return DrmServiceCertificate::AddDrmServiceCertificate( return DrmServiceCertificate::AddDrmServiceCertificate(
root_cert_.get(), signed_cert, encrypted_private_key, kPassphrase); 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(), test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id); &encrypted_client_id);
ClientIdentification decrypted_client_id; ClientIdentification decrypted_client_id;
EXPECT_EQ(util::OkStatus(), EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
DrmServiceCertificate::DecryptClientIdentification( encrypted_client_id, &decrypted_client_id));
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id)); decrypted_client_id));
} }
@@ -220,27 +219,24 @@ TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) {
test_keys_.public_test_key_2_2048_bits(), test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id); &encrypted_client_id);
ClientIdentification decrypted_client_id; ClientIdentification decrypted_client_id;
EXPECT_EQ(util::OkStatus(), EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
DrmServiceCertificate::DecryptClientIdentification( encrypted_client_id, &decrypted_client_id));
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id)); decrypted_client_id));
EncryptClientIdentification(serial_number2, provider_id, EncryptClientIdentification(serial_number2, provider_id,
test_keys_.public_test_key_2_2048_bits(), test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id); &encrypted_client_id);
EXPECT_EQ(util::OkStatus(), EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
DrmServiceCertificate::DecryptClientIdentification( encrypted_client_id, &decrypted_client_id));
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id)); decrypted_client_id));
EncryptClientIdentification(serial_number3, provider_id, EncryptClientIdentification(serial_number3, provider_id,
test_keys_.public_test_key_2_2048_bits(), test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id); &encrypted_client_id);
EXPECT_EQ(util::OkStatus(), EXPECT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
DrmServiceCertificate::DecryptClientIdentification( encrypted_client_id, &decrypted_client_id));
encrypted_client_id, &decrypted_client_id));
EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id)); decrypted_client_id));
@@ -292,9 +288,8 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
test_keys_.public_test_key_2_2048_bits(), test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id); &encrypted_client_id);
ClientIdentification decrypted_client_id; ClientIdentification decrypted_client_id;
ASSERT_EQ(util::OkStatus(), ASSERT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
DrmServiceCertificate::DecryptClientIdentification( encrypted_client_id, &decrypted_client_id));
encrypted_client_id, &decrypted_client_id));
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id)); decrypted_client_id));
@@ -310,9 +305,8 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
invalid = encrypted_client_id; invalid = encrypted_client_id;
++(*invalid.mutable_encrypted_client_id_iv())[4]; ++(*invalid.mutable_encrypted_client_id_iv())[4];
EXPECT_NE(util::OkStatus(), EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
DrmServiceCertificate::DecryptClientIdentification( invalid, &decrypted_client_id));
invalid, &decrypted_client_id));
invalid.clear_encrypted_client_id_iv(); invalid.clear_encrypted_client_id_iv();
EXPECT_EQ( EXPECT_EQ(
@@ -324,9 +318,8 @@ TEST_F(DrmServiceCertificateTest, InvalidEncryptedClientIdentification) {
invalid = encrypted_client_id; invalid = encrypted_client_id;
++(*invalid.mutable_encrypted_client_id())[0]; ++(*invalid.mutable_encrypted_client_id())[0];
EXPECT_NE(util::OkStatus(), EXPECT_NE(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
DrmServiceCertificate::DecryptClientIdentification( invalid, &decrypted_client_id));
invalid, &decrypted_client_id));
invalid.clear_encrypted_client_id(); invalid.clear_encrypted_client_id();
EXPECT_EQ( EXPECT_EQ(
@@ -349,9 +342,8 @@ TEST_F(DrmServiceCertificateTest, PrivateKeyDecryptError) {
test_keys_.public_test_key_2_2048_bits(), test_keys_.public_test_key_2_2048_bits(),
&encrypted_client_id); &encrypted_client_id);
ClientIdentification decrypted_client_id; ClientIdentification decrypted_client_id;
ASSERT_EQ(util::OkStatus(), ASSERT_EQ(OkStatus(), DrmServiceCertificate::DecryptClientIdentification(
DrmServiceCertificate::DecryptClientIdentification( encrypted_client_id, &decrypted_client_id));
encrypted_client_id, &decrypted_client_id));
ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_, ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(client_id_,
decrypted_client_id)); decrypted_client_id));

View File

@@ -113,99 +113,99 @@ void RemoteAttestationVerifier::EnableTestDrmCertificates(bool enable) {
ca_.reset(); ca_.reset();
} }
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation( Status RemoteAttestationVerifier::VerifyRemoteAttestation(
const std::string& message, const RemoteAttestation& remote_attestation, const std::string& message, const RemoteAttestation& remote_attestation,
std::string* remote_attestation_cert_sn) { std::string* remote_attestation_cert_sn) {
DCHECK(remote_attestation_cert_sn); DCHECK(remote_attestation_cert_sn);
// Sanity check RemoteAttestation. // Sanity check RemoteAttestation.
if (!remote_attestation.has_certificate()) { if (!remote_attestation.has_certificate()) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-certificate-missing")); "remote-attestation-certificate-missing"));
} }
if (!remote_attestation.has_salt()) { if (!remote_attestation.has_salt()) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-salt-missing")); "remote-attestation-salt-missing"));
} }
if (!remote_attestation.has_signature()) { if (!remote_attestation.has_signature()) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-signature-missing")); "remote-attestation-signature-missing"));
} }
// Decrypt ClientIdentification containing remote attestation certificate. // Decrypt ClientIdentification containing remote attestation certificate.
// A service cert would be looked up first, then that cert will be used // A service cert would be looked up first, then that cert will be used
// to decrypt the ClientIdentification. // to decrypt the ClientIdentification.
ClientIdentification client_id; ClientIdentification client_id;
util::Status status = DrmServiceCertificate::DecryptClientIdentification( Status status = DrmServiceCertificate::DecryptClientIdentification(
remote_attestation.certificate(), &client_id); remote_attestation.certificate(), &client_id);
if (!status.ok()) return status; if (!status.ok()) return status;
if (client_id.type() != if (client_id.type() !=
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) { ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
std::string("remote-attestation-invalid-client-id-type (") + std::string("remote-attestation-invalid-client-id-type (") +
absl::StrCat(client_id.type()) + ")")); absl::StrCat(client_id.type()) + ")"));
} }
return VerifyRemoteAttestation(message, remote_attestation, client_id, return VerifyRemoteAttestation(message, remote_attestation, client_id,
remote_attestation_cert_sn); remote_attestation_cert_sn);
} }
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation( Status RemoteAttestationVerifier::VerifyRemoteAttestation(
const std::string& message, const RemoteAttestation& remote_attestation, const std::string& message, const RemoteAttestation& remote_attestation,
const std::string& privacy_key) { const std::string& privacy_key) {
// Sanity check RemoteAttestation. // Sanity check RemoteAttestation.
if (!remote_attestation.has_certificate()) { if (!remote_attestation.has_certificate()) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-certificate-missing")); "remote-attestation-certificate-missing"));
} }
if (!remote_attestation.has_salt()) { if (!remote_attestation.has_salt()) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-salt-missing")); "remote-attestation-salt-missing"));
} }
if (!remote_attestation.has_signature()) { if (!remote_attestation.has_signature()) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-signature-missing")); "remote-attestation-signature-missing"));
} }
// Decrypt ClientIdentification containing remote attestation certificate, // Decrypt ClientIdentification containing remote attestation certificate,
// directly using an explicitly provided key |privacy_key|. // directly using an explicitly provided key |privacy_key|.
ClientIdentification client_id; ClientIdentification client_id;
util::Status status = DecryptEncryptedClientIdentification( Status status = DecryptEncryptedClientIdentification(
remote_attestation.certificate(), privacy_key, &client_id); remote_attestation.certificate(), privacy_key, &client_id);
if (!status.ok()) return status; if (!status.ok()) return status;
if (client_id.type() != if (client_id.type() !=
ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) { ClientIdentification::REMOTE_ATTESTATION_CERTIFICATE) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
std::string("remote-attestation-invalid-client-id-type (") + std::string("remote-attestation-invalid-client-id-type (") +
absl::StrCat(client_id.type()) + ")")); absl::StrCat(client_id.type()) + ")"));
} }
std::string remote_attestation_cert_sn; std::string remote_attestation_cert_sn;
return VerifyRemoteAttestation(message, remote_attestation, client_id, return VerifyRemoteAttestation(message, remote_attestation, client_id,
&remote_attestation_cert_sn); &remote_attestation_cert_sn);
} }
util::Status RemoteAttestationVerifier::VerifyRemoteAttestation( Status RemoteAttestationVerifier::VerifyRemoteAttestation(
const std::string& message, const RemoteAttestation& remote_attestation, const std::string& message, const RemoteAttestation& remote_attestation,
const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) { const ClientIdentification& client_id, std::string* remote_attestation_cert_sn) {
if (!client_id.has_token()) { if (!client_id.has_token()) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-token-missing")); "remote-attestation-token-missing"));
} }
// Load and verify the certificate chain. // Load and verify the certificate chain.
std::unique_ptr<X509CertChain> cert_chain(new X509CertChain); 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 (!status.ok()) return status;
if (cert_chain->GetNumCerts() < 1) { if (cert_chain->GetNumCerts() < 1) {
return (util::Status(error_space, INVALID_MESSAGE, return (Status(error_space, INVALID_MESSAGE,
"remote-attestation-empty-certificate-chain")); "remote-attestation-empty-certificate-chain"));
} }
std::string device_mode_string = std::string device_mode_string =
cert_chain->GetCert(0)->GetSubjectNameField(kDeviceModeFieldName); cert_chain->GetCert(0)->GetSubjectNameField(kDeviceModeFieldName);
if (device_mode_string != kExpectedDeviceMode) { if (device_mode_string != kExpectedDeviceMode) {
return (util::Status(error_space, REMOTE_ATTESTATION_FAILED, return (Status(error_space, REMOTE_ATTESTATION_FAILED,
std::string("remote-attestation-device-not-verified (") + std::string("remote-attestation-device-not-verified (") +
device_mode_string + " / " + kDeviceModeFieldName + device_mode_string + " / " + kDeviceModeFieldName +
")")); ")"));
} }
ca_mutex_.ReaderLock(); ca_mutex_.ReaderLock();
if (ca_ == NULL) { if (ca_ == NULL) {
@@ -217,10 +217,9 @@ util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
status = ca_->VerifyCertChain(*cert_chain); status = ca_->VerifyCertChain(*cert_chain);
ca_mutex_.ReaderUnlock(); ca_mutex_.ReaderUnlock();
if (!status.ok()) { if (!status.ok()) {
return (util::Status( return (Status(error_space, REMOTE_ATTESTATION_FAILED,
error_space, REMOTE_ATTESTATION_FAILED, std::string("remote-attestation-cert-chain-validation-failed: ") +
std::string("remote-attestation-cert-chain-validation-failed: ") + status.error_message()));
status.error_message()));
} }
// Verify the remote attestation signature. // Verify the remote attestation signature.
std::unique_ptr<RsaPublicKey> leaf_key; std::unique_ptr<RsaPublicKey> leaf_key;
@@ -232,30 +231,30 @@ util::Status RemoteAttestationVerifier::VerifyRemoteAttestation(
} }
} }
if (!leaf_key) { if (!leaf_key) {
return util::Status(error_space, REMOTE_ATTESTATION_FAILED, return Status(error_space, REMOTE_ATTESTATION_FAILED,
"remote-attestation-cert-chain-no-leaf"); "remote-attestation-cert-chain-no-leaf");
} }
if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt, if (!leaf_key->VerifySignatureSha256Pkcs7(message_with_salt,
remote_attestation.signature())) { remote_attestation.signature())) {
return (util::Status(error_space, REMOTE_ATTESTATION_FAILED, return (Status(error_space, REMOTE_ATTESTATION_FAILED,
"remote-attestation-signature-verification-failed: ")); "remote-attestation-signature-verification-failed: "));
} }
*remote_attestation_cert_sn = cert_chain->GetCert(0)->GetSerialNumber(); *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_); absl::WriterMutexLock lock(&ca_mutex_);
std::unique_ptr<X509Cert> ca_cert(new X509Cert); 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)); enable_test_certificates_ ? kTestRootCaDerCert : kProdRootCaDerCert));
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
ca_.reset(new X509CA(ca_cert.release())); ca_.reset(new X509CA(ca_cert.release()));
return util::OkStatus(); return OkStatus();
} }
} // namespace widevine } // namespace widevine

View File

@@ -50,9 +50,9 @@ class RemoteAttestationVerifier {
// return will contain the serial number for the client's remote attestation // return will contain the serial number for the client's remote attestation
// certificate. // certificate.
// This method is thread-safe. // This method is thread-safe.
util::Status VerifyRemoteAttestation( Status VerifyRemoteAttestation(const std::string& message,
const std::string& message, const RemoteAttestation& remote_attestation, const RemoteAttestation& remote_attestation,
std::string* remote_attestation_cert_sn); std::string* remote_attestation_cert_sn);
// Call to verify a RemoteAttestation challenge response, used in certificate // Call to verify a RemoteAttestation challenge response, used in certificate
// provisioning protocol. // provisioning protocol.
@@ -61,9 +61,9 @@ class RemoteAttestationVerifier {
// |privacy_key| is used to decrypt the EncryptedClientIdentification within // |privacy_key| is used to decrypt the EncryptedClientIdentification within
// the |remote_attestation| message. // the |remote_attestation| message.
// This method is thread-safe. // This method is thread-safe.
util::Status VerifyRemoteAttestation( Status VerifyRemoteAttestation(const std::string& message,
const std::string& message, const RemoteAttestation& remote_attestation, const RemoteAttestation& remote_attestation,
const std::string& privacy_key); const std::string& privacy_key);
private: private:
// Common subroutine to perform the verification. // 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 // |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 // return will contain the serial number for the client's remote attestation
// certificate. // certificate.
util::Status VerifyRemoteAttestation( Status VerifyRemoteAttestation(const std::string& message,
const std::string& message, const RemoteAttestation& remote_attestation, const RemoteAttestation& remote_attestation,
const ClientIdentification& client_id, const ClientIdentification& client_id,
std::string* remote_attestation_cert_sn); std::string* remote_attestation_cert_sn);
util::Status LoadCa(); Status LoadCa();
bool enable_test_certificates_; bool enable_test_certificates_;
absl::Mutex ca_mutex_; absl::Mutex ca_mutex_;

View File

@@ -29,6 +29,14 @@ std::string Sha256_Hash(const std::string& message) {
return digest; 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) { std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name) {
// X.667 14 Setting the fields of a name-based UUID. // X.667 14 Setting the fields of a name-based UUID.
// - Allocate a UUID to use as a "name space identifier" for all UUIDs // - Allocate a UUID to use as a "name space identifier" for all UUIDs

View File

@@ -21,6 +21,9 @@ std::string Sha1_Hash(const std::string& message);
// Calculates SHA256 hash. // Calculates SHA256 hash.
std::string Sha256_Hash(const std::string& message); 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, // 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 // 16-byte binary representation. Name_space is a GUID prefix; name is a unique
// name in the namespace. // name in the namespace.

View File

@@ -48,6 +48,18 @@ TEST(ShaUtilTest, Sha256) {
Sha256_Hash("random data")); 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) { TEST(ShaUtilTest, GenerateSha1Uuid) {
std::string name_space = std::string name_space =
absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774"); absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774");

View File

@@ -11,55 +11,50 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "common/status.h"
#include "common/aes_cbc_util.h" #include "common/aes_cbc_util.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/sha_util.h" #include "common/sha_util.h"
#include "common/status.h"
namespace widevine { namespace widevine {
namespace signature_util { namespace signature_util {
util::Status GenerateAesSignature(const std::string& message, const std::string& aes_key, Status GenerateAesSignature(const std::string& message, const std::string& aes_key,
const std::string& aes_iv, std::string* signature) { const std::string& aes_iv, std::string* signature) {
if (signature == nullptr) { 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); std::string hash = Sha1_Hash(message);
if (hash.empty()) { 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); std::string sig = crypto_util::EncryptAesCbc(aes_key, aes_iv, hash);
if (sig.empty()) { if (sig.empty()) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "Computed AES signature is empty");
"Computed AES signature is empty");
} }
*signature = sig; *signature = sig;
return util::OkStatus(); return OkStatus();
} }
util::Status GenerateRsaSignature(const std::string& message, Status GenerateRsaSignature(const std::string& message, const std::string& private_key,
const std::string& private_key, std::string* signature) {
std::string* signature) {
if (signature == nullptr) { 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( std::unique_ptr<RsaPrivateKey> rsa_private_key(
RsaPrivateKey::Create(private_key)); RsaPrivateKey::Create(private_key));
if (rsa_private_key == nullptr) { if (rsa_private_key == nullptr) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "Failed to construct a RsaPrivateKey");
"Failed to construct a RsaPrivateKey");
} }
std::string sig; std::string sig;
if (!rsa_private_key->GenerateSignature(message, &sig)) { if (!rsa_private_key->GenerateSignature(message, &sig)) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "Failed to generate a RSA signature");
"Failed to generate a RSA signature");
} }
if (sig.empty()) { if (sig.empty()) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "Computed RSA signature is empty");
"Computed RSA signature is empty");
} }
*signature = sig; *signature = sig;
return util::OkStatus(); return OkStatus();
} }
} // namespace signature_util } // namespace signature_util

View File

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

View File

@@ -16,7 +16,7 @@ namespace widevine {
using crypto_util::kSigningKeySizeBits; using crypto_util::kSigningKeySizeBits;
uint32_t SigningKeyMaterialSize(ProtocolVersion protocol_version) { uint32_t SigningKeyMaterialSizeBits(ProtocolVersion protocol_version) {
if (protocol_version <= VERSION_2_0) { if (protocol_version <= VERSION_2_0) {
return kSigningKeySizeBits; return kSigningKeySizeBits;
} else { } else {

View File

@@ -20,7 +20,7 @@
// std::string derived_key = DeriveKey(key_str, // std::string derived_key = DeriveKey(key_str,
// label, // label,
// context, // context,
// SigningKeyMaterialSize(VERSION_2_1)); // SigningKeyMaterialSizeBits(VERSION_2_1));
// std::string server_derived_key = GetServerSigningKey(derived_key); // std::string server_derived_key = GetServerSigningKey(derived_key);
// std::string client_derived_key = GetClientSigninKey(derived_key); // std::string client_derived_key = GetClientSigninKey(derived_key);
#ifndef COMMON_SIGNING_KEY_UTIL_H_ #ifndef COMMON_SIGNING_KEY_UTIL_H_
@@ -36,7 +36,7 @@ namespace widevine {
// Returns the size of the signing key based on the License Protocol // 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 // Version. Signing keys for version 2.0 have a length of 256. Signing
// keys for version 2.1 have a length of 512. // 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 // 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 // depend on the size of the key. Keys that are 512 bits in length

View File

@@ -30,14 +30,14 @@ std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) {
} }
} // namespace } // namespace
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_0) { TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_0) {
ASSERT_EQ(crypto_util::kSigningKeySizeBits, 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, ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2,
SigningKeyMaterialSize(VERSION_2_1)); SigningKeyMaterialSizeBits(VERSION_2_1));
} }
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) { TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) {

View File

@@ -12,10 +12,9 @@
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "common/status.h" #include "util/error_space.h"
namespace widevine { namespace widevine {
namespace util {
namespace { namespace {
@@ -72,5 +71,4 @@ std::ostream& operator<<(std::ostream& os, const Status& x) {
return os; return os;
} }
} // namespace util
} // namespace widevine } // namespace widevine

View File

@@ -14,7 +14,6 @@
#include "util/error_space.h" #include "util/error_space.h"
namespace widevine { namespace widevine {
namespace util {
namespace error { namespace error {
enum StatusCode { enum StatusCode {
@@ -104,7 +103,6 @@ std::ostream& operator<<(std::ostream& os, const Status& x);
#define CHECK_OK(expression) CHECK(expression.ok()) << expression.ToString() #define CHECK_OK(expression) CHECK(expression.ok()) << expression.ToString()
} // namespace util
} // namespace widevine } // namespace widevine
#endif // COMMON_STATUS_H_ #endif // COMMON_STATUS_H_

View File

@@ -10,7 +10,6 @@
#include "testing/gunit.h" #include "testing/gunit.h"
namespace widevine { namespace widevine {
namespace util {
TEST(StatusTest, OK_Status) { TEST(StatusTest, OK_Status) {
// test case for ok status. // test case for ok status.
@@ -59,5 +58,4 @@ TEST(StatusTest, NOT_EQUAL_OPERATOR_NONE_MSG) {
} }
} // namespace util
} // namespace widevine } // namespace widevine

View File

@@ -16,9 +16,9 @@
namespace widevine { namespace widevine {
namespace string_util { 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) { if (output == nullptr) {
return util::Status(util::error::INTERNAL, "output is nullptr."); return Status(error::INTERNAL, "output is nullptr.");
} }
std::stringstream sstream(bitset); std::stringstream sstream(bitset);
@@ -29,7 +29,7 @@ util::Status BitsetStringToBinaryString(const std::string& bitset, std::string*
*output += c; *output += c;
} }
return util::OkStatus(); return OkStatus();
} }
} // namespace string_util } // namespace string_util

View File

@@ -17,7 +17,7 @@ namespace string_util {
// Converts std::string representation of a bitset to its binary equivalent string. // Converts std::string representation of a bitset to its binary equivalent string.
// For example, converts "01110100011001010111001101110100" to "test". // 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 string_util
} // namespace widevine } // namespace widevine

View File

@@ -18,32 +18,31 @@
namespace widevine { namespace widevine {
util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key, Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
const std::string& message, const std::string& message,
std::string* signature) { std::string* signature) {
CHECK(signature); CHECK(signature);
if (pem_private_key.empty()) { 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()) { if (message.empty()) {
return util::Status(util::error::INVALID_ARGUMENT, "Empty message"); return Status(error::INVALID_ARGUMENT, "Empty message");
} }
BIO* bio(NULL); BIO* bio(NULL);
bio = BIO_new_mem_buf(const_cast<char*>(pem_private_key.data()), bio = BIO_new_mem_buf(const_cast<char*>(pem_private_key.data()),
pem_private_key.size()); pem_private_key.size());
if (bio == NULL) { 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); RSA* key(NULL);
std::unique_ptr<char[]> sig_buffer; std::unique_ptr<char[]> sig_buffer;
unsigned int sig_size; unsigned int sig_size;
unsigned char digest[SHA256_DIGEST_LENGTH]; unsigned char digest[SHA256_DIGEST_LENGTH];
key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL); key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
if (key == NULL) { if (key == NULL) {
status = util::Status(util::Status::canonical_space(), status = Status(Status::canonical_space(), error::INVALID_ARGUMENT,
util::error::INVALID_ARGUMENT, "PEM RSA private key load failed");
"PEM RSA private key load failed");
goto cleanup; goto cleanup;
} }
SHA256(reinterpret_cast<const unsigned char*>(message.data()), message.size(), 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), if (RSA_sign(NID_sha256, digest, sizeof(digest),
reinterpret_cast<unsigned char*>(sig_buffer.get()), &sig_size, reinterpret_cast<unsigned char*>(sig_buffer.get()), &sig_size,
key) != 1) { key) != 1) {
status = status = Status(Status::canonical_space(), error::INTERNAL,
util::Status(util::Status::canonical_space(), util::error::INTERNAL, "RSA private encrypt failed");
"RSA private encrypt failed");
goto cleanup; goto cleanup;
} }
signature->assign(sig_buffer.get(), sig_size); signature->assign(sig_buffer.get(), sig_size);

View File

@@ -23,9 +23,9 @@ namespace widevine {
// |message| is the message to be signed, and |signature| is a pointer to a // |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 // std::string where the signature will be stored. The caller returns ownership of
// all paramters. // all paramters.
util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key, Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
const std::string& message, const std::string& message,
std::string* signature); std::string* signature);
} // namespace widevine } // namespace widevine

View File

@@ -14,13 +14,11 @@
#include "common/vmp_checker.h" #include "common/vmp_checker.h"
namespace widevine { namespace widevine {
util::Status VerifyVmpData( Status VerifyVmpData(const std::string& vmp_data,
const std::string& vmp_data, PlatformVerificationStatus* platform_verification_status) {
PlatformVerificationStatus* platform_verification_status) {
*platform_verification_status = PLATFORM_UNVERIFIED; *platform_verification_status = PLATFORM_UNVERIFIED;
VmpChecker::Result vmp_result; VmpChecker::Result vmp_result;
util::Status status = Status status = VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result);
VmpChecker::Instance()->VerifyVmpData(vmp_data, &vmp_result);
if (status.ok()) { if (status.ok()) {
switch (vmp_result) { switch (vmp_result) {
case VmpChecker::kUnverified: case VmpChecker::kUnverified:

View File

@@ -20,9 +20,8 @@ namespace widevine {
// Retrieve the PlatformVerificationStatus for |vmp_data|. The // Retrieve the PlatformVerificationStatus for |vmp_data|. The
// PlatformVerificationStatus is defined at // PlatformVerificationStatus is defined at
util::Status VerifyVmpData( Status VerifyVmpData(const std::string& vmp_data,
const std::string& vmp_data, PlatformVerificationStatus* platform_verification_status);
PlatformVerificationStatus* platform_verification_status);
} // namespace widevine } // namespace widevine
#endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_ #endif // COMMON_VERIFIED_MEDIA_PIPELINE_H_

View File

@@ -248,9 +248,9 @@ VmpChecker::VmpChecker() : allow_development_vmp_(false) {}
VmpChecker::~VmpChecker() {} VmpChecker::~VmpChecker() {}
util::Status VmpChecker::SelectCertificateType(CertificateType cert_type) { Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert); std::unique_ptr<X509Cert> ca_cert(new X509Cert);
util::Status status = ca_cert->LoadDer( Status status = ca_cert->LoadDer(
cert_type == kCertificateTypeProduction cert_type == kCertificateTypeProduction
? std::string(reinterpret_cast<const char*>( ? std::string(reinterpret_cast<const char*>(
kProdVmpCodeSigningDrmRootCertificate), kProdVmpCodeSigningDrmRootCertificate),
@@ -262,7 +262,7 @@ util::Status VmpChecker::SelectCertificateType(CertificateType cert_type) {
ca_.reset(new X509CA(ca_cert.release())); ca_.reset(new X509CA(ca_cert.release()));
return util::OkStatus(); return OkStatus();
} }
VmpChecker* VmpChecker::Instance() { VmpChecker* VmpChecker::Instance() {
@@ -271,17 +271,16 @@ VmpChecker* VmpChecker::Instance() {
} }
// Verify VMP data and return appropriate result. // 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(!vmp_data.empty());
DCHECK(result); 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; vmp::VmpData vmp_data_obj;
if (!vmp_data_obj.ParseFromString(vmp_data)) { if (!vmp_data_obj.ParseFromString(vmp_data)) {
LOG(INFO) << "Error deserializing VmpData."; LOG(INFO) << "Error deserializing VmpData.";
return util::Status(error_space, INVALID_MESSAGE, return Status(error_space, INVALID_MESSAGE, "vmp-data-deserialize-failed");
"vmp-data-deserialize-failed");
} }
std::vector<std::unique_ptr<X509Cert>> code_signing_certs; 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(); for (int cert_idx = 0; cert_idx < vmp_data_obj.certificates_size();
++cert_idx) { ++cert_idx) {
code_signing_certs.emplace_back(new X509Cert); 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))); vmp_data_obj.certificates(cert_idx)));
if (!status.ok()) return status; 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, if (code_signing_certs.back()->GetV3BooleanExtension(kDevelopmentFlagOid,
&dev_flag) && &dev_flag) &&
dev_flag) { dev_flag) {
return util::Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED,
"development-vmp-certificate-not-allowed"); "development-vmp-certificate-not-allowed");
} }
} }
status = ca_->VerifyCert(*code_signing_certs.back()); 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()) { if (binary_info.signature().empty()) {
LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\"."; LOG(INFO) << "Unsigned binary \"" << binary_info.file_name() << "\".";
*result = kTampered; *result = kTampered;
return util::OkStatus(); return OkStatus();
} }
if (binary_info.certificate_index() >= code_signing_certs.size()) { if (binary_info.certificate_index() >= code_signing_certs.size()) {
LOG(INFO) << "Invalid code signing certificate index."; LOG(INFO) << "Invalid code signing certificate index.";
*result = kTampered; *result = kTampered;
return util::OkStatus(); return OkStatus();
} }
X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get(); X509Cert* cert = code_signing_certs[binary_info.certificate_index()].get();
std::unique_ptr<RsaPublicKey> key(cert->GetRsaPublicKey()); 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 \"" LOG(INFO) << "Code signature verification failed for file \""
<< binary_info.file_name() << "\"."; << binary_info.file_name() << "\".";
*result = kTampered; *result = kTampered;
return util::OkStatus(); return OkStatus();
} }
if (binary_info.flags() & kBlessedBinaryFlag) ++num_blessed_binaries; 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 LOG(INFO) << "Invalid number of blessed binaries (" << num_blessed_binaries
<< ")."; << ").";
*result = kTampered; *result = kTampered;
return util::OkStatus(); return OkStatus();
} }
VLOG(2) << "VMP verification success. Secure storage: " VLOG(2) << "VMP verification success. Secure storage: "
<< secure_storage_verified; << secure_storage_verified;
*result = secure_storage_verified ? kSecureStorageVerified : kVerified; *result = secure_storage_verified ? kSecureStorageVerified : kVerified;
return util::OkStatus(); return OkStatus();
} }
} // namespace widevine } // namespace widevine

View File

@@ -16,8 +16,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "common/status.h"
#include "common/certificate_type.h" #include "common/certificate_type.h"
#include "common/status.h"
namespace widevine { namespace widevine {
class X509CA; class X509CA;
@@ -35,10 +35,10 @@ class VmpChecker {
static VmpChecker* Instance(); static VmpChecker* Instance();
// Select the type of root to use. Not thread-safe. // 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. // 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. // Enable/disable development code signing certificates.
void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; } void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; }

View File

@@ -16,10 +16,10 @@
#include "absl/synchronization/mutex.h" #include "absl/synchronization/mutex.h"
#include "util/endian/endian.h" #include "util/endian/endian.h"
#include "util/gtl/map_util.h" #include "util/gtl/map_util.h"
#include "common/status.h"
#include "common/aes_cbc_util.h" #include "common/aes_cbc_util.h"
#include "common/ecb_util.h" #include "common/ecb_util.h"
#include "common/sha_util.h" #include "common/sha_util.h"
#include "common/status.h"
namespace widevine { namespace widevine {
@@ -108,20 +108,20 @@ bool WvmTokenHandler::IsSystemIdKnown(uint32_t system_id) {
return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id); return PreprovKeysMap::GetSingleton()->IsSystemIdKnown(system_id);
} }
util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token, Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
std::string* device_key_out, std::string* device_key_out,
Cipher* cipher_out, Cipher* cipher_out,
bool* insecure_out) { bool* insecure_out) {
const std::string default_make_model; const std::string default_make_model;
return DecryptDeviceKey(token, default_make_model, device_key_out, cipher_out, return DecryptDeviceKey(token, default_make_model, device_key_out, cipher_out,
insecure_out); insecure_out);
} }
util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token, Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
const std::string& make_model, const std::string& make_model,
std::string* device_key_out, std::string* device_key_out,
Cipher* cipher_out, Cipher* cipher_out,
bool* insecure_out) { bool* insecure_out) {
DCHECK(device_key_out); DCHECK(device_key_out);
// DCHECK below is commented out because preprov_keys_ being nullptr // DCHECK below is commented out because preprov_keys_ being nullptr
// is a valid test in wvm_token_handler_test.cc. If we have // 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. // presubmit because evidently Kokoro does debug build.
// DCHECK(preprov_keys_); // DCHECK(preprov_keys_);
if (token.size() < kKeyboxSizeBytes) { if (token.size() < kKeyboxSizeBytes) {
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
"Keybox token is too short.");
} }
if (PreprovKeysMap::GetSingleton()->IsEmpty()) { if (PreprovKeysMap::GetSingleton()->IsEmpty()) {
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT,
"Pre-provisioning key map is nullptr."); "Pre-provisioning key map is nullptr.");
} }
uint32_t system_id = GetSystemId(token); uint32_t system_id = GetSystemId(token);
@@ -143,7 +142,7 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
std::vector<PreprovKey> key_vector = std::vector<PreprovKey> key_vector =
PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id); PreprovKeysMap::GetSingleton()->GetPreprovKeys(system_id);
util::Status status; Status status;
// First pass through the matching system Ids is an attempt to find an // First pass through the matching system Ids is an attempt to find an
// alternate preprov key specific to this make/model. // alternate preprov key specific to this make/model.
const PreprovKey* preferred_ppk = NULL; 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); ppk.key_bytes, token, device_key_out, insecure_out, &version);
if (version != 2) { if (version != 2) {
// Only version 2 keyboxes supported. // Only version 2 keyboxes supported.
return util::Status(util::error::PERMISSION_DENIED, return Status(error::PERMISSION_DENIED,
absl::StrCat("invalid-keybox-version ", version)); absl::StrCat("invalid-keybox-version ", version));
} }
if (status.ok()) { if (status.ok()) {
if (cipher_out) { if (cipher_out) {
@@ -176,8 +175,8 @@ util::Status WvmTokenHandler::DecryptDeviceKey(absl::string_view token,
// Return error from last attempt. // Return error from last attempt.
return status; return status;
} }
return util::Status( return Status(
util::error::NOT_FOUND, error::NOT_FOUND,
absl::StrCat("Unknown system id: ", system_id).c_str()); // NOLINT 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 // decrypted device key to encrypt the given asset key. Returns the encrypted
// asset key in |result|. // asset key in |result|.
// On failure, returns an error from the Widevine Server SDK error space. // On failure, returns an error from the Widevine Server SDK error space.
util::Status WvmTokenHandler::GetEncryptedAssetKey( Status WvmTokenHandler::GetEncryptedAssetKey(absl::string_view token,
absl::string_view token, absl::string_view raw_asset_key, absl::string_view raw_asset_key,
const std::string& make_model, std::string* result) { const std::string& make_model,
std::string* result) {
std::string device_key; std::string device_key;
Cipher cipher = AES; Cipher cipher = AES;
util::Status status = Status status =
DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr); DecryptDeviceKey(token, make_model, &device_key, &cipher, nullptr);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
@@ -215,20 +215,19 @@ std::string WvmTokenHandler::GetEncryptedUniqueId(absl::string_view token) {
return encrypted_unique_id; return encrypted_unique_id;
} }
util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
absl::string_view preprov_key, absl::string_view token, absl::string_view preprov_key, absl::string_view token,
std::string* device_key_out) { std::string* device_key_out) {
return DecryptDeviceKeyWithPreprovKey(preprov_key, token, device_key_out, return DecryptDeviceKeyWithPreprovKey(preprov_key, token, device_key_out,
nullptr, nullptr); nullptr, nullptr);
} }
util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
absl::string_view preprov_key, absl::string_view token, absl::string_view preprov_key, absl::string_view token,
std::string* device_key_out, bool* insecure_out, uint32_t* version) { std::string* device_key_out, bool* insecure_out, uint32_t* version) {
CHECK(device_key_out); CHECK(device_key_out);
if (token.size() < kKeyboxSizeBytes) { if (token.size() < kKeyboxSizeBytes) {
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT, "Keybox token is too short.");
"Keybox token is too short.");
} }
if (version) { if (version) {
*version = BigEndian::Load32(token.data()); *version = BigEndian::Load32(token.data());
@@ -243,7 +242,7 @@ util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
if (unique_id.size() != 16) { if (unique_id.size() != 16) {
// Decrypting 16 bytes should result in 16 bytes. // Decrypting 16 bytes should result in 16 bytes.
LOG(WARNING) << "Internal error decrypting unique id from token."; 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); absl::string_view encrypted_bits = token.substr(24, 48);
@@ -252,7 +251,7 @@ util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
if (decrypted_bits.size() != 48) { if (decrypted_bits.size() != 48) {
// Decrypting 48 bytes should result in 48 bytes. // Decrypting 48 bytes should result in 48 bytes.
LOG(WARNING) << "Internal error decrypting device key from token."; 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]; uint8_t keybox_flags = decrypted_bits[36];
absl::string_view device_key = absl::string_view device_key =
@@ -269,48 +268,43 @@ util::Status WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
keybox_flags = 0; keybox_flags = 0;
} }
if (expected_hash != actual_hash) { if (expected_hash != actual_hash) {
return util::Status(util::error::PERMISSION_DENIED, return Status(error::PERMISSION_DENIED, "Keybox validation failed.");
"Keybox validation failed.");
} }
*device_key_out = std::string(device_key); *device_key_out = std::string(device_key);
if (insecure_out) { if (insecure_out) {
*insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0; *insecure_out = (keybox_flags & kKeyboxFlagInsecure) != 0;
} }
return util::OkStatus(); return OkStatus();
} }
util::Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key, Status WvmTokenHandler::EncryptAssetKey(absl::string_view device_key,
absl::string_view raw_asset_key, absl::string_view raw_asset_key,
Cipher cipher, std::string* result) { Cipher cipher, std::string* result) {
CHECK(result); CHECK(result);
if (device_key.size() != 16) { if (device_key.size() != 16) {
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT, "Invalid device key: size != 16");
"Invalid device key: size != 16");
} }
if (raw_asset_key.size() < 16) { if (raw_asset_key.size() < 16) {
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT, "Invalid asset key: size < 16");
"Invalid asset key: size < 16");
} }
// Truncate extra characters in the key; wvm always uses 16. // Truncate extra characters in the key; wvm always uses 16.
absl::string_view asset_key = raw_asset_key.substr(0, 16); absl::string_view asset_key = raw_asset_key.substr(0, 16);
switch (cipher) { switch (cipher) {
case DES3: case DES3:
if (!crypto_util::Encrypt3DesEcb(device_key, asset_key, result)) { if (!crypto_util::Encrypt3DesEcb(device_key, asset_key, result)) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "Error encrypting asset key with 3DES.");
"Error encrypting asset key with 3DES.");
} }
return util::OkStatus(); return OkStatus();
case AES: case AES:
if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) { if (!crypto_util::EncryptAesEcb(device_key, asset_key, result)) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "Error encrypting asset key with AES.");
"Error encrypting asset key with AES.");
} }
return util::OkStatus(); return OkStatus();
case PASS_THRU: case PASS_THRU:
result->assign(raw_asset_key.data(), raw_asset_key.size()); result->assign(raw_asset_key.data(), raw_asset_key.size());
return util::OkStatus(); return OkStatus();
default: default:
return util::Status(util::error::INVALID_ARGUMENT, "Unknown cipher type"); return Status(error::INVALID_ARGUMENT, "Unknown cipher type");
} }
} }

View File

@@ -70,24 +70,23 @@ class WvmTokenHandler {
// to use with the device key. // to use with the device key.
// insecure_out may be null; if not, *insecure_out will be set to the // insecure_out may be null; if not, *insecure_out will be set to the
// decrypted value of the 'insecure keybox' flag. // decrypted value of the 'insecure keybox' flag.
static util::Status DecryptDeviceKey(absl::string_view token, static Status DecryptDeviceKey(absl::string_view token,
std::string* device_key_out, std::string* device_key_out, Cipher* cipher_out,
Cipher* cipher_out, bool* insecure_out); bool* insecure_out);
// Same as above, except takes in the make/model from the license request. // 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 // For legacy WVM license, we have some special cases where we need to inspect
// the make/model as we apply alternate keys. // the make/model as we apply alternate keys.
static util::Status DecryptDeviceKey(absl::string_view token, static Status DecryptDeviceKey(absl::string_view token,
const std::string& make_model, const std::string& make_model,
std::string* device_key_out, std::string* device_key_out, Cipher* cipher_out,
Cipher* cipher_out, bool* insecure_out); bool* insecure_out);
// Decrypt a token using the preprov key for its system ID, and use the // 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 // decrypted device key to encrypt the given asset key. Returns the encrypted
// asset key in result. // asset key in result.
static util::Status GetEncryptedAssetKey(absl::string_view token, static Status GetEncryptedAssetKey(absl::string_view token,
absl::string_view raw_asset_key, absl::string_view raw_asset_key,
const std::string& make_model, const std::string& make_model, std::string* result);
std::string* result);
// Extract the system ID component of a token (bytes 4-8). // Extract the system ID component of a token (bytes 4-8).
static uint32_t GetSystemId(absl::string_view token); 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), // 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 // the only possible cause of failure is the decrypted device key hash
// being incorrect. // being incorrect.
static util::Status DecryptDeviceKeyWithPreprovKey( static Status DecryptDeviceKeyWithPreprovKey(
absl::string_view preprov_key_bytes, absl::string_view token, absl::string_view preprov_key_bytes, absl::string_view token,
std::string* device_key_out); std::string* device_key_out);
// Same as above, but allows extracting the 'insecure keybox' flag and keybox // Same as above, but allows extracting the 'insecure keybox' flag and keybox
// version. // version.
static util::Status DecryptDeviceKeyWithPreprovKey( static Status DecryptDeviceKeyWithPreprovKey(
absl::string_view preprov_key_bytes, absl::string_view token, absl::string_view preprov_key_bytes, absl::string_view token,
std::string* device_key_out, bool* insecure_out, uint32_t* version); std::string* device_key_out, bool* insecure_out, uint32_t* version);
// Given a decrypted device key as returned by DecryptToken(), use it to // Given a decrypted device key as returned by DecryptToken(), use it to
// encrypt an asset key with the given cipher. // encrypt an asset key with the given cipher.
static util::Status EncryptAssetKey(absl::string_view device_key, static Status EncryptAssetKey(absl::string_view device_key,
absl::string_view raw_asset_key, absl::string_view raw_asset_key, Cipher cipher,
Cipher cipher, std::string* result); std::string* result);
private: private:
DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler); DISALLOW_IMPLICIT_CONSTRUCTORS(WvmTokenHandler);

View File

@@ -29,12 +29,6 @@ namespace widevine {
using absl::BytesToHexString; using absl::BytesToHexString;
using absl::HexStringToBytes; 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) { TEST(WvmTokenHandlerTest, GetSystemId) {
EXPECT_EQ(kTestSystemId, EXPECT_EQ(kTestSystemId,
WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex))); WvmTokenHandler::GetSystemId(HexStringToBytes(kTestToken1Hex)));
@@ -52,7 +46,7 @@ TEST(WvmTokenHandlerTest, GetEncryptedUniqueId) {
} }
TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) { TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
util::Status status; Status status;
std::string device_key; std::string device_key;
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
@@ -76,7 +70,7 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
HexStringToBytes(kTestPreprovKeyHex), token, &device_key); HexStringToBytes(kTestPreprovKeyHex), token, &device_key);
EXPECT_FALSE(status.ok()); 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()); EXPECT_TRUE(device_key.empty());
} }
@@ -86,18 +80,18 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKeyWithPreprovKey) {
TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) { TEST(WvmTokenHandlerTest, DecryptDeviceKey_PreprovKeysNullPtr) {
// Not calling WvmTokenHandler::SetPreprovKeys() // Not calling WvmTokenHandler::SetPreprovKeys()
// So preprov_keys_ would be nullptr. // So preprov_keys_ would be nullptr.
util::Status status; Status status;
std::string device_key; std::string device_key;
status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex), status = WvmTokenHandler::DecryptDeviceKey(HexStringToBytes(kTestToken1Hex),
&device_key, nullptr, nullptr); &device_key, nullptr, nullptr);
EXPECT_FALSE(status.ok()); 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 // Same tests as DecryptDeviceKeyWithPreprovKey(), but we use the handler's
// table of preprov keys instead of providing our own. // table of preprov keys instead of providing our own.
TEST(WvmTokenHandlerTest, DecryptDeviceKey) { TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
util::Status status; Status status;
std::string device_key; std::string device_key;
WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector()); WvmTokenHandler::SetPreprovKeys(GetPreprovKeyVector());
@@ -120,7 +114,7 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
status = status =
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr); WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
EXPECT_FALSE(status.ok()); 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()); EXPECT_TRUE(device_key.empty());
// Test with nonexistent system id. Should produce NOT_FOUND. // Test with nonexistent system id. Should produce NOT_FOUND.
@@ -132,7 +126,7 @@ TEST(WvmTokenHandlerTest, DecryptDeviceKey) {
status = status =
WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr); WvmTokenHandler::DecryptDeviceKey(token, &device_key, nullptr, nullptr);
EXPECT_FALSE(status.ok()); 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()); EXPECT_TRUE(device_key.empty());
} }
@@ -142,7 +136,7 @@ TEST(WvmTokenHandlerTest, GetEncryptedAssetKey) {
std::string raw_asset_key = "asset-key-000000"; std::string raw_asset_key = "asset-key-000000";
std::string asset_key; std::string asset_key;
std::string make_model; std::string make_model;
util::Status status = WvmTokenHandler::GetEncryptedAssetKey( Status status = WvmTokenHandler::GetEncryptedAssetKey(
HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key); HexStringToBytes(kTestToken1Hex), raw_asset_key, make_model, &asset_key);
EXPECT_OK(status); EXPECT_OK(status);
EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key)); EXPECT_EQ("305d5f979074b1c4f932be70d3cc850c", BytesToHexString(asset_key));
@@ -194,7 +188,7 @@ TEST(WvmTokenHandlerTest, FilterOnMakeModel) {
std::string raw_asset_key = "asset-key-000000"; std::string raw_asset_key = "asset-key-000000";
std::string asset_key; std::string asset_key;
// Check 3DES encryption of asset keys // Check 3DES encryption of asset keys
util::Status status = WvmTokenHandler::EncryptAssetKey( Status status = WvmTokenHandler::EncryptAssetKey(
HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key, HexStringToBytes(kTestDeviceKey3DesHex), raw_asset_key,
WvmTokenHandler::DES3, &asset_key); WvmTokenHandler::DES3, &asset_key);
EXPECT_OK(status); EXPECT_OK(status);
@@ -220,7 +214,7 @@ TEST(WvmTokenHandlerTest, FilterOnMakeModel) {
} }
TEST(WvmTokenHandlerTest, AncientKeybox) { TEST(WvmTokenHandlerTest, AncientKeybox) {
util::Status status; Status status;
std::string device_key; std::string device_key;
std::string v1_token( std::string v1_token(
@@ -228,7 +222,7 @@ TEST(WvmTokenHandlerTest, AncientKeybox) {
status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey( status = WvmTokenHandler::DecryptDeviceKeyWithPreprovKey(
HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex), HexStringToBytes(v1_token), HexStringToBytes(kTestToken1Hex),
&device_key); &device_key);
EXPECT_EQ(util::error::PERMISSION_DENIED, status.error_code()); EXPECT_EQ(error::PERMISSION_DENIED, status.error_code());
EXPECT_TRUE(device_key.empty()); EXPECT_TRUE(device_key.empty());
} }

View File

@@ -67,22 +67,20 @@ X509Cert::~X509Cert() {
X509Cert::X509Cert(X509* openssl_cert) : openssl_cert_(openssl_cert) {} 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()) { 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); BIO* bio(NULL);
X509* new_cert(NULL); X509* new_cert(NULL);
bio = BIO_new_mem_buf(const_cast<char*>(pem_cert.data()), pem_cert.size()); bio = BIO_new_mem_buf(const_cast<char*>(pem_cert.data()), pem_cert.size());
if (bio == NULL) { 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); new_cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
if (new_cert == NULL) { if (new_cert == NULL) {
status = util::Status(util::Status::canonical_space(), status = Status(error::INVALID_ARGUMENT, "PEM certificate load failed");
util::error::INVALID_ARGUMENT,
"PEM certificate load failed");
goto cleanup; goto cleanup;
} }
if (openssl_cert_ != NULL) { if (openssl_cert_ != NULL) {
@@ -97,22 +95,21 @@ cleanup:
return status; return status;
} }
util::Status X509Cert::LoadDer(const std::string& der_cert) { Status X509Cert::LoadDer(const std::string& der_cert) {
if (der_cert.empty()) { 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 = const unsigned char* cert_data =
reinterpret_cast<const unsigned char*>(der_cert.data()); reinterpret_cast<const unsigned char*>(der_cert.data());
X509* new_cert = d2i_X509(NULL, &cert_data, der_cert.size()); X509* new_cert = d2i_X509(NULL, &cert_data, der_cert.size());
if (new_cert == NULL) { if (new_cert == NULL) {
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT, "DER certificate load failed");
"DER certificate load failed");
} }
if (openssl_cert_ != NULL) { if (openssl_cert_ != NULL) {
X509_free(openssl_cert_); X509_free(openssl_cert_);
} }
openssl_cert_ = new_cert; openssl_cert_ = new_cert;
return util::OkStatus(); return OkStatus();
} }
std::string X509Cert::GetPem() const { std::string X509Cert::GetPem() const {
@@ -222,38 +219,36 @@ bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const
return true; return true;
} }
util::Status X509Cert::Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time, Status X509Cert::Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
int64_t* epoch_seconds) const { int64_t* epoch_seconds) const {
if (asn1_time == nullptr) { if (asn1_time == nullptr) {
// This code is exported to shared source. The exported code does not yet // This code is exported to shared source. The exported code does not yet
// support MakeStatus. // support MakeStatus.
// NOLINTNEXTLINE // NOLINTNEXTLINE
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT, "asn1_time cannot be null.");
"asn1_time cannot be null.");
} }
if (epoch_seconds == nullptr) { if (epoch_seconds == nullptr) {
// NOLINTNEXTLINE // NOLINTNEXTLINE
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT, "epoch_seconds cannot be null.");
"epoch_seconds cannot be null.");
} }
ScopedAsn1Time epoch_time(ASN1_TIME_new()); ScopedAsn1Time epoch_time(ASN1_TIME_new());
if (!ASN1_TIME_set(epoch_time.get(), 0)) { if (!ASN1_TIME_set(epoch_time.get(), 0)) {
// NOLINTNEXTLINE // 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 day = 0;
int seconds = 0; int seconds = 0;
if (!ASN1_TIME_diff(&day, &seconds, epoch_time.get(), asn1_time)) { if (!ASN1_TIME_diff(&day, &seconds, epoch_time.get(), asn1_time)) {
// NOLINTNEXTLINE // NOLINTNEXTLINE
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL,
"Failed to convert asn1 time to epoch time."); "Failed to convert asn1 time to epoch time.");
} }
*epoch_seconds = 24L * 3600L * day + seconds; *epoch_seconds = 24L * 3600L * day + seconds;
return util::OkStatus(); return OkStatus();
} }
X509CertChain::~X509CertChain() { Reset(); } X509CertChain::~X509CertChain() { Reset(); }
@@ -265,7 +260,7 @@ void X509CertChain::Reset() {
cert_chain_.clear(); 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 kBeginCertificate[] = "-----BEGIN CERTIFICATE-----";
static const char kEndCertificate[] = "-----END 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) { if (end_pos != std::string::npos) {
end_pos += sizeof(kEndCertificate) - 1; end_pos += sizeof(kEndCertificate) - 1;
std::unique_ptr<X509Cert> new_cert(new X509Cert); 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)); pem_cert_chain.substr(begin_pos, end_pos - begin_pos));
if (!status.ok()) { if (!status.ok()) {
return status; 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); 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()); ScopedX509Stack cert_stack(sk_X509_new_null());
CBS cbs; CBS cbs;
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pk7_cert_chain.data()), CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pk7_cert_chain.data()),
pk7_cert_chain.size()); pk7_cert_chain.size());
if (!PKCS7_get_certificates(cert_stack.get(), &cbs)) { if (!PKCS7_get_certificates(cert_stack.get(), &cbs)) {
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT,
"Unable to load PKCS#7 certificate chain"); "Unable to load PKCS#7 certificate chain");
} }
while (sk_X509_num(cert_stack.get()) > 0) { 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()))); new X509Cert(sk_X509_pop(cert_stack.get())));
} }
return util::OkStatus(); return OkStatus();
} }
std::string X509CertChain::GetPkcs7() { std::string X509CertChain::GetPkcs7() {
@@ -357,44 +352,42 @@ X509CA::~X509CA() {
} }
} }
util::Status X509CA::InitializeStore() { Status X509CA::InitializeStore() {
absl::WriterMutexLock lock(&openssl_store_mutex_); absl::WriterMutexLock lock(&openssl_store_mutex_);
if (openssl_store_ == NULL) { if (openssl_store_ == NULL) {
if (ca_cert_ == 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(); openssl_store_ = X509_STORE_new();
if (openssl_store_ == NULL) { if (openssl_store_ == NULL) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "Failed to allocate X.509 store");
"Failed to allocate X.509 store");
} }
if (X509_STORE_add_cert(openssl_store_, if (X509_STORE_add_cert(openssl_store_,
const_cast<X509*>(ca_cert_->openssl_cert())) == 0) { const_cast<X509*>(ca_cert_->openssl_cert())) == 0) {
X509_STORE_free(openssl_store_); X509_STORE_free(openssl_store_);
openssl_store_ = NULL; openssl_store_ = NULL;
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL,
"Failed to add X.509 CA certificate to store"); "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); 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) { if (cert_chain.GetNumCerts() < 1) {
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT,
"Cannot verify empty certificate chain"); "Cannot verify empty certificate chain");
} }
ScopedX509StackOnly intermediates(sk_X509_new_null()); ScopedX509StackOnly intermediates(sk_X509_new_null());
if (!intermediates) { if (!intermediates) {
return util::Status( return Status(error::INTERNAL,
util::Status::canonical_space(), util::error::INTERNAL, "Failed to allocate X.509 intermediate certificate stack");
"Failed to allocate X.509 intermediate certificate stack");
} }
const X509Cert* leaf_cert(nullptr); const X509Cert* leaf_cert(nullptr);
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) { 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) { if (!leaf_cert) {
return util::Status(util::Status::canonical_space(), return Status(error::INVALID_ARGUMENT,
util::error::INVALID_ARGUMENT, "X.509 certificate chain without leaf certificate.");
"X.509 certificate chain without leaf certificate.");
} }
return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get()); return OpenSslX509Verify(leaf_cert->openssl_cert(), intermediates.get());
} }
util::Status X509CA::VerifyCertWithChain(const X509Cert& cert, Status X509CA::VerifyCertWithChain(const X509Cert& cert,
const X509CertChain& cert_chain) { const X509CertChain& cert_chain) {
ScopedX509StackOnly intermediates(sk_X509_new_null()); ScopedX509StackOnly intermediates(sk_X509_new_null());
if (!intermediates) { if (!intermediates) {
// MakeStatus is now preferred. But we don't support it in the exported // MakeStatus is now preferred. But we don't support it in the exported
// version, yet. So, ignore lint here. // version, yet. So, ignore lint here.
// NOLINTNEXTLINE // NOLINTNEXTLINE
return util::Status( return Status(error::INTERNAL,
util::Status::canonical_space(), util::error::INTERNAL, "Failed to allocate X.509 intermediate certificate stack");
"Failed to allocate X.509 intermediate certificate stack");
} }
for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) { for (size_t idx = 0; idx < cert_chain.GetNumCerts(); ++idx) {
sk_X509_push(intermediates.get(), sk_X509_push(intermediates.get(),
@@ -432,14 +423,14 @@ util::Status X509CA::VerifyCertWithChain(const X509Cert& cert,
return OpenSslX509Verify(cert.openssl_cert(), intermediates.get()); return OpenSslX509Verify(cert.openssl_cert(), intermediates.get());
} }
util::Status X509CA::OpenSslX509Verify(const X509* cert, Status X509CA::OpenSslX509Verify(const X509* cert,
STACK_OF(X509) * intermediates) { STACK_OF(X509) * intermediates) {
DCHECK(cert); DCHECK(cert);
absl::ReaderMutexLock lock(&openssl_store_mutex_); absl::ReaderMutexLock lock(&openssl_store_mutex_);
if (openssl_store_ == NULL) { if (openssl_store_ == NULL) {
openssl_store_mutex_.ReaderUnlock(); openssl_store_mutex_.ReaderUnlock();
util::Status status = InitializeStore(); Status status = InitializeStore();
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
@@ -447,23 +438,21 @@ util::Status X509CA::OpenSslX509Verify(const X509* cert,
} }
ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new()); ScopedX509StoreCtx store_ctx(X509_STORE_CTX_new());
if (!store_ctx) { if (!store_ctx) {
return util::Status(util::Status::canonical_space(), util::error::INTERNAL, return Status(error::INTERNAL, "Failed to allocate X.509 store context");
"Failed to allocate X.509 store context");
} }
if (X509_STORE_CTX_init(store_ctx.get(), openssl_store_, if (X509_STORE_CTX_init(store_ctx.get(), openssl_store_,
const_cast<X509*>(cert), intermediates) == 0) { const_cast<X509*>(cert), intermediates) == 0) {
return util::Status(util::Status::canonical_space(), util::error::INTERNAL, return Status(error::INTERNAL, "Failed to initialize X.509 store context");
"Failed to initialize X.509 store context");
} }
int x509_status = X509_verify_cert(store_ctx.get()); int x509_status = X509_verify_cert(store_ctx.get());
if (x509_status != 1) { if (x509_status != 1) {
return util::Status(util::Status::canonical_space(), util::error::INTERNAL, return Status(error::INTERNAL,
std::string("X.509 certificate chain validation failed: ") + std::string("X.509 certificate chain validation failed: ") +
X509_verify_cert_error_string( X509_verify_cert_error_string(
X509_STORE_CTX_get_error(store_ctx.get()))); X509_STORE_CTX_get_error(store_ctx.get())));
} }
return util::OkStatus(); return OkStatus();
} }
} // namespace widevine } // namespace widevine

View File

@@ -24,13 +24,13 @@
#include "openssl/pem.h" #include "openssl/pem.h"
#include "openssl/x509.h" #include "openssl/x509.h"
#include "openssl/x509v3.h" #include "openssl/x509v3.h"
#include "common/status.h"
#include "common/openssl_util.h" #include "common/openssl_util.h"
#include "common/rsa_key.h" #include "common/rsa_key.h"
#include "common/status.h"
namespace widevine { 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 which holds a single X.509 certificates.
class X509Cert { class X509Cert {
@@ -43,11 +43,11 @@ class X509Cert {
// Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is // Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
// a PEM-encoded certificate. // 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 // Load an X.509 certificate. Takes a single parameter, |pem_cert|, which is
// a DER-encoded certificate. // 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. // Return a std::string containing the PEM-encoded certificate.
std::string GetPem() const; std::string GetPem() const;
@@ -91,8 +91,8 @@ class X509Cert {
private: private:
explicit X509Cert(X509* openssl_cert); explicit X509Cert(X509* openssl_cert);
util::Status Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time, Status Asn1TimeToEpochSeconds(const ASN1_TIME* asn1_time,
int64_t* epoch_seconds) const; int64_t* epoch_seconds) const;
X509* openssl_cert_; X509* openssl_cert_;
std::string subject_name_; std::string subject_name_;
@@ -112,12 +112,12 @@ class X509CertChain {
// |pem_cert_chain|, which is the concatenation of a number of PEM X.509 // |pem_cert_chain|, which is the concatenation of a number of PEM X.509
// certificates, beginning with the leaf certificate, and ending with the // certificates, beginning with the leaf certificate, and ending with the
// certificate signed by the root CA. // 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, // 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 // |pk7_cert_chain|, which is a DER-encoded PKCS#7 X.509 certificate
// container. // 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 // Writes the |cert_chain_| to a DER-encoded PKCS#7 X.509 cryptographic
// message. The final message does not include signed data. // 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 // Does X.509 PKI validation of |cert| against the root CA certificate
// used when constructing X509CA. This method is thread-safe. // 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 // Does X.509 PKI validation of |cert_chain| against the root CA certificate
// used when constructing X509CA. This method is thread-safe. // 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| // Does X.509 PKI validation of |cert| using the |cert_chain|
// certificates. This method allows |cert| to be an ICA. This method is // certificates. This method allows |cert| to be an ICA. This method is
// thread-safe. // thread-safe.
util::Status VerifyCertWithChain(const X509Cert& cert, Status VerifyCertWithChain(const X509Cert& cert,
const X509CertChain& cert_chain); const X509CertChain& cert_chain);
private: private:
util::Status InitializeStore(); Status InitializeStore();
util::Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * stack); Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * intermediates);
std::unique_ptr<X509Cert> ca_cert_; std::unique_ptr<X509Cert> ca_cert_;
absl::Mutex openssl_store_mutex_; absl::Mutex openssl_store_mutex_;

View File

@@ -355,23 +355,23 @@ const bool kTestDevCodeSigningCertFlagValue = true;
TEST(X509CertTest, LoadCert) { TEST(X509CertTest, LoadCert) {
X509Cert test_cert; X509Cert test_cert;
EXPECT_EQ(util::OkStatus(), EXPECT_EQ(OkStatus(),
test_cert.LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); 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. // TODO(user): Add more specific status checks to failure tests.
EXPECT_NE(util::OkStatus(), test_cert.LoadDer("bad cert")); EXPECT_NE(OkStatus(), test_cert.LoadDer("bad cert"));
EXPECT_NE(util::OkStatus(), test_cert.LoadPem("bad cert")); EXPECT_NE(OkStatus(), test_cert.LoadPem("bad cert"));
EXPECT_NE(util::OkStatus(), test_cert.LoadDer("")); EXPECT_NE(OkStatus(), test_cert.LoadDer(""));
EXPECT_NE(util::OkStatus(), test_cert.LoadPem("")); EXPECT_NE(OkStatus(), test_cert.LoadPem(""));
} }
TEST(X509CertTest, VerifySignature) { TEST(X509CertTest, VerifySignature) {
X509Cert test_cert; 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 message(absl::HexStringToBytes(kTestMessage));
std::string signature; std::string signature;
ASSERT_EQ(util::OkStatus(), GenerateRsaSignatureSha256Pkcs1( ASSERT_EQ(OkStatus(), GenerateRsaSignatureSha256Pkcs1(kTestCertPrivateKey,
kTestCertPrivateKey, message, &signature)); message, &signature));
std::unique_ptr<RsaPublicKey> pub_key(test_cert.GetRsaPublicKey()); std::unique_ptr<RsaPublicKey> pub_key(test_cert.GetRsaPublicKey());
ASSERT_TRUE(pub_key); ASSERT_TRUE(pub_key);
EXPECT_TRUE(pub_key->VerifySignatureSha256Pkcs7(message, signature)); EXPECT_TRUE(pub_key->VerifySignatureSha256Pkcs7(message, signature));
@@ -384,7 +384,7 @@ TEST(X509CertTest, VerifySignature) {
TEST(X509CertTest, GetSubjectNameField) { TEST(X509CertTest, GetSubjectNameField) {
X509Cert test_cert; 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_C, test_cert.GetSubjectNameField("C"));
EXPECT_EQ(kTestPemCertSubjectField_CN, test_cert.GetSubjectNameField("CN")); EXPECT_EQ(kTestPemCertSubjectField_CN, test_cert.GetSubjectNameField("CN"));
EXPECT_EQ("", test_cert.GetSubjectNameField("invalid_field")); EXPECT_EQ("", test_cert.GetSubjectNameField("invalid_field"));
@@ -392,13 +392,13 @@ TEST(X509CertTest, GetSubjectNameField) {
TEST(X509CertTest, GetSerialNumber) { TEST(X509CertTest, GetSerialNumber) {
X509Cert test_cert; X509Cert test_cert;
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber()); EXPECT_EQ(kTestPemCertSerialNumber, test_cert.GetSerialNumber());
} }
TEST(X509CertTest, GetNotBeforeSeconds) { TEST(X509CertTest, GetNotBeforeSeconds) {
X509Cert test_cert; X509Cert test_cert;
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
int64_t not_before_seconds = 0; int64_t not_before_seconds = 0;
ASSERT_TRUE(test_cert.GetNotBeforeSeconds(&not_before_seconds)); ASSERT_TRUE(test_cert.GetNotBeforeSeconds(&not_before_seconds));
EXPECT_EQ(kTestPemCertNotBeforeSeconds, not_before_seconds); EXPECT_EQ(kTestPemCertNotBeforeSeconds, not_before_seconds);
@@ -406,7 +406,7 @@ TEST(X509CertTest, GetNotBeforeSeconds) {
TEST(X509CertTest, GetNotAfterSeconds) { TEST(X509CertTest, GetNotAfterSeconds) {
X509Cert test_cert; X509Cert test_cert;
ASSERT_EQ(util::OkStatus(), test_cert.LoadPem(kTestPemCert)); ASSERT_EQ(OkStatus(), test_cert.LoadPem(kTestPemCert));
int64_t not_after_seconds = 0; int64_t not_after_seconds = 0;
ASSERT_TRUE(test_cert.GetNotAfterSeconds(&not_after_seconds)); ASSERT_TRUE(test_cert.GetNotAfterSeconds(&not_after_seconds));
EXPECT_EQ(kTestPemCertNotAfterSeconds, not_after_seconds); EXPECT_EQ(kTestPemCertNotAfterSeconds, not_after_seconds);
@@ -414,7 +414,7 @@ TEST(X509CertTest, GetNotAfterSeconds) {
TEST(X509CertTest, CertChain) { TEST(X509CertTest, CertChain) {
X509CertChain test_chain; X509CertChain test_chain;
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
ASSERT_EQ(2, test_chain.GetNumCerts()); ASSERT_EQ(2, test_chain.GetNumCerts());
EXPECT_FALSE(test_chain.GetCert(0) == NULL); EXPECT_FALSE(test_chain.GetCert(0) == NULL);
EXPECT_FALSE(test_chain.GetCert(1) == NULL); EXPECT_FALSE(test_chain.GetCert(1) == NULL);
@@ -423,7 +423,7 @@ TEST(X509CertTest, CertChain) {
TEST(X509CertTest, IsCaCertificate) { TEST(X509CertTest, IsCaCertificate) {
X509CertChain test_chain; X509CertChain test_chain;
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
ASSERT_EQ(2, test_chain.GetNumCerts()); ASSERT_EQ(2, test_chain.GetNumCerts());
EXPECT_FALSE(test_chain.GetCert(0)->IsCaCertificate()); EXPECT_FALSE(test_chain.GetCert(0)->IsCaCertificate());
EXPECT_TRUE(test_chain.GetCert(1)->IsCaCertificate()); EXPECT_TRUE(test_chain.GetCert(1)->IsCaCertificate());
@@ -431,84 +431,84 @@ TEST(X509CertTest, IsCaCertificate) {
TEST(X509CertTest, ChainVerificationPem) { TEST(X509CertTest, ChainVerificationPem) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert); std::unique_ptr<X509Cert> ca_cert(new X509Cert);
ASSERT_EQ(util::OkStatus(), ASSERT_EQ(OkStatus(),
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
X509CA ca(ca_cert.release()); X509CA ca(ca_cert.release());
X509CertChain test_chain; X509CertChain test_chain;
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert));
ASSERT_EQ(1, test_chain.GetNumCerts()); ASSERT_EQ(1, test_chain.GetNumCerts());
EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain)); EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain));
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
} }
TEST(X509CertTest, ChainVerificationPkcs7) { TEST(X509CertTest, ChainVerificationPkcs7) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert); std::unique_ptr<X509Cert> ca_cert(new X509Cert);
ASSERT_EQ(util::OkStatus(), ASSERT_EQ(OkStatus(),
ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert))); ca_cert->LoadDer(absl::HexStringToBytes(kTestRootCaDerCert)));
X509CA ca(ca_cert.release()); X509CA ca(ca_cert.release());
X509CertChain test_chain; X509CertChain test_chain;
ASSERT_EQ(util::OkStatus(), ASSERT_EQ(OkStatus(),
test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain))); test_chain.LoadPkcs7(absl::HexStringToBytes(kTestPk7CertChain)));
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCert)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCert));
ASSERT_EQ(1, test_chain.GetNumCerts()); ASSERT_EQ(1, test_chain.GetNumCerts());
EXPECT_NE(util::OkStatus(), ca.VerifyCertChain(test_chain)); EXPECT_NE(OkStatus(), ca.VerifyCertChain(test_chain));
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemCertChain)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemCertChain));
EXPECT_EQ(util::OkStatus(), ca.VerifyCertChain(test_chain)); EXPECT_EQ(OkStatus(), ca.VerifyCertChain(test_chain));
} }
TEST(X509CertTest, VerifyCertWithChainIca) { TEST(X509CertTest, VerifyCertWithChainIca) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert); 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()); X509CA ca(ca_cert.release());
// Verify the ICA with the root succeeds. // Verify the ICA with the root succeeds.
X509CertChain test_chain; X509CertChain test_chain;
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestRootCaPemCert)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
ASSERT_EQ(1, test_chain.GetNumCerts()); ASSERT_EQ(1, test_chain.GetNumCerts());
X509Cert ica_cert; X509Cert ica_cert;
ASSERT_EQ(util::OkStatus(), ica_cert.LoadPem(kTestPemIca)); ASSERT_EQ(OkStatus(), ica_cert.LoadPem(kTestPemIca));
EXPECT_EQ(util::OkStatus(), ca.VerifyCertWithChain(ica_cert, test_chain)); EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(ica_cert, test_chain));
} }
TEST(X509CertTest, VerifyCertWithChainLeaf) { TEST(X509CertTest, VerifyCertWithChainLeaf) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert); 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()); X509CA ca(ca_cert.release());
// Verify the leaf with the root and ICA succeeds. // Verify the leaf with the root and ICA succeeds.
X509CertChain test_chain; X509CertChain test_chain;
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestPemIca)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestPemIca));
ASSERT_EQ(1, test_chain.GetNumCerts()); ASSERT_EQ(1, test_chain.GetNumCerts());
X509Cert leaf_cert; X509Cert leaf_cert;
ASSERT_EQ(util::OkStatus(), leaf_cert.LoadPem(kTestPemCert)); ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert));
EXPECT_EQ(util::OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain)); EXPECT_EQ(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
} }
TEST(X509CertTest, VerifyCertWithChainLeafMissincIca) { TEST(X509CertTest, VerifyCertWithChainLeafMissincIca) {
std::unique_ptr<X509Cert> ca_cert(new X509Cert); 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()); X509CA ca(ca_cert.release());
// Verify the leaf with only the root fails (ICA missing). // Verify the leaf with only the root fails (ICA missing).
X509CertChain test_chain; X509CertChain test_chain;
ASSERT_EQ(util::OkStatus(), test_chain.LoadPem(kTestRootCaPemCert)); ASSERT_EQ(OkStatus(), test_chain.LoadPem(kTestRootCaPemCert));
ASSERT_EQ(1, test_chain.GetNumCerts()); ASSERT_EQ(1, test_chain.GetNumCerts());
X509Cert leaf_cert; X509Cert leaf_cert;
ASSERT_EQ(util::OkStatus(), leaf_cert.LoadPem(kTestPemCert)); ASSERT_EQ(OkStatus(), leaf_cert.LoadPem(kTestPemCert));
EXPECT_NE(util::OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain)); EXPECT_NE(OkStatus(), ca.VerifyCertWithChain(leaf_cert, test_chain));
} }
TEST(X509CertTest, GetPkcs7) { TEST(X509CertTest, GetPkcs7) {
X509CertChain test_chain; 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(); std::string pkcs7_certificate = test_chain.GetPkcs7();
ASSERT_NE(pkcs7_certificate.size(), 0); ASSERT_NE(pkcs7_certificate.size(), 0);
X509CertChain new_test_chain; 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()); ASSERT_EQ(test_chain.GetNumCerts(), new_test_chain.GetNumCerts());
for (int i = 0; i < test_chain.GetNumCerts(); i++) { for (int i = 0; i < test_chain.GetNumCerts(); i++) {
ASSERT_EQ(test_chain.GetCert(i)->GetPem(), ASSERT_EQ(test_chain.GetCert(i)->GetPem(),
@@ -518,12 +518,12 @@ TEST(X509CertTest, GetPkcs7) {
TEST(X509CertTest, BooleanExtension) { TEST(X509CertTest, BooleanExtension) {
std::unique_ptr<X509Cert> cert1(new X509Cert); std::unique_ptr<X509Cert> cert1(new X509Cert);
ASSERT_EQ(util::OkStatus(), cert1->LoadPem(kTestPemCert)); ASSERT_EQ(OkStatus(), cert1->LoadPem(kTestPemCert));
bool extension_value; bool extension_value;
EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value)); EXPECT_FALSE(cert1->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
std::unique_ptr<X509Cert> cert2(new X509Cert); 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)); ASSERT_TRUE(cert2->GetV3BooleanExtension(kDevCertFlagOid, &extension_value));
EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value); EXPECT_EQ(kTestDevCodeSigningCertFlagValue, extension_value);
} }

View File

@@ -1,584 +0,0 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
# Build file for curl.
exports_files(["COPYING"])
cc_library(
name = "curl",
srcs = [
"include/curl_config.h", # generated by genrule below
"lib/amigaos.c",
"lib/amigaos.h",
"lib/arpa_telnet.h",
"lib/asyn-ares.c",
"lib/asyn-thread.c",
"lib/asyn.h",
"lib/base64.c",
"lib/config-amigaos.h",
"lib/config-dos.h",
"lib/config-mac.h",
"lib/config-os400.h",
"lib/config-riscos.h",
"lib/config-symbian.h",
"lib/config-tpf.h",
"lib/config-vxworks.h",
"lib/config-win32.h",
"lib/config-win32ce.h",
"lib/conncache.c",
"lib/conncache.h",
"lib/connect.c",
"lib/connect.h",
"lib/content_encoding.c",
"lib/content_encoding.h",
"lib/cookie.c",
"lib/cookie.h",
"lib/curl_addrinfo.c",
"lib/curl_addrinfo.h",
"lib/curl_base64.h",
"lib/curl_ctype.c",
"lib/curl_ctype.h",
"lib/curl_des.c",
"lib/curl_des.h",
"lib/curl_endian.c",
"lib/curl_endian.h",
"lib/curl_fnmatch.c",
"lib/curl_fnmatch.h",
"lib/curl_gethostname.c",
"lib/curl_gethostname.h",
"lib/curl_gssapi.c",
"lib/curl_gssapi.h",
"lib/curl_hmac.h",
"lib/curl_ldap.h",
"lib/curl_md4.h",
"lib/curl_md5.h",
"lib/curl_memory.h",
"lib/curl_memrchr.c",
"lib/curl_memrchr.h",
"lib/curl_multibyte.c",
"lib/curl_multibyte.h",
"lib/curl_ntlm_core.c",
"lib/curl_ntlm_core.h",
"lib/curl_ntlm_wb.c",
"lib/curl_ntlm_wb.h",
"lib/curl_path.c",
"lib/curl_path.h",
"lib/curl_printf.h",
"lib/curl_range.c",
"lib/curl_range.h",
"lib/curl_rtmp.c",
"lib/curl_rtmp.h",
"lib/curl_sasl.c",
"lib/curl_sasl.h",
"lib/curl_sec.h",
"lib/curl_setup.h",
"lib/curl_setup_once.h",
"lib/curl_sha256.h",
"lib/curl_sspi.c",
"lib/curl_sspi.h",
"lib/curl_threads.c",
"lib/curl_threads.h",
"lib/curlx.h",
"lib/dict.c",
"lib/dict.h",
"lib/doh.c",
"lib/doh.h",
"lib/dotdot.c",
"lib/dotdot.h",
"lib/easy.c",
"lib/easyif.h",
"lib/escape.c",
"lib/escape.h",
"lib/file.c",
"lib/file.h",
"lib/fileinfo.c",
"lib/fileinfo.h",
"lib/formdata.c",
"lib/formdata.h",
"lib/ftp.c",
"lib/ftp.h",
"lib/ftplistparser.c",
"lib/ftplistparser.h",
"lib/getenv.c",
"lib/getinfo.c",
"lib/getinfo.h",
"lib/gopher.c",
"lib/gopher.h",
"lib/hash.c",
"lib/hash.h",
"lib/hmac.c",
"lib/hostasyn.c",
"lib/hostcheck.c",
"lib/hostcheck.h",
"lib/hostip.c",
"lib/hostip.h",
"lib/hostip4.c",
"lib/hostip6.c",
"lib/hostsyn.c",
"lib/http.c",
"lib/http.h",
"lib/http2.c",
"lib/http2.h",
"lib/http_chunks.c",
"lib/http_chunks.h",
"lib/http_digest.c",
"lib/http_digest.h",
"lib/http_negotiate.c",
"lib/http_negotiate.h",
"lib/http_ntlm.c",
"lib/http_ntlm.h",
"lib/http_proxy.c",
"lib/http_proxy.h",
"lib/idn_win32.c",
"lib/if2ip.c",
"lib/if2ip.h",
"lib/imap.c",
"lib/imap.h",
"lib/inet_ntop.c",
"lib/inet_ntop.h",
"lib/inet_pton.c",
"lib/inet_pton.h",
"lib/krb5.c",
"lib/ldap.c",
"lib/llist.c",
"lib/llist.h",
"lib/md4.c",
"lib/md5.c",
"lib/memdebug.c",
"lib/memdebug.h",
"lib/mime.c",
"lib/mime.h",
"lib/mprintf.c",
"lib/multi.c",
"lib/multihandle.h",
"lib/multiif.h",
"lib/netrc.c",
"lib/netrc.h",
"lib/non-ascii.c",
"lib/non-ascii.h",
"lib/nonblock.c",
"lib/nonblock.h",
"lib/nwlib.c",
"lib/nwos.c",
"lib/openldap.c",
"lib/parsedate.c",
"lib/parsedate.h",
"lib/pingpong.c",
"lib/pingpong.h",
"lib/pipeline.c",
"lib/pipeline.h",
"lib/pop3.c",
"lib/pop3.h",
"lib/progress.c",
"lib/progress.h",
"lib/psl.c",
"lib/psl.h",
"lib/rand.c",
"lib/rand.h",
"lib/rtsp.c",
"lib/rtsp.h",
"lib/security.c",
"lib/select.c",
"lib/select.h",
"lib/sendf.c",
"lib/sendf.h",
"lib/setopt.c",
"lib/setopt.h",
"lib/setup-os400.h",
"lib/setup-vms.h",
"lib/sha256.c",
"lib/share.c",
"lib/share.h",
"lib/sigpipe.h",
"lib/slist.c",
"lib/slist.h",
"lib/smb.c",
"lib/smb.h",
"lib/smtp.c",
"lib/smtp.h",
"lib/sockaddr.h",
"lib/socks.c",
"lib/socks.h",
"lib/socks_gssapi.c",
"lib/socks_sspi.c",
"lib/speedcheck.c",
"lib/speedcheck.h",
"lib/splay.c",
"lib/splay.h",
"lib/ssh-libssh.c",
"lib/ssh.c",
"lib/ssh.h",
"lib/strcase.c",
"lib/strcase.h",
"lib/strdup.c",
"lib/strdup.h",
"lib/strerror.c",
"lib/strerror.h",
"lib/strtok.c",
"lib/strtok.h",
"lib/strtoofft.c",
"lib/strtoofft.h",
"lib/system_win32.c",
"lib/system_win32.h",
"lib/telnet.c",
"lib/telnet.h",
"lib/tftp.c",
"lib/tftp.h",
"lib/timeval.c",
"lib/timeval.h",
"lib/transfer.c",
"lib/transfer.h",
"lib/url.c",
"lib/url.h",
"lib/urlapi-int.h",
"lib/urlapi.c",
"lib/urldata.h",
"lib/vauth/cleartext.c",
"lib/vauth/cram.c",
"lib/vauth/digest.c",
"lib/vauth/digest.h",
"lib/vauth/digest_sspi.c",
"lib/vauth/krb5_gssapi.c",
"lib/vauth/krb5_sspi.c",
"lib/vauth/ntlm.c",
"lib/vauth/ntlm.h",
"lib/vauth/ntlm_sspi.c",
"lib/vauth/oauth2.c",
"lib/vauth/spnego_gssapi.c",
"lib/vauth/spnego_sspi.c",
"lib/vauth/vauth.c",
"lib/vauth/vauth.h",
"lib/version.c",
"lib/vtls/cyassl.c",
"lib/vtls/cyassl.h",
"lib/vtls/darwinssl.c",
"lib/vtls/darwinssl.h",
"lib/vtls/gskit.c",
"lib/vtls/gskit.h",
"lib/vtls/gtls.c",
"lib/vtls/gtls.h",
"lib/vtls/mbedtls.c",
"lib/vtls/mbedtls.h",
"lib/vtls/mesalink.c",
"lib/vtls/mesalink.h",
"lib/vtls/nss.c",
"lib/vtls/nssg.h",
"lib/vtls/openssl.c",
"lib/vtls/openssl.h",
"lib/vtls/polarssl.c",
"lib/vtls/polarssl.h",
"lib/vtls/polarssl_threadlock.c",
"lib/vtls/polarssl_threadlock.h",
"lib/vtls/schannel.c",
"lib/vtls/schannel.h",
"lib/vtls/schannel_verify.c",
"lib/vtls/vtls.c",
"lib/vtls/vtls.h",
"lib/warnless.c",
"lib/warnless.h",
"lib/wildcard.c",
"lib/wildcard.h",
"lib/x509asn1.c",
"lib/x509asn1.h",
],
hdrs = [
"include/curl/curl.h",
"include/curl/curlver.h",
"include/curl/easy.h",
"include/curl/mprintf.h",
"include/curl/multi.h",
"include/curl/stdcheaders.h",
"include/curl/system.h",
"include/curl/typecheck-gcc.h",
"include/curl/urlapi.h",
],
copts = [
"-Iexternal/curl/lib",
"-D_GNU_SOURCE",
"-DHAVE_CONFIG_H",
"-DCURL_DISABLE_FTP",
"-DCURL_DISABLE_NTLM", # turning it off in configure is not enough
"-DHAVE_LIBZ",
"-DHAVE_ZLIB_H",
"-Wno-std::string-plus-int",
"-DCURL_MAX_WRITE_SIZE=65536",
# dealing with conflicting types when building
"-DBUILDING_LIBCURL",
],
includes = [
"include",
# lib/curl_setup.h is included by many .c files under lib/
"lib"
],
linkopts = [
"-lrt",
],
visibility = ["//visibility:public"],
deps = [
"@zlib_repo//:zlib",
"@boringssl_repo//:ssl",
],
)
genrule(
name = "configure",
outs = ["include/curl_config.h"],
cmd = "\n".join([
"cat <<'EOF' >$@",
"#ifndef EXTERNAL_CURL_INCLUDE_CURL_CONFIG_H_",
"#define EXTERNAL_CURL_INCLUDE_CURL_CONFIG_H_",
"",
"#if !defined(_WIN32) && !defined(__APPLE__)",
"# include <openssl/opensslv.h>",
"# if defined(OPENSSL_IS_BORINGSSL)",
"# define HAVE_BORINGSSL 1",
"# endif",
"#endif",
"",
"#if defined(_WIN32)",
"# include \"lib/config-win32.h\"",
"# define BUILDING_LIBCURL 1",
"# define CURL_DISABLE_CRYPTO_AUTH 1",
"# define CURL_DISABLE_IMAP 1",
"# define CURL_DISABLE_LDAP 1",
"# define CURL_DISABLE_LDAPS 1",
"# define CURL_DISABLE_POP3 1",
"# define CURL_PULL_WS2TCPIP_H 1",
"# define HTTP_ONLY 1",
"#elif defined(__APPLE__)",
"# define HAVE_FSETXATTR_6 1",
"# define HAVE_SETMODE 1",
"# define HAVE_SYS_FILIO_H 1",
"# define HAVE_SYS_SOCKIO_H 1",
"# define OS \"x86_64-apple-darwin15.5.0\"",
"# define USE_DARWINSSL 1",
"#else",
"# define CURL_CA_BUNDLE \"/etc/ssl/certs/ca-certificates.crt\"",
"# define GETSERVBYPORT_R_ARGS 6",
"# define GETSERVBYPORT_R_BUFSIZE 4096",
"# define HAVE_BORINGSSL 1",
"# define HAVE_CLOCK_GETTIME_MONOTONIC 1",
"# define HAVE_CRYPTO_CLEANUP_ALL_EX_DATA 1",
"# define HAVE_FSETXATTR_5 1",
"# define HAVE_GETHOSTBYADDR_R 1",
"# define HAVE_GETHOSTBYADDR_R_8 1",
"# define HAVE_GETHOSTBYNAME_R 1",
"# define HAVE_GETHOSTBYNAME_R_6 1",
"# define HAVE_GETSERVBYPORT_R 1",
"# define HAVE_LIBSSL 1",
"# define HAVE_MALLOC_H 1",
"# define HAVE_MSG_NOSIGNAL 1",
"# define HAVE_OPENSSL_CRYPTO_H 1",
"# define HAVE_OPENSSL_ERR_H 1",
"# define HAVE_OPENSSL_PEM_H 1",
"# define HAVE_OPENSSL_PKCS12_H 1",
"# define HAVE_OPENSSL_RSA_H 1",
"# define HAVE_OPENSSL_SSL_H 1",
"# define HAVE_OPENSSL_X509_H 1",
"# define HAVE_RAND_EGD 1",
"# define HAVE_RAND_STATUS 1",
"# define HAVE_SSL_GET_SHUTDOWN 1",
"# define HAVE_STROPTS_H 1",
"# define HAVE_TERMIOS_H 1",
"# define OS \"x86_64-pc-linux-gnu\"",
"# define RANDOM_FILE \"/dev/urandom\"",
"# define USE_OPENSSL 1",
"#endif",
"",
"#if !defined(_WIN32)",
"# define CURL_DISABLE_DICT 1",
"# define CURL_DISABLE_FILE 1",
"# define CURL_DISABLE_GOPHER 1",
"# define CURL_DISABLE_IMAP 1",
"# define CURL_DISABLE_LDAP 1",
"# define CURL_DISABLE_LDAPS 1",
"# define CURL_DISABLE_POP3 1",
"# define CURL_DISABLE_SMTP 1",
"# define CURL_DISABLE_TELNET 1",
"# define CURL_DISABLE_TFTP 1",
"# define CURL_EXTERN_SYMBOL __attribute__ ((__visibility__ (\"default\")))",
"# define ENABLE_IPV6 1",
"# define GETHOSTNAME_TYPE_ARG2 size_t",
"# define GETNAMEINFO_QUAL_ARG1 const",
"# define GETNAMEINFO_TYPE_ARG1 struct sockaddr *",
"# define GETNAMEINFO_TYPE_ARG2 socklen_t",
"# define GETNAMEINFO_TYPE_ARG46 socklen_t",
"# define GETNAMEINFO_TYPE_ARG7 int",
"# define HAVE_ALARM 1",
"# define HAVE_ALLOCA_H 1",
"# define HAVE_ARPA_INET_H 1",
"# define HAVE_ARPA_TFTP_H 1",
"# define HAVE_ASSERT_H 1",
"# define HAVE_BASENAME 1",
"# define HAVE_BOOL_T 1",
"# define HAVE_CONNECT 1",
"# define HAVE_DLFCN_H 1",
"# define HAVE_ERRNO_H 1",
"# define HAVE_FCNTL 1",
"# define HAVE_FCNTL_H 1",
"# define HAVE_FCNTL_O_NONBLOCK 1",
"# define HAVE_FDOPEN 1",
"# define HAVE_FORK 1",
"# define HAVE_FREEADDRINFO 1",
"# define HAVE_FREEIFADDRS 1",
"# if !defined(__ANDROID__)",
"# define HAVE_FSETXATTR 1",
"# endif",
"# define HAVE_FTRUNCATE 1",
"# define HAVE_GAI_STRERROR 1",
"# define HAVE_GETADDRINFO 1",
"# define HAVE_GETADDRINFO_THREADSAFE 1",
"# define HAVE_GETEUID 1",
"# define HAVE_GETHOSTBYADDR 1",
"# define HAVE_GETHOSTBYNAME 1",
"# define HAVE_GETHOSTNAME 1",
"# if !defined(__ANDROID__)",
"# define HAVE_GETIFADDRS 1",
"# endif",
"# define HAVE_GETNAMEINFO 1",
"# define HAVE_GETPPID 1",
"# define HAVE_GETPROTOBYNAME 1",
"# define HAVE_GETPWUID 1",
"# if !defined(__ANDROID__)",
"# define HAVE_GETPWUID_R 1",
"# endif",
"# define HAVE_GETRLIMIT 1",
"# define HAVE_GETTIMEOFDAY 1",
"# define HAVE_GMTIME_R 1",
"# if !defined(__ANDROID__)",
"# define HAVE_IFADDRS_H 1",
"# endif",
"# define HAVE_IF_NAMETOINDEX 1",
"# define HAVE_INET_ADDR 1",
"# define HAVE_INET_NTOP 1",
"# define HAVE_INET_PTON 1",
"# define HAVE_INTTYPES_H 1",
"# define HAVE_IOCTL 1",
"# define HAVE_IOCTL_FIONBIO 1",
"# define HAVE_IOCTL_SIOCGIFADDR 1",
"# define HAVE_LIBGEN_H 1",
"# define HAVE_LIBZ 1",
"# define HAVE_LIMITS_H 1",
"# define HAVE_LL 1",
"# define HAVE_LOCALE_H 1",
"# define HAVE_LOCALTIME_R 1",
"# define HAVE_LONGLONG 1",
"# define HAVE_MEMORY_H 1",
"# define HAVE_NETDB_H 1",
"# define HAVE_NETINET_IN_H 1",
"# define HAVE_NETINET_TCP_H 1",
"# define HAVE_NET_IF_H 1",
"# define HAVE_PERROR 1",
"# define HAVE_PIPE 1",
"# define HAVE_POLL 1",
"# define HAVE_POLL_FINE 1",
"# define HAVE_POLL_H 1",
"# define HAVE_POSIX_STRERROR_R 1",
"# define HAVE_PWD_H 1",
"# define HAVE_RECV 1",
"# define HAVE_SELECT 1",
"# define HAVE_SEND 1",
"# define HAVE_SETJMP_H 1",
"# define HAVE_SETLOCALE 1",
"# define HAVE_SETRLIMIT 1",
"# define HAVE_SETSOCKOPT 1",
"# define HAVE_SGTTY_H 1",
"# define HAVE_SIGACTION 1",
"# define HAVE_SIGINTERRUPT 1",
"# define HAVE_SIGNAL 1",
"# define HAVE_SIGNAL_H 1",
"# define HAVE_SIGSETJMP 1",
"# define HAVE_SIG_ATOMIC_T 1",
"# define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1",
"# define HAVE_SOCKET 1",
"# define HAVE_SOCKETPAIR 1",
"# define HAVE_STDBOOL_H 1",
"# define HAVE_STDINT_H 1",
"# define HAVE_STDIO_H 1",
"# define HAVE_STDLIB_H 1",
"# define HAVE_STRCASECMP 1",
"# define HAVE_STRDUP 1",
"# define HAVE_STRERROR_R 1",
"# define HAVE_STRINGS_H 1",
"# define HAVE_STRING_H 1",
"# define HAVE_STRNCASECMP 1",
"# define HAVE_STRSTR 1",
"# define HAVE_STRTOK_R 1",
"# define HAVE_STRTOLL 1",
"# define HAVE_STRUCT_SOCKADDR_STORAGE 1",
"# define HAVE_STRUCT_TIMEVAL 1",
"# define HAVE_SYS_IOCTL_H 1",
"# define HAVE_SYS_PARAM_H 1",
"# define HAVE_SYS_POLL_H 1",
"# define HAVE_SYS_RESOURCE_H 1",
"# define HAVE_SYS_SELECT_H 1",
"# define HAVE_SYS_SOCKET_H 1",
"# define HAVE_SYS_STAT_H 1",
"# define HAVE_SYS_TIME_H 1",
"# define HAVE_SYS_TYPES_H 1",
"# define HAVE_SYS_UIO_H 1",
"# define HAVE_SYS_UN_H 1",
"# define HAVE_SYS_WAIT_H 1",
"# define HAVE_SYS_XATTR_H 1",
"# define HAVE_TIME_H 1",
"# define HAVE_UNAME 1",
"# define HAVE_UNISTD_H 1",
"# define HAVE_UTIME 1",
"# define HAVE_UTIME_H 1",
"# define HAVE_VARIADIC_MACROS_C99 1",
"# define HAVE_VARIADIC_MACROS_GCC 1",
"# define HAVE_WRITABLE_ARGV 1",
"# define HAVE_WRITEV 1",
"# define HAVE_ZLIB_H 1",
"# define LT_OBJDIR \".libs/\"",
"# define PACKAGE \"curl\"",
"# define PACKAGE_BUGREPORT \"a suitable curl mailing list: https://curl.haxx.se/mail/\"",
"# define PACKAGE_NAME \"curl\"",
"# define PACKAGE_STRING \"curl -\"",
"# define PACKAGE_TARNAME \"curl\"",
"# define PACKAGE_URL \"\"",
"# define PACKAGE_VERSION \"-\"",
"# define RECV_TYPE_ARG1 int",
"# define RECV_TYPE_ARG2 void *",
"# define RECV_TYPE_ARG3 size_t",
"# define RECV_TYPE_ARG4 int",
"# define RECV_TYPE_RETV ssize_t",
"# define RETSIGTYPE void",
"# define SELECT_QUAL_ARG5",
"# define SELECT_TYPE_ARG1 int",
"# define SELECT_TYPE_ARG234 fd_set *",
"# define SELECT_TYPE_ARG5 struct timeval *",
"# define SELECT_TYPE_RETV int",
"# define SEND_QUAL_ARG2 const",
"# define SEND_TYPE_ARG1 int",
"# define SEND_TYPE_ARG2 void *",
"# define SEND_TYPE_ARG3 size_t",
"# define SEND_TYPE_ARG4 int",
"# define SEND_TYPE_RETV ssize_t",
"# define SIZEOF_INT 4",
"# define SIZEOF_LONG 8",
"# define SIZEOF_OFF_T 8",
"# define SIZEOF_SHORT 2",
"# define SIZEOF_SIZE_T 8",
"# define SIZEOF_TIME_T 8",
"# define SIZEOF_VOIDP 8",
"# define SIZEOF_CURL_OFF_T 8",
"# define STDC_HEADERS 1",
"# define STRERROR_R_TYPE_ARG3 size_t",
"# define TIME_WITH_SYS_TIME 1",
"# define VERSION \"-\"",
"# ifndef _DARWIN_USE_64_BIT_INODE",
"# define _DARWIN_USE_64_BIT_INODE 1",
"# endif",
"#endif",
"",
"#endif // EXTERNAL_CURL_INCLUDE_CURL_CONFIG_H_",
"EOF",
]),
)

View File

@@ -16,6 +16,7 @@ package(
filegroup( filegroup(
name = "binary_release_files", name = "binary_release_files",
srcs = [ srcs = [
"test_ecmg_messages.h",
"wv_cas_ecm_example.cc", "wv_cas_ecm_example.cc",
":wv_cas_ecm_example", ":wv_cas_ecm_example",
], ],
@@ -26,20 +27,9 @@ cc_library(
hdrs = ["constants.h"], hdrs = ["constants.h"],
) )
cc_binary(
name = "simulcrypt_client",
srcs = ["simulcrypt_client.cc"],
deps = [
"//base",
],
)
cc_library( cc_library(
name = "test_simulcrypt_messages", name = "test_ecmg_messages",
hdrs = ["test_simulcrypt_messages.h"], hdrs = ["test_ecmg_messages.h"],
deps = [
"//base",
],
) )
cc_binary( cc_binary(
@@ -57,7 +47,7 @@ cc_binary(
srcs = ["wv_cas_key_fetcher_example.cc"], srcs = ["wv_cas_key_fetcher_example.cc"],
deps = [ deps = [
"//base", "//base",
"//util:status", "//common:status",
"//media_cas_packager_sdk/public:wv_cas_key_fetcher", "//media_cas_packager_sdk/public:wv_cas_key_fetcher",
"//protos/public:media_cas_encryption_proto", "//protos/public:media_cas_encryption_proto",
], ],

View File

@@ -1,80 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Example client for demonstrating network calls to public/simulcrypt_server.
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "gflags/gflags.h"
#include "glog/logging.h"
DEFINE_string(server, "", "Server host name");
DEFINE_int32(port, 0, "Server port number");
constexpr uint32_t kBufferSize = 256;
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(!FLAGS_server.empty()) << "need --server";
CHECK(FLAGS_port != 0) << "need --port";
struct hostent server;
{
int buflen = 1024;
char buf[1024];
struct hostent *result;
int h_errnop;
gethostbyname_r(/* __name= */ FLAGS_server.c_str(),
/* __result_buf= */ &server, /* __buf= */ buf,
/* __buflen= */ buflen,
/* __result= */ &result, /* __h_errnop= */ &h_errnop);
}
struct sockaddr_in server_addr;
bzero(reinterpret_cast<char *>(&server_addr), sizeof(server_addr));
server_addr.sin_family = AF_INET;
// TODO(user): Consider using inet_pton() to populate server_addr.sin_addr.
bcopy(server.h_addr, reinterpret_cast<char *>(&server_addr.sin_addr.s_addr),
server.h_length);
server_addr.sin_port = htons(FLAGS_port);
int socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);
CHECK(socket_fd >= 0) << "failed to opening socket";
CHECK(connect(socket_fd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) >= 0)
<< "failed to connect to socket";
printf("Please enter the message: ");
char buffer[kBufferSize];
bzero(buffer, kBufferSize);
fgets(buffer, kBufferSize - 1, stdin);
int total_bytes_written = 0;
while (total_bytes_written != strlen(buffer)) {
int num_bytes_written = write(socket_fd, buffer, strlen(buffer));
if (num_bytes_written < 0) {
LOG(FATAL) << "ERROR writing to socket: " << strerror(errno);
}
total_bytes_written += num_bytes_written;
}
bzero(buffer, kBufferSize);
if (read(socket_fd, buffer, kBufferSize - 1) < 0) {
LOG(FATAL) << "ERROR reading from socket: " << strerror(errno);
}
printf("Read from buffer: %s\n", buffer);
close(socket_fd);
return 0;
}

View File

@@ -0,0 +1,203 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Example ECMG messages used in unit tests and example client.
#ifndef MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_ECMG_MESSAGES_H_
#define MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_ECMG_MESSAGES_H_
namespace widevine {
namespace cas {
const char kTestChannelSetup[] = {
'\x03', // protocol_version
'\x00', '\x01', // message_type - Channel_setup
'\x00', '\x0e', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x01', // parameter_type- SUPER_CAS_ID
'\x00', '\x04', // parameter_length
'\x4a', '\xd4', '\x00', '\x00' // parameter_value
};
const char kTestChannelStatus[] = {
'\x03', // protocol_version
'\x00', '\x03', // message_type - Channel_status
'\x00', '\x39', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x02', // parameter_type - setion_TSpkt_flag
'\x00', '\x01', // parameter_length
'\x01', // parameter_value
'\x00', '\x03', // parameter_type - delay_start
'\x00', '\x02', // parameter_length
'\x00', '\xc8', // parameter_value
'\x00', '\x04', // parameter_type - delay_stop
'\x00', '\x02', // parameter_length
'\x00', '\xc8', // parameter_value
'\x00', '\x07', // parameter_type - ECM_rep_period
'\x00', '\x02', // parameter_length
'\x00', '\x64', // parameter_value
'\x00', '\x08', // parameter_type - max_streams
'\x00', '\x02', // parameter_length
'\x00', '\x00', // parameter_value
'\x00', '\x09', // parameter_type - min_CP_duration
'\x00', '\x02', // parameter_length
'\x00', '\x64', // parameter_value
'\x00', '\x0a', // parameter_type - lead_CW
'\x00', '\x01', // parameter_value
'\x01', // parameter_value
'\x00', '\x0b', // parameter_type - CW_per_msg
'\x00', '\x01', // parameter_length
'\x02', // parameter_value
'\x00', '\x0c', // parameter_type - max_comp_time
'\x00', '\x02', // parameter_length
'\x00', '\x64' // parameter_value
};
const char kTestStreamSetup[] = {
'\x03', // protocol_version
'\x01', '\x01', // message_type - Stream_setup
'\x00', '\x18', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x19', // parameter_type - ECM_id
'\x00', '\x02', // parameter_length
'\x00', '\x02', // parameter_value
'\x00', '\x10', // parameter_type - nominal_CP_duration
'\x00', '\x02', // parameter_length
'\x00', '\x64' // parameter_value
};
const char kTestStreamStatus[] = {
'\x03', // protocol_version
'\x01', '\x03', // message_type - Stream_status
'\x00', '\x17', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x19', // parameter_type - ECM_id
'\x00', '\x02', // parameter_length
'\x00', '\x02', // parameter_value
'\x00', '\x11', // parameter_type - access_criteria_transfer_mode
'\x00', '\x01', // parameter_length
'\x01' // parameter_value
};
const char kTestCwProvision[] = {
'\x03', // protocol_version
'\x02', '\x01', // message_type - CW_provision
'\x00', '\x44', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x12', // parameter_type - CP_number
'\x00', '\x02', // parameter_length
'\x00', '\x00', // parameter_value
'\x00', '\x13', // parameter_type - CP_duration
'\x00', '\x02', // parameter_length
'\x00', '\x64', // parameter_value
'\x00', '\x14', // parameter_type - CP_CW_Combination
'\x00', '\x12', // parameter_length
'\x00', '\x00', // parameter_value - CP (2 bytes) then CW next (16 bytes)
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
'\x00', '\x14', // parameter_type - CP_CW_Combination
'\x00', '\x12', // parameter_length
'\x00', '\x01', // parameter_value - CP (2 bytes) then CW next (16 bytes)
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'};
// CW is encrypted using hardcoded fixed entitlement key.
const char kTestEcmResponse[] = {
'\x03', // protocol_version
'\x02', '\x02', // message_type - ECM_response
'\x00', '\xd2', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x12', // parameter_type - CP_number
'\x00', '\x02', // parameter_length
'\x00', '\x00', // parameter_value
'\x00', '\x15', // parameter_type - ECM_datagram
'\x00', '\xbc', // parameter_length
// parameter_value - ECM_datagram
'\x47', '\x40', '\x02', '\x10', '\x00', '\x80', '\x70', '\x95', '\x4a',
'\xd4', '\x01', '\x05', '\x80', '\x66', '\x61', '\x6b', '\x65', '\x5f',
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x31', '\x2e', '\x2e',
'\x2e', '\x2e', '\xef', '\x40', '\x57', '\x48', '\xa7', '\xad', '\xdd',
'\x34', '\x73', '\xfe', '\x5d', '\x1c', '\x65', '\xa0', '\xbf', '\x93',
'\xfe', '\x01', '\x4b', '\x1d', '\xcd', '\x9e', '\x1d', '\x3a', '\x36',
'\x99', '\x8f', '\x47', '\xa1', '\x3b', '\x46', '\xf1', '\xde', '\x9e',
'\xc2', '\x88', '\xf8', '\x27', '\x2f', '\xea', '\xa1', '\x63', '\x9b',
'\x1b', '\x6a', '\x56', '\x2d', '\x26', '\x31', '\x32', '\x33', '\x34',
'\x35', '\x36', '\x37', '\x38', '\x66', '\x61', '\x6b', '\x65', '\x5f',
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x32', '\x2e', '\x2e',
'\x2e', '\x2e', '\xf4', '\x71', '\x2a', '\x4b', '\x6d', '\x6d', '\x14',
'\x4d', '\x2e', '\x53', '\xe7', '\x4b', '\x9f', '\x4b', '\x0a', '\x34',
'\xb4', '\xfd', '\xbe', '\x86', '\x21', '\x35', '\x1e', '\xda', '\x81',
'\x89', '\x6f', '\x70', '\xd3', '\xd2', '\xb2', '\x79', '\xf2', '\xcd',
'\xeb', '\xc5', '\xaf', '\x89', '\xab', '\xeb', '\xf0', '\x1b', '\xd0',
'\xd3', '\xe9', '\x7d', '\x81', '\x8a', '\x31', '\x32', '\x33', '\x34',
'\x35', '\x36', '\x37', '\x38', '\xff', '\xff', '\xff', '\xff', '\xff',
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'};
const char kTestStreamCloseRequest[] = {
'\x03', // protocol_version
'\x01', '\x04', // message_type - Stream_close_request
'\x00', '\x0c', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01' // parameter_value
};
const char kTestStreamCloseResponse[] = {
'\x03', // protocol_version
'\x01', '\x05', // message_type - Stream_close_response
'\x00', '\x0c', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value
'\x00', '\x0f', // parameter_type - ECM_stream_id
'\x00', '\x02', // parameter_length
'\x00', '\x01' // parameter_value
};
const char kTestChannelClose[] = {
'\x03', // protocol_version
'\x00', '\x04', // message_type - Channel_close
'\x00', '\x06', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length
'\x00', '\x01' // parameter_value
};
} // namespace cas
} // namespace widevine
#endif // MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_ECMG_MESSAGES_H_

View File

@@ -1,166 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Example Simulcrypt messages used in unit tests and example client.
#ifndef MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_SIMULCRYPT_MESSAGES_H_
#define MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_SIMULCRYPT_MESSAGES_H_
namespace widevine {
namespace cas {
const char kTestEcmgStreamSetupMessage[] = { // protocol_version
'\x01',
// message_type - Stream_set-up
'\x01', '\x01',
// message_length
// 18 bytes below, 3 parameters 6 bytes each
'\x00', '\x12',
// parameter_type - ECM_channel_id
'\x00', '\x0e',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\x01',
// parameter_type - ECM_stream_id
'\x00', '\x0f',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\x02',
// parameter_type - nominal_CP_duration
'\x00', '\x10',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\x03'};
const char kTestEcmgCwProvisionMessageWithOneCw[] = {
// protocol_version
'\x01',
// message_type - CW_provision
'\x02', '\x01',
// message_length
// 64 bytes below, 3 * 6 + 8 + 10 + 1 * 22 + 6
'\x00', '\x40',
// parameter_type - ECM_channel_id
'\x00', '\x0e',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\x01',
// parameter_type - ECM_stream_id
'\x00', '\x0f',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\x02',
// parameter_type - CP_number
'\x00', '\x12',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\xa1',
// parameter_type - access_criteria
'\x00', '\x0d',
// parameter_length
'\x00', '\x04',
// parameter_value
'\x00', '\x00', '\x00', '\x00',
// parameter_type - CW_encryption
'\x00', '\x18',
// parameter_length
'\x00', '\x06',
// parameter_value
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
// parameter_type - CP_CW_combination
'\x00', '\x14',
// parameter_length - 2 + 16
'\x00', '\x12',
// parameter_value - CP then CW
// CP
'\x00', '\xa1',
// CW (16 bytes)
'\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11',
'\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11',
// parameter_type - CP_duration
'\x00', '\x13',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\x0a'};
const char kTestEcmgCwProvisionMessageWithTwoCw[] = {
// protocol_version
'\x01',
// message_type - CW_provision
'\x02', '\x01',
// message_length
// 86 bytes below, 3 * 6 + 8 + 10 + 2 * 22 + 6
'\x00', '\x56',
// parameter_type - ECM_channel_id
'\x00', '\x0e',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\x01',
// parameter_type - ECM_stream_id
'\x00', '\x0f',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\x02',
// parameter_type - CP_number
'\x00', '\x12',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\xa1',
// parameter_type - access_criteria
'\x00', '\x0d',
// parameter_length
'\x00', '\x04',
// parameter_value
'\x00', '\x00', '\x00', '\x00',
// parameter_type - CW_encryption
'\x00', '\x18',
// parameter_length
'\x00', '\x06',
// parameter_value
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
// parameter_type - CP_CW_combination
'\x00', '\x14',
// parameter_length - 2 + 16
'\x00', '\x12',
// parameter_value - CP then CW
// CP
'\x00', '\xa1',
// CW (16 bytes)
'\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11',
'\x11', '\x11', '\x11', '\x11', '\x11', '\x11', '\x11',
// parameter_type - CP_CW_combination
'\x00', '\x14',
// parameter_length - 2 + 16
'\x00', '\x12',
// parameter_value - CP then CW
// CP
'\x00', '\xa2',
// CW (16 bytes)
'\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22',
'\x22', '\x22', '\x22', '\x22', '\x22', '\x22', '\x22',
// parameter_type - CP_duration
'\x00', '\x13',
// parameter_length
'\x00', '\x02',
// parameter_value
'\x00', '\x0a'};
} // namespace cas
} // namespace widevine
#endif // MEDIA_CAS_PACKAGER_SDK_EXAMPLE_TEST_SIMULCRYPT_MESSAGES_H_

View File

@@ -20,8 +20,6 @@ DEFINE_string(content_id, "21140844", "Content ID");
DEFINE_bool(key_rotation, true, "Whether key rotation is enabled"); DEFINE_bool(key_rotation, true, "Whether key rotation is enabled");
DEFINE_string(track_type, "SD", "Provider name"); DEFINE_string(track_type, "SD", "Provider name");
namespace util = widevine::util;
int main(int argc, char **argv) { int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true); gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(!FLAGS_content_id.empty() && !FLAGS_track_type.empty()) CHECK(!FLAGS_content_id.empty() && !FLAGS_track_type.empty())
@@ -47,7 +45,7 @@ int main(int argc, char **argv) {
std::string signed_response_str; std::string signed_response_str;
widevine::cas::WvCasKeyFetcher key_fetcher; widevine::cas::WvCasKeyFetcher key_fetcher;
util::Status status = widevine::Status status =
key_fetcher.RequestEntitlementKey(request_str, &signed_response_str); key_fetcher.RequestEntitlementKey(request_str, &signed_response_str);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << "Failed to request entitlement key"; LOG(ERROR) << "Failed to request entitlement key";

0
external_build_files/curl.BUILD Executable file → Normal file
View File

0
external_build_files/zlib.BUILD Executable file → Normal file
View File

View File

@@ -28,9 +28,9 @@ cc_library(
"//base", "//base",
"@abseil_repo//absl/base:core_headers", "@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//common:status",
"//common:aes_cbc_util", "//common:aes_cbc_util",
"//common:random_util", "//common:random_util",
"//common:status",
"//common:string_util", "//common:string_util",
"//media_cas_packager_sdk/public:wv_cas_types", "//media_cas_packager_sdk/public:wv_cas_types",
"//protos/public:media_cas_encryption_proto", "//protos/public:media_cas_encryption_proto",
@@ -76,27 +76,42 @@ cc_test(
) )
cc_library( cc_library(
name = "ecmg", name = "ecmg_client_handler",
srcs = ["ecmg.cc"], srcs = ["ecmg_client_handler.cc"],
hdrs = [ hdrs = [
"ecmg.h", "ecmg_client_handler.h",
"ecmg_constants.h", "ecmg_constants.h",
], ],
deps = [ deps = [
":ecm", ":ecm",
":ecm_generator", ":ecm_generator",
":fixed_key_fetcher", ":fixed_key_fetcher",
":mpeg2ts",
":util", ":util",
"//base", "//base",
"@abseil_repo//absl/base:core_headers", "@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/memory", "@abseil_repo//absl/memory",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//common:crypto_util",
"//common:status", "//common:status",
"//example:constants", "//example:constants",
"//media_cas_packager_sdk/public:wv_cas_ecm",
"//media_cas_packager_sdk/public:wv_cas_types", "//media_cas_packager_sdk/public:wv_cas_types",
], ],
) )
cc_test(
name = "ecmg_client_handler_test",
size = "small",
srcs = ["ecmg_client_handler_test.cc"],
deps = [
":ecmg_client_handler",
"//testing:gunit_main",
"@abseil_repo//absl/memory",
"//example:test_ecmg_messages",
],
)
cc_library( cc_library(
name = "fixed_key_fetcher", name = "fixed_key_fetcher",
srcs = [ srcs = [
@@ -131,34 +146,6 @@ cc_library(
], ],
) )
cc_library(
name = "simulcrypt",
srcs = ["simulcrypt.cc"],
hdrs = [
"simulcrypt.h",
"simulcrypt_constants.h",
],
deps = [
":ecmg",
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"//common:status",
],
)
cc_test(
name = "simulcrypt_test",
size = "small",
srcs = ["simulcrypt_test.cc"],
deps = [
":simulcrypt",
"//testing:gunit_main",
"//example:test_simulcrypt_messages",
],
)
cc_library( cc_library(
name = "ts_packet", name = "ts_packet",
srcs = [ srcs = [

View File

@@ -14,9 +14,9 @@
#include "glog/logging.h" #include "glog/logging.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "common/status.h"
#include "common/aes_cbc_util.h" #include "common/aes_cbc_util.h"
#include "common/random_util.h" #include "common/random_util.h"
#include "common/status.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "protos/public/media_cas_encryption.pb.h" #include "protos/public/media_cas_encryption.pb.h"
@@ -112,31 +112,31 @@ bool ConvertIvSizeParam(EcmIvSize param, size_t* size) {
} // namespace } // namespace
util::Status CasEcm::Initialize(const std::string& content_id, Status CasEcm::Initialize(const std::string& content_id,
const std::string& content_provider, const std::string& content_provider,
const EcmInitParameters& ecm_init_parameters, const EcmInitParameters& ecm_init_parameters,
std::string* key_request_message) { std::string* key_request_message) {
if (initialized_) { if (initialized_) {
return {util::error::INTERNAL, "Already initialized."}; return {error::INTERNAL, "Already initialized."};
} }
if (content_id.empty()) { if (content_id.empty()) {
return {util::error::INVALID_ARGUMENT, "Content ID is empty."}; return {error::INVALID_ARGUMENT, "Content ID is empty."};
} }
if (content_provider.empty()) { if (content_provider.empty()) {
return {util::error::INVALID_ARGUMENT, "Content Provider is empty."}; return {error::INVALID_ARGUMENT, "Content Provider is empty."};
} }
if (key_request_message == nullptr) { if (key_request_message == nullptr) {
return {util::error::INVALID_ARGUMENT, "key_request_message is null."}; return {error::INVALID_ARGUMENT, "key_request_message is null."};
} }
if (ecm_init_parameters.track_types.empty()) { if (ecm_init_parameters.track_types.empty()) {
return {util::error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT,
"Parameter track_ids must be set with one or more Track IDs."}; "Parameter track_ids must be set with one or more Track IDs."};
} }
if (!ConvertIvSizeParam(ecm_init_parameters.content_iv_size, if (!ConvertIvSizeParam(ecm_init_parameters.content_iv_size,
&content_iv_size_)) { &content_iv_size_)) {
return {util::error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT,
"Parameter content_iv_size must be kIvSize8 or kIvSize16."}; "Parameter content_iv_size must be kIvSize8 or kIvSize16."};
} }
@@ -151,7 +151,7 @@ util::Status CasEcm::Initialize(const std::string& content_id,
generation_ = kMaxGeneration; generation_ = kMaxGeneration;
// Construct and return CasEncryptionRequest message for caller to use. // Construct and return CasEncryptionRequest message for caller to use.
util::Status status = CreateEntitlementRequest(key_request_message); Status status = CreateEntitlementRequest(key_request_message);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << "Entitlement request message could not be created."; LOG(ERROR) << "Entitlement request message could not be created.";
return status; return status;
@@ -160,31 +160,30 @@ util::Status CasEcm::Initialize(const std::string& content_id,
// Everything is set up except entitlement keys. // Everything is set up except entitlement keys.
ClearEntitlementKeys(); ClearEntitlementKeys();
initialized_ = true; initialized_ = true;
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcm::ProcessCasEncryptionResponse(const std::string& response) { Status CasEcm::ProcessCasEncryptionResponse(const std::string& response) {
if (!initialized_) { if (!initialized_) {
return {util::error::INTERNAL, "Not initialized."}; return {error::INTERNAL, "Not initialized."};
} }
if (response.empty()) { if (response.empty()) {
return {util::error::INVALID_ARGUMENT, "Response std::string is empty."}; return {error::INVALID_ARGUMENT, "Response std::string is empty."};
} }
return ParseEntitlementResponse(response); return ParseEntitlementResponse(response);
} }
util::Status CasEcm::GenerateEcm(EntitledKeyInfo* even_key, Status CasEcm::GenerateEcm(EntitledKeyInfo* even_key, EntitledKeyInfo* odd_key,
EntitledKeyInfo* odd_key, const std::string& track_type, std::string* serialized_ecm,
const std::string& track_type, uint32_t* generation) {
std::string* serialized_ecm, uint32_t* generation) {
if (!initialized_) { if (!initialized_) {
return {util::error::INTERNAL, "Not initialized."}; return {error::INTERNAL, "Not initialized."};
} }
if (!HaveEntitlementKeys()) { if (!HaveEntitlementKeys()) {
return {util::error::INTERNAL, "Need entitlement key."}; return {error::INTERNAL, "Need entitlement key."};
} }
if (!paired_keys_required_) { if (!paired_keys_required_) {
return {util::error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT,
"Key rotation not enabled - use GenerateSingleKeyEcm()."}; "Key rotation not enabled - use GenerateSingleKeyEcm()."};
} }
@@ -195,18 +194,18 @@ util::Status CasEcm::GenerateEcm(EntitledKeyInfo* even_key,
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation); return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
} }
util::Status CasEcm::GenerateSingleKeyEcm(EntitledKeyInfo* key, Status CasEcm::GenerateSingleKeyEcm(EntitledKeyInfo* key,
const std::string& track_type, const std::string& track_type,
std::string* serialized_ecm, std::string* serialized_ecm,
uint32_t* generation) { uint32_t* generation) {
if (!initialized_) { if (!initialized_) {
return {util::error::INTERNAL, "Not initialized."}; return {error::INTERNAL, "Not initialized."};
} }
if (!HaveEntitlementKeys()) { if (!HaveEntitlementKeys()) {
return {util::error::INTERNAL, "Need entitlement key."}; return {error::INTERNAL, "Need entitlement key."};
} }
if (paired_keys_required_) { if (paired_keys_required_) {
return {util::error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT,
"Key rotation enabled - use GenerateEcm()."}; "Key rotation enabled - use GenerateEcm()."};
} }
@@ -216,17 +215,17 @@ util::Status CasEcm::GenerateSingleKeyEcm(EntitledKeyInfo* key,
return GenerateEcmCommon(keys, track_type, serialized_ecm, generation); return GenerateEcmCommon(keys, track_type, serialized_ecm, generation);
} }
util::Status CasEcm::GenerateEcmCommon( Status CasEcm::GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
const std::vector<EntitledKeyInfo*>& keys, const std::string& track_type, const std::string& track_type,
std::string* serialized_ecm, uint32_t* generation) { std::string* serialized_ecm, uint32_t* generation) {
if (serialized_ecm == nullptr) { if (serialized_ecm == nullptr) {
return {util::error::INVALID_ARGUMENT, "No return ecm std::string pointer."}; return {error::INVALID_ARGUMENT, "No return ecm std::string pointer."};
} }
if (generation == nullptr) { if (generation == nullptr) {
return {util::error::INVALID_ARGUMENT, "No return generation pointer."}; return {error::INVALID_ARGUMENT, "No return generation pointer."};
} }
util::Status status = ValidateKeys(keys); Status status = ValidateKeys(keys);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
@@ -254,37 +253,34 @@ util::Status CasEcm::GenerateEcmCommon(
if (kMaxEcmSizeBytes < serialized_ecm->size()) { if (kMaxEcmSizeBytes < serialized_ecm->size()) {
generation_ = previous_generation; generation_ = previous_generation;
serialized_ecm->clear(); serialized_ecm->clear();
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "Maximum size of ECM has been exceeded.");
"Maximum size of ECM has been exceeded.");
} }
*generation = generation_; *generation = generation_;
return util::OkStatus(); return OkStatus();
} }
void CasEcm::IncrementGeneration() { void CasEcm::IncrementGeneration() {
generation_ = (generation_ >= kMaxGeneration) ? 0 : generation_ + 1; generation_ = (generation_ >= kMaxGeneration) ? 0 : generation_ + 1;
} }
util::Status CasEcm::WrapEntitledKeys( Status CasEcm::WrapEntitledKeys(const std::string& track_type,
const std::string& track_type, const std::vector<EntitledKeyInfo*> keys) { const std::vector<EntitledKeyInfo*> keys) {
if (!initialized_) { if (!initialized_) {
return {util::error::INTERNAL, "Not initialized."}; return {error::INTERNAL, "Not initialized."};
} }
if (keys.empty()) { if (keys.empty()) {
return {util::error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT, "Vector of EntitledKeyInfo is empty."};
"Vector of EntitledKeyInfo is empty."};
} }
auto ekey_map_entry = entitlement_keys_.find(track_type); auto ekey_map_entry = entitlement_keys_.find(track_type);
if (ekey_map_entry == entitlement_keys_.end()) { if (ekey_map_entry == entitlement_keys_.end()) {
return {util::error::INTERNAL, return {error::INTERNAL, "No Entitlement Key found for given track_type."};
"No Entitlement Key found for given track_type."};
} }
const auto& ekey_list = ekey_map_entry->second; const auto& ekey_list = ekey_map_entry->second;
if (ekey_list.size() != keys.size()) { if (ekey_list.size() != keys.size()) {
return {util::error::INTERNAL, return {error::INTERNAL,
"Number of Entitled keys and Entitlement keys must match."}; "Number of Entitled keys and Entitlement keys must match."};
} }
@@ -298,7 +294,7 @@ util::Status CasEcm::WrapEntitledKeys(
if (entitled_key->wrapped_key_iv.empty()) { if (entitled_key->wrapped_key_iv.empty()) {
CHECK(RandomBytes(kWrappedKeyIvSizeBytes, &entitled_key->wrapped_key_iv)); CHECK(RandomBytes(kWrappedKeyIvSizeBytes, &entitled_key->wrapped_key_iv));
} }
util::Status status = Status status =
WrapKey(entitlement_key->key_value, entitled_key->wrapped_key_iv, WrapKey(entitlement_key->key_value, entitled_key->wrapped_key_iv,
entitled_key->key_value, &entitled_key->wrapped_key_value); entitled_key->key_value, &entitled_key->wrapped_key_value);
if (!status.ok()) { if (!status.ok()) {
@@ -306,13 +302,12 @@ util::Status CasEcm::WrapEntitledKeys(
} }
entitlement_key++; entitlement_key++;
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcm::WrapKey(const std::string& wrapping_key, Status CasEcm::WrapKey(const std::string& wrapping_key, const std::string& wrapping_iv,
const std::string& wrapping_iv, const std::string& key_value, const std::string& key_value, std::string* wrapped_key) {
std::string* wrapped_key) { Status status = ValidateKeyValue(wrapping_key, kWrappingKeySizeBytes);
util::Status status = ValidateKeyValue(wrapping_key, kWrappingKeySizeBytes);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
@@ -328,14 +323,14 @@ util::Status CasEcm::WrapKey(const std::string& wrapping_key,
*wrapped_key = *wrapped_key =
crypto_util::EncryptAesCbcNoPad(wrapping_key, wrapping_iv, key_value); crypto_util::EncryptAesCbcNoPad(wrapping_key, wrapping_iv, key_value);
if (wrapped_key->empty()) { if (wrapped_key->empty()) {
return util::Status(util::error::INTERNAL, "Failed to wrap key"); return Status(error::INTERNAL, "Failed to wrap key");
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) { Status CasEcm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) {
for (const auto& key : keys) { for (const auto& key : keys) {
util::Status status; Status status;
status = ValidateKeyId(key->key_id); status = ValidateKeyId(key->key_id);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
@@ -349,13 +344,12 @@ util::Status CasEcm::ValidateKeys(const std::vector<EntitledKeyInfo*>& keys) {
return status; return status;
} }
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcm::ValidateWrappedKeys( Status CasEcm::ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys) {
const std::vector<EntitledKeyInfo*>& keys) {
for (const auto& key : keys) { for (const auto& key : keys) {
util::Status status; Status status;
status = ValidateKeyId(key->key_id); status = ValidateKeyId(key->key_id);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
@@ -374,31 +368,31 @@ util::Status CasEcm::ValidateWrappedKeys(
return status; return status;
} }
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcm::ValidateKeyId(const std::string& key_id) { Status CasEcm::ValidateKeyId(const std::string& key_id) {
if (key_id.size() != kKeyIdSizeBytes) { if (key_id.size() != kKeyIdSizeBytes) {
return {util::error::INVALID_ARGUMENT, "Key ID must be 16 bytes."}; return {error::INVALID_ARGUMENT, "Key ID must be 16 bytes."};
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcm::ValidateKeyValue(const std::string& key_value, Status CasEcm::ValidateKeyValue(const std::string& key_value,
size_t key_value_size) { size_t key_value_size) {
if (key_value.size() != key_value_size) { if (key_value.size() != key_value_size) {
return util::Status( return Status(
util::error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
absl::StrCat("Key is wrong size (", key_value.size(), " bytes).")); absl::StrCat("Key is wrong size (", key_value.size(), " bytes)."));
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcm::ValidateIv(const std::string& iv, size_t size) { Status CasEcm::ValidateIv(const std::string& iv, size_t size) {
if (iv.size() != size) { if (iv.size() != size) {
return {util::error::INVALID_ARGUMENT, "IV is wrong size."}; return {error::INVALID_ARGUMENT, "IV is wrong size."};
} }
return util::OkStatus(); return OkStatus();
} }
std::string CasEcm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) { std::string CasEcm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
@@ -427,7 +421,7 @@ std::string CasEcm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
LOG(FATAL) << "ECM bitset incorret size: " << ecm_bitset.size(); LOG(FATAL) << "ECM bitset incorret size: " << ecm_bitset.size();
} }
std::string serialized_ecm; std::string serialized_ecm;
util::Status status = Status status =
string_util::BitsetStringToBinaryString(ecm_bitset, &serialized_ecm); string_util::BitsetStringToBinaryString(ecm_bitset, &serialized_ecm);
if (!status.ok()) { if (!status.ok()) {
LOG(FATAL) << "Failed to convert ECM bitset to std::string"; LOG(FATAL) << "Failed to convert ECM bitset to std::string";
@@ -441,7 +435,7 @@ std::string CasEcm::SerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
return serialized_ecm; return serialized_ecm;
} }
util::Status CasEcm::CreateEntitlementRequest(std::string* request_string) { Status CasEcm::CreateEntitlementRequest(std::string* request_string) {
CasEncryptionRequest request; CasEncryptionRequest request;
request.set_content_id(content_id_); request.set_content_id(content_id_);
@@ -454,43 +448,42 @@ util::Status CasEcm::CreateEntitlementRequest(std::string* request_string) {
if (!request.SerializeToString(request_string)) { if (!request.SerializeToString(request_string)) {
request_string->clear(); request_string->clear();
return {util::error::INTERNAL, "Failure serializing request."}; return {error::INTERNAL, "Failure serializing request."};
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcm::ParseEntitlementResponse(const std::string& response_string) { Status CasEcm::ParseEntitlementResponse(const std::string& response_string) {
// TODO(user): parse valid response. NOT Implemented. // TODO(user): parse valid response. NOT Implemented.
ClearEntitlementKeys(); ClearEntitlementKeys();
SignedCasEncryptionResponse signed_response; SignedCasEncryptionResponse signed_response;
if (!signed_response.ParseFromString(response_string)) { if (!signed_response.ParseFromString(response_string)) {
return {util::error::INTERNAL, "Failure parsing signed response."}; return {error::INTERNAL, "Failure parsing signed response."};
} }
// TODO(user): Should verify signature. // TODO(user): Should verify signature.
CasEncryptionResponse response; CasEncryptionResponse response;
if (!response.ParseFromString(signed_response.response())) { if (!response.ParseFromString(signed_response.response())) {
return {util::error::INTERNAL, "Failure parsing signed response."}; return {error::INTERNAL, "Failure parsing signed response."};
} }
if (response.status() != CasEncryptionResponse_Status_OK) { if (response.status() != CasEncryptionResponse_Status_OK) {
return util::Status( return Status(error::INTERNAL, absl::StrCat("Failure reported by server: ",
util::error::INTERNAL, response.status(), " : ",
absl::StrCat("Failure reported by server: ", response.status(), " : ", response.status_message()));
response.status_message()));
} }
if (content_id_ != response.content_id()) { if (content_id_ != response.content_id()) {
return util::Status( return Status(
util::error::INTERNAL, error::INTERNAL,
absl::StrCat("Content ID mismatch in Entitlement Response - expected: ", absl::StrCat("Content ID mismatch in Entitlement Response - expected: ",
content_id_, " received: ", response.content_id())); content_id_, " received: ", response.content_id()));
} }
if (response.entitlement_keys().empty()) { if (response.entitlement_keys().empty()) {
return {util::error::INTERNAL, "Failure: no entitlement keys in response."}; return {error::INTERNAL, "Failure: no entitlement keys in response."};
} }
size_t keys_needed = (paired_keys_required_ ? 2 : 1) * track_types_.size(); size_t keys_needed = (paired_keys_required_ ? 2 : 1) * track_types_.size();
if (keys_needed > response.entitlement_keys().size()) { if (keys_needed > response.entitlement_keys().size()) {
return util::Status( return Status(
util::error::INTERNAL, error::INTERNAL,
absl::StrCat( absl::StrCat(
"Wrong number of keys in Entitlement Response - expected: ", "Wrong number of keys in Entitlement Response - expected: ",
keys_needed, " got: ", response.entitlement_keys().size())); keys_needed, " got: ", response.entitlement_keys().size()));
@@ -509,7 +502,7 @@ util::Status CasEcm::ParseEntitlementResponse(const std::string& response_string
EntitlementKeyInfo ekey; EntitlementKeyInfo ekey;
ekey.key_id = key.key_id(); ekey.key_id = key.key_id();
ekey.key_value = key.key(); ekey.key_value = key.key();
util::Status status = ValidateKeyValue(key.key(), kWrappingKeySizeBytes); Status status = ValidateKeyValue(key.key(), kWrappingKeySizeBytes);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
@@ -534,10 +527,9 @@ util::Status CasEcm::ParseEntitlementResponse(const std::string& response_string
if (!CheckEntitlementKeys()) { if (!CheckEntitlementKeys()) {
LOG(ERROR) << "Could not stage entitlement keys from response:"; LOG(ERROR) << "Could not stage entitlement keys from response:";
response.ShortDebugString(); response.ShortDebugString();
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "No suitable entitlement key was found.");
"No suitable entitlement key was found.");
} }
return util::OkStatus(); return OkStatus();
} }
size_t CasEcm::CountEntitlementKeys() const { size_t CasEcm::CountEntitlementKeys() const {

View File

@@ -91,10 +91,10 @@ class CasEcm {
// Notes: // Notes:
// The returned |key_request_message| must be sent to the server and // The returned |key_request_message| must be sent to the server and
// the response correctly parsed before ECMs can be generated. // the response correctly parsed before ECMs can be generated.
virtual util::Status Initialize(const std::string& content_id, virtual Status Initialize(const std::string& content_id,
const std::string& content_provider, const std::string& content_provider,
const EcmInitParameters& ecm_init_parameters, const EcmInitParameters& ecm_init_parameters,
std::string* key_request_message); std::string* key_request_message);
// Parse a CasEncryptionResponse message holding the entitlement keys for // Parse a CasEncryptionResponse message holding the entitlement keys for
// generating the ECM stream. The entitlement keys are used to encrypt the // generating the ECM stream. The entitlement keys are used to encrypt the
@@ -102,7 +102,7 @@ class CasEcm {
// Args: // Args:
// |response| a serialized CasEncryptionRequest message from the server // |response| a serialized CasEncryptionRequest message from the server
// holding entitlement key information (or error information). // holding entitlement key information (or error information).
virtual util::Status ProcessCasEncryptionResponse(const std::string& response); virtual Status ProcessCasEncryptionResponse(const std::string& response);
// Accept keys and IVs and construct an ECM that will fit into a Transport // Accept keys and IVs and construct an ECM that will fit into a Transport
// Stream packet payload (184 bytes). // Stream packet payload (184 bytes).
@@ -118,10 +118,9 @@ class CasEcm {
// entitlement key. Wrapping modifies the original structure. // entitlement key. Wrapping modifies the original structure.
// Generation is a mod 32 counter. If the ECM has any changes from the // Generation is a mod 32 counter. If the ECM has any changes from the
// previous ECM, the generation is increased by one. // previous ECM, the generation is increased by one.
virtual util::Status GenerateEcm(EntitledKeyInfo* even_key, virtual Status GenerateEcm(EntitledKeyInfo* even_key,
EntitledKeyInfo* odd_key, EntitledKeyInfo* odd_key, const std::string& track_type,
const std::string& track_type, std::string* serialized_ecm, uint32_t* generation);
std::string* serialized_ecm, uint32_t* generation);
// Accept a key and IV and construct an ECM that will fit into a Transport // Accept a key and IV and construct an ECM that will fit into a Transport
// Stream packet payload (184 bytes). This call is specifically for the case // Stream packet payload (184 bytes). This call is specifically for the case
@@ -135,10 +134,10 @@ class CasEcm {
// with the initialized settings. // with the initialized settings.
// Generation is a mod 32 counter. If the ECM has any changes from the // Generation is a mod 32 counter. If the ECM has any changes from the
// previous ECM, the generation is increased by one. // previous ECM, the generation is increased by one.
virtual util::Status GenerateSingleKeyEcm(EntitledKeyInfo* key, virtual Status GenerateSingleKeyEcm(EntitledKeyInfo* key,
const std::string& track_type, const std::string& track_type,
std::string* serialized_ecm, std::string* serialized_ecm,
uint32_t* generation); uint32_t* generation);
protected: // For unit tests. protected: // For unit tests.
// Take the input entitled |keys| and our current state, and generate // Take the input entitled |keys| and our current state, and generate
@@ -154,8 +153,8 @@ class CasEcm {
// |track_type| the track type for the keys. The type_track must match one // |track_type| the track type for the keys. The type_track must match one
// of the |EcmInitParameters::track_types| strings passed into Initialize(). // of the |EcmInitParameters::track_types| strings passed into Initialize().
// |key| the Entitled Key to be wrapped. // |key| the Entitled Key to be wrapped.
virtual util::Status WrapEntitledKeys( virtual Status WrapEntitledKeys(const std::string& track_type,
const std::string& track_type, const std::vector<EntitledKeyInfo*> keys); const std::vector<EntitledKeyInfo*> keys);
private: private:
// Entitlement key - |key_value| is used to wrap the content key, and |key_id| // Entitlement key - |key_value| is used to wrap the content key, and |key_id|
@@ -194,29 +193,27 @@ class CasEcm {
} }
// Common helper for GenerateEcm() and GenerateSingleKeyEcm() // Common helper for GenerateEcm() and GenerateSingleKeyEcm()
virtual util::Status GenerateEcmCommon( virtual Status GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
const std::vector<EntitledKeyInfo*>& keys, const std::string& track_type, const std::string& track_type,
std::string* serialized_ecm, uint32_t* generation); std::string* serialized_ecm, uint32_t* generation);
// Wrap |key_value| using |wrapping_key| (entitlement key) and |wrapping_iv|. // Wrap |key_value| using |wrapping_key| (entitlement key) and |wrapping_iv|.
// Returns the resulting wrapped key in |wrapped_key|. // Returns the resulting wrapped key in |wrapped_key|.
// Return a status indicating whether there has been any error. // Return a status indicating whether there has been any error.
virtual util::Status WrapKey(const std::string& wrapping_key, virtual Status WrapKey(const std::string& wrapping_key, const std::string& wrapping_iv,
const std::string& wrapping_iv, const std::string& key_value, std::string* wrapped_key);
const std::string& key_value, std::string* wrapped_key);
virtual util::Status ValidateKeys(const std::vector<EntitledKeyInfo*>& keys); virtual Status ValidateKeys(const std::vector<EntitledKeyInfo*>& keys);
virtual util::Status ValidateWrappedKeys( virtual Status ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys);
const std::vector<EntitledKeyInfo*>& keys);
util::Status ValidateKeyId(const std::string& key_id); Status ValidateKeyId(const std::string& key_id);
util::Status ValidateKeyValue(const std::string& key_value, size_t key_value_size); Status ValidateKeyValue(const std::string& key_value, size_t key_value_size);
util::Status ValidateIv(const std::string& iv, size_t size); Status ValidateIv(const std::string& iv, size_t size);
// TODO(user): need unit tests for CreateEntitlementRequest. // TODO(user): need unit tests for CreateEntitlementRequest.
virtual util::Status CreateEntitlementRequest(std::string* request_string); virtual Status CreateEntitlementRequest(std::string* request_string);
// TODO(user): need unit tests for ParseEntitlementResponse. // TODO(user): need unit tests for ParseEntitlementResponse.
virtual util::Status ParseEntitlementResponse(const std::string& response_string); virtual Status ParseEntitlementResponse(const std::string& response_string);
virtual uint32_t generation() const { return generation_; } virtual uint32_t generation() const { return generation_; }
virtual CryptoMode crypto_mode() const { return crypto_mode_; } virtual CryptoMode crypto_mode() const { return crypto_mode_; }

View File

@@ -20,7 +20,7 @@ static constexpr int kMaxBytesKeyIdField = 16;
std::string CasEcmGenerator::GenerateEcm(const EcmParameters& params) { std::string CasEcmGenerator::GenerateEcm(const EcmParameters& params) {
std::vector<EntitledKeyInfo> keys; std::vector<EntitledKeyInfo> keys;
util::Status status = ProcessEcmParameters(params, &keys); Status status = ProcessEcmParameters(params, &keys);
if (!status.ok() || !initialized_) { if (!status.ok() || !initialized_) {
LOG(ERROR) << " EcmParameters is not set up properly: " << status; LOG(ERROR) << " EcmParameters is not set up properly: " << status;
return ""; return "";
@@ -43,7 +43,7 @@ std::string CasEcmGenerator::GenerateEcm(const EcmParameters& params) {
return serialized_ecm; return serialized_ecm;
} }
util::Status CasEcmGenerator::ProcessEcmParameters( Status CasEcmGenerator::ProcessEcmParameters(
const EcmParameters& ecm_params, std::vector<EntitledKeyInfo>* keys) { const EcmParameters& ecm_params, std::vector<EntitledKeyInfo>* keys) {
initialized_ = false; initialized_ = false;
rotation_enabled_ = ecm_params.rotation_enabled; rotation_enabled_ = ecm_params.rotation_enabled;
@@ -52,11 +52,11 @@ util::Status CasEcmGenerator::ProcessEcmParameters(
keys->clear(); keys->clear();
uint32_t keys_needed = ecm_params.rotation_enabled ? 2 : 1; uint32_t keys_needed = ecm_params.rotation_enabled ? 2 : 1;
if (ecm_params.key_params.size() < keys_needed) { if (ecm_params.key_params.size() < keys_needed) {
return {util::error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT,
"Number of supplied keys is wrong (check rotation periods)."}; "Number of supplied keys is wrong (check rotation periods)."};
} }
for (int i = 0; i < keys_needed; i++) { for (int i = 0; i < keys_needed; i++) {
util::Status status = ValidateKeyParameters(ecm_params.key_params[i]); Status status = ValidateKeyParameters(ecm_params.key_params[i]);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
@@ -70,80 +70,77 @@ util::Status CasEcmGenerator::ProcessEcmParameters(
current_key_index_ = 0; current_key_index_ = 0;
current_key_even_ = true; current_key_even_ = true;
initialized_ = true; initialized_ = true;
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcmGenerator::ValidateKeyId(const std::string& id) { Status CasEcmGenerator::ValidateKeyId(const std::string& id) {
if (id.empty()) { if (id.empty()) {
return {util::error::INVALID_ARGUMENT, "Key id is empty."}; return {error::INVALID_ARGUMENT, "Key id is empty."};
} }
if (id.size() > kMaxBytesKeyIdField) { if (id.size() > kMaxBytesKeyIdField) {
return {util::error::INVALID_ARGUMENT, "Key id is too long."}; return {error::INVALID_ARGUMENT, "Key id is too long."};
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcmGenerator::ValidateKeyData(const std::string& key_data) { Status CasEcmGenerator::ValidateKeyData(const std::string& key_data) {
if (key_data.empty()) { if (key_data.empty()) {
return {util::error::INVALID_ARGUMENT, "Key data is empty."}; return {error::INVALID_ARGUMENT, "Key data is empty."};
} }
if (key_data.size() != kKeyDataSize) { if (key_data.size() != kKeyDataSize) {
return {util::error::INVALID_ARGUMENT, "Key data is wrong size."}; return {error::INVALID_ARGUMENT, "Key data is wrong size."};
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcmGenerator::ValidateIv(const std::string& iv, Status CasEcmGenerator::ValidateIv(const std::string& iv, size_t required_size) {
size_t required_size) {
if (iv.empty()) { if (iv.empty()) {
return {util::error::INVALID_ARGUMENT, "IV is empty."}; return {error::INVALID_ARGUMENT, "IV is empty."};
} }
if (required_size != 8 && required_size != 16) { if (required_size != 8 && required_size != 16) {
return {util::error::INTERNAL, "IV size has not been set up correctly."}; return {error::INTERNAL, "IV size has not been set up correctly."};
} }
if (iv.size() != required_size) { if (iv.size() != required_size) {
return {util::error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT, "IV has wrong or inconsistent size."};
"IV has wrong or inconsistent size."};
} }
return util::OkStatus(); return OkStatus();
} }
util::Status CasEcmGenerator::ValidateWrappedKeyIv(const std::string& iv) { Status CasEcmGenerator::ValidateWrappedKeyIv(const std::string& iv) {
// All wrapped key IVs must be 16 bytes. // All wrapped key IVs must be 16 bytes.
util::Status status = ValidateIv(iv, kIvSize16); Status status = ValidateIv(iv, kIvSize16);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << " Wrapped key IV is not valid: " << status; LOG(ERROR) << " Wrapped key IV is not valid: " << status;
} }
return status; return status;
} }
util::Status CasEcmGenerator::ValidateContentIv(const std::string& iv) { Status CasEcmGenerator::ValidateContentIv(const std::string& iv) {
// If content_iv_size_ is zero, use this IV as the size for all future IVs in // If content_iv_size_ is zero, use this IV as the size for all future IVs in
// this stream. // this stream.
if (content_iv_size_ == 0) { if (content_iv_size_ == 0) {
content_iv_size_ = iv.size(); content_iv_size_ = iv.size();
} }
util::Status status = ValidateIv(iv, content_iv_size_); Status status = ValidateIv(iv, content_iv_size_);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << " Content IV is not valid: " << status; LOG(ERROR) << " Content IV is not valid: " << status;
} }
return status; return status;
} }
util::Status CasEcmGenerator::ValidateKeyParameters( Status CasEcmGenerator::ValidateKeyParameters(const KeyParameters& key_params) {
const KeyParameters& key_params) { Status status;
util::Status status;
status = ValidateKeyId(key_params.key_id); status = ValidateKeyId(key_params.key_id);
if (!status.ok()) return status; if (!status.ok()) return status;
if (key_params.content_ivs.empty()) { if (key_params.content_ivs.empty()) {
return {util::error::INVALID_ARGUMENT, "Content IVs is empty."}; return {error::INVALID_ARGUMENT, "Content IVs is empty."};
} }
for (int i = 0; i < key_params.content_ivs.size(); i++) { for (int i = 0; i < key_params.content_ivs.size(); i++) {
status = ValidateContentIv(key_params.content_ivs[i]); status = ValidateContentIv(key_params.content_ivs[i]);
if (!status.ok()) return status; if (!status.ok()) return status;
} }
return util::OkStatus(); return OkStatus();
} }
} // namespace cas } // namespace cas

View File

@@ -74,16 +74,16 @@ class CasEcmGenerator {
private: private:
friend class CasEcmGeneratorTest; friend class CasEcmGeneratorTest;
util::Status ProcessEcmParameters(const EcmParameters& ecm_params, Status ProcessEcmParameters(const EcmParameters& ecm_params,
std::vector<EntitledKeyInfo>* keys); std::vector<EntitledKeyInfo>* keys);
util::Status ProcessEcmParameters(const EcmParameters& ecm_params); Status ProcessEcmParameters(const EcmParameters& ecm_params);
util::Status ValidateKeyId(const std::string& id); Status ValidateKeyId(const std::string& id);
util::Status ValidateKeyData(const std::string& key_data); Status ValidateKeyData(const std::string& key_data);
util::Status ValidateWrappedKeyIv(const std::string& iv); Status ValidateWrappedKeyIv(const std::string& iv);
util::Status ValidateIv(const std::string& iv, size_t required_size); Status ValidateIv(const std::string& iv, size_t required_size);
util::Status ValidateContentIv(const std::string& iv); Status ValidateContentIv(const std::string& iv);
util::Status ValidateKeyParameters(const KeyParameters& key_params); Status ValidateKeyParameters(const KeyParameters& key_params);
bool initialized_ = false; bool initialized_ = false;
uint32_t generation_ = 0; uint32_t generation_ = 0;

View File

@@ -54,8 +54,8 @@ constexpr char kFakeCasEncryptionResponseKeyId[] = "fake_key_id.....";
constexpr char kFakeCasEncryptionResponseKeyData[] = constexpr char kFakeCasEncryptionResponseKeyData[] =
"fakefakefakefakefakefakefakefake"; "fakefakefakefakefakefakefakefake";
util::Status HandleCasEncryptionRequest(const std::string& request_string, Status HandleCasEncryptionRequest(const std::string& request_string,
std::string* signed_response_string) { std::string* signed_response_string) {
CasEncryptionRequest request; CasEncryptionRequest request;
request.ParseFromString(request_string); request.ParseFromString(request_string);
@@ -89,7 +89,7 @@ util::Status HandleCasEncryptionRequest(const std::string& request_string,
SignedCasEncryptionResponse signed_response; SignedCasEncryptionResponse signed_response;
signed_response.set_response(response_string); signed_response.set_response(response_string);
signed_response.SerializeToString(signed_response_string); signed_response.SerializeToString(signed_response_string);
return util::OkStatus(); return OkStatus();
} }
} // namespace } // namespace
@@ -99,8 +99,8 @@ class CasEcmGeneratorTest : public testing::Test {
void SetUp() override { void SetUp() override {
} }
util::Status ProcessEcmParameters(const EcmParameters& params, Status ProcessEcmParameters(const EcmParameters& params,
std::vector<EntitledKeyInfo>* keys) { std::vector<EntitledKeyInfo>* keys) {
return ecm_gen_.ProcessEcmParameters(params, keys); return ecm_gen_.ProcessEcmParameters(params, keys);
} }
@@ -170,7 +170,7 @@ TEST_F(CasEcmGeneratorTest, InitializeNoRotation) {
SetTestConfig1(&ecm_params); SetTestConfig1(&ecm_params);
util::Status status = ProcessEcmParameters(ecm_params, &keys); Status status = ProcessEcmParameters(ecm_params, &keys);
ASSERT_OK(status); ASSERT_OK(status);
ASSERT_EQ(EntitlementKeySize(ecm_params), 16); ASSERT_EQ(EntitlementKeySize(ecm_params), 16);
@@ -254,7 +254,7 @@ TEST_F(CasEcmGeneratorTest, InitializeSimpleRotation) {
SetTestConfig2(&ecm_params); SetTestConfig2(&ecm_params);
ecm_init_params_.key_rotation_enabled = true; ecm_init_params_.key_rotation_enabled = true;
util::Status status = ProcessEcmParameters(ecm_params, &keys); Status status = ProcessEcmParameters(ecm_params, &keys);
EXPECT_TRUE(status.ok()); EXPECT_TRUE(status.ok());
EXPECT_TRUE(ecm_gen_.initialized()); EXPECT_TRUE(ecm_gen_.initialized());

View File

@@ -68,14 +68,14 @@ class MockCasEcm : public CasEcm {
return SerializeEcm(keys); return SerializeEcm(keys);
} }
virtual util::Status MockWrapEntitledKeys( virtual Status MockWrapEntitledKeys(
const std::string& track_type, const std::vector<EntitledKeyInfo*>& keys) { const std::string& track_type, const std::vector<EntitledKeyInfo*>& keys) {
for (auto entitled_key : keys) { for (auto entitled_key : keys) {
entitled_key->entitlement_key_id = "entitlement_Mock"; entitled_key->entitlement_key_id = "entitlement_Mock";
entitled_key->wrapped_key_value = "MockMockMockMock"; entitled_key->wrapped_key_value = "MockMockMockMock";
entitled_key->wrapped_key_iv = "WRAPPED_KIV....x"; entitled_key->wrapped_key_iv = "WRAPPED_KIV....x";
} }
return util::OkStatus(); return OkStatus();
} }
void MockSetup(bool two_keys, const std::string& track_type, uint32_t generation, void MockSetup(bool two_keys, const std::string& track_type, uint32_t generation,
@@ -226,7 +226,7 @@ TEST_F(CasEcmTest, GenerateEcmNotInitialized) {
EntitledKeyInfo key1; EntitledKeyInfo key1;
std::string ecm_data; std::string ecm_data;
uint32_t gen; uint32_t gen;
EXPECT_EQ(util::error::INTERNAL, EXPECT_EQ(error::INTERNAL,
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm_data, &gen) ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm_data, &gen)
.error_code()); .error_code());
} }
@@ -237,7 +237,7 @@ TEST_F(CasEcmTest, GenerateEcm2NotInitialized) {
EntitledKeyInfo key2; EntitledKeyInfo key2;
std::string ecm_data; std::string ecm_data;
uint32_t gen; uint32_t gen;
EXPECT_EQ(util::error::INTERNAL, EXPECT_EQ(error::INTERNAL,
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm_data, &gen) ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm_data, &gen)
.error_code()); .error_code());
} }
@@ -245,7 +245,7 @@ TEST_F(CasEcmTest, GenerateEcm2NotInitialized) {
TEST_F(CasEcmTest, SetResponseNotInitialized) { TEST_F(CasEcmTest, SetResponseNotInitialized) {
CasEcm ecm_gen; CasEcm ecm_gen;
const std::string response("anything"); const std::string response("anything");
EXPECT_EQ(util::error::INTERNAL, EXPECT_EQ(error::INTERNAL,
ecm_gen.ProcessCasEncryptionResponse(response).error_code()); ecm_gen.ProcessCasEncryptionResponse(response).error_code());
} }
@@ -253,7 +253,7 @@ TEST_F(CasEcmTest, InitNoTracksFail) {
CasEcm ecm_gen; CasEcm ecm_gen;
std::string request; std::string request;
EXPECT_EQ( EXPECT_EQ(
util::error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
ecm_gen.Initialize(content_id_, provider_, params_default_, &request) ecm_gen.Initialize(content_id_, provider_, params_default_, &request)
.error_code()); .error_code());
} }
@@ -270,7 +270,7 @@ TEST_F(CasEcmTest, SecondInitFail) {
std::string request; std::string request;
ASSERT_OK( ASSERT_OK(
ecm_gen.Initialize(content_id_, provider_, params_simple_, &request)); ecm_gen.Initialize(content_id_, provider_, params_simple_, &request));
EXPECT_EQ(util::error::INTERNAL, EXPECT_EQ(error::INTERNAL,
ecm_gen.Initialize(content_id_, provider_, params_simple_, &request) ecm_gen.Initialize(content_id_, provider_, params_simple_, &request)
.error_code()); .error_code());
} }
@@ -280,7 +280,7 @@ TEST_F(CasEcmTest, InitEmptyContentIdFail) {
const std::string empty_content_id; const std::string empty_content_id;
std::string request; std::string request;
EXPECT_EQ( EXPECT_EQ(
util::error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
ecm_gen.Initialize(empty_content_id, provider_, params_simple_, &request) ecm_gen.Initialize(empty_content_id, provider_, params_simple_, &request)
.error_code()); .error_code());
} }
@@ -290,7 +290,7 @@ TEST_F(CasEcmTest, InitEmptyProviderFail) {
const std::string empty_provider; const std::string empty_provider;
std::string request; std::string request;
EXPECT_EQ( EXPECT_EQ(
util::error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
ecm_gen.Initialize(content_id_, empty_provider, params_simple_, &request) ecm_gen.Initialize(content_id_, empty_provider, params_simple_, &request)
.error_code()); .error_code());
} }
@@ -315,7 +315,7 @@ TEST_F(CasEcmTest, GenerateWithNoEntitlementOneKeyFail) {
uint32_t gen; uint32_t gen;
// This will fail because the entitlement key has not been acquired // This will fail because the entitlement key has not been acquired
// (via server call and ProcessCasEncryptionResponse()). // (via server call and ProcessCasEncryptionResponse()).
EXPECT_EQ(util::error::INTERNAL, EXPECT_EQ(error::INTERNAL,
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen) ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)
.error_code()); .error_code());
} }
@@ -334,13 +334,13 @@ TEST_F(CasEcmTest, GenerateWithNoEntitlementTwoKeysFail) {
// This will fail because the entitlement keys have not been acquired // This will fail because the entitlement keys have not been acquired
// (via server call and ProcessCasEncryptionResponse()). // (via server call and ProcessCasEncryptionResponse()).
EXPECT_EQ( EXPECT_EQ(
util::error::INTERNAL, error::INTERNAL,
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code()); ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
} }
TEST_F(CasEcmTest, RequestNullFail) { TEST_F(CasEcmTest, RequestNullFail) {
CasEcm ecm_gen; CasEcm ecm_gen;
EXPECT_EQ(util::error::INVALID_ARGUMENT, EXPECT_EQ(error::INVALID_ARGUMENT,
ecm_gen.Initialize(content_id_, provider_, params_simple_, nullptr) ecm_gen.Initialize(content_id_, provider_, params_simple_, nullptr)
.error_code()); .error_code());
} }
@@ -374,7 +374,7 @@ TEST_F(CasEcmTest, BadResponseFail) {
std::string response; std::string response;
ServerCall(request, &response, false, true); ServerCall(request, &response, false, true);
EXPECT_EQ(util::error::INTERNAL, EXPECT_EQ(error::INTERNAL,
ecm_gen.ProcessCasEncryptionResponse(response).error_code()); ecm_gen.ProcessCasEncryptionResponse(response).error_code());
} }
@@ -411,7 +411,7 @@ TEST_F(CasEcmTest, GenerateOneKeyNoGenFail) {
ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response)); ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response));
std::string ecm; std::string ecm;
EXPECT_EQ(util::error::INVALID_ARGUMENT, EXPECT_EQ(error::INVALID_ARGUMENT,
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, nullptr) ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, nullptr)
.error_code()); .error_code());
} }
@@ -431,7 +431,7 @@ TEST_F(CasEcmTest, GenerateOneKeyBadKeyIdFail) {
std::string ecm; std::string ecm;
uint32_t gen; uint32_t gen;
EXPECT_EQ(util::error::INVALID_ARGUMENT, EXPECT_EQ(error::INVALID_ARGUMENT,
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen) ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)
.error_code()); .error_code());
} }
@@ -455,7 +455,7 @@ TEST_F(CasEcmTest, GenerateOneKeyWrong) {
EXPECT_THAT(0, gen); EXPECT_THAT(0, gen);
EXPECT_THAT(77, ecm.size()); EXPECT_THAT(77, ecm.size());
EXPECT_EQ( EXPECT_EQ(
util::error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
ecm_gen.GenerateEcm(&key1, &key1, kTrackTypeSD, &ecm, &gen).error_code()); ecm_gen.GenerateEcm(&key1, &key1, kTrackTypeSD, &ecm, &gen).error_code());
} }
@@ -477,7 +477,7 @@ TEST_F(CasEcmTest, GenerateTwoKeysIvSizeFail) {
std::string ecm; std::string ecm;
uint32_t gen; uint32_t gen;
EXPECT_EQ( EXPECT_EQ(
util::error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code()); ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
} }
@@ -548,7 +548,7 @@ TEST_F(CasEcmTest, GenerateThreeKeysIvSize16x16Fail) {
std::string ecm; std::string ecm;
uint32_t gen; uint32_t gen;
EXPECT_EQ( EXPECT_EQ(
util::error::INTERNAL, error::INTERNAL,
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code()); ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
} }

View File

@@ -1,282 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "media_cas_packager_sdk/internal/ecmg.h"
#include <stddef.h>
#include <stdio.h>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include "glog/logging.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "common/status.h"
#include "example/constants.h"
#include "media_cas_packager_sdk/internal/ecm_generator.h"
#include "media_cas_packager_sdk/internal/ecmg_constants.h"
#include "media_cas_packager_sdk/internal/util.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
namespace widevine {
namespace cas {
namespace {
// Local helper function that processes all the parameters in an ECMG message.
util::Status ProcessParameters(const char* message, size_t message_length,
EcmgParameters* parameters) {
DCHECK(message);
DCHECK(parameters);
uint16_t parameter_type;
uint16_t parameter_length;
// 'offset' is used to track where we are within |message|.
size_t offset = 0;
// There could be CW_per_msg instances of CP_CW_combinations,
// so we need to track how many we have processed so far
// in order to know where to store the next CP_CW_combination.
int current_cp_cw_combination_index = 0;
while (offset != message_length) {
BigEndianToHost16(&parameter_type, message + offset);
offset += PARAMETER_TYPE_SIZE;
BigEndianToHost16(&parameter_length, message + offset);
offset += PARAMETER_LENGTH_SIZE;
switch (parameter_type) {
case ACCESS_CRITERIA: {
LOG(WARNING) << "Ignoring access_criteria parameter of "
<< parameter_length << " bytes long";
offset += parameter_length;
break;
}
case ECM_CHANNEL_ID: {
if (parameter_length != ECM_CHANNEL_ID_SIZE) {
return util::Status(
util::error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", parameter_length,
" for parameter type ", parameter_type));
}
BigEndianToHost16(&parameters->ecm_channel_id, message + offset);
offset += parameter_length;
break;
}
case ECM_STREAM_ID: {
if (parameter_length != ECM_STREAM_ID_SIZE) {
return util::Status(
util::error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", parameter_length,
" for parameter type ", parameter_type));
}
BigEndianToHost16(&parameters->ecm_stream_id, message + offset);
offset += parameter_length;
break;
}
case CP_CW_COMBINATION: {
if (current_cp_cw_combination_index > 2) {
// We can have at most 3 CP_CW_Combinations.
return util::Status(util::error::INVALID_ARGUMENT,
"We only support up to 2 control words in the "
"CW_provision message");
}
EcmgCpCwCombination* combination =
&parameters->cp_cw_combinations[current_cp_cw_combination_index++];
BigEndianToHost16(&combination->cp, message + offset);
offset += CP_SIZE;
size_t cw_size = parameter_length - CP_SIZE;
combination->cw = std::string(message + offset, cw_size);
offset += cw_size;
// TODO(user): This is a temporary hack to let the ECM generator
// know how many keys to include in the ECM.
// CW_per_msg should have been set during channel set-up instead.
parameters->cw_per_msg = current_cp_cw_combination_index;
break;
}
case CP_DURATION: {
if (parameter_length != CP_DURATION_SIZE) {
return util::Status(
util::error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", parameter_length,
" for parameter type ", parameter_type));
}
BigEndianToHost16(&parameters->cp_duration, message + offset);
offset += parameter_length;
break;
}
case CP_NUMBER: {
if (parameter_length != CP_NUMBER_SIZE) {
return util::Status(
util::error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", parameter_length,
" for parameter type ", parameter_type));
}
BigEndianToHost16(&parameters->cp_number, message + offset);
offset += parameter_length;
break;
}
case CW_ENCRYPTION: {
LOG(WARNING) << "Ignoring CW_encryption parameter of "
<< parameter_length << " bytes long";
offset += parameter_length;
break;
}
case NOMINAL_CP_DURATION: {
if (parameter_length != NOMINAL_CP_DURATION_SIZE) {
return util::Status(
util::error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", parameter_length,
" for parameter type ", parameter_type));
}
BigEndianToHost16(&parameters->nominal_cp_duration, message + offset);
offset += parameter_length;
break;
}
default: {
return util::Status(
util::error::UNIMPLEMENTED,
absl::StrCat("No implementation yet to process parameter of type ",
parameter_type));
break;
}
}
}
return util::OkStatus();
}
} // namespace
util::Status Ecmg::ProcessStreamSetupMessage(const char* message,
size_t message_length) {
DCHECK(message);
EcmgParameters parameters;
util::Status status = ProcessParameters(message, message_length, &parameters);
if (!status.ok()) {
return status;
}
if (parameters.ecm_channel_id == 0 || parameters.ecm_stream_id == 0 ||
parameters.nominal_cp_duration == 0) {
return util::Status(util::error::INVALID_ARGUMENT,
"Missing required parameter");
}
if (channels_.find(parameters.ecm_channel_id) == channels_.end()) {
std::unique_ptr<EcmgChannel> new_channel = absl::make_unique<EcmgChannel>();
channels_[parameters.ecm_channel_id] = std::move(new_channel);
}
EcmgChannel* channel =
channels_.find(parameters.ecm_channel_id)->second.get();
auto stream_entry = channel->streams.find(parameters.ecm_stream_id);
if (stream_entry == channel->streams.end()) {
std::unique_ptr<EcmgStream> new_stream = absl::make_unique<EcmgStream>();
new_stream->nominal_cp_duration = parameters.nominal_cp_duration;
channel->streams[parameters.ecm_stream_id] = std::move(new_stream);
} else {
EcmgStream* existing_stream = stream_entry->second.get();
existing_stream->nominal_cp_duration = parameters.nominal_cp_duration;
}
return util::OkStatus();
}
util::Status Ecmg::ProcessCwProvisionMessage(const char* message,
size_t message_length,
std::string* response) {
DCHECK(message);
DCHECK(response);
EcmgParameters parameters;
util::Status status = ProcessParameters(message, message_length, &parameters);
if (!status.ok()) {
return status;
}
if (parameters.ecm_channel_id == 0 || parameters.ecm_stream_id == 0 ||
parameters.cp_number == 0 || parameters.cw_per_msg == 0) {
return util::Status(util::error::INVALID_ARGUMENT,
"Missing required parameter");
}
// TODO(user): Figure out what to do with ECM_channel_ID and ECM_stream_ID.
// - We certainly need to check the channel/stream has been setup
// - Retrieve config parameters such as lead_CW and CW_per_msg
// - In some config, we need to keep CW for previous CP to be included in the
// current ECM
// TODO(user): Remove debug loop below.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < parameters.cp_cw_combinations[i].cw.size(); j++) {
printf("%x ",
static_cast<uint16_t>(parameters.cp_cw_combinations[i].cw[j]));
}
std::cout << std::endl;
}
bool key_rotation_enabled = parameters.cw_per_msg > 1;
// Create an instance of CasEcm in order to set the entitlement keys.
// TODO(user): The section of code below for constructing CasEcm should
// be optimized. There should be a single instance of CasEcm for each stream.
// Right now, this is hard to do because CasEcmGenerator contains the CasEcm.
std::unique_ptr<CasEcm> ecm = absl::make_unique<CasEcm>();
// TODO(user): Revisit this hardcoded ecm_init_params.
EcmInitParameters ecm_init_params;
ecm_init_params.content_iv_size = kIvSize8;
ecm_init_params.key_rotation_enabled = key_rotation_enabled;
// TODO(user): Allow caller to specify the crypto mode.
ecm_init_params.crypto_mode = CryptoMode::kAesCtr;
// Only encrypt one video track.
ecm_init_params.track_types.push_back(kDefaultTrackTypeSd);
std::string entitlement_request;
std::string entitlement_response;
// 'content_id' and 'provider' are used in entitlement key request/response
// only, NOT needed for generating ECM.
// So for initial demo, we can just use hardcoded value because we are using
// hardcoded entitlement key anyway.
// TODO(user): When we want to retrieve entitlement key from License Server
// we need to figure out a way to provide real 'content_id' and 'provder'
// to this function here from the Simulcrypt API.
if (!(status = ecm->Initialize(kDefaultContentId, kDefaultProvider,
ecm_init_params, &entitlement_request))
.ok()) {
return status;
}
if (!(status = fixed_key_fetcher_.RequestEntitlementKey(
entitlement_request, &entitlement_response))
.ok()) {
return status;
}
if (!(status = ecm->ProcessCasEncryptionResponse(entitlement_response))
.ok()) {
return status;
}
CasEcmGenerator ecm_generator;
ecm_generator.set_ecm(std::move(ecm));
EcmParameters ecm_param;
ecm_param.rotation_enabled = key_rotation_enabled;
for (int i = 0; i <= parameters.cw_per_msg; i++) {
ecm_param.key_params.emplace_back();
ecm_param.key_params[i].key_data = parameters.cp_cw_combinations[i].cw;
// TODO(user): MUST have a better way to derive/retrieve key_id.
// Currently set it to be the same as the key itself just for demo purpose.
ecm_param.key_params[i].key_id = ecm_param.key_params[i].key_data;
// TODO(user): MUST have a better way to generate/retrieve content_iv.
ecm_param.key_params[i].content_ivs.push_back(
std::string(kDefaultContentIv8Bytes));
}
std::string serialized_ecm = ecm_generator.GenerateEcm(ecm_param);
std::cout << "serialized_ecm: " << serialized_ecm << std::endl;
for (int i = 0; i < serialized_ecm.size(); i++) {
printf("'\\x%x', ", static_cast<uint16_t>(serialized_ecm.at(i)));
}
std::cout << std::endl;
LOG(INFO) << "ECM size: " << serialized_ecm.size();
return util::OkStatus();
}
} // namespace cas
} // namespace widevine

View File

@@ -1,94 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_H_
#include <stddef.h>
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include <cstdint>
#include "common/status.h"
#include "media_cas_packager_sdk/internal/ecm.h"
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
namespace widevine {
namespace cas {
// A struct that represent a CP_CW_Combination.
struct EcmgCpCwCombination {
uint16_t cp = 0; // crypto period
std::string cw; // control word
};
// A struct that is used to hold all possible parameters for a ECMG message.
struct EcmgParameters {
// Default value of 0 for fields below is usually considered invalid.
// Hence checking against 0 is used to detect whether each parameter
// is set in the message.
uint8_t cw_per_msg = 0;
uint16_t ecm_channel_id = 0;
uint16_t ecm_stream_id = 0;
uint16_t nominal_cp_duration = 0;
uint16_t cp_number = 0; // crypto period number
uint16_t cp_duration = 0; // crypto period duration
// CW_per_msg could 1, 2, or 3,
// so there can be up to 3 CP_CW_Combinations
EcmgCpCwCombination cp_cw_combinations[3];
};
// A struct that holds information about a ECMG stream within a channel.
struct EcmgStream {
uint16_t nominal_cp_duration = 0;
};
// A struct that holds information about a ECMG channel.
struct EcmgChannel {
// Map from ECM_stream_ID to an instance of EcmgStream.
std::map<uint16_t, std::unique_ptr<EcmgStream>> streams;
};
// A class that process Simulcrypt ECMG messages.
// This class is NOT thread-safe.
class Ecmg {
public:
Ecmg() = default;
Ecmg(const Ecmg&) = delete;
Ecmg& operator=(const Ecmg&) = delete;
virtual ~Ecmg() = default;
// Process |message| of length |message_length|.
// |message| is expected to be a Stream_set-up message.
// Any error during processing would be turned via util::Status.
util::Status ProcessStreamSetupMessage(const char* message,
size_t message_length);
// Process |message| of length |message_length|.
// |message| is expected to be a CW_provision request message.
// ECM_response response message will be returned via |response|.
// Any error during processing would be turned via util::Status.
util::Status ProcessCwProvisionMessage(const char* message,
size_t message_length,
std::string* response);
private:
// Keep track of all the channels.
std::map<uint16_t, std::unique_ptr<EcmgChannel>> channels_;
FixedKeyFetcher fixed_key_fetcher_;
};
} // namespace cas
} // namespace widevine
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_H_

View File

@@ -0,0 +1,619 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
#include <cstddef>
#include <cstdio>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include "glog/logging.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "common/crypto_util.h"
#include "example/constants.h"
#include "media_cas_packager_sdk/internal/ecm_generator.h"
#include "media_cas_packager_sdk/internal/ecmg_constants.h"
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
#include "media_cas_packager_sdk/internal/util.h"
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
// 'section_TSpkt_flag' defines the format of the ECM.
// We only support MPEG-2 transport stream packet format for now.
// We do NOT support MPEG-2 section format yet.
// TODO(user): Understand the difference between the two formats.
static constexpr uint8_t kSectionTSpktFlag = 0x01;
// 'max_stream' parameter defines the max number of simultaneous opened streams
// suppported by an ECMG on a channel.
// A value of 0 means that this maximum is not known.
static constexpr uint16_t kMaxStream = 0;
// 'lead_CW' parameter defines the number of contro lwords required in
// advance to build an ECM.
// TODO(user): Support other values of 'lead_CW' in combination with
// other values for 'CW_per_msg'.
static constexpr uint8_t kLeadCw = 1;
// ' CW_per_msg' parameter defines the number of control words needed by the
// ECMG per control word provision message.
static constexpr uint8_t kCwPerMsg = 2;
namespace widevine {
namespace cas {
namespace {
// Local helper function that processes all the params in an ECMG request.
Status HandleParameters(const char* const request, size_t request_length,
EcmgParameters* params) {
DCHECK(request);
DCHECK(params);
uint16_t param_type;
uint16_t param_length;
// 'offset' is used to track where we are within |request|.
size_t offset = 0;
// There could be CW_per_msg instances of CP_CW_combinations,
// so we need to track how many we have processed so far
// in order to know where to store the next CP_CW_combination.
int current_cp_cw_combination_index = 0;
while (offset != request_length) {
BigEndianToHost16(&param_type, request + offset);
offset += PARAMETER_TYPE_SIZE;
BigEndianToHost16(&param_length, request + offset);
offset += PARAMETER_LENGTH_SIZE;
switch (param_type) {
case ACCESS_CRITERIA: {
LOG(WARNING) << "Ignoring access_criteria parameter of " << param_length
<< " bytes long";
offset += param_length;
break;
}
case CP_CW_COMBINATION: {
if (current_cp_cw_combination_index > 2) {
// We can have at most 3 CP_CW_Combinations.
return Status(error::INVALID_ARGUMENT,
"We only support up to 2 control words in the "
"CW_provision request");
}
EcmgCpCwCombination* combination =
&params->cp_cw_combinations[current_cp_cw_combination_index++];
BigEndianToHost16(&combination->cp, request + offset);
offset += CP_SIZE;
size_t cw_size = param_length - CP_SIZE;
combination->cw = std::string(request + offset, cw_size);
offset += cw_size;
// TODO(user): This is a temporary hack to let the ECM generator
// know how many keys to include in the ECM.
// CW_per_msg should have been set during channel set-up instead.
params->cw_per_msg = current_cp_cw_combination_index;
break;
}
case CP_DURATION: {
if (param_length != CP_DURATION_SIZE) {
return Status(error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
BigEndianToHost16(&params->cp_duration, request + offset);
offset += param_length;
break;
}
case CP_NUMBER: {
if (param_length != CP_NUMBER_SIZE) {
return Status(error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
BigEndianToHost16(&params->cp_number, request + offset);
offset += param_length;
break;
}
case CW_ENCRYPTION: {
LOG(WARNING) << "Ignoring CW_encryption parameter of " << param_length
<< " bytes long";
offset += param_length;
break;
}
case ECM_CHANNEL_ID: {
if (param_length != ECM_CHANNEL_ID_SIZE) {
return Status(error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
BigEndianToHost16(&params->ecm_channel_id, request + offset);
offset += param_length;
break;
}
case ECM_ID: {
if (param_length != ECM_ID_SIZE) {
return Status(error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
BigEndianToHost16(&params->ecm_id, request + offset);
offset += param_length;
break;
}
case ECM_STREAM_ID: {
if (param_length != ECM_STREAM_ID_SIZE) {
return Status(error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
BigEndianToHost16(&params->ecm_stream_id, request + offset);
offset += param_length;
break;
}
case NOMINAL_CP_DURATION: {
if (param_length != NOMINAL_CP_DURATION_SIZE) {
return Status(error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
BigEndianToHost16(&params->nominal_cp_duration, request + offset);
offset += param_length;
break;
}
case SUPER_CAS_ID: {
if (param_length != SUPER_CAS_ID_SIZE) {
return Status(error::INVALID_ARGUMENT,
absl::StrCat("Invalid parameter length ", param_length,
" for parameter type ", param_type));
}
BigEndianToHost32(&params->super_cas_id, request + offset);
offset += param_length;
break;
}
default: {
return Status(
error::UNIMPLEMENTED,
absl::StrCat("No implementation yet to process parameter of type ",
param_type));
break;
}
}
}
return OkStatus();
}
// Add 'protocol_version', 'message_type', 'message_length' to the message.
// TODO(user): Per jfore@, consider pass in a pointer to a structure
// #pragma pack(push, 1) // exact fit - no padding
// struct MessageHeader{
// uint8_t protocol_version;
// uint16_t message_type;
// uint16_t message_length;
// };
// #pragma pack(pop) // restore previous pack
void BuildMessageHeader(uint16_t message_type, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
*message_length = 0;
message[*message_length] = ECMG_SCS_PROTOCOL_VERSION;
*message_length += PROTOCOL_VERSION_SIZE;
Host16ToBigEndian(message + *message_length, &message_type);
*message_length += MESSAGE_TYPE_SIZE;
// NOTE: 'message_length' needs to be updated later after we have added all
// the params so we know the exact length of the all the params.
// Use 0 for 'total_param_length' until we know the length of the message.
uint16_t total_param_length = 0;
Host16ToBigEndian(message + *message_length, &total_param_length);
*message_length += MESSAGE_LENGTH_SIZE;
}
// Add a uint16_t parameter to the message.
void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
uint16_t param_length = 2;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
Host16ToBigEndian(message + *message_length, &param_value);
*message_length += param_length;
}
// Add a uint8_t parameter to the message.
void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
uint16_t param_length = 1;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
memcpy(message + *message_length, &param_value, param_length);
*message_length += param_length;
}
// Add a param that is |param_length| bytes long.
void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length,
char* message, size_t* message_length) {
DCHECK(param_value);
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
memcpy(message + *message_length, param_value, param_length);
*message_length += param_length;
}
void BuildChannelError(uint16_t channel_id, uint16_t error_status, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_CHANNEL_ERROR, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ERROR_STATUS, error_status, message, message_length);
// No setting Error_information parameter yet.
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}
void BuildChannelStatus(uint16_t channel_id, EcmgConfig* config, char* message,
size_t* message_length) {
DCHECK(config);
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_CHANNEL_STATUS, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint8Param(SECTION_TSPKT_FLAG, kSectionTSpktFlag, message, message_length);
// No setting AC_delay_start parameter yet.
// No setting AC_delay_stop parameter yet.
AddUint16Param(DELAY_START, config->delay_start, message, message_length);
AddUint16Param(DELAY_STOP, config->delay_stop, message, message_length);
// No setting transition_delay_start parameter yet.
// No setting transition_delay_stop parameter yet.
AddUint16Param(ECM_REP_PERIOD, config->ecm_rep_period, message,
message_length);
AddUint16Param(MAX_STREAMS, kMaxStream, message, message_length);
// min_CP_duration needs to be at least max_comp_time. So we just use
// max_comp_time here for now.
AddUint16Param(MIN_CP_DURATION, config->max_comp_time, message,
message_length);
AddUint8Param(LEAD_CW, kLeadCw, message, message_length);
AddUint8Param(CW_PER_MESSAGE, kCwPerMsg, message, message_length);
AddUint16Param(MAX_COMP_TIME, config->max_comp_time, message, message_length);
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}
void BuildStreamError(uint16_t channel_id, uint16_t stream_id, uint16_t error_status,
char* message, size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_STREAM_ERROR, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
AddUint16Param(ERROR_STATUS, error_status, message, message_length);
// No setting Error_information parameter yet.
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}
void BuildStreamStatus(uint16_t channel_id, uint16_t stream_id, uint16_t ecm_id,
uint8_t access_criteria_transfer_mode, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_STREAM_STATUS, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
AddUint16Param(ECM_ID, ecm_id, message, message_length);
AddUint8Param(ACCESS_CRITERIA_TRANSFER_MODE, access_criteria_transfer_mode,
message, message_length);
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}
void BuildStreamCloseResponse(uint16_t channel_id, uint16_t stream_id,
char* message, size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_STREAM_CLOSE_RESPONSE, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}
void BuildEcmResponse(uint16_t channel_id, uint16_t stream_id, uint16_t cp_number,
uint8_t* ecm_datagram, char* message,
size_t* message_length) {
DCHECK(ecm_datagram);
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_ECM_RESPONSE, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
AddUint16Param(CP_NUMBER, cp_number, message, message_length);
AddParam(ECM_DATAGRAM, ecm_datagram, kTsPacketSize, message, message_length);
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}
} // namespace
EcmgClientHandler::EcmgClientHandler(EcmgConfig* ecmg_config) {
DCHECK(ecmg_config);
ecmg_config_ = ecmg_config;
channel_id_set_ = false;
}
void EcmgClientHandler::HandleRequest(const char* const request, char* response,
size_t* response_length) {
DCHECK(request);
DCHECK(response);
uint8_t protocol_version;
uint16_t request_type;
uint16_t request_length;
// 'offset' is used to track where we are within |request|.
size_t offset = 0;
memcpy(&protocol_version, request, PROTOCOL_VERSION_SIZE);
if (protocol_version != ECMG_SCS_PROTOCOL_VERSION) {
// TODO(user): Should send an error response.
return;
}
offset += PROTOCOL_VERSION_SIZE;
BigEndianToHost16(&request_type, request + offset);
offset += MESSAGE_TYPE_SIZE;
BigEndianToHost16(&request_length, request + offset);
offset += MESSAGE_LENGTH_SIZE;
EcmgParameters params;
Status status = HandleParameters(request + offset, request_length, &params);
if (!status.ok()) {
// TODO(user): Should send an error response.
return;
}
switch (request_type) {
case ECMG_CHANNEL_SETUP: {
HandleChannelSetup(params, response, response_length);
break;
}
case ECMG_CHANNEL_CLOSE: {
HandleChannelClose(params, response, response_length);
break;
}
case ECMG_STREAM_SETUP: {
HandleStreamSetup(params, response, response_length);
break;
}
case ECMG_STREAM_CLOSE_REQUEST: {
HandleStreamCloseRequest(params, response, response_length);
break;
}
case ECMG_CW_PROVISION: {
HandleCwProvision(params, response, response_length);
break;
}
default: {
// Unhandled or unknown request types.
break;
}
}
}
void EcmgClientHandler::HandleChannelSetup(const EcmgParameters& params,
char* response,
size_t* response_length) {
DCHECK(response);
DCHECK(response_length);
// TODO(user): Check SUPER_CAS_ID, if it is unexpected, return
// UNKNOWN_SUPER_CAS_ID_VALUE error.
if (channel_id_set_) {
BuildChannelError(params.ecm_channel_id, INVAID_MESSAGE, response,
response_length);
return;
}
// TODO(user): To support multi-threading of serving concurrent clients,
// there needs to be a set of channel_ids shared by multiple client handlers
// threads.
// And here we need to check if the channel_id is already in the set, if
// yes, return an error.
channel_id_ = params.ecm_channel_id;
channel_id_set_ = true;
BuildChannelStatus(params.ecm_channel_id, ecmg_config_, response,
response_length);
}
void EcmgClientHandler::HandleChannelClose(const EcmgParameters& params,
char* response,
size_t* response_length) {
DCHECK(response);
DCHECK(response_length);
if (channel_id_ != params.ecm_channel_id) {
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
UNKNOWN_ECM_CHANNEL_ID_VALUE, response, response_length);
return;
}
channel_id_set_ = false;
streams_.clear();
*response_length = 0;
}
void EcmgClientHandler::HandleStreamSetup(const EcmgParameters& params,
char* response,
size_t* response_length) {
DCHECK(response);
DCHECK(response_length);
if (channel_id_ != params.ecm_channel_id) {
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
UNKNOWN_ECM_CHANNEL_ID_VALUE, response, response_length);
return;
}
// TODO(user): To support multi-threading of serving concurrent clients,
// there needs to be a set of stream_ids shared by multiple client handlers
// threads.
// And here we need to check if the stream_id is already in the set, if
// yes, return an error.
if (streams_.count(params.ecm_stream_id) > 0) {
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
ECM_STREAM_ID_VALUE_ALREADY_IN_USE, response,
response_length);
return;
}
// TODO(user): What do I do with the ECM_id here, currently not doing
// anything with it?
streams_[params.ecm_stream_id] = params.ecm_id;
BuildStreamStatus(params.ecm_channel_id, params.ecm_stream_id, params.ecm_id,
ecmg_config_->access_criteria_transfer_mode, response,
response_length);
}
void EcmgClientHandler::HandleStreamCloseRequest(const EcmgParameters& params,
char* response,
size_t* response_length) {
DCHECK(response);
DCHECK(response_length);
if (channel_id_ != params.ecm_channel_id) {
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
UNKNOWN_ECM_CHANNEL_ID_VALUE, response, response_length);
return;
}
if (streams_.count(params.ecm_stream_id) == 0) {
BuildStreamError(params.ecm_channel_id, params.ecm_stream_id,
UNKNOWN_ECM_STREAM_ID_VALUE, response, response_length);
return;
}
streams_.erase(params.ecm_stream_id);
BuildStreamCloseResponse(params.ecm_channel_id, params.ecm_stream_id,
response, response_length);
}
void EcmgClientHandler::HandleCwProvision(const EcmgParameters& params,
char* response,
size_t* response_length) {
DCHECK(response);
DCHECK(response_length);
if (channel_id_ != params.ecm_channel_id) {
BuildChannelError(params.ecm_channel_id, UNKNOWN_ECM_CHANNEL_ID_VALUE,
response, response_length);
return;
}
if (streams_.count(params.ecm_stream_id) == 0) {
BuildChannelError(params.ecm_channel_id, UNKNOWN_ECM_STREAM_ID_VALUE,
response, response_length);
return;
}
if (params.cw_per_msg < kCwPerMsg) {
BuildChannelError(params.ecm_channel_id,
NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM, response,
response_length);
return;
}
uint8_t ecm_datagram[kTsPacketSize];
BuildEcmDatagram(params, ecm_datagram);
BuildEcmResponse(params.ecm_channel_id, params.ecm_stream_id,
params.cp_number, ecm_datagram, response, response_length);
}
void EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params,
uint8_t* ecm_datagram) {
DCHECK(ecm_datagram);
// TODO(user): Remove debug loop below.
for (int i = 0; i < 3; i++) {
std::cout << "CW: ";
for (int j = 0; j < params.cp_cw_combinations[i].cw.size(); j++) {
printf("'\\x%02x', ",
static_cast<uint16_t>(params.cp_cw_combinations[i].cw[j]));
}
std::cout << std::endl;
}
// Step 1: Generate entitlement keys.
bool key_rotation_enabled = params.cw_per_msg > 1;
// Create an instance of CasEcm in order to set the entitlement keys.
// TODO(user): The section of code below for constructing CasEcm should
// be optimized. There should be a single instance of CasEcm for each stream.
// Right now, this is hard to do because CasEcmGenerator contains the CasEcm.
std::unique_ptr<CasEcm> ecm = absl::make_unique<CasEcm>();
// TODO(user): Revisit this hardcoded ecm_init_params.
EcmInitParameters ecm_init_params;
ecm_init_params.content_iv_size = kIvSize8;
ecm_init_params.key_rotation_enabled = key_rotation_enabled;
// TODO(user): Allow crypto mode to be set to different value? Via flag?
ecm_init_params.crypto_mode = CryptoMode::kDvbCsa2;
// Only encrypt one video track.
// TODO(user): Support multiple tracks? How?
ecm_init_params.track_types.push_back(kDefaultTrackTypeSd);
std::string entitlement_request;
std::string entitlement_response;
// 'content_id' and 'provider' are used in entitlement key request/response
// only, NOT needed for generating ECM.
// So for initial demo, we can just use hardcoded value because we are using
// hardcoded entitlement key anyway.
// TODO(user): When we want to retrieve entitlement key from License Server
// we need to figure out a way to provide real 'content_id' and 'provder'
// to this function here from the Simulcrypt API.
Status status;
if (!(status = ecm->Initialize(kDefaultContentId, kDefaultProvider,
ecm_init_params, &entitlement_request))
.ok()) {
// TODO(user): Should send an error response.
return;
}
if (!(status = fixed_key_fetcher_.RequestEntitlementKey(
entitlement_request, &entitlement_response))
.ok()) {
// TODO(user): Should send an error Channel_status.
return;
}
if (!(status = ecm->ProcessCasEncryptionResponse(entitlement_response))
.ok()) {
// TODO(user): Should send an error Channel_status.
return;
}
// Step 2: Generate serialized ECM.
CasEcmGenerator ecm_generator;
ecm_generator.set_ecm(std::move(ecm));
EcmParameters ecm_param;
ecm_param.rotation_enabled = key_rotation_enabled;
for (int i = 0; i <= params.cw_per_msg; i++) {
ecm_param.key_params.emplace_back();
ecm_param.key_params[i].key_data = params.cp_cw_combinations[i].cw;
ecm_param.key_params[i].key_id = widevine::crypto_util::DeriveKeyId(
ecm_param.key_params[i].key_data);
// TODO(user): How to derive this content_iv, maybe based on key?
std::string content_iv = "12345678";
ecm_param.key_params[i].content_ivs.push_back(content_iv);
}
std::string serialized_ecm = ecm_generator.GenerateEcm(ecm_param);
std::cout << "serialized_ecm: " << serialized_ecm << std::endl;
for (int i = 0; i < serialized_ecm.size(); i++) {
printf("'\\x%x', ", static_cast<uint16_t>(serialized_ecm.at(i)));
}
std::cout << std::endl;
LOG(INFO) << "ECM size: " << serialized_ecm.size();
// Step 3: Make a TS packet carrying the serialized ECM.
// TODO(user): Is it correct to set 'pid' using ECM_id?
// TODO(user): Where do I get the continuity_counter?
uint8_t continuity_counter = 0;
WvCasEcm wv_cas_ecm;
WvCasStatus cas_status = wv_cas_ecm.GenerateTsPacket(
serialized_ecm, params.ecm_id,
params.cp_number % 2 == 0 ? kTsPacketTableId80 : kTsPacketTableId81,
&continuity_counter, ecm_datagram);
if (cas_status != OK) {
// TODO(user): Should send an error Channel_status.
return;
}
}
} // namespace cas
} // namespace widevine

View File

@@ -0,0 +1,101 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_
#include <cstddef>
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include <cstdint>
#include "common/status.h"
#include "media_cas_packager_sdk/internal/ecm.h"
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
namespace widevine {
namespace cas {
// A struct that captures the Ecmg configs.
struct EcmgConfig {
int16_t delay_start;
int16_t delay_stop;
uint16_t ecm_rep_period;
uint16_t max_comp_time;
uint8_t access_criteria_transfer_mode;
};
// A struct that represent a CP_CW_Combination.
struct EcmgCpCwCombination {
uint16_t cp; // crypto period
std::string cw; // control word
};
// A struct that is used to hold all possible params for a ECMG request.
struct EcmgParameters {
// CW_per_msg could 1, 2, or 3,
// so there can be up to 3 CP_CW_Combinations
EcmgCpCwCombination cp_cw_combinations[3];
uint16_t cp_duration; // crypto period duration
uint16_t cp_number; // crypto period number
uint8_t cw_per_msg;
uint16_t ecm_channel_id;
uint16_t ecm_stream_id;
uint16_t ecm_id;
uint16_t nominal_cp_duration;
uint32_t super_cas_id;
};
// A class that handles one (and only one) ECMG client.
// This class is NOT thread-safe.
class EcmgClientHandler {
public:
explicit EcmgClientHandler(EcmgConfig* ecmg_config);
EcmgClientHandler(const EcmgClientHandler&) = delete;
EcmgClientHandler& operator=(const EcmgClientHandler&) = delete;
virtual ~EcmgClientHandler() = default;
// Handle a |request| from the client.
// If any response is generated, it would returned via |response|
// and |response_length|.
void HandleRequest(const char* const request, char* response,
size_t* response_length);
private:
void HandleChannelSetup(const EcmgParameters& params, char* response,
size_t* response_length);
// TODO(user): HandleChannelTest()
void HandleChannelClose(const EcmgParameters& params, char* response,
size_t* response_length);
void HandleStreamSetup(const EcmgParameters& params, char* response,
size_t* response_length);
// TODO(user): HandleStreamTest()
void HandleStreamCloseRequest(const EcmgParameters& params, char* response,
size_t* response_length);
void HandleCwProvision(const EcmgParameters& params, char* response,
size_t* response_length);
void BuildEcmDatagram(const EcmgParameters& params, uint8_t* ecm_datagram);
EcmgConfig* ecmg_config_;
// Per spec, "There is always one (and only one) channel per TCP connection".
bool channel_id_set_;
uint16_t channel_id_;
// Map from ECM_stream_id to ECM_id.
std::unordered_map<uint16_t, uint16_t> streams_;
FixedKeyFetcher fixed_key_fetcher_;
};
} // namespace cas
} // namespace widevine
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CLIENT_HANDLER_H_

View File

@@ -0,0 +1,85 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
#include <string>
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/memory/memory.h"
#include "example/test_ecmg_messages.h"
namespace widevine {
namespace cas {
namespace {
#define BUFFER_SIZE (1024)
class EcmgClientHandlerTest : public ::testing::Test {
protected:
EcmgClientHandlerTest() {
config_.delay_start = 200;
config_.delay_stop = 200;
config_.ecm_rep_period = 100;
config_.max_comp_time = 100;
config_.access_criteria_transfer_mode = 1;
client_handler_ = absl::make_unique<EcmgClientHandler>(&config_);
}
protected:
// Helper function for debugging the tests.
void PrintMessage(const std::string &description, const char *const message,
size_t length) {
printf("%s ", description.c_str());
fflush(stdout);
for (size_t i = 0; i < length; i++) {
printf("'\\x%02x', ", static_cast<uint16_t>(*(message + i)));
fflush(stdout);
}
printf("\n");
fflush(stdout);
}
EcmgConfig config_;
std::unique_ptr<EcmgClientHandler> client_handler_;
};
// TODO(user): Add unit tests for error cases.
TEST_F(EcmgClientHandlerTest, SuccessSequence) {
char response[BUFFER_SIZE];
size_t response_length;
client_handler_->HandleRequest(kTestChannelSetup, response, &response_length);
EXPECT_EQ(62, response_length);
EXPECT_EQ(0, memcmp(kTestChannelStatus, response, response_length));
client_handler_->HandleRequest(kTestStreamSetup, response, &response_length);
EXPECT_EQ(28, response_length);
EXPECT_EQ(0, memcmp(kTestStreamStatus, response, response_length));
client_handler_->HandleRequest(kTestCwProvision, response, &response_length);
EXPECT_EQ(215, response_length);
// Only comparing the bytes in front of the ECM_datagram, because
// random wrapping IV is generated each time causing the ECM_datagram
// to be non-deterministic.
EXPECT_EQ(0, memcmp(kTestEcmResponse, response, 27));
client_handler_->HandleRequest(kTestStreamCloseRequest, response,
&response_length);
EXPECT_EQ(17, response_length);
EXPECT_EQ(0, memcmp(kTestStreamCloseResponse, response, response_length));
client_handler_->HandleRequest(kTestChannelClose, response, &response_length);
EXPECT_EQ(0, response_length);
}
} // namespace
} // namespace cas
} // namespace widevine

View File

@@ -9,45 +9,95 @@
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_ #ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_ #define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
// ECMG <> SCS // ECMG <=> SCS protocol_version.
// Parameter_type values #define ECMG_SCS_PROTOCOL_VERSION (0x03)
#define DVB_RESERVED 0x0000
#define SUPER_CAS_ID 0x0001 // ECMG message type values.
#define SECTION_TSPKT_FLAG 0x0002 // 0x0000 DVB reserved.
#define DELAY_START 0x0003 #define ECMG_CHANNEL_SETUP (0x0001)
#define DELAY_STOP 0x0004 #define ECMG_CHANNEL_TEST (0x0002)
#define TRANSITION_DELAY_START 0x0005 #define ECMG_CHANNEL_STATUS (0x0003)
#define TRANSITION_DELAY_STOP 0x0006 #define ECMG_CHANNEL_CLOSE (0x0004)
#define ECM_REP_PERIOD 0x0007 #define ECMG_CHANNEL_ERROR (0x0005)
#define MAX_STREAMS 0x0008 // 0x0016 - 0x0100 DVB reserved.
#define MIN_CP_DURATION 0x0009 #define ECMG_STREAM_SETUP (0x0101)
#define LEAD_CW 0x000A #define ECMG_STREAM_TEST (0x0102)
#define CW_PER_MESSAGE 0x000B #define ECMG_STREAM_STATUS (0x0103)
#define MAX_COMP_TIME 0x000C #define ECMG_STREAM_CLOSE_REQUEST (0x0104)
#define ACCESS_CRITERIA 0x000D #define ECMG_STREAM_CLOSE_RESPONSE (0x0105)
#define ECM_CHANNEL_ID 0x000E #define ECMG_STREAM_ERROR (0x0106)
#define ECM_STREAM_ID 0x000F // 0x0119 - 0x0200 DVB reserved.
#define NOMINAL_CP_DURATION 0x0010 #define ECMG_CW_PROVISION (0x0201)
#define ACCESS_CRITERIA_TRANSFER_MODE 0x0011 #define ECMG_ECM_RESPONSE (0x0202)
#define CP_NUMBER 0x0012
#define CP_DURATION 0x0013 // ECMG parameter type values.
#define CP_CW_COMBINATION 0x0014 #define DVB_RESERVED (0x0000)
#define ECM_DATAGRAM 0x0015 #define SUPER_CAS_ID (0x0001)
#define AC_DELAY_START 0x0016 #define SECTION_TSPKT_FLAG (0x0002)
#define AC_DELAY_STOP 0x0017 #define DELAY_START (0x0003)
#define CW_ENCRYPTION 0x0018 #define DELAY_STOP (0x0004)
#define ECM_ID 0x0019 #define TRANSITION_DELAY_START (0x0005)
#define ERROR_STATUS 0x7000 #define TRANSITION_DELAY_STOP (0x0006)
#define ERROR_INFORMATION 0x7001 #define ECM_REP_PERIOD (0x0007)
#define MAX_STREAMS (0x0008)
#define MIN_CP_DURATION (0x0009)
#define LEAD_CW (0x000A)
#define CW_PER_MESSAGE (0x000B)
#define MAX_COMP_TIME (0x000C)
#define ACCESS_CRITERIA (0x000D)
#define ECM_CHANNEL_ID (0x000E)
#define ECM_STREAM_ID (0x000F)
#define NOMINAL_CP_DURATION (0x0010)
#define ACCESS_CRITERIA_TRANSFER_MODE (0x0011)
#define CP_NUMBER (0x0012)
#define CP_DURATION (0x0013)
#define CP_CW_COMBINATION (0x0014)
#define ECM_DATAGRAM (0x0015)
#define AC_DELAY_START (0x0016)
#define AC_DELAY_STOP (0x0017)
#define CW_ENCRYPTION (0x0018)
#define ECM_ID (0x0019)
#define ERROR_STATUS (0x7000)
#define ERROR_INFORMATION (0x7001)
// ECMG protocol error values.
#define INVAID_MESSAGE (0x0001)
#define UNSUPPORTED_PROTOCOL_VERSION (0X0002)
#define UNKNOWN_MESSAGE_TYPE_VALUE (0X0003)
#define MESSAGE_TOO_LONG (0X0004)
#define UNKNOWN_SUPER_CAS_ID_VALUE (0X0005)
#define UNKNOWN_ECM_CHANNEL_ID_VALUE (0X0006)
#define UNKNOWN_ECM_STREAM_ID_VALUE (0X0007)
#define TOO_MANY_CHANNELS_ON_THIS_ECMG (0X0008)
#define TOO_MANY_ECM_STREAMS_ON_THIS_CHANNEL (0X0009)
#define TOO_MANY_ECM_STREAMS_ON_THIS_ECMG (0X000A)
#define NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM (0X000B)
#define ECMG_OUT_OF_STORAGE_CAPACITY (0X000C)
#define ECMG_OUT_OF_COMPUTATIONAL_RESOURCES (0X000D)
#define UNKNOWN_PARAMETER_TYPE_VALUE (0X000E)
#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0X000F)
#define MISSING_MANDATORY_DVB_PARAMETER (0X0010)
#define INVALID_VALUE_FOR_DVB_PARAMETER (0X0011)
#define UNKNOWN_ECM_ID_VALUE (0X0012)
#define ECM_CHANNEL_ID_VALUE_ALREADY_IN_USE (0X0013)
#define ECM_STREAM_ID_VALUE_ALREADY_IN_USE (0X0014)
#define ECM_ID_VALUE_ALREADY_IN_USE (0X0015)
#define UNKNOWN_ERROR (0X7000)
#define UNRECOVERABLE_ERROR (0X7001)
// Size (in # of bytes) of various fields. // Size (in # of bytes) of various fields.
#define PARAMETER_TYPE_SIZE 2 #define PROTOCOL_VERSION_SIZE (1)
#define PARAMETER_LENGTH_SIZE 2 #define MESSAGE_TYPE_SIZE (2)
#define ECM_CHANNEL_ID_SIZE 2 #define MESSAGE_LENGTH_SIZE (2)
#define ECM_STREAM_ID_SIZE 2 #define PARAMETER_TYPE_SIZE (2)
#define NOMINAL_CP_DURATION_SIZE 2 #define PARAMETER_LENGTH_SIZE (2)
#define CP_NUMBER_SIZE 2 #define SUPER_CAS_ID_SIZE (4)
#define CP_DURATION_SIZE 2 #define ECM_CHANNEL_ID_SIZE (2)
#define CP_SIZE 2 #define ECM_ID_SIZE (2)
#define ECM_STREAM_ID_SIZE (2)
#define NOMINAL_CP_DURATION_SIZE (2)
#define CP_NUMBER_SIZE (2)
#define CP_DURATION_SIZE (2)
#define CP_SIZE (2)
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_ #endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_

View File

@@ -14,8 +14,8 @@
namespace widevine { namespace widevine {
namespace cas { namespace cas {
util::Status FixedKeyFetcher::RequestEntitlementKey( Status FixedKeyFetcher::RequestEntitlementKey(const std::string& request_string,
const std::string& request_string, std::string* signed_response_string) { std::string* signed_response_string) {
CasEncryptionRequest request; CasEncryptionRequest request;
request.ParseFromString(request_string); request.ParseFromString(request_string);
@@ -49,7 +49,7 @@ util::Status FixedKeyFetcher::RequestEntitlementKey(
SignedCasEncryptionResponse signed_response; SignedCasEncryptionResponse signed_response;
signed_response.set_response(response_string); signed_response.set_response(response_string);
signed_response.SerializeToString(signed_response_string); signed_response.SerializeToString(signed_response_string);
return util::OkStatus(); return OkStatus();
} }
} // namespace cas } // namespace cas

View File

@@ -49,8 +49,8 @@ class FixedKeyFetcher : public KeyFetcher {
// |signed_response_string| a serialized SignedCasEncryptionResponse // |signed_response_string| a serialized SignedCasEncryptionResponse
// message. It should be passed into // message. It should be passed into
// WvCasEcm::ProcessCasEncryptionResponse(). // WvCasEcm::ProcessCasEncryptionResponse().
util::Status RequestEntitlementKey(const std::string& request_string, Status RequestEntitlementKey(const std::string& request_string,
std::string* signed_response_string) override; std::string* signed_response_string) override;
private: private:
std::string even_entitlement_key_id_; std::string even_entitlement_key_id_;

View File

@@ -33,8 +33,8 @@ class KeyFetcher {
// |signed_response_string| a serialized SignedCasEncryptionResponse // |signed_response_string| a serialized SignedCasEncryptionResponse
// message. It should be passed into // message. It should be passed into
// WvCasEcm::ProcessCasEncryptionResponse(). // WvCasEcm::ProcessCasEncryptionResponse().
virtual util::Status RequestEntitlementKey( virtual Status RequestEntitlementKey(const std::string& request_string,
const std::string& request_string, std::string* signed_response_string) = 0; std::string* signed_response_string) = 0;
}; };
} // namespace cas } // namespace cas

View File

@@ -1,67 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "media_cas_packager_sdk/internal/simulcrypt.h"
#include <stddef.h>
#include <string.h>
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "common/status.h"
#include "media_cas_packager_sdk/internal/ecmg.h"
#include "media_cas_packager_sdk/internal/simulcrypt_constants.h"
#include "media_cas_packager_sdk/internal/util.h"
namespace widevine {
namespace cas {
// TODO(user): Caller should check |message| is at lest 5 bytes long.
util::Status Simulcrypt::ProcessMessage(const char* message, std::string* response) {
DCHECK(message);
DCHECK(response);
uint8_t protocol_version;
uint16_t message_type;
uint16_t message_length;
// 'offset' is used to track where we are within |message|.
size_t offset = 0;
memcpy(&protocol_version, message, PROTOCOL_VERSION_SIZE);
if (protocol_version != EXPECTED_PROTOCOL_VERSION) {
return util::Status(
util::error::INVALID_ARGUMENT,
absl::StrCat("Invalid protocol version ", protocol_version));
}
offset += PROTOCOL_VERSION_SIZE;
BigEndianToHost16(&message_type, message + offset);
offset += MESSAGE_TYPE_SIZE;
BigEndianToHost16(&message_length, message + offset);
offset += MESSAGE_LENGTH_SIZE;
switch (message_type) {
case ECMG_STREAM_SETUP: {
return ecmg_.ProcessStreamSetupMessage(message + offset, message_length);
break;
}
case ECMG_CW_PROVISION: {
return ecmg_.ProcessCwProvisionMessage(message + offset, message_length,
response);
break;
}
default: {
return util::Status(
util::error::UNIMPLEMENTED,
absl::StrCat("No implementation yet to process message of type ",
message_type));
break;
}
}
return util::OkStatus();
}
} // namespace cas
} // namespace widevine

View File

@@ -1,50 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_H_
#include <stddef.h>
#include <list>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <cstdint>
#include "common/status.h"
#include "media_cas_packager_sdk/internal/ecmg.h"
namespace widevine {
namespace cas {
// A class that handles Simulcrypt messages.
// The expected usage is by a TCP server that receives Simulcrypt message
// from the network then forward that message to an instance of this class
// for processing.
// This class is NOT thread-safe.
class Simulcrypt {
public:
Simulcrypt() = default;
Simulcrypt(const Simulcrypt&) = delete;
Simulcrypt& operator=(const Simulcrypt&) = delete;
virtual ~Simulcrypt() = default;
// Process a Simulcrypt |message|.
// If any response is generated, it would returned via |response|.
// Any error during processing would be turned via util::Status.
util::Status ProcessMessage(const char* message, std::string* response);
private:
Ecmg ecmg_;
};
} // namespace cas
} // namespace widevine
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_H_

View File

@@ -1,55 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_
// Message_type Values
// 0x0000 DVB reserved.
#define ECMG_CHANNEL_SETUP 0x0001
#define ECMG_CHANNEL_TEST 0x0002
#define ECMG_CHANNEL_STATUS 0x0003
#define ECMG_CHANNEL_CLOSE 0x0004
#define ECMG_CHANNEL_ERROR 0x0005
// 0x0006 - 0x0010 DVB reserved.
#define EMMG_CHANNEL_SETUP 0x0011
#define EMMG_CHANNEL_TEST 0x0012
#define EMMG_CHANNEL_STATUS 0x0013
#define EMMG_CHANNEL_CLOSE 0x0014
#define EMMG_CHANNEL_ERROR 0x0015
// 0x0016 - 0x0100 DVB reserved.
#define ECMG_STREAM_SETUP 0x0101
#define ECMG_STREAM_TEST 0x0102
#define ECMG_STREAM_STATUS 0x0103
#define ECMG_STREAM_CLOSE_REQUEST 0x0104
#define ECMG_STREAM_CLOSE_RESPONSE 0x0105
#define ECMG_STREAM_ERROR 0x0106
// 0x0107 - 0x0110 DVB reserved.
#define EMMG_STREAM_SETUP 0x0111
#define EMMG_STREAM_TEST 0x0112
#define EMMG_STREAM_STATUS 0x0113
#define EMMG_STREAM_CLOSE_REQUEST 0x0114
#define EMMG_STREAM_CLOSE_RESPONSE 0x0115
#define EMMG_STREAM_ERROR 0x0116
#define EMMG_STREAM_BW_REQUEST 0x0117
#define EMMG_STREAM_BW_ALLOCATION 0x0118
// 0x0119 - 0x0200 DVB reserved.
#define ECMG_CW_PROVISION 0x0201
#define ECMG_ECM_RESPONSE 0x0202
// 0x0203 - 0x0210 DVB reserved.
#define EMMG_DATA_PROVISION 0x0211
// 0x0212 - 0x0300 DVB reserved.
#define EXPECTED_PROTOCOL_VERSION 0x01
// Size (in # of bytes) of various fields.
#define PROTOCOL_VERSION_SIZE 1
#define MESSAGE_TYPE_SIZE 2
#define MESSAGE_LENGTH_SIZE 2
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_

View File

@@ -1,54 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "media_cas_packager_sdk/internal/simulcrypt.h"
#include <string>
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "example/test_simulcrypt_messages.h"
namespace widevine {
namespace cas {
namespace {
class SimulcryptTest : public ::testing::Test {
protected:
SimulcryptTest() {}
protected:
Simulcrypt simulcrypt_;
};
TEST_F(SimulcryptTest, ProcessEcmgStreamSetupMessage) {
std::string response = "";
EXPECT_OK(simulcrypt_.ProcessMessage(kTestEcmgStreamSetupMessage, &response));
EXPECT_EQ("", response);
}
TEST_F(SimulcryptTest, ProcessEcmgCwProvisionMessageWithOneCw) {
std::string response = "";
EXPECT_OK(simulcrypt_.ProcessMessage(kTestEcmgCwProvisionMessageWithOneCw,
&response));
EXPECT_EQ("", response);
}
TEST_F(SimulcryptTest, ProcessEcmgCwProvisionMessageWithTwoCw) {
std::string response = "";
EXPECT_OK(simulcrypt_.ProcessMessage(kTestEcmgCwProvisionMessageWithTwoCw,
&response));
EXPECT_EQ("", response);
}
} // namespace
} // namespace cas
} // namespace widevine

View File

@@ -18,7 +18,7 @@
namespace widevine { namespace widevine {
namespace cas { namespace cas {
util::Status TsPacket::Write(std::string* output) const { Status TsPacket::Write(std::string* output) const {
DCHECK(output); DCHECK(output);
output->resize(kTsPacketSize); output->resize(kTsPacketSize);
@@ -32,8 +32,8 @@ util::Status TsPacket::Write(std::string* output) const {
std::bitset<2> adaptation_field_control(adaptation_field_control_); std::bitset<2> adaptation_field_control(adaptation_field_control_);
std::bitset<4> continuity_counter(continuity_counter_); std::bitset<4> continuity_counter(continuity_counter_);
if (adaptation_field_control_ & kAdaptationFieldOnly) { if (adaptation_field_control_ & kAdaptationFieldOnly) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL,
"TsPacket does NOT handle adaptation field yet"); "TsPacket does NOT handle adaptation field yet");
} }
// Converts header bitset to string. // Converts header bitset to string.
@@ -43,29 +43,28 @@ util::Status TsPacket::Write(std::string* output) const {
pid.to_string(), transport_scrambling_control.to_string(), pid.to_string(), transport_scrambling_control.to_string(),
adaptation_field_control.to_string(), continuity_counter.to_string()); adaptation_field_control.to_string(), continuity_counter.to_string());
if (header_bitset.size() != 4 * 8) { if (header_bitset.size() != 4 * 8) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL,
absl::StrCat("TS packet header bitset incorret size: ", absl::StrCat("TS packet header bitset incorret size: ",
header_bitset.size())); header_bitset.size()));
} }
std::string serialized_header; std::string serialized_header;
util::Status status = string_util::BitsetStringToBinaryString( Status status = string_util::BitsetStringToBinaryString(header_bitset,
header_bitset, &serialized_header); &serialized_header);
if (!status.ok()) { if (!status.ok()) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL,
"Failed to convert TS packet header bitset to std::string"); "Failed to convert TS packet header bitset to std::string");
} }
// Write TsPacket payload. // Write TsPacket payload.
if (payload_.size() != CalculatePayloadSize()) { if (payload_.size() != CalculatePayloadSize()) {
return util::Status( return Status(error::INVALID_ARGUMENT,
util::error::INVALID_ARGUMENT, absl::StrCat("Incorrect payload size: ", payload_.size()));
absl::StrCat("Incorrect payload size: ", payload_.size()));
} }
// Return header + payload as a TS packet. // Return header + payload as a TS packet.
*output = serialized_header + payload_; *output = serialized_header + payload_;
return util::OkStatus(); return OkStatus();
} }
int32_t TsPacket::CalculatePayloadSize() const { int32_t TsPacket::CalculatePayloadSize() const {

View File

@@ -63,7 +63,7 @@ class TsPacket {
virtual ~TsPacket() = default; virtual ~TsPacket() = default;
// Writes the packet into the provided output. Returns kOk on success. // Writes the packet into the provided output. Returns kOk on success.
virtual util::Status Write(std::string* output) const; virtual Status Write(std::string* output) const;
// Returns the size of payload data for the current TS packet configuration. // Returns the size of payload data for the current TS packet configuration.
int32_t CalculatePayloadSize() const; int32_t CalculatePayloadSize() const;

View File

@@ -9,8 +9,8 @@
#include "media_cas_packager_sdk/internal/util.h" #include "media_cas_packager_sdk/internal/util.h"
#include <netinet/in.h> #include <netinet/in.h>
#include <stddef.h> #include <cstddef>
#include <string.h> #include <cstring>
#include "glog/logging.h" #include "glog/logging.h"
#include "media_cas_packager_sdk/internal/ts_packet.h" #include "media_cas_packager_sdk/internal/ts_packet.h"
@@ -30,14 +30,36 @@ ContinuityCounter Increment(ContinuityCounter continuity_counter) {
void BigEndianToHost16(uint16_t* destination, const void* source) { void BigEndianToHost16(uint16_t* destination, const void* source) {
DCHECK(destination); DCHECK(destination);
DCHECK(source); DCHECK(source);
uint16_t big_endian_number; uint16_t big_endian_number = 0;
memcpy(&big_endian_number, source, 2); memcpy(&big_endian_number, source, 2);
*destination = ntohs(big_endian_number); *destination = ntohs(big_endian_number);
} }
util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, void BigEndianToHost32(uint32_t* destination, const void* source) {
uint8_t table_id, ContinuityCounter* cc, DCHECK(destination);
uint8_t* buffer, ssize_t* bytes_modified) { DCHECK(source);
uint32_t big_endian_number = 0;
memcpy(&big_endian_number, source, 4);
*destination = ntohl(big_endian_number);
}
void Host16ToBigEndian(void* destination, const uint16_t* source) {
DCHECK(destination);
DCHECK(source);
uint16_t big_endian_number = htons(*source);
memcpy(destination, &big_endian_number, 2);
}
void Host16ToBigEndian(void* destination, const int16_t* source) {
DCHECK(destination);
DCHECK(source);
uint16_t big_endian_number = htons(static_cast<uint16_t>(*source));
memcpy(destination, &big_endian_number, 2);
}
Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, uint8_t table_id,
ContinuityCounter* cc, uint8_t* buffer,
ssize_t* bytes_modified) {
DCHECK(cc); DCHECK(cc);
DCHECK(buffer); DCHECK(buffer);
DCHECK(bytes_modified); DCHECK(bytes_modified);
@@ -75,7 +97,7 @@ util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
// And write the packet. // And write the packet.
std::string ecm_ts_packet; std::string ecm_ts_packet;
util::Status status = ecm_packet.Write(&ecm_ts_packet); Status status = ecm_packet.Write(&ecm_ts_packet);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
@@ -83,7 +105,7 @@ util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
memcpy(buffer + *bytes_modified, ecm_ts_packet.data(), ecm_ts_packet.size()); memcpy(buffer + *bytes_modified, ecm_ts_packet.data(), ecm_ts_packet.size());
*bytes_modified += ecm_ts_packet.size(); *bytes_modified += ecm_ts_packet.size();
return util::OkStatus(); return OkStatus();
} }
} // namespace cas } // namespace cas

View File

@@ -24,6 +24,16 @@ namespace cas {
// the result in |destination|. // the result in |destination|.
void BigEndianToHost16(uint16_t* destination, const void* source); void BigEndianToHost16(uint16_t* destination, const void* source);
// Read 32 bits (long int) from |source|, treat it as a big-endian number
// (network byte order), finally covert it to host endianness and return
// the result in |destination|.
void BigEndianToHost32(uint32_t* destination, const void* source);
// Covert |source| to big-endian (network byte order) and copy it to
// |destination|.
void Host16ToBigEndian(void* destination, const uint16_t* source);
void Host16ToBigEndian(void* destination, const int16_t* source);
// Packages an ECM as a TS packet and inserts it into a buffer. // Packages an ECM as a TS packet and inserts it into a buffer.
// Args: // Args:
// - |ecm| is the serialized ECM. // - |ecm| is the serialized ECM.
@@ -42,9 +52,9 @@ void BigEndianToHost16(uint16_t* destination, const void* source);
// the |buffer| and is used as an offset. // the |buffer| and is used as an offset.
// |bytes_modified| will be incremented by 188 if insertion of ECM into // |bytes_modified| will be incremented by 188 if insertion of ECM into
// |buffer| is successful. // |buffer| is successful.
util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, uint8_t table_id,
uint8_t table_id, ContinuityCounter* cc, ContinuityCounter* cc, uint8_t* buffer,
uint8_t* buffer, ssize_t* bytes_modified); ssize_t* bytes_modified);
} // namespace cas } // namespace cas
} // namespace widevine } // namespace widevine

View File

@@ -58,6 +58,9 @@ constexpr char kExpectedEcmPacket[] = {
namespace widevine { namespace widevine {
namespace cas { namespace cas {
// TODO(user): Add unit tests for BigEndianToHost16, BigEndianToHost32 and
// Host16ToBigEndian.
TEST(InsertEcmAsTsPacketTest, BasicHappyPath) { TEST(InsertEcmAsTsPacketTest, BasicHappyPath) {
// declare variables used by InsertEcmAsTsPacket() // declare variables used by InsertEcmAsTsPacket()
ContinuityCounter ecm_cc_ = 0; ContinuityCounter ecm_cc_ = 0;

View File

@@ -20,7 +20,9 @@ PUBLIC_COPTS = ["-fvisibility=default"]
filegroup( filegroup(
name = "binary_release_files", name = "binary_release_files",
srcs = glob(["*.h"]), srcs = glob(["*.h"]) + [
":wv_ecmg",
],
) )
cc_binary( cc_binary(
@@ -49,15 +51,6 @@ cc_library(
], ],
) )
cc_binary(
name = "simulcrypt_server",
srcs = ["simulcrypt_server.cc"],
deps = [
"//base",
"@abseil_repo//absl/base:core_headers",
],
)
cc_library( cc_library(
name = "wv_cas_ca_descriptor", name = "wv_cas_ca_descriptor",
srcs = ["wv_cas_ca_descriptor.cc"], srcs = ["wv_cas_ca_descriptor.cc"],
@@ -97,8 +90,8 @@ cc_library(
"@abseil_repo//absl/base:core_headers", # buildcleaner: keep "@abseil_repo//absl/base:core_headers", # buildcleaner: keep
"@abseil_repo//absl/memory", # buildcleaner: keep "@abseil_repo//absl/memory", # buildcleaner: keep
"@abseil_repo//absl/strings", # buildcleaner: keep "@abseil_repo//absl/strings", # buildcleaner: keep
"//common:status",
"//common:crypto_util", "//common:crypto_util",
"//common:status",
"//example:constants", "//example:constants",
"//media_cas_packager_sdk/internal:ecm", "//media_cas_packager_sdk/internal:ecm",
"//media_cas_packager_sdk/internal:ecm_generator", "//media_cas_packager_sdk/internal:ecm_generator",
@@ -134,7 +127,6 @@ cc_library(
"@abseil_repo//absl/base:core_headers", "@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"@curl_repo//:curl", "@curl_repo//:curl",
"//common:status",
"//common:signature_util", "//common:signature_util",
"//media_cas_packager_sdk/internal:key_fetcher", "//media_cas_packager_sdk/internal:key_fetcher",
"//protos/public:media_cas_encryption_proto", "//protos/public:media_cas_encryption_proto",
@@ -153,7 +145,6 @@ cc_test(
"//external:protobuf", "//external:protobuf",
"//testing:gunit_main", "//testing:gunit_main",
"@abseil_repo//absl/strings", "@abseil_repo//absl/strings",
"//common:status",
"//protos/public:media_cas_encryption_proto", "//protos/public:media_cas_encryption_proto",
], ],
) )
@@ -175,3 +166,14 @@ cc_test(
"//testing:gunit_main", "//testing:gunit_main",
], ],
) )
cc_binary(
name = "wv_ecmg",
srcs = ["wv_ecmg.cc"],
deps = [
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"//media_cas_packager_sdk/internal:ecmg_client_handler",
],
)

View File

@@ -1,82 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Example server that listens on a port for Simulcrypt API messages.
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
#include "gflags/gflags.h"
#include "glog/logging.h"
DEFINE_int32(port, 0, "Server port number");
constexpr uint32_t kBufferSize = 256;
constexpr uint32_t kLicenseBacklog = 5;
constexpr uint32_t kWriteChunkSize = 18;
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(FLAGS_port != 0) << "need --port";
struct sockaddr_in server_address;
bzero(reinterpret_cast<char *>(&server_address), sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(FLAGS_port);
int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);
CHECK(listen_socket_fd >= 0) << "failed to open socket";
CHECK(bind(listen_socket_fd, (struct sockaddr *)&server_address,
sizeof(server_address)) >= 0)
<< "error on binding";
std::cout << "Server listening ..." << std::endl << std::flush;
int return_val = listen(listen_socket_fd, kLicenseBacklog);
switch (return_val) {
case EADDRINUSE:
LOG(FATAL) << "Another socket is already listening on the same port.";
break;
case EBADF:
LOG(FATAL) << "The argument sockfd is not a valid descriptor.";
break;
case ENOTSOCK:
LOG(FATAL) << "The argument sockfd is not a socket.";
break;
case EOPNOTSUPP:
LOG(FATAL) << "The socket is not of a type that supports the listen() "
"operation.";
default:
break;
}
struct sockaddr_in client_address;
socklen_t clilet_address_size = sizeof(client_address);
int client_socket_fd = accept(
listen_socket_fd, reinterpret_cast<struct sockaddr *>(&client_address),
&clilet_address_size);
CHECK(client_socket_fd >= 0) << "error on accept";
char buffer[kBufferSize];
bzero(buffer, kBufferSize);
if (read(client_socket_fd, buffer, kBufferSize - 1) < 0) {
LOG(FATAL) << "ERROR reading from socket";
}
printf("Here is the message: %s", buffer);
if (write(client_socket_fd, "I got your message", kWriteChunkSize) < 0) {
LOG(FATAL) << "ERROR writing to socket";
}
close(client_socket_fd);
close(listen_socket_fd);
return 0;
}

View File

@@ -95,7 +95,7 @@ WvCasStatus WvCasCaDescriptor::GenerateCaDescriptor(
return INTERNAL; return INTERNAL;
} }
std::string descriptor; std::string descriptor;
util::Status status = Status status =
string_util::BitsetStringToBinaryString(descriptor_bitset, &descriptor); string_util::BitsetStringToBinaryString(descriptor_bitset, &descriptor);
*serialized_ca_desc = descriptor; *serialized_ca_desc = descriptor;

View File

@@ -13,10 +13,10 @@
#include <vector> #include <vector>
#include "glog/logging.h" #include "glog/logging.h"
#include "common/status.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "common/crypto_util.h" #include "common/crypto_util.h"
#include "common/status.h"
#include "example/constants.h" #include "example/constants.h"
#include "media_cas_packager_sdk/internal/ecm.h" #include "media_cas_packager_sdk/internal/ecm.h"
#include "media_cas_packager_sdk/internal/ecm_generator.h" #include "media_cas_packager_sdk/internal/ecm_generator.h"
@@ -154,7 +154,7 @@ WvCasStatus WvCasEcm::GenerateEcm(
// TODO(user): When we want to retrieve entitlement key from License Server // TODO(user): When we want to retrieve entitlement key from License Server
// we need to figure out a way to provide real 'content_id' and 'provder' // we need to figure out a way to provide real 'content_id' and 'provder'
// to this function here. // to this function here.
util::Status status; Status status;
if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider, if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider,
ecm_init_params, &entitlement_request)) ecm_init_params, &entitlement_request))
.ok()) { .ok()) {
@@ -265,7 +265,7 @@ WvCasStatus WvCasEcm::GenerateSingleKeyEcm(
// TODO(user): When we want to retrieve entitlement key from License Server // TODO(user): When we want to retrieve entitlement key from License Server
// we need to figure out a way to provide real 'content_id' and 'provder' // we need to figure out a way to provide real 'content_id' and 'provder'
// to this function here. // to this function here.
util::Status status; Status status;
if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider, if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider,
ecm_init_params, &entitlement_request)) ecm_init_params, &entitlement_request))
.ok()) { .ok()) {
@@ -319,8 +319,8 @@ WvCasStatus WvCasEcm::GenerateTsPacket(const std::string& ecm, uint16_t pid,
uint8_t* continuity_counter, uint8_t* continuity_counter,
uint8_t* packet) { uint8_t* packet) {
ssize_t bytes_modified = 0; ssize_t bytes_modified = 0;
util::Status status = InsertEcmAsTsPacket( Status status = InsertEcmAsTsPacket(ecm, pid, table_id, continuity_counter,
ecm, pid, table_id, continuity_counter, packet, &bytes_modified); packet, &bytes_modified);
if (!status.ok() || bytes_modified != kTsPacketSize) { if (!status.ok() || bytes_modified != kTsPacketSize) {
memset(packet, 0, kTsPacketSize); memset(packet, 0, kTsPacketSize);
LOG(ERROR) << "Failed to generate TS packet: " << status; LOG(ERROR) << "Failed to generate TS packet: " << status;

View File

@@ -20,7 +20,6 @@
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "curl/curl.h" #include "curl/curl.h"
#include "curl/easy.h" #include "curl/easy.h"
#include "common/status.h"
#include "common/signature_util.h" #include "common/signature_util.h"
#include "protos/public/media_cas_encryption.pb.h" #include "protos/public/media_cas_encryption.pb.h"
@@ -41,12 +40,12 @@ DEFINE_string(signing_iv, "",
namespace widevine { namespace widevine {
namespace cas { namespace cas {
util::Status WvCasKeyFetcher::RequestEntitlementKey( Status WvCasKeyFetcher::RequestEntitlementKey(const std::string& request_string,
const std::string& request_string, std::string* signed_response_string) { std::string* signed_response_string) {
if (FLAGS_signing_provider.empty() || FLAGS_signing_key.empty() || if (FLAGS_signing_provider.empty() || FLAGS_signing_key.empty() ||
FLAGS_signing_iv.empty()) { FLAGS_signing_iv.empty()) {
return util::Status( return Status(
util::error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
"Flag 'signing_provider', 'signing_key' or 'signing_iv' is empty"); "Flag 'signing_provider', 'signing_key' or 'signing_iv' is empty");
} }
@@ -63,8 +62,8 @@ util::Status WvCasKeyFetcher::RequestEntitlementKey(
// NOTE: MessageToJsonString will automatically converts 'bytes' type fields // NOTE: MessageToJsonString will automatically converts 'bytes' type fields
// to base64. For example content ID '21140844' becomes 'MjExNDA4NDQ='. // to base64. For example content ID '21140844' becomes 'MjExNDA4NDQ='.
if (!MessageToJsonString(request, &request_json, print_options).ok()) { if (!MessageToJsonString(request, &request_json, print_options).ok()) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL,
"Failed to convert request message to json."); "Failed to convert request message to json.");
} }
LOG(INFO) << "Json CasEncryptionRequest: " << request_json; LOG(INFO) << "Json CasEncryptionRequest: " << request_json;
@@ -76,7 +75,7 @@ util::Status WvCasKeyFetcher::RequestEntitlementKey(
request_json, absl::HexStringToBytes(FLAGS_signing_key), request_json, absl::HexStringToBytes(FLAGS_signing_key),
absl::HexStringToBytes(FLAGS_signing_iv), &signature) absl::HexStringToBytes(FLAGS_signing_iv), &signature)
.ok()) { .ok()) {
return util::Status(util::error::INTERNAL, "Failed to sign the request."); return Status(error::INTERNAL, "Failed to sign the request.");
} }
signed_request.set_signature(signature); signed_request.set_signature(signature);
signed_request.set_signer(FLAGS_signing_provider); signed_request.set_signer(FLAGS_signing_provider);
@@ -85,23 +84,22 @@ util::Status WvCasKeyFetcher::RequestEntitlementKey(
// 'signature' fields in SignedCasEncryptionRequest to base64, because they // 'signature' fields in SignedCasEncryptionRequest to base64, because they
// are of type 'bytes'. // are of type 'bytes'.
if (!MessageToJsonString(signed_request, &signed_request_json).ok()) { if (!MessageToJsonString(signed_request, &signed_request_json).ok()) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL,
"Failed to convert signed request message to json."); "Failed to convert signed request message to json.");
} }
LOG(INFO) << "Json SignedCasEncryptionRequest: " << signed_request_json; LOG(INFO) << "Json SignedCasEncryptionRequest: " << signed_request_json;
// Makes HTTP request against License Server. // Makes HTTP request against License Server.
std::string http_response_json; std::string http_response_json;
util::Status status = Status status = MakeHttpRequest(signed_request_json, &http_response_json);
MakeHttpRequest(signed_request_json, &http_response_json);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }
LOG(INFO) << "Json HTTP response: " << http_response_json; LOG(INFO) << "Json HTTP response: " << http_response_json;
HttpResponse http_response; HttpResponse http_response;
if (!JsonStringToMessage(http_response_json, &http_response).ok()) { if (!JsonStringToMessage(http_response_json, &http_response).ok()) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL,
"Failed to convert http response json to message."); "Failed to convert http response json to message.");
} }
// Processes signed response. // Processes signed response.
@@ -110,13 +108,13 @@ util::Status WvCasKeyFetcher::RequestEntitlementKey(
LOG(INFO) << "Json CasEncryptionResponse: " << http_response.response(); LOG(INFO) << "Json CasEncryptionResponse: " << http_response.response();
CasEncryptionResponse response; CasEncryptionResponse response;
if (!JsonStringToMessage(http_response.response(), &response).ok()) { if (!JsonStringToMessage(http_response.response(), &response).ok()) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL,
"Failed to convert response json to message."); "Failed to convert response json to message.");
} }
SignedCasEncryptionResponse signed_response; SignedCasEncryptionResponse signed_response;
signed_response.set_response(response.SerializeAsString()); signed_response.set_response(response.SerializeAsString());
signed_response.SerializeToString(signed_response_string); signed_response.SerializeToString(signed_response_string);
return util::OkStatus(); return OkStatus();
} }
size_t AppendToString(void* ptr, size_t size, size_t count, std::string* output) { size_t AppendToString(void* ptr, size_t size, size_t count, std::string* output) {
@@ -125,12 +123,11 @@ size_t AppendToString(void* ptr, size_t size, size_t count, std::string* output)
return data.size(); return data.size();
} }
util::Status WvCasKeyFetcher::MakeHttpRequest( Status WvCasKeyFetcher::MakeHttpRequest(const std::string& signed_request_json,
const std::string& signed_request_json, std::string* http_response_json) const { std::string* http_response_json) const {
CHECK(http_response_json); CHECK(http_response_json);
if (FLAGS_license_server.empty()) { if (FLAGS_license_server.empty()) {
return util::Status(util::error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT, "Flag 'license_server' is empty");
"Flag 'license_server' is empty");
} }
CURL* curl; CURL* curl;
CURLcode curl_code; CURLcode curl_code;
@@ -145,15 +142,14 @@ util::Status WvCasKeyFetcher::MakeHttpRequest(
(int64_t)strlen(signed_request_json.c_str())); (int64_t)strlen(signed_request_json.c_str()));
curl_code = curl_easy_perform(curl); curl_code = curl_easy_perform(curl);
if (curl_code != CURLE_OK) { if (curl_code != CURLE_OK) {
return util::Status(util::error::INTERNAL, return Status(error::INTERNAL, "curl_easy_perform() failed: " +
"curl_easy_perform() failed: " + std::string(curl_easy_strerror(curl_code)));
std::string(curl_easy_strerror(curl_code)));
} }
curl_easy_cleanup(curl); curl_easy_cleanup(curl);
} else { } else {
return util::Status(util::error::INTERNAL, "curl_easy_init() failed"); return Status(error::INTERNAL, "curl_easy_init() failed");
} }
return util::OkStatus(); return OkStatus();
} }
} // namespace cas } // namespace cas

View File

@@ -12,7 +12,6 @@
#include <string> #include <string>
#include "gflags/gflags.h" #include "gflags/gflags.h"
#include "common/status.h"
#include "media_cas_packager_sdk/internal/key_fetcher.h" #include "media_cas_packager_sdk/internal/key_fetcher.h"
DECLARE_string(license_server); DECLARE_string(license_server);
@@ -41,15 +40,15 @@ class WvCasKeyFetcher : public KeyFetcher {
// |signed_response_string| a serialized SignedCasEncryptionResponse // |signed_response_string| a serialized SignedCasEncryptionResponse
// message. It should be passed into // message. It should be passed into
// widevine::cas::Ecm::ProcessCasEncryptionResponse(). // widevine::cas::Ecm::ProcessCasEncryptionResponse().
virtual util::Status RequestEntitlementKey(const std::string& request_string, Status RequestEntitlementKey(const std::string& request_string,
std::string* signed_response_string); std::string* signed_response_string) override;
protected: protected:
// Makes a HTTP request to License Server for entitlement key(s). // Makes a HTTP request to License Server for entitlement key(s).
// Returns the HTTP response in Json format in |http_response_json|. // Returns the HTTP response in Json format in |http_response_json|.
// Protected visibility to support unit testing. // Protected visibility to support unit testing.
virtual util::Status MakeHttpRequest(const std::string& signed_request_json, virtual Status MakeHttpRequest(const std::string& signed_request_json,
std::string* http_response_json) const; std::string* http_response_json) const;
}; };
} // namespace cas } // namespace cas

View File

@@ -14,7 +14,6 @@
#include "testing/gmock.h" #include "testing/gmock.h"
#include "testing/gunit.h" #include "testing/gunit.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "common/status.h"
#include "protos/public/media_cas_encryption.pb.h" #include "protos/public/media_cas_encryption.pb.h"
using testing::_; using testing::_;
@@ -55,9 +54,8 @@ class MockWvCasKeyFetcher : public WvCasKeyFetcher {
public: public:
MockWvCasKeyFetcher() : WvCasKeyFetcher() {} MockWvCasKeyFetcher() : WvCasKeyFetcher() {}
~MockWvCasKeyFetcher() override {} ~MockWvCasKeyFetcher() override {}
MOCK_CONST_METHOD2(MakeHttpRequest, MOCK_CONST_METHOD2(MakeHttpRequest, Status(const std::string& signed_request_json,
util::Status(const std::string& signed_request_json, std::string* http_response_json));
std::string* http_response_json));
}; };
class WvCasKeyFetcherTest : public ::testing::Test { class WvCasKeyFetcherTest : public ::testing::Test {
@@ -93,7 +91,7 @@ TEST_F(WvCasKeyFetcherTest, TestRequestEntitlementKey) {
EXPECT_CALL(mock_key_fetcher_, EXPECT_CALL(mock_key_fetcher_,
MakeHttpRequest(kSignedCasEncryptionRequest, _)) MakeHttpRequest(kSignedCasEncryptionRequest, _))
.WillOnce(DoAll(SetArgumentPointee<1>(std::string(kHttpResponse)), .WillOnce(DoAll(SetArgumentPointee<1>(std::string(kHttpResponse)),
Return(util::OkStatus()))); Return(OkStatus())));
std::string actual_signed_response; std::string actual_signed_response;
EXPECT_OK(mock_key_fetcher_.RequestEntitlementKey( EXPECT_OK(mock_key_fetcher_.RequestEntitlementKey(

View File

@@ -0,0 +1,190 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2018 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Example server that listens on a port for Simulcrypt API messages.
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <iostream>
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "absl/strings/str_cat.h"
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
static constexpr int32_t kDefaultDelayStart = 200;
static constexpr int32_t kDefaultDelayStop = 200;
static constexpr int32_t kDefaultEcmRepPeriod = 100;
static constexpr int32_t kDefaultMaxCompTime = 100;
static constexpr int32_t kAccessCriteriaTransferMode = 1;
DEFINE_int32(port, 0, "Server port number");
// ECMG related flags.
// TODO(user): Consider adding flags 'ac_delay_start', 'ac_delay_stop',
// 'transition_delay_start', 'transition_delay_stop'.
DEFINE_int32(delay_start, kDefaultDelayStart,
absl::StrCat("This flag sets the DVB SimulCrypt delay_start "
"parameter, in milliseconds. Default: ",
kDefaultDelayStart, " ms")
.c_str());
DEFINE_int32(delay_stop, kDefaultDelayStop,
absl::StrCat("This flag sets the DVB SimulCrypt delay_stop "
"parameter, in milliseconds. Default: ",
kDefaultDelayStop, " ms")
.c_str());
DEFINE_int32(ecm_rep_period, kDefaultEcmRepPeriod,
absl::StrCat("It sets the DVB SimulCrypt parameter "
"ECM_rep_period, in milliseconds. Default: ",
kDefaultEcmRepPeriod, " ms")
.c_str());
DEFINE_int32(max_comp_time, kDefaultMaxCompTime,
absl::StrCat("It sets the DVB SimulCrypt parameter max_comp_time, "
"in milliseconds. Default: ",
kDefaultMaxCompTime, " ms")
.c_str());
DEFINE_int32(access_criteria_transfer_mode, kAccessCriteriaTransferMode,
absl::StrCat("It sets the DVB SimulCrypt parameter "
"access_criteria_transfer_mode. Default: ",
kAccessCriteriaTransferMode)
.c_str());
#define LISTEN_QUEUE_SIZE (20)
#define BUFFER_SIZE (1024)
using widevine::cas::EcmgClientHandler;
using widevine::cas::EcmgConfig;
void BuildEcmgConfig(EcmgConfig* config) {
DCHECK(config);
config->delay_start = FLAGS_delay_start;
config->delay_stop = FLAGS_delay_stop;
config->ecm_rep_period = FLAGS_ecm_rep_period;
config->max_comp_time = FLAGS_max_comp_time;
config->access_criteria_transfer_mode = FLAGS_access_criteria_transfer_mode;
}
void PrintMessage(const std::string& description, const char* const message,
size_t length) {
LOG(INFO) << description;
for (size_t i = 0; i < length; i++) {
printf("'\\x%02x', ", static_cast<uint16_t>(*(message + i)));
fflush(stdout);
}
printf("\n");
fflush(stdout);
}
void ServeClient(int socket_fd, EcmgClientHandler* ecmg) {
DCHECK(ecmg);
char request[BUFFER_SIZE];
char response[BUFFER_SIZE];
while (true) {
bzero(request, BUFFER_SIZE);
bzero(response, BUFFER_SIZE);
size_t response_length = 0;
size_t request_length = recv(socket_fd, request, BUFFER_SIZE, 0);
if (request_length == 0) {
LOG(ERROR) << "No more request from client";
return;
}
if (request_length < 0) {
LOG(ERROR) << "Failed to receive request from client";
return;
}
PrintMessage("Request", request, request_length);
ecmg->HandleRequest(request, response, &response_length);
PrintMessage("Response", response, response_length);
if (send(socket_fd, response, response_length, 0) < 0) {
LOG(INFO) << "Failed to send response to client";
return;
}
}
}
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(FLAGS_port != 0) << "need --port";
EcmgConfig ecmg_config;
BuildEcmgConfig(&ecmg_config);
// Server address.
struct sockaddr_in server_address;
bzero(reinterpret_cast<char*>(&server_address), sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(FLAGS_port);
// Create a listening socket.
int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);
CHECK(listen_socket_fd >= 0) << "Failed to open listening socket";
// Set SO_REUSEADDR on a socket to true (1).
int optval = 1;
setsockopt(listen_socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval));
// Bind address.
CHECK(bind(listen_socket_fd, (struct sockaddr*)&server_address,
sizeof(server_address)) >= 0)
<< "Failed to bind on server socket";
// Listen for connection from clients.
std::cout << "Server listening ..." << std::endl << std::flush;
int return_val = listen(listen_socket_fd, LISTEN_QUEUE_SIZE);
switch (return_val) {
case EADDRINUSE:
LOG(FATAL) << "Another socket is already listening on the same port.";
break;
case EBADF:
LOG(FATAL) << "The argument sockfd is not a valid descriptor.";
break;
case ENOTSOCK:
LOG(FATAL) << "The argument sockfd is not a socket.";
break;
case EOPNOTSUPP:
LOG(FATAL) << "The socket is not of a type that supports the listen() "
"operation.";
default:
break;
}
// A single client handler, allow only 1 TCP connection / 1 channel at a time.
EcmgClientHandler client_handler(&ecmg_config);
// While loop to serve different client connections.
while (true) {
struct sockaddr_in client_address;
socklen_t client_address_size = sizeof(client_address);
int client_socket_fd = accept(
listen_socket_fd, reinterpret_cast<struct sockaddr*>(&client_address),
&client_address_size);
LOG(INFO) << "\nTCP connection start\n";
if (client_socket_fd < 0) {
LOG(ERROR) << "Failed to accept connection request from client";
} else {
// TODO(user): Support multi-threading of serving concurrent clients.
// TODO(user): Per jfore@ suggestion, look into using
// http://man7.org/linux/man-pages/man7/epoll.7.html
ServeClient(client_socket_fd, &client_handler);
}
LOG(INFO) << "\nTCP connection closed\n";
close(client_socket_fd);
usleep(1000);
}
// Close listening socket.
close(listen_socket_fd);
return 0;
}

View File

@@ -11,13 +11,6 @@ syntax = "proto2";
package widevine.cas; package widevine.cas;
// Encrypt/decrypt mode.
enum CasCryptoMode {
CRYPTO_MODE_UNSPECIFIED = 0;
CTR = 1;
CBC = 2;
};
// Widevine private data in the CA descriptor. // Widevine private data in the CA descriptor.
message CaDescriptorPrivateData { message CaDescriptorPrivateData {
// Provider name. // Provider name.

View File

@@ -11,9 +11,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#define EXPECT_OK(expression) \ #define EXPECT_OK(expression) EXPECT_EQ(error::OK, expression.error_code())
EXPECT_EQ(util::error::OK, expression.error_code()) #define ASSERT_OK(expression) ASSERT_EQ(error::OK, expression.error_code())
#define ASSERT_OK(expression) \
ASSERT_EQ(util::error::OK, expression.error_code())
#endif // TESTING_GUNIT_H_ #endif // TESTING_GUNIT_H_

View File

@@ -1,48 +0,0 @@
################################################################################
# Copyright 2018 Google LLC.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
# Build file for zlib.
package(default_visibility = ["//visibility:public"])
cc_library(
name = "zlib",
srcs = [
"adler32.c",
"compress.c",
"crc32.c",
"crc32.h",
"deflate.c",
"deflate.h",
"gzclose.c",
"gzguts.h",
"gzlib.c",
"gzread.c",
"gzwrite.c",
"infback.c",
"inffast.c",
"inffast.h",
"inffixed.h",
"inflate.c",
"inflate.h",
"inftrees.c",
"inftrees.h",
"trees.c",
"trees.h",
"uncompr.c",
"zconf.h",
"zutil.c",
"zutil.h",
],
hdrs = ["zlib.h"],
copts = [
"-Wno-shift-negative-value",
"-Wno-implicit-function-declaration",
],
includes = ["."],
)