Files
oemcrypto/oemcrypto/odk/test/odk_test.cpp
Fred Gylys-Colwell 4de11d11e8 Initial v16 ODK Library
This commit has the initial ODK library.  Partners may use this code
to begin integrating the ODK library into their platform.  The
functionality is not complete, but this should help partners get an
early start playing with build files.
2019-10-04 16:47:20 -07:00

636 lines
22 KiB
C++

/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include <bits/stdint-uintn.h>
#include <string.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include "odk.h"
#include "odk_test.h"
size_t ODK_FieldLength(ODK_FieldType type) {
switch (type) {
case ODK_UINT32:
return sizeof(uint32_t);
case ODK_UINT64:
return sizeof(uint64_t);
case ODK_SUBSTRING:
return sizeof(uint32_t) + sizeof(uint32_t);
case ODK_DEVICEID:
return DEVICE_ID_MAX;
default:
return SIZE_MAX;
}
}
size_t ODK_AllocSize(ODK_FieldType type) {
if (type == ODK_SUBSTRING) {
return sizeof(OEMCrypto_Substring);
}
return ODK_FieldLength(type);
}
OEMCryptoResult ODK_WriteSingleField(uint8_t* const buf,
const ODK_Field* const field) {
if (!field || !field->value) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT32: {
uint32_t u32 = htobe32(*static_cast<uint32_t*>(field->value));
memcpy(buf, &u32, sizeof(u32));
break;
}
case ODK_UINT64: {
uint64_t u64 = htobe64(*static_cast<uint64_t*>(field->value));
memcpy(buf, &u64, sizeof(u64));
break;
}
case ODK_SUBSTRING: {
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
uint32_t off = htobe32(s->offset);
uint32_t len = htobe32(s->length);
memcpy(buf, &off, sizeof(off));
memcpy(buf + sizeof(off), &len, sizeof(len));
break;
}
case ODK_DEVICEID: {
const uint8_t* const id = static_cast<uint8_t*>(field->value);
memcpy(buf, id, DEVICE_ID_MAX);
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf,
const ODK_Field* const field) {
if (!field || !field->value) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT32: {
memcpy(field->value, buf, sizeof(uint32_t));
uint32_t* u32p = static_cast<uint32_t*>(field->value);
*u32p = be32toh(*u32p);
break;
}
case ODK_UINT64: {
memcpy(field->value, buf, sizeof(uint64_t));
uint64_t* u64p = static_cast<uint64_t*>(field->value);
*u64p = be64toh(*u64p);
break;
}
case ODK_SUBSTRING: {
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
uint32_t off = 0;
uint32_t len = 0;
memcpy(&off, buf, sizeof(off));
memcpy(&len, buf + sizeof(off), sizeof(len));
s->offset = be32toh(off);
s->length = be32toh(len);
break;
}
case ODK_DEVICEID: {
uint8_t* const id = static_cast<uint8_t*>(field->value);
memcpy(id, buf, DEVICE_ID_MAX);
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
/*
* Parameters:
* [in] size_in: buffer size
* [out] size_out: bytes processed
*/
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* const buf,
const size_t size_in, size_t* size_out,
std::vector<ODK_Field>& fields) {
if (!buf || !size_out) {
return ODK_ERROR_CORE_MESSAGE;
}
size_t off = 0, off2 = 0;
for (size_t i = 0; i < fields.size(); i++) {
if (__builtin_add_overflow(off, ODK_FieldLength(fields[i].type), &off2) ||
off2 > size_in) {
return ODK_ERROR_CORE_MESSAGE;
}
uintptr_t base = reinterpret_cast<uintptr_t>(buf);
if (__builtin_add_overflow(base, off, &base)) {
return ODK_ERROR_CORE_MESSAGE;
}
uint8_t* const buf_off = buf + off;
if (mode == ODK_WRITE) {
ODK_WriteSingleField(buf_off, &fields[i]);
} else if (mode == ODK_READ) {
ODK_ReadSingleField(buf_off, &fields[i]);
} else {
return ODK_ERROR_CORE_MESSAGE;
}
off = off2;
}
*size_out = off;
if (*size_out > size_in) {
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ReadFields(const uint8_t* const buf, const size_t size_in,
size_t* size_out,
std::vector<ODK_Field>& fields) {
return ODK_IterFields(ODK_READ, const_cast<uint8_t*>(buf), size_in, size_out,
fields);
}
OEMCryptoResult ODK_WriteFields(uint8_t* const buf, const size_t size_in,
size_t* size_out,
std::vector<ODK_Field>& fields) {
return ODK_IterFields(ODK_WRITE, buf, size_in, size_out, fields);
}
OEMCryptoResult ODK_ValidateSubstrings(const size_t size, const size_t n,
const ODK_Field* const fields) {
if (!fields) {
return ODK_ERROR_CORE_MESSAGE;
}
size_t off = 0;
for (size_t i = 0; i < n; i++) {
if (fields[i].type != ODK_SUBSTRING) {
continue;
}
if (!fields[i].value) {
return ODK_ERROR_CORE_MESSAGE;
}
size_t end = 0;
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(fields[i].value);
if (s->offset > size ||
__builtin_add_overflow(s->offset, s->length, &end) || end > size) {
return ODK_ERROR_CORE_MESSAGE;
}
}
return OEMCrypto_SUCCESS;
}
void expect_eq_buf(const void* s1, const void* s2, size_t n) {
if (memcmp(s1, s2, n)) {
const void* buffers[] = {s1, s2};
for (int i = 0; i < 2; i++) {
char _tmp[] = "/tmp/fileXXXXXX";
mkstemp(_tmp);
std::string tmp(_tmp);
std::fstream out(tmp, std::ios::out | std::ios::binary);
out.write((char*)buffers[i], n);
out.close();
std:
std::cerr << "buffer " << i << " dumped to " << tmp << std::endl;
}
FAIL();
}
}
template <typename F>
void ValidateRequest(uint32_t message_type,
std::vector<ODK_Field>& extra_fields,
const F& odk_prepare_func) {
uint32_t message_size = 0;
uint32_t api_version = 16;
uint32_t nonce = 0xdeadbeef;
uint32_t session_id = 0xcafebabe;
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type}, {ODK_UINT32, &message_size},
{ODK_UINT32, &api_version}, {ODK_UINT32, &nonce},
{ODK_UINT32, &session_id},
};
total_fields.insert(total_fields.end(), extra_fields.begin(),
extra_fields.end());
for (auto& field : total_fields) {
message_size += ODK_FieldLength(field.type);
}
uint8_t* buf = new uint8_t[message_size]();
uint8_t* buf2 = new uint8_t[message_size]();
size_t bytes_written = message_size;
EXPECT_EQ(
OEMCrypto_SUCCESS,
odk_prepare_func(buf, &bytes_written, api_version, nonce, session_id));
EXPECT_EQ(bytes_written, message_size);
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX,
&bytes_written, total_fields));
EXPECT_EQ(bytes_written, message_size);
expect_eq_buf(buf, buf2, message_size);
delete[] buf;
delete[] buf2;
}
template <typename F>
void ValidateResponse(uint32_t message_type,
std::vector<ODK_Field>& extra_fields,
const F& odk_parse_func) {
uint32_t message_size = 0;
uint32_t api_version = 16;
uint32_t nonce = 0xdeadbeef;
uint32_t session_id = 0xcafebabe;
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type}, {ODK_UINT32, &message_size},
{ODK_UINT32, &api_version}, {ODK_UINT32, &nonce},
{ODK_UINT32, &session_id},
};
uint32_t header_size = 0;
for (auto& field : total_fields) {
header_size += ODK_FieldLength(field.type);
}
total_fields.insert(total_fields.end(), extra_fields.begin(),
extra_fields.end());
for (auto& field : total_fields) {
message_size += ODK_FieldLength(field.type);
}
uint8_t* buf = new uint8_t[message_size]();
uint8_t* buf2 = new uint8_t[message_size]();
uint8_t* zero = new uint8_t[message_size]();
size_t bytes_read = 0, bytes_written = 0;
// serialize input to buf
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf, SIZE_MAX,
&bytes_written, total_fields));
EXPECT_EQ(bytes_written, message_size);
// zero-out input
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_READ, zero, bytes_written,
&bytes_read, extra_fields));
EXPECT_TRUE(bytes_written > bytes_read &&
bytes_written - bytes_read == header_size);
// parse buf with odk
EXPECT_EQ(OEMCrypto_SUCCESS,
odk_parse_func(buf, bytes_written, api_version, nonce, session_id));
// serialize odk output to buf2
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX,
&bytes_written, total_fields));
EXPECT_EQ(bytes_written, message_size);
expect_eq_buf(buf, buf2, message_size);
delete[] buf;
delete[] buf2;
delete[] zero;
}
TEST(OdkTest, SerializeFields) {
uint32_t x[] = {0, 1, 2};
uint64_t y[] = {3ll << 32, 4ll << 32, 5ll << 32};
OEMCrypto_Substring s = {.offset = 6, .length = 7};
std::vector<ODK_Field> fields = {
{ODK_UINT32, &x[0]}, {ODK_UINT32, &x[1]}, {ODK_UINT32, &x[2]},
{ODK_UINT64, &y[0]}, {ODK_UINT64, &y[1]}, {ODK_UINT64, &y[2]},
{ODK_SUBSTRING, &s},
};
uint8_t buf[1024] = {0};
uint8_t buf2[1024] = {0};
size_t bytes_read = 0, bytes_written = 0;
ODK_IterFields(ODK_WRITE, buf, SIZE_MAX, &bytes_read, fields);
ODK_IterFields(ODK_READ, buf, bytes_read, &bytes_written, fields);
ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX, &bytes_read, fields);
expect_eq_buf(buf, buf2, bytes_read);
}
TEST(OdkTest, SerializeFieldsStress) {
const int n = 1024;
std::vector<ODK_Field> fields(n);
std::srand(0);
size_t total_size = 0;
for (int i = 0; i < n; i++) {
fields[i].type = static_cast<ODK_FieldType>(std::rand() %
static_cast<int>(ODK_NUMTYPES));
size_t field_size = ODK_AllocSize(fields[i].type);
fields[i].value = malloc(ODK_AllocSize(fields[i].type));
total_size += ODK_FieldLength(fields[i].type);
}
uint8_t* buf = new uint8_t[total_size];
for (int i = 0; i < total_size; i++) {
buf[i] = std::rand() & 0xff;
}
size_t bytes_read = 0, bytes_written = 0;
uint8_t* buf2 = new uint8_t[total_size];
ODK_IterFields(ODK_READ, buf, total_size, &bytes_read, fields);
EXPECT_EQ(bytes_read, total_size);
ODK_IterFields(ODK_WRITE, buf2, total_size, &bytes_written, fields);
EXPECT_EQ(bytes_written, total_size);
expect_eq_buf(buf, buf2, total_size);
// cleanup
for (int i = 0; i < n; i++) {
free(fields[i].value);
}
delete[] buf;
delete[] buf2;
}
TEST(OdkTest, LicenseRequest) {
std::vector<ODK_Field> empty;
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
return ODK_PrepareCoreLicenseRequest(buf, SIZE_MAX, size, api_version,
nonce, session_id);
};
ValidateRequest(ODK_License_Request_Type, empty, odk_prepare_func);
}
TEST(OdkTest, RenewalRequest) {
uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &system_time_seconds},
};
ODK_ClockValues clock_values = {0};
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
return ODK_PrepareCoreRenewalRequest(buf, SIZE_MAX, size, api_version,
nonce, session_id, &clock_values,
system_time_seconds);
};
ValidateRequest(ODK_Renewal_Request_Type, extra_fields, odk_prepare_func);
}
TEST(OdkTest, ProvisionRequest) {
uint32_t device_id_length = DEVICE_ID_MAX / 2;
uint8_t device_id[DEVICE_ID_MAX] = {0};
memset(device_id, 0xff, device_id_length);
std::vector<ODK_Field> extra_fields = {
{ODK_UINT32, &device_id_length},
{ODK_DEVICEID, device_id},
};
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
return ODK_PrepareCoreProvisioningRequest(buf, SIZE_MAX, size, api_version,
nonce, session_id, device_id,
device_id_length);
};
ValidateRequest(ODK_Provisioning_Request_Type, extra_fields,
odk_prepare_func);
}
TEST(OdkTest, LicenseResponse) {
ODK_ParsedLicense parsed_license = {
.enc_mac_keys_iv = {.offset = 0, .length = 1},
.enc_mac_keys = {.offset = 2, .length = 3},
.pst = {.offset = 4, .length = 5},
.srm_restriction_data = {.offset = 6, .length = 7},
.license_type = 8,
.nonce_required = 0xDEADC0DE,
.timer_limits =
{
.soft_expiry = 9,
.earliest_playback_start_seconds = 10,
.latest_playback_start_seconds = 11,
.initial_playback_duration_seconds = 12,
.renewal_playback_duration_seconds = 13,
.license_duration_seconds = 14,
},
.key_array_length = 3,
.key_array =
{
{
.key_id = {.offset = 15, .length = 16},
.key_data_iv = {.offset = 17, .length = 18},
.key_data = {.offset = 19, .length = 20},
.key_control_iv = {.offset = 21, .length = 22},
.key_control = {.offset = 23, .length = 24},
},
{
.key_id = {.offset = 25, .length = 26},
.key_data_iv = {.offset = 27, .length = 28},
.key_data = {.offset = 29, .length = 30},
.key_control_iv = {.offset = 31, .length = 32},
.key_control = {.offset = 33, .length = 34},
},
{
.key_id = {.offset = 35, .length = 36},
.key_data_iv = {.offset = 37, .length = 38},
.key_data = {.offset = 39, .length = 40},
.key_control_iv = {.offset = 41, .length = 42},
.key_control = {.offset = 43, .length = 44},
},
},
};
uint32_t message_type = ODK_License_Response_Type;
std::vector<ODK_Field> extra_fields = {
{ODK_SUBSTRING, &parsed_license.enc_mac_keys_iv},
{ODK_SUBSTRING, &parsed_license.enc_mac_keys},
{ODK_SUBSTRING, &parsed_license.pst},
{ODK_SUBSTRING, &parsed_license.srm_restriction_data},
{ODK_UINT32, &parsed_license.license_type},
{ODK_UINT32, &parsed_license.nonce_required},
{ODK_UINT32, &parsed_license.timer_limits.soft_expiry},
{ODK_UINT64,
&parsed_license.timer_limits.earliest_playback_start_seconds},
{ODK_UINT64, &parsed_license.timer_limits.latest_playback_start_seconds},
{ODK_UINT64,
&parsed_license.timer_limits.initial_playback_duration_seconds},
{ODK_UINT64,
&parsed_license.timer_limits.renewal_playback_duration_seconds},
{ODK_UINT64, &parsed_license.timer_limits.license_duration_seconds},
{ODK_UINT32, &parsed_license.key_array_length},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_id},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data_iv},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control_iv},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_id},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data_iv},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control_iv},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_id},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data_iv},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control_iv},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control},
};
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
return ODK_ParseLicense(buf, size + 128, api_version, nonce, session_id, 0,
0, 3, &parsed_license);
};
ValidateResponse(ODK_License_Response_Type, extra_fields, odk_parse_func);
}
TEST(OdkTest, RenewalResponse) {
uint64_t system_time = 0xfaceb00c;
uint64_t playback_clock = 11;
uint64_t playback_timer = 12;
uint64_t message_playback_clock = 10;
std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &message_playback_clock},
};
ODK_TimerLimits timer_limits = {
.soft_expiry = 0,
.earliest_playback_start_seconds = 0,
.latest_playback_start_seconds = 100,
.initial_playback_duration_seconds = 10,
.renewal_playback_duration_seconds = 20,
.license_duration_seconds = 100,
};
ODK_ClockValues clock_values = {
.time_of_license_signed = 0,
.time_of_first_decrypt = system_time - playback_clock,
.time_of_last_decrypt = 0,
.time_when_timer_expires = system_time + playback_timer,
.timer_status = 0,
.status = kUnused,
};
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
OEMCryptoResult err =
ODK_ParseRenewal(buf, size, api_version, nonce, session_id, system_time,
&timer_limits, &clock_values, &playback_timer);
EXPECT_EQ(ODK_SET_TIMER, err);
EXPECT_EQ(timer_limits.renewal_playback_duration_seconds, playback_timer);
EXPECT_EQ(clock_values.time_when_timer_expires,
system_time + playback_timer);
// manually restore message_playback_clock since ODK_ParseRenewal doesn't
// generate output
message_playback_clock = 10;
return OEMCrypto_SUCCESS;
};
ValidateResponse(ODK_Renewal_Response_Type, extra_fields, odk_parse_func);
}
TEST(OdkTest, ProvisionResponse) {
uint32_t device_id_length = DEVICE_ID_MAX / 2;
uint8_t device_id[DEVICE_ID_MAX] = {0};
memset(device_id, 0xff, device_id_length);
ODK_ParsedProvisioning parsed_response = {
.enc_private_key = {.offset = 0, .length = 1},
.enc_private_key_iv = {.offset = 2, .length = 3},
.encrypted_message_key = {.offset = 4, .length = 5},
};
std::vector<ODK_Field> extra_fields = {
{ODK_UINT32, &device_id_length},
{ODK_DEVICEID, device_id},
{ODK_UINT32, &parsed_response.key_type},
{ODK_SUBSTRING, &parsed_response.enc_private_key},
{ODK_SUBSTRING, &parsed_response.enc_private_key_iv},
{ODK_SUBSTRING, &parsed_response.encrypted_message_key},
};
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
OEMCryptoResult err =
ODK_ParseProvisioning(buf, size + 16, api_version, nonce, session_id,
device_id, device_id_length, &parsed_response);
// restore device id because it is not part of parsed_response
device_id_length = DEVICE_ID_MAX / 2;
memset(device_id, 0xff, device_id_length);
return err;
};
ValidateResponse(ODK_Provisioning_Response_Type, extra_fields,
odk_parse_func);
}
TEST(OdkSizeTest, LicenseRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint32_t api_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreLicenseRequest(message, message_length,
&core_message_length, api_version,
nonce, session_id));
// All messages have at least a five 4-byte fields.
size_t minimum_message_size = 5 * 4;
EXPECT_GE(core_message_length, minimum_message_size);
}
TEST(OdkSizeTest, RenewalRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint32_t api_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
ODK_ClockValues clock_values;
clock_values.time_of_first_decrypt = 10;
uint64_t system_time_seconds = 15;
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreRenewalRequest(
message, message_length, &core_message_length, api_version,
nonce, session_id, &clock_values, system_time_seconds));
// All messages have at least a five 4-byte fields.
size_t minimum_message_size = 5 * 4;
EXPECT_GE(core_message_length, minimum_message_size);
}
TEST(OdkSizeTest, ProvisioningRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint32_t api_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
uint8_t* device_id = nullptr;
uint32_t device_id_length = 0;
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreProvisioningRequest(
message, message_length, &core_message_length, api_version,
nonce, session_id, nullptr, device_id_length));
// All messages have at least a five 4-byte fields.
size_t minimum_message_size = 5 * 4;
EXPECT_GE(core_message_length, minimum_message_size);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}