Source release 19.3.0

This commit is contained in:
John W. Bruce
2024-09-05 07:02:36 +00:00
parent cd8256726f
commit 11c108a8da
122 changed files with 2259 additions and 1082 deletions

View File

@@ -434,3 +434,7 @@ OEMCryptoResult _oecc154(const uint8_t* clear_private_key_bytes,
// OEMCrypto_MarkOfflineSession defined in v19.2
OEMCryptoResult _oecc153(OEMCrypto_SESSION session);
// OEMCrypto_SetSessionUsage defined in v18.7
OEMCryptoResult _oecc155(OEMCrypto_SESSION session, uint32_t intent,
uint32_t mode);

View File

@@ -12,6 +12,7 @@
#include "OEMCryptoCENC.h"
#include "string_conversions.h"
#include "wv_factory_extractor.h"
namespace {
// Make and Model for system ID resolution.
@@ -24,116 +25,8 @@ const std::string kDeviceName = "prov40 test client";
const std::string kDeviceProduct = "prov40 test";
const std::string kDeviceBuildInfo = "prov40 test build";
// == Utils ==
std::string StringMapToJson(
const std::map<std::string, std::string>& string_map) {
std::string json = "{";
for (const auto& value_pair : string_map) {
std::string escaped_value =
std::regex_replace(value_pair.second, std::regex("\""), "\\\"");
json.append("\"" + value_pair.first + "\": " + "\"" + escaped_value +
"\",");
}
json.resize(json.size() - 1); // Remove the last comma.
json.append("}");
return json;
}
// == Primary ==
bool GetBccAndBuildInfo(std::vector<uint8_t>* bcc,
std::string* oemcrypto_build_info) {
// Step 1: Initialize.
OEMCryptoResult result = OEMCrypto_Initialize();
if (result != OEMCrypto_SUCCESS) {
std::cerr << "Failed to initialize: result = " << result << std::endl;
return false;
}
// Step 2: Get BCC.
const OEMCrypto_ProvisioningMethod method = OEMCrypto_GetProvisioningMethod();
if (method != OEMCrypto_BootCertificateChain) {
std::cerr << "ProvisioningMethod is not BCC type: method = ";
std::cerr << method << std::endl;
OEMCrypto_Terminate();
return false;
}
bcc->resize(0);
size_t bcc_size = 0;
std::vector<uint8_t> additional_signature; // It should be empty.
size_t additional_signature_size = 0;
result = OEMCrypto_GetBootCertificateChain(bcc->data(), &bcc_size,
additional_signature.data(),
&additional_signature_size);
if (additional_signature_size != 0) {
std::cerr << "The additional_signature_size required by OEMCrypto is "
<< additional_signature_size
<< ", while it is expected to be zero." << std::endl;
OEMCrypto_Terminate();
return false;
}
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
bcc->resize(bcc_size);
additional_signature.resize(additional_signature_size);
result = OEMCrypto_GetBootCertificateChain(bcc->data(), &bcc_size,
additional_signature.data(),
&additional_signature_size);
}
if (result != OEMCrypto_SUCCESS) {
std::cerr << "Failed to get BCC: result = " << result << std::endl;
OEMCrypto_Terminate();
return false;
}
bcc->resize(bcc_size);
// Step 3: Get oemcrypto build info.
oemcrypto_build_info->resize(0);
size_t oemcrypto_build_info_size = 0;
result = OEMCrypto_BuildInformation(oemcrypto_build_info->data(),
&oemcrypto_build_info_size);
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
oemcrypto_build_info->resize(oemcrypto_build_info_size);
result = OEMCrypto_BuildInformation(oemcrypto_build_info->data(),
&oemcrypto_build_info_size);
}
if (result != OEMCrypto_SUCCESS) {
std::cerr << "Failed to get build information: result = " << result
<< std::endl;
OEMCrypto_Terminate();
return false;
}
oemcrypto_build_info->resize(oemcrypto_build_info_size);
// Step 4: Cleanup.
result = OEMCrypto_Terminate();
if (result != OEMCrypto_SUCCESS) {
std::cerr << "Failed to terminate: result = " << result << std::endl;
return false;
}
return true;
}
bool GenerateBccRecord(const std::vector<uint8_t>& bcc,
const std::string& oemcrypto_build_info,
std::string* bcc_record) {
std::map<std::string, std::string> record;
record["company"] = kDeviceMake;
record["model"] = kDeviceModel;
record["architecture"] = kDeviceArchitecture;
record["name"] = kDeviceName;
record["product"] = kDeviceProduct;
record["build_info"] = kDeviceBuildInfo;
record["bcc"] = wvutil::Base64Encode(bcc);
record["oemcrypto_build_info"] = oemcrypto_build_info;
const std::string record_json = StringMapToJson(record);
bcc_record->assign(record_json.begin(), record_json.end());
return true;
}
bool OutputBccRecord(const std::string& path, const std::string& record) {
std::cout << "Writing BCC record to file " << path << std::endl;
std::cout << record << std::endl;
@@ -154,17 +47,27 @@ int main(int argc, char** argv) {
}
const std::string bcc_path = argv[1];
std::vector<uint8_t> bcc;
std::string oemcrypto_build_info;
if (!GetBccAndBuildInfo(&bcc, &oemcrypto_build_info)) {
std::cerr << "Failed to get BCC or OEMCrypto build info" << std::endl;
widevine::ClientInfo client_info;
client_info.company_name = kDeviceMake;
client_info.arch_name = kDeviceArchitecture;
client_info.device_name = kDeviceName;
client_info.model_name = kDeviceModel;
client_info.product_name = kDeviceProduct;
client_info.build_info = kDeviceBuildInfo;
auto extractor = widevine::WidevineFactoryExtractor::Create(client_info);
if (extractor == nullptr) {
std::cerr << "Failed to create WidevineFactoryExtractor" << std::endl;
return 1;
}
std::string bcc_record;
if (!GenerateBccRecord(bcc, oemcrypto_build_info, &bcc_record)) {
std::cerr << "Failed to generate BCC record" << std::endl;
widevine::Status status = extractor->GenerateUploadRequest(bcc_record);
if (status != widevine::Status::kSuccess) {
std::cerr << "Fail to generate BCC record: " << status << std::endl;
return 1;
}
if (!OutputBccRecord(bcc_path, bcc_record)) {
std::cerr << "Failed to output BCC record" << std::endl;
return 1;

View File

@@ -26,6 +26,8 @@ format below:
+-----------------------+----------------------+--------------------------+
| Private Key |
+-----------------------+
| (DER-encoded PKCS#8) |
+-----------------------+
|oem_private_key| should be a RSA key in PKCS#8 PrivateKeyInfo format.
|oem_public_cert| should be a DER-encoded PKCS#7 certificate chain.

View File

@@ -17,7 +17,6 @@ void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
switch (dest_buffer->type) {
case OEMCrypto_BufferType_Clear:
dest_buffer->buffer.clear.clear_buffer += bytes;
dest_buffer->buffer.clear.clear_buffer_length -= bytes;
break;
case OEMCrypto_BufferType_Secure:
@@ -99,6 +98,11 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
const size_t length =
subsample.num_bytes_clear + subsample.num_bytes_encrypted;
fake_sample.buffers.input_data_length = length;
if (fake_sample.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Clear) {
fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length =
length;
}
fake_sample.subsamples = &subsample;
fake_sample.subsamples_length = 1;
@@ -144,6 +148,11 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
if (subsample.num_bytes_clear > 0) {
fake_sample.buffers.input_data_length = subsample.num_bytes_clear;
if (fake_sample.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Clear) {
fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length =
subsample.num_bytes_clear;
}
fake_subsample.num_bytes_clear = subsample.num_bytes_clear;
fake_subsample.num_bytes_encrypted = 0;
fake_subsample.block_offset = 0;
@@ -167,6 +176,11 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
if (subsample.num_bytes_encrypted > 0) {
fake_sample.buffers.input_data_length = subsample.num_bytes_encrypted;
if (fake_sample.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Clear) {
fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length =
subsample.num_bytes_encrypted;
}
fake_subsample.num_bytes_clear = 0;
fake_subsample.num_bytes_encrypted = subsample.num_bytes_encrypted;
fake_subsample.block_offset = subsample.block_offset;

View File

@@ -10,7 +10,9 @@
#include <cstring>
#include "log.h"
#include "oec_test_data.h"
#include "string_conversions.h"
#include "test_sleep.h"
namespace wvoec {
@@ -68,6 +70,12 @@ void DeviceFeatures::Initialize() {
provisioning_method == OEMCrypto_BootCertificateChain ||
provisioning_method == OEMCrypto_DrmReprovisioning;
printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false");
if (rsa_test_key().empty()) {
set_rsa_test_key(
std::vector<uint8_t>(kTestRSAPKCS8PrivateKeyInfo2_2048,
kTestRSAPKCS8PrivateKeyInfo2_2048 +
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)));
}
generic_crypto =
(OEMCrypto_ERROR_NOT_IMPLEMENTED !=
OEMCrypto_Generic_Encrypt(buffer, 0, buffer, 0, iv,
@@ -129,6 +137,9 @@ void DeviceFeatures::Initialize() {
case LOAD_TEST_RSA_KEY:
printf("LOAD_TEST_RSA_KEY: Call LoadTestRSAKey before deriving keys.\n");
break;
case PRELOADED_RSA_KEY:
printf("PRELOADED_RSA_KEY: Device has test RSA key baked in.\n");
break;
case TEST_PROVISION_30:
printf("TEST_PROVISION_30: Device provisioned with OEM Cert.\n");
break;
@@ -153,9 +164,10 @@ void DeviceFeatures::PickDerivedKey() {
return;
case OEMCrypto_DrmCertificate:
case OEMCrypto_DrmReprovisioning:
if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) {
derive_key_method = LOAD_TEST_RSA_KEY;
}
derive_key_method =
(OEMCrypto_ERROR_NOT_IMPLEMENTED == OEMCrypto_LoadTestRSAKey())
? PRELOADED_RSA_KEY
: LOAD_TEST_RSA_KEY;
return;
case OEMCrypto_Keybox:
if (OEMCrypto_ERROR_NOT_IMPLEMENTED !=

View File

@@ -38,6 +38,7 @@ class DeviceFeatures {
LOAD_TEST_RSA_KEY, // Call LoadTestRSAKey before deriving keys.
TEST_PROVISION_30, // Device has OEM Certificate installed.
TEST_PROVISION_40, // Device has Boot Certificate Chain installed.
PRELOADED_RSA_KEY, // Device has test RSA key baked in.
};
enum DeriveMethod derive_key_method;
@@ -65,6 +66,16 @@ class DeviceFeatures {
// Get a list of output types that should be tested.
const std::vector<OutputType>& GetOutputTypes();
// If the device has a baked in cert, then this is the public key that should
// be used for testing.
const std::vector<uint8_t>& rsa_test_key() const { return rsa_test_key_; };
void set_rsa_test_key(const std::vector<uint8_t>& rsa_test_key) {
rsa_test_key_ = rsa_test_key;
}
void set_rsa_test_key(std::vector<uint8_t>&& rsa_test_key) {
rsa_test_key_ = std::move(rsa_test_key);
}
private:
// Decide which method should be used to derive session keys, based on
// supported featuers.
@@ -77,6 +88,7 @@ class DeviceFeatures {
// A list of possible output types.
std::vector<OutputType> output_types_;
bool initialized_ = false;
std::vector<uint8_t> rsa_test_key_;
};
// There is one global set of features for the version of OEMCrypto being

View File

@@ -582,7 +582,7 @@ void ProvisioningRoundTrip::VerifyLoadFailed() {
}
void Provisioning40RoundTrip::PrepareSession(bool is_oem_key) {
const size_t buffer_size = 5000; // Make sure it is large enough.
const size_t buffer_size = 10240; // Make sure it is large enough.
std::vector<uint8_t> public_key(buffer_size);
size_t public_key_size = buffer_size;
std::vector<uint8_t> public_key_signature(buffer_size);
@@ -644,7 +644,7 @@ OEMCryptoResult Provisioning40RoundTrip::LoadDRMCertResponse() {
}
void Provisioning40CastRoundTrip::PrepareSession() {
const size_t buffer_size = 5000; // Make sure it is large enough.
const size_t buffer_size = 10240; // Make sure it is large enough.
std::vector<uint8_t> public_key(buffer_size);
size_t public_key_size = buffer_size;
std::vector<uint8_t> public_key_signature(buffer_size);
@@ -2041,10 +2041,9 @@ void Session::LoadOEMCert(bool verify_cert) {
void Session::SetTestRsaPublicKey() {
public_ec_.reset();
public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo(
kTestRSAPKCS8PrivateKeyInfo2_2048,
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048));
ASSERT_TRUE(public_rsa_) << "Could not parse test RSA public key #2";
public_rsa_ =
util::RsaPublicKey::LoadPrivateKeyInfo(global_features.rsa_test_key());
ASSERT_TRUE(public_rsa_) << "Could not parse test RSA public key";
}
void Session::SetPublicKeyFromPrivateKeyInfo(OEMCrypto_PrivateKeyType key_type,

View File

@@ -2,17 +2,79 @@
// source code may only be used and distributed under the Widevine
// License Agreement.
//
#include "oemcrypto_basic_test.h"
#include <ctype.h>
#include <inttypes.h>
#include <algorithm>
#include <map>
#include <ostream>
#include <set>
#include <string>
#include <vector>
#include <jsmn.h>
#include "OEMCryptoCENC.h"
#include "clock.h"
#include "jsmn.h"
#include "log.h"
#include "oemcrypto_corpus_generator_helper.h"
#include "oemcrypto_resource_test.h"
#include "test_sleep.h"
void PrintTo(const jsmntype_t& type, std::ostream* out) {
switch (type) {
case JSMN_UNDEFINED:
*out << "Undefined";
return;
case JSMN_OBJECT:
*out << "Object";
return;
case JSMN_ARRAY:
*out << "Array";
return;
case JSMN_STRING:
*out << "String";
return;
case JSMN_PRIMITIVE:
*out << "Primitive";
return;
}
*out << "Unknown(" << static_cast<int>(type) << ')';
}
namespace wvoec {
namespace {
// Counts the number of ancestor tokens of the provided |root_index| token.
// The result does not count the root itself.
//
// JSMN tokens specify the count of immediate ancessor tokens, but
// not the total.
// - Primitives never have children
// - Strings have 0 if they are a value, and 1 if they are the
// name of an object member
// - Objects have the count of members (each key-value pair is 1,
// regardless of the value's children elements)
// - Arrays have the count of elements (regardless of the values members)
//
int32_t JsmnAncestorCount(const std::vector<jsmntok_t>& tokens,
int32_t root_index) {
if (root_index >= static_cast<int32_t>(tokens.size())) return 0;
int32_t count = 0;
int32_t iter = root_index;
int32_t remainder = 1;
while (remainder > 0 && iter < static_cast<int32_t>(tokens.size())) {
const int32_t child_count = tokens[iter].size;
remainder += child_count;
count += child_count;
iter++;
remainder--;
}
return count;
}
} // namespace
void OEMCryptoClientTest::SetUp() {
::testing::Test::SetUp();
wvutil::TestSleep::SyncFakeClock();
@@ -180,16 +242,14 @@ TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) {
*/
TEST_F(OEMCryptoClientTest, VersionNumber) {
const std::string log_message =
"OEMCrypto unit tests for API 19.2. Tests last updated 2024-06-24";
"OEMCrypto unit tests for API 19.3. Tests last updated 2024-09-04";
cout << " " << log_message << "\n";
cout << " "
<< "These tests are part of Android U."
<< "\n";
cout << " " << "These tests are part of Android V." << "\n";
LOGI("%s", log_message.c_str());
// If any of the following fail, then it is time to update the log message
// above.
EXPECT_EQ(ODK_MAJOR_VERSION, 19);
EXPECT_EQ(ODK_MINOR_VERSION, 2);
EXPECT_EQ(ODK_MINOR_VERSION, 3);
EXPECT_EQ(kCurrentAPI, static_cast<unsigned>(ODK_MAJOR_VERSION));
OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel();
EXPECT_GT(level, OEMCrypto_Level_Unknown);
@@ -316,29 +376,154 @@ TEST_F(OEMCryptoClientTest, CheckNullBuildInformationAPI17) {
}
}
// Verifies that OEMCrypto_BuildInformation() is behaving as expected
// by assigning appropriate values to the build info size.
TEST_F(OEMCryptoClientTest, CheckBuildInformation_OutputLengthAPI17) {
if (wvoec::global_features.api_version < 17) {
GTEST_SKIP() << "Test for versions 17 and up only.";
}
constexpr size_t kZero = 0;
constexpr char kNullChar = '\0';
// Allocating single byte to avoid potential null dereference.
std::string build_info(1, kNullChar);
size_t build_info_length = 0;
OEMCryptoResult result =
OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER);
ASSERT_GT(build_info_length, kZero)
<< "Signaling ERROR_SHORT_BUFFER should have assigned a length";
// Force a ERROR_SHORT_BUFFER using a non-zero value.
// Note: It is assumed that vendors will provide more than a single
// character of info.
const size_t second_attempt_length =
(build_info_length >= 2) ? build_info_length / 2 : 1;
build_info.assign(second_attempt_length, kNullChar);
build_info_length = build_info.size();
result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER)
<< "second_attempt_length = " << second_attempt_length
<< ", build_info_length" << build_info_length;
// OEM specified build info length should be larger than the
// original length if returning ERROR_SHORT_BUFFER.
ASSERT_GT(build_info_length, second_attempt_length);
// Final attempt with a buffer large enough buffer, padding to
// ensure the caller truncates.
constexpr size_t kBufferPadSize = 42;
const size_t expected_length = build_info_length;
const size_t final_attempt_length = expected_length + kBufferPadSize;
build_info.assign(final_attempt_length, kNullChar);
build_info_length = build_info.size();
result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
ASSERT_EQ(result, OEMCrypto_SUCCESS)
<< "final_attempt_length = " << final_attempt_length
<< ", expected_length = " << expected_length
<< ", build_info_length = " << build_info_length;
// Ensure not empty.
ASSERT_GT(build_info_length, kZero) << "Build info cannot be empty";
// Ensure it was truncated down from the padded length.
ASSERT_LT(build_info_length, final_attempt_length)
<< "Should have truncated from oversized buffer: expected_length = "
<< expected_length;
// Ensure the real length is within the size originally specified.
// OK if final length is smaller than estimated length.
ASSERT_LE(build_info_length, expected_length);
}
// Verifies that OEMCrypto_BuildInformation() is behaving as expected
// by checking the resulting contents.
// Does not validate whether output if valid JSON for v18.
TEST_F(OEMCryptoClientTest, CheckBuildInformation_OutputContentAPI17) {
if (wvoec::global_features.api_version < 17) {
GTEST_SKIP() << "Test for versions 17 and up only.";
}
constexpr size_t kZero = 0;
constexpr char kNullChar = '\0';
// Allocating single byte to avoid potential null dereference.
std::string build_info(1, kNullChar);
size_t build_info_length = 0;
OEMCryptoResult result =
OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
ASSERT_EQ(result, OEMCrypto_ERROR_SHORT_BUFFER);
ASSERT_GT(build_info_length, kZero)
<< "Signaling ERROR_SHORT_BUFFER should have assigned a length";
// Expect successful acquisition of build information.
const size_t expected_length = build_info_length;
build_info.assign(expected_length, kNullChar);
result = OEMCrypto_BuildInformation(&build_info[0], &build_info_length);
ASSERT_EQ(result, OEMCrypto_SUCCESS)
<< "expected_length = " << expected_length
<< ", build_info_length = " << build_info_length;
// Ensure not empty.
ASSERT_GT(build_info_length, kZero) << "Build info cannot be empty";
// Ensure the real length is within the size originally specified.
ASSERT_LE(build_info_length, expected_length)
<< "Cannot specify success if buffer was too small";
build_info.resize(build_info_length);
// Ensure there isn't a trailing null byte.
ASSERT_NE(build_info.back(), kNullChar)
<< "Build info must not contain trailing null byte";
// Ensure all build info characters are printable, or a limited
// set of white space characters (case of JSON build info).
const auto is_valid_build_info_white_space = [](const char& ch) -> bool {
constexpr char kSpace = ' ';
constexpr char kLineFeed = '\n';
constexpr char kTab = '\t';
return ch == kLineFeed || ch == kTab || ch == kSpace;
};
const auto is_valid_build_info_char = [&](const char& ch) -> bool {
return ::isprint(ch) || is_valid_build_info_white_space(ch);
};
ASSERT_TRUE(std::all_of(build_info.begin(), build_info.end(),
is_valid_build_info_char))
<< "Build info is not printable: " << wvutil::b2a_hex(build_info);
// Ensure build info isn't just white space.
ASSERT_FALSE(std::all_of(build_info.begin(), build_info.end(),
is_valid_build_info_white_space))
<< "Build info is just white space: " << wvutil::b2a_hex(build_info);
}
TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
if (wvoec::global_features.api_version < 18) {
GTEST_SKIP() << "Test for versions 18 and up only.";
}
std::string build_info;
OEMCryptoResult sts = OEMCrypto_BuildInformation(&build_info[0], nullptr);
ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
size_t buf_length = 0;
constexpr char kNullChar = '\0';
constexpr size_t kZero = 0;
// Step 1: Get Build Info
size_t buffer_length = 0;
// OEMCrypto must allow |buffer| to be null so long as |buffer_length|
// is provided and initially set to zero.
sts = OEMCrypto_BuildInformation(nullptr, &buf_length);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
build_info.resize(buf_length);
const size_t max_final_size = buf_length;
sts = OEMCrypto_BuildInformation(&build_info[0], &buf_length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_LE(buf_length, max_final_size);
build_info.resize(buf_length);
OEMCryptoResult result = OEMCrypto_BuildInformation(nullptr, &buffer_length);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
ASSERT_GT(buffer_length, kZero);
std::string build_info(buffer_length, kNullChar);
const size_t max_final_size = buffer_length;
result = OEMCrypto_BuildInformation(&build_info[0], &buffer_length);
ASSERT_EQ(OEMCrypto_SUCCESS, result);
ASSERT_LE(buffer_length, max_final_size);
build_info.resize(buffer_length);
// Step 2: Parse as JSON
jsmn_parser p;
jsmn_init(&p);
std::vector<jsmntok_t> tokens;
int32_t num_tokens =
const int32_t num_tokens =
jsmn_parse(&p, build_info.c_str(), build_info.size(), nullptr, 0);
EXPECT_GT(num_tokens, 0)
<< "Failed to parse BuildInformation as JSON, parse returned "
@@ -346,45 +531,186 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
tokens.resize(num_tokens);
jsmn_init(&p);
int32_t jsmn_result = jsmn_parse(&p, build_info.c_str(), build_info.size(),
tokens.data(), num_tokens);
const int32_t jsmn_result = jsmn_parse(
&p, build_info.c_str(), build_info.size(), tokens.data(), num_tokens);
EXPECT_GE(jsmn_result, 0)
<< "Failed to parse BuildInformation as JSON, parse returned "
<< jsmn_result << "for following build info: " << build_info;
std::map<std::string, jsmntype_t> expected;
expected["soc_vendor"] = JSMN_STRING;
expected["soc_model"] = JSMN_STRING;
expected["ta_ver"] = JSMN_STRING;
expected["uses_opk"] = JSMN_PRIMITIVE;
expected["tee_os"] = JSMN_STRING;
expected["tee_os_ver"] = JSMN_STRING;
// Step 3a: Ensure info is a single JSON object.
const jsmntok_t& object_token = tokens[0];
ASSERT_EQ(object_token.type, JSMN_OBJECT)
<< "Build info is not a JSON object: " << build_info;
// for values in token
// build string from start,end
// check for existence in map
// check if value matches expectation
// remove from map
for (int32_t i = 0; i < jsmn_result; i++) {
jsmntok_t token = tokens[i];
std::string key = build_info.substr(token.start, token.end - token.start);
if (expected.find(key) != expected.end()) {
EXPECT_EQ(expected.find(key)->second, tokens[i + 1].type)
<< "Type is incorrect for key " << key;
expected.erase(key);
// Step 3b: Verify schema of defined fields.
// Required fields must be present in the build information,
// and be of the correct type.
const std::map<std::string, jsmntype_t> kRequiredFields = {
// SOC manufacturer name
{"soc_vendor", JSMN_STRING},
// SOC model name
{"soc_model", JSMN_STRING},
// TA version in string format eg "1.12.3+tag", "2.0"
{"ta_ver", JSMN_STRING},
// [bool] Whether TA was built with Widevine's OPK
{"uses_opk", JSMN_PRIMITIVE},
// Trusted OS intended to run the TA, eg "Trusty", "QSEE", "OP-TEE"
{"tee_os", JSMN_STRING},
// Version of Trusted OS intended to run the TA
{"tee_os_ver", JSMN_STRING},
// [bool] Whether this is a debug build of the TA
// Not forcing behavior until implementations fix
// them self
// {"is_debug", JSMN_PRIMITIVE},
};
const std::string kSpecialCaseReeKey = "ree";
// Optional fields may be present in the build information;
// if they are, then the must be the correct type.
const std::map<std::string, jsmntype_t> kOptionalFields = {
// Name of company or entity that provides OEMCrypto.
{"implementor", JSMN_STRING},
// Git commit hash of the code repository.
{"git_commit", JSMN_STRING},
// ISO 8601 formatted timestamp of the time the TA was compiled
{"build_timestamp", JSMN_STRING},
// Whether this was built with FACTORY_MODE_ONLY defined
{"is_factory_mode", JSMN_PRIMITIVE},
// ... provide information about liboemcrypto.so
// Special case, see kOptionalReeFields for details.
{kSpecialCaseReeKey, JSMN_OBJECT},
// Technically required, but several implementations
// do not implement this fields.
{"is_debug", JSMN_PRIMITIVE},
};
// A set of the required fields found when examining the
// build information, use to verify all fields are present.
std::set<std::string> found_required_fields;
// Stores the tokens of the "ree" field, if set, used to
// validate its content.
std::vector<jsmntok_t> ree_tokens;
bool has_ree_info = false;
// Start: first object key token
// Condition: key-value pair (2 tokens)
// Iter: next key-value pair (2 tokens)
for (int32_t i = 1; (i + 1) < jsmn_result; i += 2) {
// JSMN objects consist of pairs of key-value pairs (keys are always
// JSMN_STRING).
const jsmntok_t& key_token = tokens[i];
ASSERT_EQ(key_token.type, JSMN_STRING)
<< "Bad object key: i = " << i << ", build_info = " << build_info;
const jsmntok_t& value_token = tokens[i + 1];
const std::string key =
build_info.substr(key_token.start, key_token.end - key_token.start);
if (kRequiredFields.find(key) != kRequiredFields.end()) {
ASSERT_EQ(value_token.type, kRequiredFields.at(key))
<< "Unexpected required field type: field = " << key
<< ", build_info = " << build_info;
found_required_fields.insert(key);
} else if (kOptionalFields.find(key) != kOptionalFields.end()) {
ASSERT_EQ(value_token.type, kOptionalFields.at(key))
<< "Unexpected optional field type: field = " << key
<< ", build_info = " << build_info;
} // Do not validate vendor fields.
if (key == kSpecialCaseReeKey) {
// Store the tokens of the "ree" field for additional validation.
const int32_t first_ree_field_index = i + 2;
const int32_t ree_token_count = JsmnAncestorCount(tokens, i + 1);
const auto first_ree_field_iter = tokens.begin() + first_ree_field_index;
ree_tokens.assign(first_ree_field_iter,
first_ree_field_iter + ree_token_count);
has_ree_info = true;
}
// Skip potential nested tokens.
i += JsmnAncestorCount(tokens, i + 1);
}
// if map is not empty, return false
if (expected.size() > 0) {
std::string missing;
for (const auto& e : expected) {
missing.append(e.first);
missing.append(" ");
// Step 3c: Ensure all required fields were found.
if (found_required_fields.size() != kRequiredFields.size()) {
// Generate a list of all the missing fields.
std::string missing_fields;
for (const auto& required_field : kRequiredFields) {
if (found_required_fields.find(required_field.first) !=
found_required_fields.end())
continue;
if (!missing_fields.empty()) {
missing_fields.append(", ");
}
missing_fields.push_back('"');
missing_fields.append(required_field.first);
missing_fields.push_back('"');
}
FAIL() << "JSON does not contain all required keys. Missing keys: ["
<< missing << "] in string " << build_info;
FAIL() << "Build info JSON object does not contain all required keys; "
<< "missing_fields = [" << missing_fields
<< "], build_info = " << build_info;
return;
}
// If no "ree" field tokens, then end here.
if (!has_ree_info) return;
// Step 4a: Verify "ree" object scheme.
ASSERT_FALSE(ree_tokens.empty())
<< "REE field was specified, but contents were empty: build_info = "
<< build_info;
// The optional field "ree", if present, must follow the required
// format.
const std::map<std::string, jsmntype_t> kReeRequiredFields = {
// liboemcrypto.so version in string format eg "2.15.0+tag"
{"liboemcrypto_ver", JSMN_STRING},
// git hash of code that compiled liboemcrypto.so
{"git_commit", JSMN_STRING},
// ISO 8601 timestamp for when liboemcrypto.so was built
{"build_timestamp", JSMN_STRING}};
found_required_fields.clear();
for (int32_t i = 0; (i + 1) < static_cast<int32_t>(ree_tokens.size());
i += 2) {
const jsmntok_t& key_token = ree_tokens[i];
ASSERT_EQ(key_token.type, JSMN_STRING)
<< "Bad REE object key: i = " << i << ", build_info = " << build_info;
const jsmntok_t& value_token = ree_tokens[i + 1];
const std::string key =
build_info.substr(key_token.start, key_token.end - key_token.start);
if (kReeRequiredFields.find(key) != kReeRequiredFields.end()) {
ASSERT_EQ(value_token.type, kReeRequiredFields.at(key))
<< "Unexpected optional REE field type: ree_field = " << key
<< ", build_info = " << build_info;
found_required_fields.insert(key);
} // Do not validate vendor fields.
// Skip potential nested tokens.
i += JsmnAncestorCount(ree_tokens, i + 1);
}
// Step 4b: Ensure all required fields of the "ree" object were found.
if (found_required_fields.size() == kReeRequiredFields.size()) return;
// Generate a list of all the missing REE fields.
std::string missing_ree_fields;
for (const auto& required_field : kReeRequiredFields) {
if (found_required_fields.find(required_field.first) !=
found_required_fields.end())
continue;
if (!missing_ree_fields.empty()) {
missing_ree_fields.append(", ");
}
missing_ree_fields.push_back('"');
missing_ree_fields.append(required_field.first);
missing_ree_fields.push_back('"');
}
FAIL() << "REE info JSON object does not contain all required keys; "
<< "missing_ree_fields = [" << missing_ree_fields
<< "], build_info = " << build_info;
}
TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) {

View File

@@ -121,38 +121,6 @@ TEST_P(OEMCryptoLicenseTest, RejectCensAPI16) {
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
}
// 'cbc1' mode is no longer supported in v16
TEST_P(OEMCryptoLicenseTest, RejectCbc1API16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
vector<uint8_t> key_handle;
OEMCryptoResult sts;
sts = GetKeyHandleIntoVector(session_.session_id(),
session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length,
OEMCrypto_CipherMode_CBCS, key_handle);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
// Create a zero pattern to indicate this is 'cbc1'
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(key_handle.data(), key_handle.size(),
&sample_description, 1, &pattern);
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
}
TEST_P(OEMCryptoLicenseTest, RejectCbcsWithBlockOffset) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());

View File

@@ -70,6 +70,9 @@ void SessionUtil::EnsureTestROT() {
case DeviceFeatures::TEST_PROVISION_30:
// Can use oem certificate to install test rsa key.
break;
case DeviceFeatures::PRELOADED_RSA_KEY:
// There is already a key.
break;
case wvoec::DeviceFeatures::TEST_PROVISION_40:
// OEM certificate is retrieved from the server.
break;

View File

@@ -96,6 +96,9 @@ TEST_F(OEMCryptoSessionTests, License_IncrementCounterAPI18) {
if (wvoec::global_features.api_version < 18) {
GTEST_SKIP() << "Test for versions 18 and up only.";
}
if (OEMCrypto_SecurityLevel() == OEMCrypto_Level3) {
GTEST_SKIP() << "L3 does not support license counter.";
}
Session s;
s.open();
LicenseRoundTrip license_messages(&s);
@@ -141,6 +144,9 @@ TEST_F(OEMCryptoSessionTests, MasterGeneration_IncrementCounterAPI18) {
GTEST_SKIP() << "Usage table not supported, so master generation number "
"does not need to be checked.";
}
if (OEMCrypto_SecurityLevel() == OEMCrypto_Level3) {
GTEST_SKIP() << "L3 does not support license counter.";
}
Session s1;
s1.open();
LicenseRoundTrip license_messages(&s1);