ODK and Shared Libraries

In this code drop we introduce the ODK dependency. The reference
implementation has been updated to make use of the ODK and the related
tests have been included.

In addition, we have included an example of how a shared libraries can
be created. This will allow make it easier to test and verify different
implementations of the API.

Most other changes introduce by this code drop were made to clean-up the
reference implementation and limit dependencies.
This commit is contained in:
Aaron Vaage
2020-07-23 16:13:28 -07:00
parent 5d90e8d89b
commit 789377fed2
37 changed files with 1160 additions and 1127 deletions

View File

@@ -9,6 +9,10 @@
#include "crypto_utils/aes_cbc_encryptor.h"
#include "crypto_utils/crypto_util.h"
#include "crypto_utils/sha_util.h"
#include "oemcrypto/odk/include/core_message_deserialize.h"
#include "oemcrypto/odk/include/core_message_serialize_proto.h"
#include "oemcrypto/odk/include/odk.h"
#include "oemcrypto/odk/include/odk_structs.h"
namespace widevine {
@@ -98,6 +102,95 @@ std::string DeriveIV(const std::vector<uint8_t>& context) {
return crypto_util::DeriveIv(context_str);
}
void UpdateKeyControlBlock(
video_widevine::License_KeyContainer_SecurityLevel level,
video_widevine::License_KeyContainer_KeyControl* key_control) {
// The key control block is an 128 bit structure containing the following
// fields. The fields are defined to be in big-endian byte order.
//
// Bytes 0..3: Verification.
// Constant bytes “kctl”, “kc09”, “kc10”, “kc11”, ... “kc15”.
// Bytes 4..7: Obsolete.
// Bytes 8..11: Nonce.
// Bytes 12..15: Control Bits
// Bits 27..26: Security_Level (Only for L3 white-box implementations)
// 0 = SW_SECURE_CRYPTO
// 1 = SW_SECURE_DECODE
// 2 = HW_SECURE_CRYPTO
// 3 = HW_SECURE_DECODE or HW_SECURE_ALL
std::vector<uint8_t> key_control_block = {
'k', 'c', 't', 'l', 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
switch (level) {
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
key_control_block[12] = 0x00 << 2;
break;
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
key_control_block[12] = 0x01 << 2;
break;
case video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO:
key_control_block[12] = 0x02 << 2;
break;
default:
key_control_block[12] = 0x03 << 2;
break;
}
// Key Control Block is no longer encrypted, so no need to set IV.
key_control->set_key_control_block(key_control_block.data(),
key_control_block.size());
}
std::string GenerateCoreMessage(
const std::string& serialized_request,
const std::string& serialized_license_response) {
constexpr uint16_t api_major_version = 16;
constexpr uint16_t api_minor_version = 5;
static_assert(api_major_version == ODK_MAJOR_VERSION,
"Verify ODK library is compatible.");
constexpr uint32_t session_id = 0xcafebabe;
constexpr uint32_t nonce = 0xdeadbeef;
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
// Start by making a call to determine how big the core_message for the
// request needs to be.
size_t core_message_length = 0;
auto odk_result = ODK_PrepareCoreLicenseRequest(
reinterpret_cast<uint8_t*>(const_cast<char*>(serialized_request.data())),
serialized_request.size(), &core_message_length, &nonce_values);
CHECK_EQ(odk_result, OEMCrypto_ERROR_SHORT_BUFFER);
// Now that we know the size, create |combined_request_message| with room
// for the core message and append |serialized_request_|, as the combined
// buffer is needed by ODK_PrepareCoreLicenseRequest().
std::string combined_request_message;
combined_request_message.resize(core_message_length);
combined_request_message.append(serialized_request);
odk_result = ODK_PrepareCoreLicenseRequest(
reinterpret_cast<uint8_t*>(
const_cast<char*>(combined_request_message.data())),
combined_request_message.size(), &core_message_length, &nonce_values);
CHECK_EQ(odk_result, OEMCrypto_SUCCESS);
// As the core_message is the first part of |combined_request_message|,
// extract it.
const std::string request_core_message =
combined_request_message.substr(0, core_message_length);
std::string core_message_hash = widevine::Sha256_Hash(request_core_message);
oemcrypto_core_message::ODK_LicenseRequest core_request{};
CHECK(oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage(
request_core_message, &core_request));
std::string oemcrypto_core_message;
CHECK(oemcrypto_core_message::serialize::CreateCoreLicenseResponseFromProto(
serialized_license_response, core_request, core_message_hash,
/* nonce_required= */ true, &oemcrypto_core_message));
return oemcrypto_core_message;
}
} // namespace
// static
@@ -132,6 +225,7 @@ TestLicenseBuilder::TestLicenseBuilder() {
InitializeResponse(request_, &response_);
serialized_request_ = request_.SerializeAsString();
container_key_ = crypto_util::DeriveKey(
session_key_, crypto_util::kWrappingKeyLabel, serialized_request_,
crypto_util::kWrappingKeySizeBits);
@@ -156,17 +250,20 @@ void TestLicenseBuilder::AddSigningKey(const std::vector<uint8_t>& key,
}
void TestLicenseBuilder::AddStubbedContentKey() {
const video_widevine::License_KeyContainer_SecurityLevel kLevel =
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
auto* container = response_.add_key();
container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT);
container->set_id("stubbed-content-key");
container->set_level(
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO);
container->set_level(kLevel);
container->set_iv("0000000000000000");
// We don't bother encrypting the key, it should never be used and there is no
// way to verify it.
container->set_key("0000000000000000");
// way to verify it. Note that the ODK automatically strips padding, so
// this key needs to include 16 bytes of padding.
container->set_key("00000000000000000000000000000000");
UpdateKeyControlBlock(kLevel, container->mutable_key_control());
}
void TestLicenseBuilder::AddContentKey(
@@ -192,6 +289,7 @@ void TestLicenseBuilder::AddContentKey(
std::vector<uint8_t> final_key = key;
final_key.insert(final_key.end(), padding.begin(), padding.end());
container->set_key(EncryptKey(container_key_, key_iv, final_key));
UpdateKeyControlBlock(level, container->mutable_key_control());
}
void TestLicenseBuilder::AddOperatorSessionKey(
@@ -235,6 +333,10 @@ void TestLicenseBuilder::SetVerificationStatus(VerificationStatus setting) {
}
}
void TestLicenseBuilder::SetUseODK(bool setting) {
use_odk_ = setting;
}
void TestLicenseBuilder::Build(const RsaPublicKey& public_key,
License* license) const {
DCHECK(license);
@@ -246,19 +348,26 @@ void TestLicenseBuilder::Build(const RsaPublicKey& public_key,
crypto_util::kSigningKeySizeBits * 2);
signing_key.resize(crypto_util::kSigningKeySizeBytes);
const std::string signature_str =
crypto_util::CreateSignatureHmacSha256(signing_key, message_str);
std::string session_key_str;
CHECK(public_key.Encrypt(session_key_, &session_key_str));
license->request = std::vector<uint8_t>(serialized_request_.begin(),
serialized_request_.end());
license->message =
std::vector<uint8_t>(message_str.begin(), message_str.end());
license->signature =
std::vector<uint8_t>(signature_str.begin(), signature_str.end());
license->session_key =
std::vector<uint8_t>(session_key_str.begin(), session_key_str.end());
const std::string oemcrypto_core_message =
use_odk_ ? GenerateCoreMessage(serialized_request_, message_str)
: std::string();
// If |use_odk_| is false, |oemcrypto_core_message| will be empty.
const std::string signature_str = crypto_util::CreateSignatureHmacSha256(
signing_key, oemcrypto_core_message + message_str);
license->request.assign(serialized_request_.begin(),
serialized_request_.end());
if (!oemcrypto_core_message.empty()) {
license->core_message.assign(oemcrypto_core_message.begin(),
oemcrypto_core_message.end());
}
license->message.assign(message_str.begin(), message_str.end());
license->signature.assign(signature_str.begin(), signature_str.end());
license->session_key.assign(session_key_str.begin(), session_key_str.end());
}
} // namespace widevine