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

View File

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

View File

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

View File

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

View File

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

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
// used to encrypt the information.
// |client_id| is owned by caller.
// Returns util::Status::OK, if successful, else an error.
util::Status DecryptEncryptedClientIdentification(
// Returns Status::OK, if successful, else an error.
Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id);
// Decrypts the encrypted client identification in |encrypted_client_id| into
// |client_id| using |privacy_key|.
// |client_id| is owned by caller.
// Returns util::Status::OK, if successful, else an error.
util::Status DecryptEncryptedClientIdentification(
// Returns Status::OK, if successful, else an error.
Status DecryptEncryptedClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
const std::string& privacy_key, ClientIdentification* client_id);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,6 +29,14 @@ std::string Sha256_Hash(const std::string& message) {
return digest;
}
std::string Sha512_Hash(const std::string& message) {
std::string digest;
digest.resize(SHA512_DIGEST_LENGTH);
SHA512(reinterpret_cast<const uint8_t*>(message.data()), message.size(),
reinterpret_cast<uint8_t*>(&digest[0]));
return digest;
}
std::string GenerateSha1Uuid(const std::string& name_space, const std::string& name) {
// X.667 14 Setting the fields of a name-based UUID.
// - Allocate a UUID to use as a "name space identifier" for all UUIDs

View File

@@ -21,6 +21,9 @@ std::string Sha1_Hash(const std::string& message);
// Calculates SHA256 hash.
std::string Sha256_Hash(const std::string& message);
// Calculate SHA512 hash.
std::string Sha512_Hash(const std::string& message);
// Generates a UUID as specified in ITU-T X.667 ch. 14, SHA-1 name-based,
// 16-byte binary representation. Name_space is a GUID prefix; name is a unique
// name in the namespace.

View File

@@ -48,6 +48,18 @@ TEST(ShaUtilTest, Sha256) {
Sha256_Hash("random data"));
}
TEST(ShaUtilTest, Sha512) {
const uint8_t kExpected[] = {
0x8f, 0x49, 0x93, 0x1f, 0x4d, 0x4a, 0x67, 0x6f, 0x9a, 0x7e, 0x62,
0x60, 0xea, 0xe1, 0x54, 0xfe, 0xe2, 0x75, 0x3c, 0xec, 0x3b, 0xb2,
0x2e, 0xd7, 0x51, 0xcc, 0x39, 0xf9, 0x89, 0x69, 0xad, 0xd0, 0x14,
0xaa, 0xbe, 0x40, 0xce, 0xe3, 0xab, 0xef, 0x10, 0x2f, 0x24, 0x0e,
0xd8, 0x26, 0x7b, 0xb5, 0x7d, 0x86, 0xce, 0xbd, 0xd7, 0x32, 0x86,
0x3a, 0x5e, 0x9e, 0x8d, 0x23, 0x77, 0x10, 0x80, 0x0c};
EXPECT_EQ(std::string(kExpected, kExpected + sizeof(kExpected)),
Sha512_Hash("random data"));
}
TEST(ShaUtilTest, GenerateSha1Uuid) {
std::string name_space =
absl::HexStringToBytes("4d20ad7dd95bc4b250fae56fb143e774");

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,14 +30,14 @@ std::string GenerateDerivedKey(widevine::ProtocolVersion protocol_version) {
}
} // namespace
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_0) {
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_0) {
ASSERT_EQ(crypto_util::kSigningKeySizeBits,
SigningKeyMaterialSize(VERSION_2_0));
SigningKeyMaterialSizeBits(VERSION_2_0));
}
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeProtocolVersion_2_1) {
TEST(DerivedKeyUtilTest, SigningKeyMaterialSizeBitsProtocolVersion_2_1) {
ASSERT_EQ(crypto_util::kSigningKeySizeBits * 2,
SigningKeyMaterialSize(VERSION_2_1));
SigningKeyMaterialSizeBits(VERSION_2_1));
}
TEST(DerivedKeyUtilTest, GetServerSigningKeyProtocolVersion2_1) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,8 +16,8 @@
#include <memory>
#include <string>
#include "common/status.h"
#include "common/certificate_type.h"
#include "common/status.h"
namespace widevine {
class X509CA;
@@ -35,10 +35,10 @@ class VmpChecker {
static VmpChecker* Instance();
// Select the type of root to use. Not thread-safe.
virtual util::Status SelectCertificateType(CertificateType cert_type);
virtual Status SelectCertificateType(CertificateType cert_type);
// Verify VMP data and return appropriate result.
virtual util::Status VerifyVmpData(const std::string& vmp_data, Result* result);
virtual Status VerifyVmpData(const std::string& vmp_data, Result* result);
// Enable/disable development code signing certificates.
void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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(
name = "binary_release_files",
srcs = [
"test_ecmg_messages.h",
"wv_cas_ecm_example.cc",
":wv_cas_ecm_example",
],
@@ -26,20 +27,9 @@ cc_library(
hdrs = ["constants.h"],
)
cc_binary(
name = "simulcrypt_client",
srcs = ["simulcrypt_client.cc"],
deps = [
"//base",
],
)
cc_library(
name = "test_simulcrypt_messages",
hdrs = ["test_simulcrypt_messages.h"],
deps = [
"//base",
],
name = "test_ecmg_messages",
hdrs = ["test_ecmg_messages.h"],
)
cc_binary(
@@ -57,7 +47,7 @@ cc_binary(
srcs = ["wv_cas_key_fetcher_example.cc"],
deps = [
"//base",
"//util:status",
"//common:status",
"//media_cas_packager_sdk/public:wv_cas_key_fetcher",
"//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_string(track_type, "SD", "Provider name");
namespace util = widevine::util;
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(!FLAGS_content_id.empty() && !FLAGS_track_type.empty())
@@ -47,7 +45,7 @@ int main(int argc, char **argv) {
std::string signed_response_str;
widevine::cas::WvCasKeyFetcher key_fetcher;
util::Status status =
widevine::Status status =
key_fetcher.RequestEntitlementKey(request_str, &signed_response_str);
if (!status.ok()) {
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",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"//common:status",
"//common:aes_cbc_util",
"//common:random_util",
"//common:status",
"//common:string_util",
"//media_cas_packager_sdk/public:wv_cas_types",
"//protos/public:media_cas_encryption_proto",
@@ -76,27 +76,42 @@ cc_test(
)
cc_library(
name = "ecmg",
srcs = ["ecmg.cc"],
name = "ecmg_client_handler",
srcs = ["ecmg_client_handler.cc"],
hdrs = [
"ecmg.h",
"ecmg_client_handler.h",
"ecmg_constants.h",
],
deps = [
":ecm",
":ecm_generator",
":fixed_key_fetcher",
":mpeg2ts",
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/memory",
"@abseil_repo//absl/strings",
"//common:crypto_util",
"//common:status",
"//example:constants",
"//media_cas_packager_sdk/public:wv_cas_ecm",
"//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(
name = "fixed_key_fetcher",
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(
name = "ts_packet",
srcs = [

View File

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

View File

@@ -91,10 +91,10 @@ class CasEcm {
// Notes:
// The returned |key_request_message| must be sent to the server and
// the response correctly parsed before ECMs can be generated.
virtual util::Status Initialize(const std::string& content_id,
const std::string& content_provider,
const EcmInitParameters& ecm_init_parameters,
std::string* key_request_message);
virtual Status Initialize(const std::string& content_id,
const std::string& content_provider,
const EcmInitParameters& ecm_init_parameters,
std::string* key_request_message);
// Parse a CasEncryptionResponse message holding the entitlement keys for
// generating the ECM stream. The entitlement keys are used to encrypt the
@@ -102,7 +102,7 @@ class CasEcm {
// Args:
// |response| a serialized CasEncryptionRequest message from the server
// 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
// Stream packet payload (184 bytes).
@@ -118,10 +118,9 @@ class CasEcm {
// entitlement key. Wrapping modifies the original structure.
// Generation is a mod 32 counter. If the ECM has any changes from the
// previous ECM, the generation is increased by one.
virtual util::Status GenerateEcm(EntitledKeyInfo* even_key,
EntitledKeyInfo* odd_key,
const std::string& track_type,
std::string* serialized_ecm, uint32_t* generation);
virtual Status GenerateEcm(EntitledKeyInfo* even_key,
EntitledKeyInfo* odd_key, const std::string& track_type,
std::string* serialized_ecm, uint32_t* generation);
// 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
@@ -135,10 +134,10 @@ class CasEcm {
// with the initialized settings.
// Generation is a mod 32 counter. If the ECM has any changes from the
// previous ECM, the generation is increased by one.
virtual util::Status GenerateSingleKeyEcm(EntitledKeyInfo* key,
const std::string& track_type,
std::string* serialized_ecm,
uint32_t* generation);
virtual Status GenerateSingleKeyEcm(EntitledKeyInfo* key,
const std::string& track_type,
std::string* serialized_ecm,
uint32_t* generation);
protected: // For unit tests.
// 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
// of the |EcmInitParameters::track_types| strings passed into Initialize().
// |key| the Entitled Key to be wrapped.
virtual util::Status WrapEntitledKeys(
const std::string& track_type, const std::vector<EntitledKeyInfo*> keys);
virtual Status WrapEntitledKeys(const std::string& track_type,
const std::vector<EntitledKeyInfo*> keys);
private:
// 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()
virtual util::Status GenerateEcmCommon(
const std::vector<EntitledKeyInfo*>& keys, const std::string& track_type,
std::string* serialized_ecm, uint32_t* generation);
virtual Status GenerateEcmCommon(const std::vector<EntitledKeyInfo*>& keys,
const std::string& track_type,
std::string* serialized_ecm, uint32_t* generation);
// Wrap |key_value| using |wrapping_key| (entitlement key) and |wrapping_iv|.
// Returns the resulting wrapped key in |wrapped_key|.
// Return a status indicating whether there has been any error.
virtual util::Status WrapKey(const std::string& wrapping_key,
const std::string& wrapping_iv,
const std::string& key_value, std::string* wrapped_key);
virtual Status WrapKey(const std::string& wrapping_key, const std::string& wrapping_iv,
const std::string& key_value, std::string* wrapped_key);
virtual util::Status ValidateKeys(const std::vector<EntitledKeyInfo*>& keys);
virtual util::Status ValidateWrappedKeys(
const std::vector<EntitledKeyInfo*>& keys);
virtual Status ValidateKeys(const std::vector<EntitledKeyInfo*>& keys);
virtual Status ValidateWrappedKeys(const std::vector<EntitledKeyInfo*>& keys);
util::Status ValidateKeyId(const std::string& key_id);
util::Status ValidateKeyValue(const std::string& key_value, size_t key_value_size);
util::Status ValidateIv(const std::string& iv, size_t size);
Status ValidateKeyId(const std::string& key_id);
Status ValidateKeyValue(const std::string& key_value, size_t key_value_size);
Status ValidateIv(const std::string& iv, size_t size);
// 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.
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 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::vector<EntitledKeyInfo> keys;
util::Status status = ProcessEcmParameters(params, &keys);
Status status = ProcessEcmParameters(params, &keys);
if (!status.ok() || !initialized_) {
LOG(ERROR) << " EcmParameters is not set up properly: " << status;
return "";
@@ -43,7 +43,7 @@ std::string CasEcmGenerator::GenerateEcm(const EcmParameters& params) {
return serialized_ecm;
}
util::Status CasEcmGenerator::ProcessEcmParameters(
Status CasEcmGenerator::ProcessEcmParameters(
const EcmParameters& ecm_params, std::vector<EntitledKeyInfo>* keys) {
initialized_ = false;
rotation_enabled_ = ecm_params.rotation_enabled;
@@ -52,11 +52,11 @@ util::Status CasEcmGenerator::ProcessEcmParameters(
keys->clear();
uint32_t keys_needed = ecm_params.rotation_enabled ? 2 : 1;
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)."};
}
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()) {
return status;
}
@@ -70,80 +70,77 @@ util::Status CasEcmGenerator::ProcessEcmParameters(
current_key_index_ = 0;
current_key_even_ = 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()) {
return {util::error::INVALID_ARGUMENT, "Key id is empty."};
return {error::INVALID_ARGUMENT, "Key id is empty."};
}
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()) {
return {util::error::INVALID_ARGUMENT, "Key data is empty."};
return {error::INVALID_ARGUMENT, "Key data is empty."};
}
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,
size_t required_size) {
Status CasEcmGenerator::ValidateIv(const std::string& iv, size_t required_size) {
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) {
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) {
return {util::error::INVALID_ARGUMENT,
"IV has wrong or inconsistent size."};
return {error::INVALID_ARGUMENT, "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.
util::Status status = ValidateIv(iv, kIvSize16);
Status status = ValidateIv(iv, kIvSize16);
if (!status.ok()) {
LOG(ERROR) << " Wrapped key IV is not valid: " << 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
// this stream.
if (content_iv_size_ == 0) {
content_iv_size_ = iv.size();
}
util::Status status = ValidateIv(iv, content_iv_size_);
Status status = ValidateIv(iv, content_iv_size_);
if (!status.ok()) {
LOG(ERROR) << " Content IV is not valid: " << status;
}
return status;
}
util::Status CasEcmGenerator::ValidateKeyParameters(
const KeyParameters& key_params) {
util::Status status;
Status CasEcmGenerator::ValidateKeyParameters(const KeyParameters& key_params) {
Status status;
status = ValidateKeyId(key_params.key_id);
if (!status.ok()) return status;
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++) {
status = ValidateContentIv(key_params.content_ivs[i]);
if (!status.ok()) return status;
}
return util::OkStatus();
return OkStatus();
}
} // namespace cas

View File

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

View File

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

View File

@@ -68,14 +68,14 @@ class MockCasEcm : public CasEcm {
return SerializeEcm(keys);
}
virtual util::Status MockWrapEntitledKeys(
virtual Status MockWrapEntitledKeys(
const std::string& track_type, const std::vector<EntitledKeyInfo*>& keys) {
for (auto entitled_key : keys) {
entitled_key->entitlement_key_id = "entitlement_Mock";
entitled_key->wrapped_key_value = "MockMockMockMock";
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,
@@ -226,7 +226,7 @@ TEST_F(CasEcmTest, GenerateEcmNotInitialized) {
EntitledKeyInfo key1;
std::string ecm_data;
uint32_t gen;
EXPECT_EQ(util::error::INTERNAL,
EXPECT_EQ(error::INTERNAL,
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm_data, &gen)
.error_code());
}
@@ -237,7 +237,7 @@ TEST_F(CasEcmTest, GenerateEcm2NotInitialized) {
EntitledKeyInfo key2;
std::string ecm_data;
uint32_t gen;
EXPECT_EQ(util::error::INTERNAL,
EXPECT_EQ(error::INTERNAL,
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm_data, &gen)
.error_code());
}
@@ -245,7 +245,7 @@ TEST_F(CasEcmTest, GenerateEcm2NotInitialized) {
TEST_F(CasEcmTest, SetResponseNotInitialized) {
CasEcm ecm_gen;
const std::string response("anything");
EXPECT_EQ(util::error::INTERNAL,
EXPECT_EQ(error::INTERNAL,
ecm_gen.ProcessCasEncryptionResponse(response).error_code());
}
@@ -253,7 +253,7 @@ TEST_F(CasEcmTest, InitNoTracksFail) {
CasEcm ecm_gen;
std::string request;
EXPECT_EQ(
util::error::INVALID_ARGUMENT,
error::INVALID_ARGUMENT,
ecm_gen.Initialize(content_id_, provider_, params_default_, &request)
.error_code());
}
@@ -270,7 +270,7 @@ TEST_F(CasEcmTest, SecondInitFail) {
std::string request;
ASSERT_OK(
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)
.error_code());
}
@@ -280,7 +280,7 @@ TEST_F(CasEcmTest, InitEmptyContentIdFail) {
const std::string empty_content_id;
std::string request;
EXPECT_EQ(
util::error::INVALID_ARGUMENT,
error::INVALID_ARGUMENT,
ecm_gen.Initialize(empty_content_id, provider_, params_simple_, &request)
.error_code());
}
@@ -290,7 +290,7 @@ TEST_F(CasEcmTest, InitEmptyProviderFail) {
const std::string empty_provider;
std::string request;
EXPECT_EQ(
util::error::INVALID_ARGUMENT,
error::INVALID_ARGUMENT,
ecm_gen.Initialize(content_id_, empty_provider, params_simple_, &request)
.error_code());
}
@@ -315,7 +315,7 @@ TEST_F(CasEcmTest, GenerateWithNoEntitlementOneKeyFail) {
uint32_t gen;
// This will fail because the entitlement key has not been acquired
// (via server call and ProcessCasEncryptionResponse()).
EXPECT_EQ(util::error::INTERNAL,
EXPECT_EQ(error::INTERNAL,
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)
.error_code());
}
@@ -334,13 +334,13 @@ TEST_F(CasEcmTest, GenerateWithNoEntitlementTwoKeysFail) {
// This will fail because the entitlement keys have not been acquired
// (via server call and ProcessCasEncryptionResponse()).
EXPECT_EQ(
util::error::INTERNAL,
error::INTERNAL,
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
}
TEST_F(CasEcmTest, RequestNullFail) {
CasEcm ecm_gen;
EXPECT_EQ(util::error::INVALID_ARGUMENT,
EXPECT_EQ(error::INVALID_ARGUMENT,
ecm_gen.Initialize(content_id_, provider_, params_simple_, nullptr)
.error_code());
}
@@ -374,7 +374,7 @@ TEST_F(CasEcmTest, BadResponseFail) {
std::string response;
ServerCall(request, &response, false, true);
EXPECT_EQ(util::error::INTERNAL,
EXPECT_EQ(error::INTERNAL,
ecm_gen.ProcessCasEncryptionResponse(response).error_code());
}
@@ -411,7 +411,7 @@ TEST_F(CasEcmTest, GenerateOneKeyNoGenFail) {
ASSERT_OK(ecm_gen.ProcessCasEncryptionResponse(response));
std::string ecm;
EXPECT_EQ(util::error::INVALID_ARGUMENT,
EXPECT_EQ(error::INVALID_ARGUMENT,
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, nullptr)
.error_code());
}
@@ -431,7 +431,7 @@ TEST_F(CasEcmTest, GenerateOneKeyBadKeyIdFail) {
std::string ecm;
uint32_t gen;
EXPECT_EQ(util::error::INVALID_ARGUMENT,
EXPECT_EQ(error::INVALID_ARGUMENT,
ecm_gen.GenerateSingleKeyEcm(&key1, kTrackTypeSD, &ecm, &gen)
.error_code());
}
@@ -455,7 +455,7 @@ TEST_F(CasEcmTest, GenerateOneKeyWrong) {
EXPECT_THAT(0, gen);
EXPECT_THAT(77, ecm.size());
EXPECT_EQ(
util::error::INVALID_ARGUMENT,
error::INVALID_ARGUMENT,
ecm_gen.GenerateEcm(&key1, &key1, kTrackTypeSD, &ecm, &gen).error_code());
}
@@ -477,7 +477,7 @@ TEST_F(CasEcmTest, GenerateTwoKeysIvSizeFail) {
std::string ecm;
uint32_t gen;
EXPECT_EQ(
util::error::INVALID_ARGUMENT,
error::INVALID_ARGUMENT,
ecm_gen.GenerateEcm(&key1, &key2, kTrackTypeSD, &ecm, &gen).error_code());
}
@@ -548,7 +548,7 @@ TEST_F(CasEcmTest, GenerateThreeKeysIvSize16x16Fail) {
std::string ecm;
uint32_t gen;
EXPECT_EQ(
util::error::INTERNAL,
error::INTERNAL,
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_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
// ECMG <> SCS
// Parameter_type values
#define DVB_RESERVED 0x0000
#define SUPER_CAS_ID 0x0001
#define SECTION_TSPKT_FLAG 0x0002
#define DELAY_START 0x0003
#define DELAY_STOP 0x0004
#define TRANSITION_DELAY_START 0x0005
#define TRANSITION_DELAY_STOP 0x0006
#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 <=> SCS protocol_version.
#define ECMG_SCS_PROTOCOL_VERSION (0x03)
// ECMG 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)
// 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)
// 0x0119 - 0x0200 DVB reserved.
#define ECMG_CW_PROVISION (0x0201)
#define ECMG_ECM_RESPONSE (0x0202)
// ECMG parameter type values.
#define DVB_RESERVED (0x0000)
#define SUPER_CAS_ID (0x0001)
#define SECTION_TSPKT_FLAG (0x0002)
#define DELAY_START (0x0003)
#define DELAY_STOP (0x0004)
#define TRANSITION_DELAY_START (0x0005)
#define TRANSITION_DELAY_STOP (0x0006)
#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.
#define PARAMETER_TYPE_SIZE 2
#define PARAMETER_LENGTH_SIZE 2
#define ECM_CHANNEL_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
#define PROTOCOL_VERSION_SIZE (1)
#define MESSAGE_TYPE_SIZE (2)
#define MESSAGE_LENGTH_SIZE (2)
#define PARAMETER_TYPE_SIZE (2)
#define PARAMETER_LENGTH_SIZE (2)
#define SUPER_CAS_ID_SIZE (4)
#define ECM_CHANNEL_ID_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_

View File

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

View File

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

View File

@@ -33,8 +33,8 @@ class KeyFetcher {
// |signed_response_string| a serialized SignedCasEncryptionResponse
// message. It should be passed into
// WvCasEcm::ProcessCasEncryptionResponse().
virtual util::Status RequestEntitlementKey(
const std::string& request_string, std::string* signed_response_string) = 0;
virtual Status RequestEntitlementKey(const std::string& request_string,
std::string* signed_response_string) = 0;
};
} // 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 cas {
util::Status TsPacket::Write(std::string* output) const {
Status TsPacket::Write(std::string* output) const {
DCHECK(output);
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<4> continuity_counter(continuity_counter_);
if (adaptation_field_control_ & kAdaptationFieldOnly) {
return util::Status(util::error::INTERNAL,
"TsPacket does NOT handle adaptation field yet");
return Status(error::INTERNAL,
"TsPacket does NOT handle adaptation field yet");
}
// 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(),
adaptation_field_control.to_string(), continuity_counter.to_string());
if (header_bitset.size() != 4 * 8) {
return util::Status(util::error::INTERNAL,
absl::StrCat("TS packet header bitset incorret size: ",
header_bitset.size()));
return Status(error::INTERNAL,
absl::StrCat("TS packet header bitset incorret size: ",
header_bitset.size()));
}
std::string serialized_header;
util::Status status = string_util::BitsetStringToBinaryString(
header_bitset, &serialized_header);
Status status = string_util::BitsetStringToBinaryString(header_bitset,
&serialized_header);
if (!status.ok()) {
return util::Status(util::error::INTERNAL,
"Failed to convert TS packet header bitset to std::string");
return Status(error::INTERNAL,
"Failed to convert TS packet header bitset to std::string");
}
// Write TsPacket payload.
if (payload_.size() != CalculatePayloadSize()) {
return util::Status(
util::error::INVALID_ARGUMENT,
absl::StrCat("Incorrect payload size: ", payload_.size()));
return Status(error::INVALID_ARGUMENT,
absl::StrCat("Incorrect payload size: ", payload_.size()));
}
// Return header + payload as a TS packet.
*output = serialized_header + payload_;
return util::OkStatus();
return OkStatus();
}
int32_t TsPacket::CalculatePayloadSize() const {

View File

@@ -63,7 +63,7 @@ class TsPacket {
virtual ~TsPacket() = default;
// 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.
int32_t CalculatePayloadSize() const;

View File

@@ -9,8 +9,8 @@
#include "media_cas_packager_sdk/internal/util.h"
#include <netinet/in.h>
#include <stddef.h>
#include <string.h>
#include <cstddef>
#include <cstring>
#include "glog/logging.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) {
DCHECK(destination);
DCHECK(source);
uint16_t big_endian_number;
uint16_t big_endian_number = 0;
memcpy(&big_endian_number, source, 2);
*destination = ntohs(big_endian_number);
}
util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
uint8_t table_id, ContinuityCounter* cc,
uint8_t* buffer, ssize_t* bytes_modified) {
void BigEndianToHost32(uint32_t* destination, const void* source) {
DCHECK(destination);
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(buffer);
DCHECK(bytes_modified);
@@ -75,7 +97,7 @@ util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
// And write the 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()) {
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());
*bytes_modified += ecm_ts_packet.size();
return util::OkStatus();
return OkStatus();
}
} // namespace cas

View File

@@ -24,6 +24,16 @@ namespace cas {
// the result in |destination|.
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.
// Args:
// - |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.
// |bytes_modified| will be incremented by 188 if insertion of ECM into
// |buffer| is successful.
util::Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid,
uint8_t table_id, ContinuityCounter* cc,
uint8_t* buffer, ssize_t* bytes_modified);
Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, uint8_t table_id,
ContinuityCounter* cc, uint8_t* buffer,
ssize_t* bytes_modified);
} // namespace cas
} // namespace widevine

View File

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

View File

@@ -20,7 +20,9 @@ PUBLIC_COPTS = ["-fvisibility=default"]
filegroup(
name = "binary_release_files",
srcs = glob(["*.h"]),
srcs = glob(["*.h"]) + [
":wv_ecmg",
],
)
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(
name = "wv_cas_ca_descriptor",
srcs = ["wv_cas_ca_descriptor.cc"],
@@ -97,8 +90,8 @@ cc_library(
"@abseil_repo//absl/base:core_headers", # buildcleaner: keep
"@abseil_repo//absl/memory", # buildcleaner: keep
"@abseil_repo//absl/strings", # buildcleaner: keep
"//common:status",
"//common:crypto_util",
"//common:status",
"//example:constants",
"//media_cas_packager_sdk/internal:ecm",
"//media_cas_packager_sdk/internal:ecm_generator",
@@ -134,7 +127,6 @@ cc_library(
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"@curl_repo//:curl",
"//common:status",
"//common:signature_util",
"//media_cas_packager_sdk/internal:key_fetcher",
"//protos/public:media_cas_encryption_proto",
@@ -153,7 +145,6 @@ cc_test(
"//external:protobuf",
"//testing:gunit_main",
"@abseil_repo//absl/strings",
"//common:status",
"//protos/public:media_cas_encryption_proto",
],
)
@@ -175,3 +166,14 @@ cc_test(
"//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;
}
std::string descriptor;
util::Status status =
Status status =
string_util::BitsetStringToBinaryString(descriptor_bitset, &descriptor);
*serialized_ca_desc = descriptor;

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,6 @@
#include "testing/gmock.h"
#include "testing/gunit.h"
#include "absl/strings/escaping.h"
#include "common/status.h"
#include "protos/public/media_cas_encryption.pb.h"
using testing::_;
@@ -55,9 +54,8 @@ class MockWvCasKeyFetcher : public WvCasKeyFetcher {
public:
MockWvCasKeyFetcher() : WvCasKeyFetcher() {}
~MockWvCasKeyFetcher() override {}
MOCK_CONST_METHOD2(MakeHttpRequest,
util::Status(const std::string& signed_request_json,
std::string* http_response_json));
MOCK_CONST_METHOD2(MakeHttpRequest, Status(const std::string& signed_request_json,
std::string* http_response_json));
};
class WvCasKeyFetcherTest : public ::testing::Test {
@@ -93,7 +91,7 @@ TEST_F(WvCasKeyFetcherTest, TestRequestEntitlementKey) {
EXPECT_CALL(mock_key_fetcher_,
MakeHttpRequest(kSignedCasEncryptionRequest, _))
.WillOnce(DoAll(SetArgumentPointee<1>(std::string(kHttpResponse)),
Return(util::OkStatus())));
Return(OkStatus())));
std::string actual_signed_response;
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;
// Encrypt/decrypt mode.
enum CasCryptoMode {
CRYPTO_MODE_UNSPECIFIED = 0;
CTR = 1;
CBC = 2;
};
// Widevine private data in the CA descriptor.
message CaDescriptorPrivateData {
// Provider name.

View File

@@ -11,9 +11,7 @@
#include <gtest/gtest.h>
#define EXPECT_OK(expression) \
EXPECT_EQ(util::error::OK, expression.error_code())
#define ASSERT_OK(expression) \
ASSERT_EQ(util::error::OK, expression.error_code())
#define EXPECT_OK(expression) EXPECT_EQ(error::OK, expression.error_code())
#define ASSERT_OK(expression) ASSERT_EQ(error::OK, expression.error_code())
#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 = ["."],
)