Initial Code Drop
This is the initial code drop of the reference implementation and test cases for the Widevine Whitebox API. In this drop, the full reference implementation for the AEAD white-box is provided and all test cases verifying the top-level behave have are enabled. Since the implementations can vary so much the testing is mostly left to verifying the return codes for specific parameter conditions. A full reference implementation for the license white-box is provided, however not all tests are implemented or enabled. A number of tests have been disabled as they required a loaded license and test licenses are still being worked on. The two license white-box API functions that are the further from competition are ProcessLicenseResponse() and MaskedDecryt(). ProcessLicenseResponse() is still being worked on and MaskedDecrypt() is waiting on Decrypt() to be fully functional. Most tests focus on verifying return code for specific parameter conditions, but as test licenses are created, tests looking to test the internal behaviour of license management will be added to ProcessLicenseResponse(), Decrypt(), and MaskedDecrypt().
This commit is contained in:
308
crypto_utils/crypto_util_test.cc
Normal file
308
crypto_utils/crypto_util_test.cc
Normal file
@@ -0,0 +1,308 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Unit tests for the crypto_util helper functions.
|
||||
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
#include "third_party/boringssl/src/include/openssl/aes.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace crypto_util {
|
||||
|
||||
const char kCENCStr[] = "cenc";
|
||||
const char kCBC1Str[] = "cbc1";
|
||||
const char kCENSStr[] = "cens";
|
||||
const char kCBCSStr[] = "cbcs";
|
||||
|
||||
static unsigned char kAes128KeyData[] = {0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82,
|
||||
0x32, 0x9e, 0x6b, 0x3b, 0x4e, 0x29,
|
||||
0xfa, 0x3b, 0x00, 0x4b};
|
||||
|
||||
static unsigned char kAes256KeyData[] = {
|
||||
0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82, 0x32, 0x9e, 0x6b, 0x3b, 0x4e,
|
||||
0x29, 0xfa, 0x3b, 0x00, 0x4b, 0x87, 0x27, 0xa4, 0x0e, 0xbd, 0x82,
|
||||
0x32, 0x9e, 0x6b, 0x3b, 0x4e, 0x29, 0xfa, 0x3b, 0x00, 0x4b};
|
||||
|
||||
static unsigned char kAes128IvData[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
|
||||
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
|
||||
0x0c, 0x0d, 0x0e, 0x0f};
|
||||
|
||||
class CryptoUtilTest : public ::testing::Test {
|
||||
public:
|
||||
CryptoUtilTest()
|
||||
: aes_128_key_(kAes128KeyData, kAes128KeyData + sizeof(kAes128KeyData)),
|
||||
aes_256_key_(kAes256KeyData, kAes256KeyData + sizeof(kAes256KeyData)),
|
||||
iv_128_(kAes128IvData, kAes128IvData + sizeof(kAes128IvData)) {}
|
||||
|
||||
protected:
|
||||
std::string aes_128_key_;
|
||||
std::string aes_256_key_;
|
||||
std::string iv_128_;
|
||||
};
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveAes128MasterKeyTest) {
|
||||
unsigned char label[] = {0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1, 0x92,
|
||||
0xa0, 0x34, 0x8a, 0x8b, 0x6b, 0x77, 0x08, 0xbc, 0x23,
|
||||
0x70, 0x16, 0xbc, 0xda, 0xfb, 0x60, 0xd1, 0xcf, 0x6a,
|
||||
0x4d, 0x40, 0xa1, 0xe3, 0xfe, 0xd3, 0xe9, 0xa6, 0x58,
|
||||
0x4c, 0xd4, 0xad, 0xa4, 0xa2};
|
||||
|
||||
unsigned char context[] = {0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d,
|
||||
0x6d, 0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5,
|
||||
0x5b, 0x80, 0x81, 0x91, 0xff};
|
||||
|
||||
unsigned char output0[] = {0xd5, 0xad, 0x2d, 0xb1, 0x5a, 0x06, 0xcb, 0x50,
|
||||
0xf2, 0x59, 0x5a, 0xb2, 0xb2, 0x0d, 0x44, 0x4e};
|
||||
|
||||
unsigned char output1[] = {
|
||||
0xdf, 0x38, 0x45, 0x97, 0x5d, 0x7a, 0x81, 0xb4, 0x94, 0x86, 0xaf, 0x0c,
|
||||
0xdc, 0x4d, 0xeb, 0x62, 0x31, 0x39, 0x67, 0x8f, 0xff, 0x5d, 0x68, 0x35,
|
||||
0xdc, 0x89, 0x5f, 0x47, 0xca, 0xe0, 0x2d, 0x3a, 0x10, 0x24, 0xf8, 0x7e,
|
||||
0x5b, 0x70, 0xe1, 0xa3, 0x4a, 0x47, 0x2f, 0x04, 0xe0, 0x34, 0x75, 0x22};
|
||||
|
||||
std::string label_str(label, label + sizeof(label));
|
||||
std::string context_str(context, context + sizeof(context));
|
||||
std::string result = DeriveKey(aes_128_key_, label_str, context_str, 128);
|
||||
|
||||
std::string output_128(output0, output0 + sizeof(output0));
|
||||
|
||||
ASSERT_EQ(result, output_128);
|
||||
|
||||
result = DeriveKey(aes_128_key_, label_str, context_str, 384);
|
||||
|
||||
std::string output_384(output1, output1 + sizeof(output1));
|
||||
|
||||
ASSERT_EQ(result, output_384);
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveAes256MasterKeyTest) {
|
||||
const unsigned char label[] = {
|
||||
0x16, 0xf1, 0xa4, 0x32, 0x9f, 0x94, 0x55, 0xc1, 0x92, 0xa0, 0x34,
|
||||
0x8a, 0x8b, 0x6b, 0x77, 0x08, 0xbc, 0x23, 0x70, 0x16, 0xbc, 0xda,
|
||||
0xfb, 0x60, 0xd1, 0xcf, 0x6a, 0x4d, 0x40, 0xa1, 0xe3, 0xfe, 0xd3,
|
||||
0xe9, 0xa6, 0x58, 0x4c, 0xd4, 0xad, 0xa4, 0xa2};
|
||||
|
||||
const unsigned char context[] = {0x4c, 0x53, 0xc0, 0xe9, 0x9e, 0x7f, 0x7d,
|
||||
0x6d, 0x0a, 0x76, 0x7c, 0xc7, 0x25, 0xb5,
|
||||
0x5b, 0x80, 0x81, 0x91, 0xff};
|
||||
|
||||
const unsigned char expected_128[] = {0x76, 0x36, 0x33, 0x0e, 0x0b, 0x2c,
|
||||
0x38, 0xc2, 0x9e, 0x53, 0x23, 0x8d,
|
||||
0x2e, 0xc6, 0x3a, 0x46};
|
||||
|
||||
const std::string label_str(label, label + sizeof(label));
|
||||
const std::string context_str(context, context + sizeof(context));
|
||||
std::string result = DeriveKey(aes_256_key_, label_str, context_str, 128);
|
||||
EXPECT_EQ(std::string(expected_128, expected_128 + sizeof(expected_128)),
|
||||
result)
|
||||
<< absl::BytesToHexString(result);
|
||||
|
||||
const unsigned char expected_256[] = {
|
||||
0xfb, 0x8f, 0xdf, 0x0e, 0x22, 0xfe, 0xf7, 0x2b, 0xd1, 0x9a, 0x1d,
|
||||
0xd2, 0xcb, 0xb0, 0x11, 0x5c, 0x6c, 0xa7, 0xe1, 0x7f, 0x72, 0xce,
|
||||
0x3a, 0x60, 0x34, 0x89, 0x6d, 0x08, 0xef, 0xde, 0x19, 0x45};
|
||||
result = DeriveKey(aes_256_key_, label_str, context_str, 256);
|
||||
EXPECT_EQ(std::string(expected_256, expected_256 + sizeof(expected_256)),
|
||||
result)
|
||||
<< absl::BytesToHexString(result);
|
||||
|
||||
const unsigned char expected_384[] = {
|
||||
0x65, 0xbc, 0xe3, 0xf3, 0xfb, 0xfa, 0xce, 0x1d, 0x24, 0x63, 0x9c, 0x8f,
|
||||
0x48, 0x0e, 0xbd, 0x76, 0xd1, 0x14, 0x0b, 0xb1, 0x3a, 0x3d, 0x6e, 0x30,
|
||||
0xa9, 0xf4, 0x40, 0x35, 0x0d, 0x6b, 0xc5, 0x1e, 0x9c, 0xa9, 0x5f, 0xf9,
|
||||
0xde, 0x96, 0xa0, 0xa4, 0x22, 0x62, 0x21, 0xc5, 0xd6, 0xd4, 0xf4, 0x6f};
|
||||
result = DeriveKey(aes_256_key_, label_str, context_str, 384);
|
||||
EXPECT_EQ(std::string(expected_384, expected_384 + sizeof(expected_384)),
|
||||
result)
|
||||
<< absl::BytesToHexString(result);
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveAesInvalidSizeModulus) {
|
||||
// This is the control case that we correctly derive 128 bits.
|
||||
EXPECT_NE("", DeriveKey(aes_128_key_, "foo", "bar", 128));
|
||||
EXPECT_EQ("", DeriveKey(aes_128_key_, "foo", "bar", 127));
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveAesMaxBlocks) {
|
||||
EXPECT_EQ(
|
||||
255 * AES_BLOCK_SIZE,
|
||||
DeriveKey(aes_128_key_, "foo", "bar", AES_BLOCK_SIZE * 8 * 255).size());
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveAesTooManyBlocks) {
|
||||
EXPECT_EQ("",
|
||||
DeriveKey(aes_128_key_, "foo", "bar", AES_BLOCK_SIZE * 8 * 256));
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveAes128InvalidKeySize) {
|
||||
EXPECT_EQ("", DeriveKey(aes_128_key_.substr(0, 15), "foo", "bar", 128));
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveAes256InvalidKeySize) {
|
||||
EXPECT_EQ("", DeriveKey(aes_256_key_.substr(0, 31), "foo", "bar", 128));
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveGroupSesionKey) {
|
||||
unsigned char output[] = {0x92, 0x6c, 0x2f, 0x5, 0xa6, 0x4f, 0xff, 0xb1,
|
||||
0x86, 0x4a, 0x1a, 0x14, 0x95, 0xeb, 0xb0, 0xf1};
|
||||
std::string group_session_key = DeriveGroupSessionKey("test_group_id", 128);
|
||||
EXPECT_EQ(crypto_util::kAes128KeySizeBytes, group_session_key.size());
|
||||
const std::string output_128(output, output + sizeof(output));
|
||||
ASSERT_EQ(output_128, group_session_key);
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha256) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, 0x99, 0x7a, 0x7d,
|
||||
0x9b, 0x0c, 0xcf, 0xfd, 0xb2, 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad,
|
||||
0x23, 0x91, 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, 0xf6,
|
||||
0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, 0x99, 0x7a, 0x63, 0x47,
|
||||
0x2e, 0x54, 0x35, 0xb5, 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2,
|
||||
0x54, 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda};
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
std::string signature(CreateSignatureHmacSha256(aes_128_key_, message));
|
||||
|
||||
ASSERT_EQ(signature.size(), 32);
|
||||
|
||||
ASSERT_TRUE(VerifySignatureHmacSha256(aes_128_key_, signature, message));
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, TestFailCreateAndVerifyHmacSha256) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, 0x99, 0x7a, 0x7d,
|
||||
0x9b, 0x0c, 0xcf, 0xfd, 0xb2, 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad,
|
||||
0x23, 0x91, 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, 0xf6,
|
||||
0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, 0x99, 0x7a, 0x63, 0x47,
|
||||
0x2e, 0x54, 0x35, 0xb5, 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2,
|
||||
0x54, 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda};
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
// Test with bogus key;
|
||||
std::string bogus_key("bogus");
|
||||
std::string signature(CreateSignatureHmacSha256(bogus_key, message));
|
||||
|
||||
// This should still produce an hmac signature.
|
||||
ASSERT_EQ(signature.size(), 32);
|
||||
|
||||
// Create valid signature to compare.
|
||||
signature = CreateSignatureHmacSha256(aes_128_key_, message);
|
||||
|
||||
// Test with bogus key.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha256(bogus_key, signature, message));
|
||||
|
||||
// Test with munged signature.
|
||||
signature[0] = 0xFF;
|
||||
ASSERT_FALSE(VerifySignatureHmacSha256(aes_128_key_, signature, message));
|
||||
|
||||
// Test with bogus signature.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha256(aes_128_key_, "bogus", message));
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, TestCreateAndVerifySignatureHmacSha1) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, 0x99, 0x7a, 0x7d,
|
||||
0x9b, 0x0c, 0xcf, 0xfd, 0xb2, 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad,
|
||||
0x23, 0x91, 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, 0xf6,
|
||||
0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, 0x99, 0x7a, 0x63, 0x47,
|
||||
0x2e, 0x54, 0x35, 0xb5, 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2,
|
||||
0x54, 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda};
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
std::string signature(CreateSignatureHmacSha1(aes_128_key_, message));
|
||||
|
||||
ASSERT_EQ(20, signature.size());
|
||||
ASSERT_TRUE(VerifySignatureHmacSha1(aes_128_key_, signature, message));
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, TestFailCreateAndVerifyHmacSha1) {
|
||||
unsigned char message_data[] = {
|
||||
0xd9, 0x24, 0x2d, 0x03, 0x93, 0x6f, 0x22, 0x53, 0x99, 0x7a, 0x7d,
|
||||
0x9b, 0x0c, 0xcf, 0xfd, 0xb2, 0x66, 0x0d, 0xaf, 0xdb, 0xa2, 0xad,
|
||||
0x23, 0x91, 0x8a, 0xdf, 0x01, 0x80, 0xa3, 0x35, 0xf9, 0xde, 0xf6,
|
||||
0x5b, 0xa2, 0x85, 0x0e, 0x2d, 0x93, 0x6f, 0x99, 0x7a, 0x63, 0x47,
|
||||
0x2e, 0x54, 0x35, 0xb5, 0xf7, 0x45, 0xed, 0x6b, 0xcf, 0xe8, 0xf2,
|
||||
0x54, 0x97, 0x69, 0x23, 0x74, 0x34, 0x9a, 0x34, 0xda};
|
||||
|
||||
std::string message(message_data, message_data + sizeof(message_data));
|
||||
// Test with bogus key;
|
||||
std::string bogus_key("bogus");
|
||||
std::string signature(CreateSignatureHmacSha1(bogus_key, message));
|
||||
|
||||
// This should still produce an hmac signature.
|
||||
ASSERT_EQ(20, signature.size());
|
||||
// Create valid signature to compare.
|
||||
signature = CreateSignatureHmacSha1(aes_128_key_, message);
|
||||
// Test with bogus key.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha1(bogus_key, signature, message));
|
||||
// Test with munged signature.
|
||||
signature[0] = 0xFF;
|
||||
ASSERT_FALSE(VerifySignatureHmacSha1(aes_128_key_, signature, message));
|
||||
// Test with bogus signature.
|
||||
ASSERT_FALSE(VerifySignatureHmacSha1(aes_128_key_, "bogus", message));
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveIv) {
|
||||
// First value in the pair is the key_id, second value is the expected IV.
|
||||
std::pair<std::string, std::string> id_iv_pairs[] = {
|
||||
{"1234567890123456", "3278234c7682d1a2e153af4912975f5f"},
|
||||
{"0987654321098765", "cf09abd30f04b60544910791a6b904cf"}};
|
||||
for (const auto& id_iv_pair : id_iv_pairs) {
|
||||
SCOPED_TRACE(absl::StrCat("test case:", id_iv_pair.first));
|
||||
EXPECT_EQ(id_iv_pair.second,
|
||||
absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
|
||||
// Repeat same call to verify derivied result is repeatable.
|
||||
EXPECT_EQ(id_iv_pair.second,
|
||||
absl::BytesToHexString(DeriveIv(id_iv_pair.first)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, DeriveKeyId) {
|
||||
// First value in the pair is the context, second value is the expected id.
|
||||
std::pair<std::string, std::string> context_id_pairs[] = {
|
||||
{"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"},
|
||||
{"0987654321098765", "084fc6bece9688ccce6b1672d9b47e22"}};
|
||||
for (const auto& context_id_pair : context_id_pairs) {
|
||||
SCOPED_TRACE(absl::StrCat("test case:", context_id_pair.first));
|
||||
EXPECT_EQ(context_id_pair.second,
|
||||
absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
|
||||
// Repeat same call to verify derivied result is repeatable.
|
||||
EXPECT_EQ(context_id_pair.second,
|
||||
absl::BytesToHexString(DeriveKeyId(context_id_pair.first)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) {
|
||||
uint32_t cc_code;
|
||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code));
|
||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("junk", &cc_code));
|
||||
ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("cencc", &cc_code));
|
||||
}
|
||||
|
||||
TEST_F(CryptoUtilTest, Verify4CCEncryptionIDFromString) {
|
||||
uint32_t cc_code = 0;
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENCStr, &cc_code));
|
||||
ASSERT_EQ(kCENCSchemeID, cc_code);
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBC1Str, &cc_code));
|
||||
ASSERT_EQ(kCBC1SchemeID, cc_code);
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCENSStr, &cc_code));
|
||||
ASSERT_EQ(kCENSSchemeID, cc_code);
|
||||
ASSERT_TRUE(FourCCEncryptionSchemeIDFromString(kCBCSStr, &cc_code));
|
||||
ASSERT_EQ(kCBCSSchemeID, cc_code);
|
||||
}
|
||||
|
||||
} // namespace crypto_util
|
||||
} // namespace widevine
|
||||
Reference in New Issue
Block a user