Specify widevine/media_cas_packager_sdk/presubmit in media_cas_packager_sdk METADATA file.
------------- Moves ecm_generator to media_cas_packager_sdk/internal. ------------- Add a simple TCP server listening on a port. My intention is to use this server to support the Simulcrypt APIs (TODO). Also add a simple TCP client binary for testing the server and also demo how to call the Simulcrypt APIs (TODO). ------------- If only a single key is in the ECM, it is the EVEN key. To make the code matches this understanding, change a parameter from 'false' to 'true'. But this change has NO impact on the produced ECM, regardless this parameter is 'false' or 'true' (i.e., whether using push_front or push_back), only a single key is in the ECM. ------------- Add classes that process Simulcrypt ECMG messages 1) Stream_set-up 2) CW_provision ------------- Renames server and client binaries. ------------- Make ecmg call ecm_generator to generate ecm. The return of the ecm to Simulcrypt caller will be implemented in the next CL. For now, using the 'key' (control word) in CW_provision message also as the 'key_id'. ------------- Move common folder ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=217358698
This commit is contained in:
1
BUILD
1
BUILD
@@ -15,6 +15,7 @@ pkg_tar(
|
|||||||
strip_prefix = "/",
|
strip_prefix = "/",
|
||||||
files = [
|
files = [
|
||||||
"//media_cas_packager_sdk/public:binary_release_files",
|
"//media_cas_packager_sdk/public:binary_release_files",
|
||||||
|
"//media_cas_packager_sdk/example:binary_release_files",
|
||||||
],
|
],
|
||||||
mode = "0644",
|
mode = "0644",
|
||||||
)
|
)
|
||||||
|
|||||||
31
common/BUILD
31
common/BUILD
@@ -1,16 +1,16 @@
|
|||||||
################################################################################
|
################################################################################
|
||||||
# Copyright 2016 Google LLC.
|
# Copyright 2017 Google LLC.
|
||||||
#
|
#
|
||||||
# This software is licensed under the terms defined in the Widevine Master
|
# This software is licensed under the terms defined in the Widevine Master
|
||||||
# License Agreement. For a copy of this agreement, please contact
|
# License Agreement. For a copy of this agreement, please contact
|
||||||
# widevine-licensing@google.com.
|
# widevine-licensing@google.com.
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
#
|
#
|
||||||
# Description:
|
# Constants, data structures, util classes for Widevine libraries.
|
||||||
# Build file for code common to multiple Widevine services.
|
|
||||||
|
|
||||||
package(default_visibility = ["//visibility:public"])
|
package(
|
||||||
|
default_visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "binary_release_files",
|
name = "binary_release_files",
|
||||||
@@ -19,6 +19,13 @@ filegroup(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "widevine_system_id",
|
||||||
|
srcs = ["widevine_system_id.cc"],
|
||||||
|
hdrs = ["widevine_system_id.h"],
|
||||||
|
deps = ["//base"],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "certificate_type",
|
name = "certificate_type",
|
||||||
hdrs = ["certificate_type.h"],
|
hdrs = ["certificate_type.h"],
|
||||||
@@ -48,10 +55,10 @@ cc_test(
|
|||||||
srcs = ["drm_root_certificate_test.cc"],
|
srcs = ["drm_root_certificate_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
":drm_root_certificate",
|
":drm_root_certificate",
|
||||||
|
":rsa_key",
|
||||||
|
":rsa_test_keys",
|
||||||
"//base",
|
"//base",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"//common:rsa_key",
|
|
||||||
"//common:rsa_test_keys",
|
|
||||||
"//protos/public:drm_certificate_proto",
|
"//protos/public:drm_certificate_proto",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:signed_drm_certificate_proto",
|
"//protos/public:signed_drm_certificate_proto",
|
||||||
@@ -64,9 +71,12 @@ cc_library(
|
|||||||
hdrs = ["certificate_util.h"],
|
hdrs = ["certificate_util.h"],
|
||||||
deps = [
|
deps = [
|
||||||
":certificate_type",
|
":certificate_type",
|
||||||
|
":drm_root_certificate",
|
||||||
":drm_service_certificate",
|
":drm_service_certificate",
|
||||||
":verified_media_pipeline",
|
":verified_media_pipeline",
|
||||||
":vmp_checker",
|
":vmp_checker",
|
||||||
|
"//base",
|
||||||
|
"//util:status",
|
||||||
"//license_server_sdk/internal:sdk",
|
"//license_server_sdk/internal:sdk",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -105,6 +115,7 @@ cc_test(
|
|||||||
deps = [
|
deps = [
|
||||||
":rsa_test_keys",
|
":rsa_test_keys",
|
||||||
":rsa_util",
|
":rsa_util",
|
||||||
|
"//base",
|
||||||
"//testing:gunit",
|
"//testing:gunit",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
"//external:openssl",
|
"//external:openssl",
|
||||||
@@ -139,6 +150,7 @@ cc_test(
|
|||||||
deps = [
|
deps = [
|
||||||
":rsa_key",
|
":rsa_key",
|
||||||
":rsa_test_keys",
|
":rsa_test_keys",
|
||||||
|
":rsa_util",
|
||||||
"//testing:gunit",
|
"//testing:gunit",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
],
|
],
|
||||||
@@ -320,6 +332,7 @@ cc_test(
|
|||||||
size = "small",
|
size = "small",
|
||||||
srcs = ["signing_key_util_test.cc"],
|
srcs = ["signing_key_util_test.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":crypto_util",
|
||||||
":signing_key_util",
|
":signing_key_util",
|
||||||
"//testing:gunit",
|
"//testing:gunit",
|
||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
@@ -519,9 +532,10 @@ cc_library(
|
|||||||
deps = [
|
deps = [
|
||||||
":certificate_type",
|
":certificate_type",
|
||||||
":error_space",
|
":error_space",
|
||||||
|
":rsa_key",
|
||||||
":x509_cert",
|
":x509_cert",
|
||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/strings",
|
"//util:status",
|
||||||
"//protos/public:errors_proto",
|
"//protos/public:errors_proto",
|
||||||
"//protos/public:verified_media_pipeline_proto",
|
"//protos/public:verified_media_pipeline_proto",
|
||||||
],
|
],
|
||||||
@@ -561,4 +575,3 @@ cc_test(
|
|||||||
"//testing:gunit_main",
|
"//testing:gunit_main",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/aes_cbc_util.h"
|
#include "common/aes_cbc_util.h"
|
||||||
|
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "common/certificate_util.h"
|
#include "common/certificate_util.h"
|
||||||
|
|
||||||
|
#include "common/certificate_type.h"
|
||||||
#include "common/drm_root_certificate.h"
|
#include "common/drm_root_certificate.h"
|
||||||
#include "common/drm_service_certificate.h"
|
#include "common/drm_service_certificate.h"
|
||||||
#include "common/verified_media_pipeline.h"
|
#include "common/verified_media_pipeline.h"
|
||||||
|
|||||||
@@ -10,14 +10,13 @@
|
|||||||
|
|
||||||
#include "common/crypto_util.h"
|
#include "common/crypto_util.h"
|
||||||
|
|
||||||
#include <openssl/hmac.h>
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "openssl/aes.h"
|
#include "openssl/aes.h"
|
||||||
#include "openssl/cmac.h"
|
#include "openssl/cmac.h"
|
||||||
#include "openssl/evp.h"
|
#include "openssl/evp.h"
|
||||||
|
#include "openssl/hmac.h"
|
||||||
|
#include "openssl/sha.h"
|
||||||
#include "util/endian/endian.h"
|
#include "util/endian/endian.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
// Unit tests for the crypto_util helper functions.
|
// Unit tests for the crypto_util helper functions.
|
||||||
|
|
||||||
#include "common/crypto_util.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
@@ -17,6 +15,7 @@
|
|||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
|
#include "common/crypto_util.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
namespace crypto_util {
|
namespace crypto_util {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "openssl/sha.h"
|
#include "openssl/sha.h"
|
||||||
|
#include "common/certificate_type.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "protos/public/drm_certificate.pb.h"
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
// Root device certificate holder class which deserializes, validates,
|
// Root device certificate holder class which deserializes, validates,
|
||||||
// and extracts the root certificate public key.
|
// and extracts the root certificate public key.
|
||||||
|
|
||||||
#ifndef COMMON_DRM_ROOT_CERTIFICATE_H__
|
#ifndef COMMON_DRM_ROOT_CERTIFICATE_H_
|
||||||
#define COMMON_DRM_ROOT_CERTIFICATE_H__
|
#define COMMON_DRM_ROOT_CERTIFICATE_H_
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -63,4 +63,4 @@ class DrmRootCertificate {
|
|||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // COMMON_DRM_ROOT_CERTIFICATE_H__
|
#endif // COMMON_DRM_ROOT_CERTIFICATE_H_
|
||||||
|
|||||||
@@ -9,11 +9,10 @@
|
|||||||
// Description:
|
// Description:
|
||||||
// Unit tests for drm_root_certificate.cc
|
// Unit tests for drm_root_certificate.cc
|
||||||
|
|
||||||
#include "common/drm_root_certificate.h"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
|
#include "common/drm_root_certificate.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "common/rsa_test_keys.h"
|
#include "common/rsa_test_keys.h"
|
||||||
#include "protos/public/drm_certificate.pb.h"
|
#include "protos/public/drm_certificate.pb.h"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "util/gtl/map_util.h"
|
#include "util/gtl/map_util.h"
|
||||||
#include "common/aes_cbc_util.h"
|
#include "common/aes_cbc_util.h"
|
||||||
|
#include "common/certificate_type.h"
|
||||||
#include "common/drm_root_certificate.h"
|
#include "common/drm_root_certificate.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
#include "common/rsa_util.h"
|
#include "common/rsa_util.h"
|
||||||
@@ -299,6 +300,32 @@ void DrmServiceCertificate::ResetServiceCertificates() {
|
|||||||
DrmServiceCertificateMap::GetInstance()->Reset();
|
DrmServiceCertificateMap::GetInstance()->Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
util::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.");
|
||||||
|
}
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
if (!drm_cert.has_creation_time_seconds()) {
|
||||||
|
return util::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();
|
||||||
|
}
|
||||||
|
|
||||||
DrmServiceCertificate::DrmServiceCertificate(
|
DrmServiceCertificate::DrmServiceCertificate(
|
||||||
const std::string& service_certificate, const std::string& provider_id,
|
const std::string& service_certificate, const std::string& provider_id,
|
||||||
const std::string& serial_number, const uint32_t creation_time_seconds,
|
const std::string& serial_number, const uint32_t creation_time_seconds,
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
// Description:
|
// Description:
|
||||||
// Service certificate holder used to decrypt encrypted client credentials.
|
// Service certificate holder used to decrypt encrypted client credentials.
|
||||||
|
|
||||||
#ifndef COMMON_DRM_SERVICE_CERTIFICATE_H__
|
#ifndef COMMON_DRM_SERVICE_CERTIFICATE_H_
|
||||||
#define COMMON_DRM_SERVICE_CERTIFICATE_H__
|
#define COMMON_DRM_SERVICE_CERTIFICATE_H_
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -86,6 +86,12 @@ class DrmServiceCertificate {
|
|||||||
const RsaPrivateKey* const private_key() const { return private_key_.get(); }
|
const RsaPrivateKey* const private_key() const { return private_key_.get(); }
|
||||||
const RsaPublicKey* const public_key() const { return public_key_.get(); }
|
const RsaPublicKey* const public_key() const { return public_key_.get(); }
|
||||||
|
|
||||||
|
// Returns the validation result of drm service certificate. Returns
|
||||||
|
// status::OK if successful, or in case of error, contact
|
||||||
|
// widevine-tam@google.com to get the next valid service certificate renewed
|
||||||
|
// via get deviceCertificate StatusList.
|
||||||
|
static util::Status ValidateDrmServiceCertificate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class DrmServiceCertificateTest;
|
friend class DrmServiceCertificateTest;
|
||||||
friend class widevine::RequestInspectorTest;
|
friend class widevine::RequestInspectorTest;
|
||||||
@@ -120,4 +126,4 @@ class DrmServiceCertificate {
|
|||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // COMMON_DRM_SERVICE_CERTIFICATE_H__
|
#endif // COMMON_DRM_SERVICE_CERTIFICATE_H_
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
// widevine-licensing@google.com.
|
// widevine-licensing@google.com.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/drm_service_certificate.h"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
@@ -16,6 +14,7 @@
|
|||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "common/aes_cbc_util.h"
|
#include "common/aes_cbc_util.h"
|
||||||
|
#include "common/drm_service_certificate.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "common/rsa_test_keys.h"
|
#include "common/rsa_test_keys.h"
|
||||||
#include "common/rsa_util.h"
|
#include "common/rsa_util.h"
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ bool EncryptAesEcb(absl::string_view key, absl::string_view src, std::string* ds
|
|||||||
}
|
}
|
||||||
dst->resize(src.size());
|
dst->resize(src.size());
|
||||||
for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) {
|
for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) {
|
||||||
AES_encrypt(reinterpret_cast<const uint8_t*>(src.data()+i),
|
AES_encrypt(reinterpret_cast<const uint8_t*>(src.data() + i),
|
||||||
reinterpret_cast<uint8_t*>(&(*dst)[i]), &aes_key);
|
reinterpret_cast<uint8_t*>(&(*dst)[i]), &aes_key);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -103,7 +103,7 @@ bool DecryptAesEcb(absl::string_view key, absl::string_view src, std::string* ds
|
|||||||
}
|
}
|
||||||
dst->resize(src.size());
|
dst->resize(src.size());
|
||||||
for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) {
|
for (int i = 0; i < src.size(); i += AES_BLOCK_SIZE) {
|
||||||
AES_decrypt(reinterpret_cast<const uint8_t*>(src.data()+i),
|
AES_decrypt(reinterpret_cast<const uint8_t*>(src.data() + i),
|
||||||
reinterpret_cast<uint8_t*>(&(*dst)[i]), &aes_key);
|
reinterpret_cast<uint8_t*>(&(*dst)[i]), &aes_key);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ bool GetContents(const std::string& file_name, std::string* contents) {
|
|||||||
LOG(WARNING) << "Failed to read all file contents.";
|
LOG(WARNING) << "Failed to read all file contents.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetContents(const std::string& file_name, const std::string& contents) {
|
bool SetContents(const std::string& file_name, const std::string& contents) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
|
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ class MockRsaPublicKey : public RsaPublicKey {
|
|||||||
|
|
||||||
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
|
MOCK_CONST_METHOD2(Encrypt, bool(const std::string& clear_message,
|
||||||
std::string* encrypted_message));
|
std::string* encrypted_message));
|
||||||
MOCK_CONST_METHOD2(VerifySignature, bool(const std::string& message,
|
MOCK_CONST_METHOD2(VerifySignature,
|
||||||
const std::string& signature));
|
bool(const std::string& message, const std::string& signature));
|
||||||
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
MOCK_CONST_METHOD1(MatchesPrivateKey, bool(const RsaPrivateKey& private_key));
|
||||||
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
MOCK_CONST_METHOD1(MatchesPublicKey, bool(const RsaPublicKey& public_key));
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ class MockRsaPublicKey : public RsaPublicKey {
|
|||||||
MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete;
|
MockRsaPublicKey& operator=(const MockRsaPublicKey&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockRsaKeyFactory : public RsaKeyFactory{
|
class MockRsaKeyFactory : public RsaKeyFactory {
|
||||||
public:
|
public:
|
||||||
MockRsaKeyFactory() {}
|
MockRsaKeyFactory() {}
|
||||||
~MockRsaKeyFactory() override {}
|
~MockRsaKeyFactory() override {}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
// RAII wrapper classes for cleaning up various OpenSSL dynamically allocated
|
// RAII wrapper classes for cleaning up various OpenSSL dynamically allocated
|
||||||
// structures.
|
// structures.
|
||||||
|
|
||||||
#ifndef COMMON_OPENSSL_UTIL_H__
|
#ifndef COMMON_OPENSSL_UTIL_H_
|
||||||
#define COMMON_OPENSSL_UTIL_H__
|
#define COMMON_OPENSSL_UTIL_H_
|
||||||
|
|
||||||
#include "openssl/bio.h"
|
#include "openssl/bio.h"
|
||||||
#include "openssl/evp.h"
|
#include "openssl/evp.h"
|
||||||
@@ -74,4 +74,4 @@ using ScopedX509InfoStack =
|
|||||||
ScopedOpenSSLStack<STACK_OF(X509_INFO), X509_INFO, X509_INFO_free>;
|
ScopedOpenSSLStack<STACK_OF(X509_INFO), X509_INFO, X509_INFO_free>;
|
||||||
using ScopedX509InfoStackOnly = ScopedOpenSSLStackOnly<STACK_OF(X509_INFO)>;
|
using ScopedX509InfoStackOnly = ScopedOpenSSLStackOnly<STACK_OF(X509_INFO)>;
|
||||||
|
|
||||||
#endif // COMMON_OPENSSL_UTIL_H__
|
#endif // COMMON_OPENSSL_UTIL_H_
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/random_util.h"
|
#include "common/random_util.h"
|
||||||
|
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
// Description:
|
// Description:
|
||||||
// Functionality used to verifier ChromeOS remote attestation.
|
// Functionality used to verifier ChromeOS remote attestation.
|
||||||
|
|
||||||
#ifndef COMMON_REMOTE_ATTESTATION_VERIFIER_H__
|
#ifndef COMMON_REMOTE_ATTESTATION_VERIFIER_H_
|
||||||
#define COMMON_REMOTE_ATTESTATION_VERIFIER_H__
|
#define COMMON_REMOTE_ATTESTATION_VERIFIER_H_
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -89,4 +89,4 @@ class RemoteAttestationVerifier {
|
|||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // COMMON_REMOTE_ATTESTATION_VERIFIER_H__
|
#endif // COMMON_REMOTE_ATTESTATION_VERIFIER_H_
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ class RsaPublicKey {
|
|||||||
virtual bool VerifySignature(const std::string& message,
|
virtual bool VerifySignature(const std::string& message,
|
||||||
const std::string& signature) const;
|
const std::string& signature) const;
|
||||||
|
|
||||||
|
|
||||||
// Verify a signature. This method takes two parameters: |message| which is a
|
// Verify a signature. This method takes two parameters: |message| which is a
|
||||||
// std::string containing the data which was signed, and |signature| which is a
|
// std::string containing the data which was signed, and |signature| which is a
|
||||||
// std::string containing the message SHA256 digest signature with PKCS#7
|
// std::string containing the message SHA256 digest signature with PKCS#7
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -42,8 +42,9 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (serialized_key == nullptr) {
|
if (serialized_key == nullptr) {
|
||||||
LOG(ERROR) << "Pointer to hold serialized RSA" << (serialize_private_key ?
|
LOG(ERROR) << "Pointer to hold serialized RSA"
|
||||||
"Private" : "Public") << "Key is nullptr.";
|
<< (serialize_private_key ? "Private" : "Public")
|
||||||
|
<< "Key is nullptr.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
BIO* bio = BIO_new(BIO_s_mem());
|
BIO* bio = BIO_new(BIO_s_mem());
|
||||||
@@ -52,9 +53,9 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if ((serialize_private_key ?
|
if ((serialize_private_key
|
||||||
i2d_RSAPrivateKey_bio(bio, const_cast<RSA*>(key)) :
|
? i2d_RSAPrivateKey_bio(bio, const_cast<RSA*>(key))
|
||||||
i2d_RSAPublicKey_bio(bio, const_cast<RSA*>(key))) != 0) {
|
: i2d_RSAPublicKey_bio(bio, const_cast<RSA*>(key))) != 0) {
|
||||||
int serialized_size = BIO_pending(bio);
|
int serialized_size = BIO_pending(bio);
|
||||||
serialized_key->assign(serialized_size, 0);
|
serialized_key->assign(serialized_size, 0);
|
||||||
if (BIO_read(bio, &(*serialized_key)[0], serialized_size) ==
|
if (BIO_read(bio, &(*serialized_key)[0], serialized_size) ==
|
||||||
@@ -64,8 +65,8 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
|
|||||||
LOG(ERROR) << "BIO_read failure";
|
LOG(ERROR) << "BIO_read failure";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << (serialize_private_key ? "Private" : "Public") <<
|
LOG(ERROR) << (serialize_private_key ? "Private" : "Public")
|
||||||
" key serialization failure";
|
<< " key serialization failure";
|
||||||
}
|
}
|
||||||
BIO_free(bio);
|
BIO_free(bio);
|
||||||
return success;
|
return success;
|
||||||
@@ -74,13 +75,15 @@ static bool SerializeRsaKey(const RSA* key, std::string* serialized_key,
|
|||||||
static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key,
|
static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key,
|
||||||
bool deserialize_private_key) {
|
bool deserialize_private_key) {
|
||||||
if (serialized_key.empty()) {
|
if (serialized_key.empty()) {
|
||||||
LOG(ERROR) << "Serialized RSA" << (deserialize_private_key ?
|
LOG(ERROR) << "Serialized RSA"
|
||||||
"Private" : "Public") << "Key is empty.";
|
<< (deserialize_private_key ? "Private" : "Public")
|
||||||
|
<< "Key is empty.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (key == nullptr) {
|
if (key == nullptr) {
|
||||||
LOG(ERROR) << "Pointer to hold new RSA " << (deserialize_private_key ?
|
LOG(ERROR) << "Pointer to hold new RSA "
|
||||||
"private" : "public") << " key is nullptr.";
|
<< (deserialize_private_key ? "private" : "public")
|
||||||
|
<< " key is nullptr.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
|
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
|
||||||
@@ -89,12 +92,12 @@ static bool DeserializeRsaKey(const std::string& serialized_key, RSA** key,
|
|||||||
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
|
LOG(ERROR) << "BIO_new_mem_buf returned nullptr";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, nullptr) :
|
*key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, nullptr)
|
||||||
d2i_RSAPublicKey_bio(bio, nullptr);
|
: d2i_RSAPublicKey_bio(bio, nullptr);
|
||||||
BIO_free(bio);
|
BIO_free(bio);
|
||||||
if (*key == nullptr) {
|
if (*key == nullptr) {
|
||||||
LOG(ERROR) << (deserialize_private_key ? "Private" : "Public") <<
|
LOG(ERROR) << (deserialize_private_key ? "Private" : "Public")
|
||||||
" RSA key deserialization failure";
|
<< " RSA key deserialization failure";
|
||||||
}
|
}
|
||||||
return *key != nullptr;
|
return *key != nullptr;
|
||||||
}
|
}
|
||||||
@@ -139,7 +142,7 @@ bool SerializePrivateKeyInfo(const RSA* private_key,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool success = false;
|
bool success = false;
|
||||||
PKCS8_PRIV_KEY_INFO *pkcs8_pki = nullptr;
|
PKCS8_PRIV_KEY_INFO* pkcs8_pki = nullptr;
|
||||||
BIO* bio = nullptr;
|
BIO* bio = nullptr;
|
||||||
if (EVP_PKEY_set1_RSA(evp, const_cast<RSA*>(private_key)) == 0) {
|
if (EVP_PKEY_set1_RSA(evp, const_cast<RSA*>(private_key)) == 0) {
|
||||||
LOG(ERROR) << "EVP_PKEY_set1_RSA failed.";
|
LOG(ERROR) << "EVP_PKEY_set1_RSA failed.";
|
||||||
@@ -203,7 +206,7 @@ bool DeserializePrivateKeyInfo(const std::string& serialized_private_key,
|
|||||||
}
|
}
|
||||||
bool success = false;
|
bool success = false;
|
||||||
EVP_PKEY* evp = nullptr;
|
EVP_PKEY* evp = nullptr;
|
||||||
PKCS8_PRIV_KEY_INFO *pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
|
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
|
||||||
if (pkcs8_pki == nullptr) {
|
if (pkcs8_pki == nullptr) {
|
||||||
LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr.";
|
LOG(ERROR) << "d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr.";
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -312,7 +315,7 @@ cleanup:
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below.
|
// Password retrieval function used by DeserializeEncryptedPrivateKeyInfo below.
|
||||||
int get_password(char *buf, int size, int rwflag, void *u) {
|
int get_password(char* buf, int size, int rwflag, void* u) {
|
||||||
CHECK(buf);
|
CHECK(buf);
|
||||||
CHECK(u);
|
CHECK(u);
|
||||||
const std::string* pass(static_cast<const std::string*>(u));
|
const std::string* pass(static_cast<const std::string*>(u));
|
||||||
@@ -372,9 +375,8 @@ bool RsaPrivateKeyToEncryptedPrivateKeyInfo(const std::string& rsa_private_key,
|
|||||||
std::string* private_key_info) {
|
std::string* private_key_info) {
|
||||||
RSA* key = nullptr;
|
RSA* key = nullptr;
|
||||||
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
|
if (DeserializeRsaPrivateKey(rsa_private_key, &key)) {
|
||||||
bool success = SerializeEncryptedPrivateKeyInfo(key,
|
bool success =
|
||||||
passphrase,
|
SerializeEncryptedPrivateKeyInfo(key, passphrase, private_key_info);
|
||||||
private_key_info);
|
|
||||||
RSA_free(key);
|
RSA_free(key);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@@ -449,8 +451,8 @@ bool ConvertToEulerTotient(const std::string& private_key,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bssl::UniquePtr<RSA> rsa(rsa_ptr);
|
bssl::UniquePtr<RSA> rsa(rsa_ptr);
|
||||||
if (!rsa_util::ConvertToEulerTotient(rsa.get())
|
if (!rsa_util::ConvertToEulerTotient(rsa.get()) ||
|
||||||
|| !rsa_util::SerializeRsaPrivateKey(rsa.get(), euler_private_key)) {
|
!rsa_util::SerializeRsaPrivateKey(rsa.get(), euler_private_key)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,8 +504,8 @@ bool ConvertToCarmichaelTotient(const std::string& private_key,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bssl::UniquePtr<RSA> rsa(rsa_ptr);
|
bssl::UniquePtr<RSA> rsa(rsa_ptr);
|
||||||
if (!rsa_util::ConvertToCarmichaelTotient(rsa.get())
|
if (!rsa_util::ConvertToCarmichaelTotient(rsa.get()) ||
|
||||||
|| !rsa_util::SerializeRsaPrivateKey(rsa.get(), carmichael_private_key)) {
|
!rsa_util::SerializeRsaPrivateKey(rsa.get(), carmichael_private_key)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ bool RsaPrivateKeyToPrivateKeyInfo(const std::string& rsa_private_key,
|
|||||||
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
bool PrivateKeyInfoToRsaPrivateKey(const std::string& private_key_info,
|
||||||
std::string* rsa_private_key);
|
std::string* rsa_private_key);
|
||||||
|
|
||||||
|
|
||||||
// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo.
|
// Serialize RSA private key into DER encoded PKCS#8 EncryptedPrivateKeyInfo.
|
||||||
// - private_key is the RSA key to be serialized, which must not be NULL.
|
// - private_key is the RSA key to be serialized, which must not be NULL.
|
||||||
// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption.
|
// - passphrase is the password to use for PKCS#5 v2.0 3DES encryption.
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
// Description:
|
// Description:
|
||||||
// Unit test for rsa_util RSA utilties library.
|
// Unit test for rsa_util RSA utilties library.
|
||||||
|
|
||||||
#include "common/rsa_util.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -21,6 +19,7 @@
|
|||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "openssl/bn.h"
|
#include "openssl/bn.h"
|
||||||
#include "common/rsa_test_keys.h"
|
#include "common/rsa_test_keys.h"
|
||||||
|
#include "common/rsa_util.h"
|
||||||
|
|
||||||
using ::testing::NotNull;
|
using ::testing::NotNull;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/sha_util.h"
|
#include "common/sha_util.h"
|
||||||
|
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/signing_key_util.h"
|
#include "common/signing_key_util.h"
|
||||||
|
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "common/crypto_util.h"
|
#include "common/crypto_util.h"
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,7 @@ class TestCertificates {
|
|||||||
virtual ~TestCertificates() {}
|
virtual ~TestCertificates() {}
|
||||||
|
|
||||||
// returns a test root certificate
|
// returns a test root certificate
|
||||||
const std::string& test_root_certificate() const {
|
const std::string& test_root_certificate() const { return test_root_certificate_; }
|
||||||
return test_root_certificate_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns a test intermediate certificate
|
// returns a test intermediate certificate
|
||||||
const std::string& test_intermediate_certificate() const {
|
const std::string& test_intermediate_certificate() const {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
// Description:
|
// Description:
|
||||||
// Auxiliary functions for license server SDK testing.
|
// Auxiliary functions for license server SDK testing.
|
||||||
|
|
||||||
#ifndef COMMON_TEST_UTILS_H__
|
#ifndef COMMON_TEST_UTILS_H_
|
||||||
#define COMMON_TEST_UTILS_H__
|
#define COMMON_TEST_UTILS_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -29,4 +29,4 @@ util::Status GenerateRsaSignatureSha256Pkcs1(const std::string& pem_private_key,
|
|||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // COMMON_TEST_UTILS_H__
|
#endif // COMMON_TEST_UTILS_H_
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
|
#include "common/certificate_type.h"
|
||||||
#include "common/error_space.h"
|
#include "common/error_space.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "common/x509_cert.h"
|
#include "common/x509_cert.h"
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ class VmpChecker {
|
|||||||
virtual util::Status VerifyVmpData(const std::string& vmp_data, Result* result);
|
virtual util::Status VerifyVmpData(const std::string& vmp_data, Result* result);
|
||||||
|
|
||||||
// Enable/disable development code signing certificates.
|
// Enable/disable development code signing certificates.
|
||||||
void set_allow_development_vmp(bool allow) {
|
void set_allow_development_vmp(bool allow) { allow_development_vmp_ = allow; }
|
||||||
allow_development_vmp_ = allow;
|
|
||||||
}
|
|
||||||
bool allow_development_vmp() const { return allow_development_vmp_; }
|
bool allow_development_vmp() const { return allow_development_vmp_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
19
common/widevine_system_id.cc
Normal file
19
common/widevine_system_id.cc
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2017 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Common Encryption (CENC) system ID for Widevine DRM.
|
||||||
|
|
||||||
|
#include "common/widevine_system_id.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
const uint8_t kWidevineSystemId[16] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
||||||
|
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
||||||
|
0xd5, 0x1d, 0x21, 0xed};
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
22
common/widevine_system_id.h
Normal file
22
common/widevine_system_id.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2017 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Common Encryption (CENC) system ID for Widevine DRM.
|
||||||
|
|
||||||
|
#ifndef COMMON_WIDEVINE_SYSTEM_ID_H_
|
||||||
|
#define COMMON_WIDEVINE_SYSTEM_ID_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
|
||||||
|
extern const uint8_t kWidevineSystemId[16];
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // COMMON_WIDEVINE_SYSTEM_ID_H_
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/wvm_token_handler.h"
|
#include "common/wvm_token_handler.h"
|
||||||
|
|
||||||
#include "testing/gmock.h"
|
#include "testing/gmock.h"
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "openssl/bio.h"
|
#include "openssl/bio.h"
|
||||||
#include "openssl/evp.h"
|
#include "openssl/evp.h"
|
||||||
#include "openssl/pem.h"
|
#include "openssl/pem.h"
|
||||||
|
#include "openssl/pkcs7.h"
|
||||||
#include "openssl/x509.h"
|
#include "openssl/x509.h"
|
||||||
#include "openssl/x509v3.h"
|
#include "openssl/x509v3.h"
|
||||||
#include "common/openssl_util.h"
|
#include "common/openssl_util.h"
|
||||||
@@ -52,8 +53,7 @@ bool PemEncodeX509Certificate(const X509& certificate,
|
|||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
std::unique_ptr<X509Cert> X509Cert::FromOpenSslCert(
|
std::unique_ptr<X509Cert> X509Cert::FromOpenSslCert(ScopedX509 certificate) {
|
||||||
ScopedX509 certificate) {
|
|
||||||
return std::unique_ptr<X509Cert>(new X509Cert(certificate.release()));
|
return std::unique_ptr<X509Cert>(new X509Cert(certificate.release()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,10 +194,11 @@ bool X509Cert::GetV3BooleanExtension(const std::string& oid, bool* value) const
|
|||||||
if (ext_pos < 0) return false;
|
if (ext_pos < 0) return false;
|
||||||
X509_EXTENSION* extension(X509_get_ext(openssl_cert_, ext_pos));
|
X509_EXTENSION* extension(X509_get_ext(openssl_cert_, ext_pos));
|
||||||
if (!extension) return false;
|
if (!extension) return false;
|
||||||
ASN1_OCTET_STRING *extension_data(X509_EXTENSION_get_data(extension));
|
ASN1_OCTET_STRING* extension_data(X509_EXTENSION_get_data(extension));
|
||||||
if (!extension_data) return false;
|
if (!extension_data) return false;
|
||||||
if ((extension_data->length != 3) || (extension_data->data[0] != 1) ||
|
if ((extension_data->length != 3) || (extension_data->data[0] != 1) ||
|
||||||
(extension_data->data[1] != 1)) return false;
|
(extension_data->data[1] != 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
*value = extension_data->data[2] != 0;
|
*value = extension_data->data[2] != 0;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
// Description:
|
// Description:
|
||||||
// X.509 certificate classes used by the license server SDK.
|
// X.509 certificate classes used by the license server SDK.
|
||||||
|
|
||||||
#ifndef COMMON_X509_CERT_H__
|
#ifndef COMMON_X509_CERT_H_
|
||||||
#define COMMON_X509_CERT_H__
|
#define COMMON_X509_CERT_H_
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -26,11 +26,10 @@
|
|||||||
#include "openssl/x509v3.h"
|
#include "openssl/x509v3.h"
|
||||||
#include "util/status.h"
|
#include "util/status.h"
|
||||||
#include "common/openssl_util.h"
|
#include "common/openssl_util.h"
|
||||||
|
#include "common/rsa_key.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
class RsaPublicKey;
|
|
||||||
|
|
||||||
// NOTE: All util::Status codes are in the canonical error space.
|
// NOTE: All util::Status codes are in the canonical error space.
|
||||||
|
|
||||||
// Class which holds a single X.509 certificates.
|
// Class which holds a single X.509 certificates.
|
||||||
@@ -141,7 +140,7 @@ class X509CA {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
util::Status InitializeStore();
|
util::Status InitializeStore();
|
||||||
util::Status OpenSslX509Verify(const X509* cert, STACK_OF(X509)* stack);
|
util::Status OpenSslX509Verify(const X509* cert, STACK_OF(X509) * stack);
|
||||||
|
|
||||||
std::unique_ptr<X509Cert> ca_cert_;
|
std::unique_ptr<X509Cert> ca_cert_;
|
||||||
absl::Mutex openssl_store_mutex_;
|
absl::Mutex openssl_store_mutex_;
|
||||||
@@ -152,4 +151,4 @@ class X509CA {
|
|||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // COMMON_X509_CERT_H__
|
#endif // COMMON_X509_CERT_H_
|
||||||
|
|||||||
@@ -6,14 +6,13 @@
|
|||||||
// widevine-licensing@google.com.
|
// widevine-licensing@google.com.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "common/x509_cert.h"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "testing/gunit.h"
|
#include "testing/gunit.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "common/rsa_key.h"
|
#include "common/rsa_key.h"
|
||||||
#include "common/test_utils.h"
|
#include "common/test_utils.h"
|
||||||
|
#include "common/x509_cert.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
const char kTestRootCaDerCert[] =
|
const char kTestRootCaDerCert[] =
|
||||||
|
|||||||
39
example/BUILD
Normal file
39
example/BUILD
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
################################################################################
|
||||||
|
# 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 the example code.
|
||||||
|
|
||||||
|
package(
|
||||||
|
default_visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "binary_release_files",
|
||||||
|
srcs = [
|
||||||
|
"simulcrypt_client.cc",
|
||||||
|
"test_simulcrypt_messages.h",
|
||||||
|
":simulcrypt_client",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "simulcrypt_client",
|
||||||
|
srcs = ["simulcrypt_client.cc"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "test_simulcrypt_messages",
|
||||||
|
hdrs = ["test_simulcrypt_messages.h"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
],
|
||||||
|
)
|
||||||
80
example/simulcrypt_client.cc
Normal file
80
example/simulcrypt_client.cc
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 that talks to server_main.
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
166
example/test_simulcrypt_messages.h
Normal file
166
example/test_simulcrypt_messages.h
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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_
|
||||||
@@ -48,3 +48,109 @@ cc_test(
|
|||||||
"//protos/public:media_cas_encryption_proto",
|
"//protos/public:media_cas_encryption_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "ecm_generator",
|
||||||
|
srcs = ["ecm_generator.cc"],
|
||||||
|
hdrs = ["ecm_generator.h"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"//util:status",
|
||||||
|
"//media_cas_packager_sdk/internal:ecm",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "ecm_generator_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["ecm_generator_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":ecm_generator",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"//common:aes_cbc_util",
|
||||||
|
"//protos/public:media_cas_encryption_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "ecmg",
|
||||||
|
srcs = ["ecmg.cc"],
|
||||||
|
hdrs = [
|
||||||
|
"ecmg.h",
|
||||||
|
"ecmg_constants.h",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":ecm",
|
||||||
|
":ecm_generator",
|
||||||
|
":fixed_key_fetcher",
|
||||||
|
":util",
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
"@abseil_repo//absl/memory",
|
||||||
|
"@abseil_repo//absl/strings",
|
||||||
|
"//util:status",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "fixed_key_fetcher",
|
||||||
|
srcs = [
|
||||||
|
"fixed_key_fetcher.cc",
|
||||||
|
],
|
||||||
|
hdrs = [
|
||||||
|
"fixed_key_fetcher.h",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":key_fetcher",
|
||||||
|
"//util:status",
|
||||||
|
"//protos/public:media_cas_encryption_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "key_fetcher",
|
||||||
|
hdrs = [
|
||||||
|
"key_fetcher.h",
|
||||||
|
],
|
||||||
|
deps = ["//util:status"],
|
||||||
|
)
|
||||||
|
|
||||||
|
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",
|
||||||
|
"//util:status",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "simulcrypt_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["simulcrypt_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":simulcrypt",
|
||||||
|
"//testing:gunit_main",
|
||||||
|
"//example:test_simulcrypt_messages",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "util",
|
||||||
|
srcs = ["util.cc"],
|
||||||
|
hdrs = ["util.h"],
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"@abseil_repo//absl/base:core_headers",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ static constexpr int kNumBitsUnusedField = 6;
|
|||||||
static constexpr size_t kKeyIdSizeBytes = 16;
|
static constexpr size_t kKeyIdSizeBytes = 16;
|
||||||
static constexpr size_t kKeyDataSizeBytes = 16;
|
static constexpr size_t kKeyDataSizeBytes = 16;
|
||||||
static constexpr size_t kWrappedKeyIvSizeBytes = 16;
|
static constexpr size_t kWrappedKeyIvSizeBytes = 16;
|
||||||
|
static constexpr size_t kWrappingKeyIvSizeBytes = 16;
|
||||||
|
|
||||||
// BitField constants for the ECM payload
|
// BitField constants for the ECM payload
|
||||||
|
|
||||||
@@ -309,6 +310,9 @@ util::Status CasEcm::WrapEntitledKeys(
|
|||||||
|
|
||||||
std::string CasEcm::WrapKey(const std::string& wrapping_key, const std::string& iv,
|
std::string CasEcm::WrapKey(const std::string& wrapping_key, const std::string& iv,
|
||||||
const std::string& key_value) {
|
const std::string& key_value) {
|
||||||
|
if (iv.size() != kWrappingKeyIvSizeBytes) {
|
||||||
|
LOG(WARNING) << "Incorrect iv size for WrapKey(): " << iv.size();
|
||||||
|
}
|
||||||
// Wrapped key IV is always 16 bytes.
|
// Wrapped key IV is always 16 bytes.
|
||||||
return crypto_util::EncryptAesCbcNoPad(wrapping_key, iv, key_value);
|
return crypto_util::EncryptAesCbcNoPad(wrapping_key, iv, key_value);
|
||||||
}
|
}
|
||||||
@@ -491,13 +495,13 @@ util::Status CasEcm::ParseEntitlementResponse(const std::string& response_string
|
|||||||
}
|
}
|
||||||
if (paired_keys_required()) {
|
if (paired_keys_required()) {
|
||||||
if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_EVEN) {
|
if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_EVEN) {
|
||||||
PushEntitlementKey(key.track_type(), true, ekey);
|
PushEntitlementKey(key.track_type(), /* is_even_key= */ true, ekey);
|
||||||
} else if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_ODD) {
|
} else if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_ODD) {
|
||||||
PushEntitlementKey(key.track_type(), false, ekey);
|
PushEntitlementKey(key.track_type(), /* is_even_key= */ false, ekey);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_SINGLE) {
|
if (key.key_slot() == CasEncryptionResponse_KeyInfo_KeySlot_SINGLE) {
|
||||||
PushEntitlementKey(key.track_type(), false, ekey);
|
PushEntitlementKey(key.track_type(), /* is_even_key= */ true, ekey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ struct EcmInitParameters {
|
|||||||
// TODO(user): Add usage example.
|
// TODO(user): Add usage example.
|
||||||
//
|
//
|
||||||
// Class CasEcm is not thread safe.
|
// Class CasEcm is not thread safe.
|
||||||
|
// TODO(user): Rename class to Ecm.
|
||||||
class CasEcm {
|
class CasEcm {
|
||||||
public:
|
public:
|
||||||
CasEcm() = default;
|
CasEcm() = default;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
// widevine-licensing@google.com.
|
// widevine-licensing@google.com.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "media_cas_packager_sdk/public/ecm_generator.h"
|
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
||||||
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
|
|
||||||
@@ -56,6 +56,10 @@ util::Status CasEcmGenerator::ProcessEcmParameters(
|
|||||||
"Number of supplied keys is wrong (check rotation periods)."};
|
"Number of supplied keys is wrong (check rotation periods)."};
|
||||||
}
|
}
|
||||||
for (int i = 0; i < keys_needed; i++) {
|
for (int i = 0; i < keys_needed; i++) {
|
||||||
|
util::Status status = ValidateKeyParameters(ecm_params.key_params[i]);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
keys->emplace_back(EntitledKeyInfo());
|
keys->emplace_back(EntitledKeyInfo());
|
||||||
EntitledKeyInfo& key = keys->back();
|
EntitledKeyInfo& key = keys->back();
|
||||||
key.key_id = ecm_params.key_params[i].key_id;
|
key.key_id = ecm_params.key_params[i].key_id;
|
||||||
@@ -131,10 +135,6 @@ util::Status CasEcmGenerator::ValidateKeyParameters(
|
|||||||
util::Status status;
|
util::Status status;
|
||||||
status = ValidateKeyId(key_params.key_id);
|
status = ValidateKeyId(key_params.key_id);
|
||||||
if (!status.ok()) return status;
|
if (!status.ok()) return status;
|
||||||
status = ValidateKeyData(key_params.wrapped_key_data);
|
|
||||||
if (!status.ok()) return status;
|
|
||||||
status = ValidateWrappedKeyIv(key_params.wrapped_key_iv);
|
|
||||||
if (!status.ok()) return status;
|
|
||||||
if (key_params.content_ivs.empty()) {
|
if (key_params.content_ivs.empty()) {
|
||||||
return {util::error::INVALID_ARGUMENT, "Content IVs is empty."};
|
return {util::error::INVALID_ARGUMENT, "Content IVs is empty."};
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,8 @@
|
|||||||
// widevine-licensing@google.com.
|
// widevine-licensing@google.com.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifndef MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_
|
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECM_GENERATOR_H_
|
||||||
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECM_GENERATOR_H_
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -27,32 +27,35 @@ namespace cas {
|
|||||||
struct KeyParameters {
|
struct KeyParameters {
|
||||||
std::string key_id;
|
std::string key_id;
|
||||||
std::string key_data;
|
std::string key_data;
|
||||||
|
// TODO(user): wrapped_key_data does not seem to be used, but assumed
|
||||||
|
// to exist by unit tests.
|
||||||
std::string wrapped_key_data;
|
std::string wrapped_key_data;
|
||||||
|
// wrapped_key_iv is randomly generated right before it is used to encrypt
|
||||||
|
// key_data in ecm.cc.
|
||||||
std::string wrapped_key_iv;
|
std::string wrapped_key_iv;
|
||||||
|
// TODO(user): Probably only need a single content_iv instead of a vector.
|
||||||
std::vector<std::string> content_ivs;
|
std::vector<std::string> content_ivs;
|
||||||
};
|
};
|
||||||
|
|
||||||
// EcmParameters holds information that is needed by the EcmGenerator. It is
|
// EcmParameters holds information that is needed by the EcmGenerator. It is
|
||||||
// partially set up with data from a KeyGenerator (obtained via GenerateKey()).
|
// partially set up with data from a KeyGenerator (obtained via GenerateKey()).
|
||||||
// IVs are added later.
|
// IVs are added later.
|
||||||
// TODO(user): may need a starting crypto period index.
|
// TODO(user): Consider rename to EcmGeneratorParameters.
|
||||||
struct EcmParameters {
|
struct EcmParameters {
|
||||||
static constexpr int kDefaultIVSize = 8;
|
// TODO(user): entitlement_key_id does not seem to be used, but assumed
|
||||||
int iv_size = kDefaultIVSize;
|
// to exist by unit tests.
|
||||||
bool current_key_even = true;
|
|
||||||
uint32_t current_key_index = 0;
|
|
||||||
std::string entitlement_key_id;
|
std::string entitlement_key_id;
|
||||||
bool rotation_enabled = true;
|
bool rotation_enabled = true;
|
||||||
|
// TODO(user): rotation_periods does not seem to be used, but assumed
|
||||||
|
// to exist by unit tests.
|
||||||
uint32_t rotation_periods;
|
uint32_t rotation_periods;
|
||||||
|
// TODO(user): Consider changing the vector to just two variables,
|
||||||
|
// one for even key, the other for odd key.
|
||||||
std::vector<KeyParameters> key_params;
|
std::vector<KeyParameters> key_params;
|
||||||
uint16_t program_id;
|
|
||||||
uint64_t rotation_period_microseconds;
|
|
||||||
// For video, this is zero. For audio, this is the size of the audio frame,
|
|
||||||
// which is a constant in the audio track.
|
|
||||||
uint16_t offset = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ECM Generator for Widevine/MediaCAS entitled keys.
|
// ECM Generator for Widevine/MediaCAS entitled keys.
|
||||||
|
// TODO(user): Rename class to EcmGenerator.
|
||||||
class CasEcmGenerator {
|
class CasEcmGenerator {
|
||||||
public:
|
public:
|
||||||
CasEcmGenerator() = default;
|
CasEcmGenerator() = default;
|
||||||
@@ -92,4 +95,4 @@ class CasEcmGenerator {
|
|||||||
} // namespace cas
|
} // namespace cas
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|
||||||
#endif // MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECM_GENERATOR_H_
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
// widevine-licensing@google.com.
|
// widevine-licensing@google.com.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "media_cas_packager_sdk/public/ecm_generator.h"
|
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
287
media_cas_packager_sdk/internal/ecmg.cc
Normal file
287
media_cas_packager_sdk/internal/ecmg.cc
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 <string.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "util/status.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"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static const char kDefaultContentId[] = "21140844";
|
||||||
|
static const char kDefaultProvider[] = "widevine";
|
||||||
|
// Size of this IV needs to match ecm_init_params.content_iv_size.
|
||||||
|
static const char kDefaultContentIv[] = {'\x01', '\x01', '\x01', '\x01',
|
||||||
|
'\x01', '\x01', '\x01', '\x01'};
|
||||||
|
static const char kDefaultTrackTypeSd[] = "SD";
|
||||||
|
|
||||||
|
// 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(¶meter_type, message + offset);
|
||||||
|
offset += PARAMETER_TYPE_SIZE;
|
||||||
|
BigEndianToHost16(¶meter_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(¶meters->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(¶meters->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 =
|
||||||
|
¶meters->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(¶meters->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(¶meters->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(¶meters->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, ¶meters);
|
||||||
|
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, ¶meters);
|
||||||
|
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;
|
||||||
|
// Only CTR is supported for now.
|
||||||
|
ecm_init_params.crypto_mode = CasCryptoMode::CTR;
|
||||||
|
// 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(kDefaultContentIv));
|
||||||
|
}
|
||||||
|
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
|
||||||
94
media_cas_packager_sdk/internal/ecmg.h
Normal file
94
media_cas_packager_sdk/internal/ecmg.h
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 "util/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_
|
||||||
53
media_cas_packager_sdk/internal/ecmg_constants.h
Normal file
53
media_cas_packager_sdk/internal/ecmg_constants.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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_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
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_ECMG_CONSTANTS_H_
|
||||||
66
media_cas_packager_sdk/internal/fixed_key_fetcher.cc
Normal file
66
media_cas_packager_sdk/internal/fixed_key_fetcher.cc
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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/fixed_key_fetcher.h"
|
||||||
|
|
||||||
|
#include "util/status.h"
|
||||||
|
#include "protos/public/media_cas_encryption.pb.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Key IDs are 16 bytes, keys are 32 bytes.
|
||||||
|
static const char* kKeyId1 = "fake_key_id1....";
|
||||||
|
static const char* kKey1 = "fakefakefakefakefakefakefake1...";
|
||||||
|
static const char* kKeyId2 = "fake_key_id2....";
|
||||||
|
static const char* kKey2 = "fakefakefakefakefakefakefake2...";
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
util::Status FixedKeyFetcher::RequestEntitlementKey(
|
||||||
|
const std::string& request_string, std::string* signed_response_string) {
|
||||||
|
CasEncryptionRequest request;
|
||||||
|
request.ParseFromString(request_string);
|
||||||
|
|
||||||
|
CasEncryptionResponse response;
|
||||||
|
response.set_status(CasEncryptionResponse_Status_OK);
|
||||||
|
response.set_content_id(request.content_id());
|
||||||
|
for (const auto& track_type : request.track_types()) {
|
||||||
|
if (request.key_rotation()) {
|
||||||
|
// Add the Even key.
|
||||||
|
auto key = response.add_entitlement_keys();
|
||||||
|
key->set_key_id(kKeyId1);
|
||||||
|
key->set_key(kKey1);
|
||||||
|
key->set_track_type(track_type);
|
||||||
|
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_EVEN);
|
||||||
|
// Add the Odd key.
|
||||||
|
key = response.add_entitlement_keys();
|
||||||
|
key->set_key_id(kKeyId2);
|
||||||
|
key->set_key(kKey2);
|
||||||
|
key->set_track_type(track_type);
|
||||||
|
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_ODD);
|
||||||
|
} else {
|
||||||
|
auto key = response.add_entitlement_keys();
|
||||||
|
key->set_key_id(kKeyId1);
|
||||||
|
key->set_key(kKey1);
|
||||||
|
key->set_track_type(track_type);
|
||||||
|
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_SINGLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string response_string;
|
||||||
|
response.SerializeToString(&response_string);
|
||||||
|
SignedCasEncryptionResponse signed_response;
|
||||||
|
signed_response.set_response(response_string);
|
||||||
|
signed_response.SerializeToString(signed_response_string);
|
||||||
|
return util::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
40
media_cas_packager_sdk/internal/fixed_key_fetcher.h
Normal file
40
media_cas_packager_sdk/internal/fixed_key_fetcher.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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_FIXED_KEY_FETCHER_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_FIXED_KEY_FETCHER_H_
|
||||||
|
|
||||||
|
#include "media_cas_packager_sdk/internal/key_fetcher.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
// Perform the same role as a WV CAS KeyFetcher, but return a
|
||||||
|
// locally-constructed response that has known (predefined) entitlement keys.
|
||||||
|
class FixedKeyFetcher : public KeyFetcher {
|
||||||
|
public:
|
||||||
|
FixedKeyFetcher() {}
|
||||||
|
~FixedKeyFetcher() override = default;
|
||||||
|
|
||||||
|
// Get entitlement keys. Process a CasEncryptionRequest message to
|
||||||
|
// determine the keys that are needed, generate a fixed set of keys,
|
||||||
|
// and package them into a SignedCasEncryptionResponse message.
|
||||||
|
// Args:
|
||||||
|
// |request_string| a serialized CasEncryptionRequest message, produced
|
||||||
|
// by WvCasEcm::Initialize().
|
||||||
|
// |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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_FIXED_KEY_FETCHER_H_
|
||||||
43
media_cas_packager_sdk/internal/key_fetcher.h
Normal file
43
media_cas_packager_sdk/internal/key_fetcher.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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_KEY_FETCHER_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_KEY_FETCHER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "util/status.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
// Interface for fetching various types of keys.
|
||||||
|
class KeyFetcher {
|
||||||
|
public:
|
||||||
|
KeyFetcher() = default;
|
||||||
|
KeyFetcher(const KeyFetcher&) = delete;
|
||||||
|
KeyFetcher& operator=(const KeyFetcher&) = delete;
|
||||||
|
virtual ~KeyFetcher() = default;
|
||||||
|
|
||||||
|
// Get entitlement keys. Process a CasEncryptionRequest message to
|
||||||
|
// determine the keys that are needed, generate a fixed set of keys,
|
||||||
|
// and package them into a SignedCasEncryptionResponse message.
|
||||||
|
// Args:
|
||||||
|
// |request_string| a serialized CasEncryptionRequest message, produced
|
||||||
|
// by WvCasEcm::Initialize().
|
||||||
|
// |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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_KEY_FETCHER_H_
|
||||||
67
media_cas_packager_sdk/internal/simulcrypt.cc
Normal file
67
media_cas_packager_sdk/internal/simulcrypt.cc
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 "util/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
|
||||||
50
media_cas_packager_sdk/internal/simulcrypt.h
Normal file
50
media_cas_packager_sdk/internal/simulcrypt.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 "util/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_
|
||||||
55
media_cas_packager_sdk/internal/simulcrypt_constants.h
Normal file
55
media_cas_packager_sdk/internal/simulcrypt_constants.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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_
|
||||||
54
media_cas_packager_sdk/internal/simulcrypt_test.cc
Normal file
54
media_cas_packager_sdk/internal/simulcrypt_test.cc
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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
|
||||||
28
media_cas_packager_sdk/internal/util.cc
Normal file
28
media_cas_packager_sdk/internal/util.cc
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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/util.h"
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
void BigEndianToHost16(uint16_t* destination, const void* source) {
|
||||||
|
DCHECK(destination);
|
||||||
|
DCHECK(source);
|
||||||
|
uint16_t big_endian_number;
|
||||||
|
memcpy(&big_endian_number, source, 2);
|
||||||
|
*destination = ntohs(big_endian_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
25
media_cas_packager_sdk/internal/util.h
Normal file
25
media_cas_packager_sdk/internal/util.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2018 Google LLC.
|
||||||
|
//
|
||||||
|
// This software is licensed under the terms defined in the Widevine Master
|
||||||
|
// License Agreement. For a copy of this agreement, please contact
|
||||||
|
// widevine-licensing@google.com.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_UTIL_H_
|
||||||
|
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_UTIL_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace widevine {
|
||||||
|
namespace cas {
|
||||||
|
|
||||||
|
// Read 16 bits (short 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 BigEndianToHost16(uint16_t* destination, const void* source);
|
||||||
|
|
||||||
|
} // namespace cas
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_UTIL_H_
|
||||||
@@ -20,13 +20,15 @@ PUBLIC_COPTS = ["-fvisibility=default"]
|
|||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "binary_release_files",
|
name = "binary_release_files",
|
||||||
srcs = glob(["*.h"]),
|
srcs = glob(["*.h"]) + [
|
||||||
|
"simulcrypt_server.cc",
|
||||||
|
":simulcrypt_server",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_binary(
|
cc_binary(
|
||||||
name = "libmedia_cas_packager_sdk.so",
|
name = "libmedia_cas_packager_sdk.so",
|
||||||
linkshared = 1,
|
linkshared = 1,
|
||||||
deps = [":ecm_generator"],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
@@ -38,31 +40,14 @@ cc_library(
|
|||||||
"@abseil_repo//absl/base:core_headers",
|
"@abseil_repo//absl/base:core_headers",
|
||||||
"//util:status",
|
"//util:status",
|
||||||
"//media_cas_packager_sdk/internal:ecm",
|
"//media_cas_packager_sdk/internal:ecm",
|
||||||
|
"//media_cas_packager_sdk/internal:ecm_generator",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_binary(
|
||||||
name = "ecm_generator",
|
name = "simulcrypt_server",
|
||||||
srcs = ["ecm_generator.cc"],
|
srcs = ["simulcrypt_server.cc"],
|
||||||
hdrs = ["ecm_generator.h"],
|
|
||||||
copts = PUBLIC_COPTS,
|
|
||||||
deps = [
|
deps = [
|
||||||
"//base",
|
"//base",
|
||||||
"@abseil_repo//absl/base:core_headers",
|
|
||||||
"//util:status",
|
|
||||||
"//media_cas_packager_sdk/internal:ecm",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "ecm_generator_test",
|
|
||||||
size = "small",
|
|
||||||
srcs = ["ecm_generator_test.cc"],
|
|
||||||
deps = [
|
|
||||||
":ecm_generator",
|
|
||||||
"//testing:gunit_main",
|
|
||||||
"@abseil_repo//absl/memory",
|
|
||||||
"//common:aes_cbc_util",
|
|
||||||
"//protos/public:media_cas_encryption_proto",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
84
media_cas_packager_sdk/public/simulcrypt_server.cc
Normal file
84
media_cas_packager_sdk/public/simulcrypt_server.cc
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user