Merge from Widevine repo of http://go/wvgerrit/93404 This is the unit tests, reference code, and documentation for OEMCrypto v16.2. Backwards compatibility should work for a v15 OEMCrypto. Some review comments will be addressed in future CLs. Bug: 141247171 Test: Unit tests Test: Media GTS tests on bonito Change-Id: I9d427c07580e180c0a4cfdc4a68f538d351c0ddd
836 lines
30 KiB
C++
836 lines
30 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 "odk.h"
|
|
|
|
#include <endian.h> // TODO(b/147944591): use this one? Or odk_endian.h?
|
|
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "OEMCryptoCENCCommon.h"
|
|
#include "core_message_deserialize.h"
|
|
#include "core_message_serialize.h"
|
|
#include "core_message_types.h"
|
|
#include "gtest/gtest.h"
|
|
#include "odk_structs.h"
|
|
#include "odk_structs_priv.h"
|
|
|
|
namespace {
|
|
|
|
using oemcrypto_core_message::ODK_LicenseRequest;
|
|
using oemcrypto_core_message::ODK_ProvisioningRequest;
|
|
using oemcrypto_core_message::ODK_RenewalRequest;
|
|
|
|
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
|
|
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
|
|
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
|
|
|
|
using oemcrypto_core_message::serialize::CreateCoreLicenseResponse;
|
|
using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
|
|
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
|
|
|
|
enum ODK_FieldType {
|
|
ODK_UINT16,
|
|
ODK_UINT32,
|
|
ODK_UINT64,
|
|
ODK_SUBSTRING,
|
|
ODK_DEVICEID,
|
|
ODK_HASH,
|
|
ODK_NUMTYPES,
|
|
};
|
|
|
|
enum ODK_FieldMode {
|
|
ODK_READ,
|
|
ODK_WRITE,
|
|
ODK_DUMP,
|
|
};
|
|
|
|
struct ODK_Field {
|
|
ODK_FieldType type;
|
|
void* value;
|
|
std::string name;
|
|
};
|
|
|
|
size_t ODK_FieldLength(ODK_FieldType type) {
|
|
switch (type) {
|
|
case ODK_UINT16:
|
|
return sizeof(uint16_t);
|
|
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 ODK_DEVICE_ID_LEN_MAX;
|
|
case ODK_HASH:
|
|
return ODK_SHA256_HASH_SIZE;
|
|
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 (!buf || !field || !field->value) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
switch (field->type) {
|
|
case ODK_UINT16: {
|
|
uint16_t u16 = htobe16(*static_cast<uint16_t*>(field->value));
|
|
memcpy(buf, &u16, sizeof(u16));
|
|
break;
|
|
}
|
|
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:
|
|
case ODK_HASH: {
|
|
const size_t field_len = ODK_FieldLength(field->type);
|
|
const uint8_t* const id = static_cast<uint8_t*>(field->value);
|
|
memcpy(buf, id, field_len);
|
|
|
|
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_UINT16: {
|
|
memcpy(field->value, buf, sizeof(uint16_t));
|
|
uint16_t* u16p = static_cast<uint16_t*>(field->value);
|
|
*u16p = be16toh(*u16p);
|
|
break;
|
|
}
|
|
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:
|
|
case ODK_HASH: {
|
|
const size_t field_len = ODK_FieldLength(field->type);
|
|
uint8_t* const id = static_cast<uint8_t*>(field->value);
|
|
memcpy(id, buf, field_len);
|
|
break;
|
|
}
|
|
default:
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
OEMCryptoResult ODK_DumpSingleField(const uint8_t* const buf,
|
|
const ODK_Field* const field) {
|
|
if (!field || !field->value) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
switch (field->type) {
|
|
case ODK_UINT16: {
|
|
uint16_t val;
|
|
memcpy(&val, buf, sizeof(uint16_t));
|
|
val = be16toh(val);
|
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case ODK_UINT32: {
|
|
uint32_t val;
|
|
memcpy(&val, buf, sizeof(uint32_t));
|
|
val = be32toh(val);
|
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case ODK_UINT64: {
|
|
uint64_t val;
|
|
memcpy(&val, buf, sizeof(uint64_t));
|
|
val = be64toh(val);
|
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case ODK_SUBSTRING: {
|
|
uint32_t off = 0;
|
|
uint32_t len = 0;
|
|
memcpy(&off, buf, sizeof(off));
|
|
memcpy(&len, buf + sizeof(off), sizeof(len));
|
|
std::cerr << field->name << ": (off=" << off << ", len=" << len << ")\n";
|
|
break;
|
|
}
|
|
case ODK_DEVICEID:
|
|
case ODK_HASH: {
|
|
const size_t field_len = ODK_FieldLength(field->type);
|
|
std::cerr << field->name << ": ";
|
|
for (size_t i = 0; i < field_len; i++) {
|
|
std::cerr << std::hex << std::setfill('0') << std::setw(2) << buf[i]
|
|
<< "\n";
|
|
}
|
|
std::cerr << "\n";
|
|
break;
|
|
}
|
|
default:
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
std::cerr << std::dec; // Return to normal.
|
|
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,
|
|
const 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;
|
|
switch (mode) {
|
|
case ODK_WRITE:
|
|
ODK_WriteSingleField(buf_off, &fields[i]);
|
|
break;
|
|
case ODK_READ:
|
|
ODK_ReadSingleField(buf_off, &fields[i]);
|
|
break;
|
|
case ODK_DUMP:
|
|
ODK_DumpSingleField(buf_off, &fields[i]);
|
|
break;
|
|
default:
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
off = off2;
|
|
}
|
|
*size_out = off;
|
|
if (*size_out > size_in) {
|
|
return ODK_ERROR_CORE_MESSAGE;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
void expect_eq_buf(const void* s1, const void* s2, size_t n,
|
|
const std::vector<ODK_Field>& fields) {
|
|
if (memcmp(s1, s2, n)) {
|
|
const void* buffers[] = {s1, s2};
|
|
for (int i = 0; i < 2; i++) {
|
|
char _tmp[] = "/tmp/fileXXXXXX";
|
|
const int temp_fd = mkstemp(_tmp);
|
|
if (temp_fd >= 0) {
|
|
close(temp_fd);
|
|
} else {
|
|
std::cerr << "Failed to open temp file." << std::endl;
|
|
break;
|
|
}
|
|
std::string tmp(_tmp);
|
|
std::fstream out(tmp, std::ios::out | std::ios::binary);
|
|
out.write(static_cast<const char*>(buffers[i]), n);
|
|
out.close();
|
|
std::cerr << "buffer " << i << " dumped to " << tmp << std::endl;
|
|
size_t bytes_written;
|
|
uint8_t* buf =
|
|
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffers[i]));
|
|
ODK_IterFields(ODK_DUMP, buf, n, &bytes_written, fields);
|
|
}
|
|
FAIL();
|
|
}
|
|
}
|
|
|
|
template <typename T, typename F, typename G>
|
|
void ValidateRequest(uint32_t message_type,
|
|
const std::vector<ODK_Field>& extra_fields,
|
|
const F& odk_prepare_func, const G& kdo_parse_func) {
|
|
uint32_t message_size = 0;
|
|
uint16_t api_major_version = ODK_MAJOR_VERSION;
|
|
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
|
uint32_t nonce = 0xdeadbeef;
|
|
uint32_t session_id = 0xcafebabe;
|
|
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
|
session_id};
|
|
std::vector<ODK_Field> total_fields = {
|
|
{ODK_UINT32, &message_type, "message_type"},
|
|
{ODK_UINT32, &message_size, "message_size"},
|
|
{ODK_UINT16, &api_minor_version, "api_minor_version"},
|
|
{ODK_UINT16, &api_major_version, "api_major_version"},
|
|
{ODK_UINT32, &nonce, "nonce"},
|
|
{ODK_UINT32, &session_id, "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, &nonce_values));
|
|
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_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, message_size, total_fields));
|
|
|
|
// odk kdo roundtrip
|
|
T t = {};
|
|
std::string oemcrypto_core_message(reinterpret_cast<char*>(buf),
|
|
message_size);
|
|
EXPECT_TRUE(kdo_parse_func(oemcrypto_core_message, &t));
|
|
nonce_values.api_minor_version = t.api_minor_version;
|
|
nonce_values.api_major_version = t.api_major_version;
|
|
nonce_values.nonce = t.nonce;
|
|
nonce_values.session_id = t.session_id;
|
|
EXPECT_EQ(OEMCrypto_SUCCESS,
|
|
odk_prepare_func(buf2, &bytes_written, &nonce_values));
|
|
EXPECT_EQ(bytes_written, message_size);
|
|
EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, message_size, total_fields));
|
|
|
|
delete[] buf;
|
|
delete[] buf2;
|
|
}
|
|
|
|
/**
|
|
* Template arguments:
|
|
* T: kdo input struct
|
|
* F: odk deserializer
|
|
* G: kdo serializer
|
|
*/
|
|
template <typename T, typename F, typename G>
|
|
void ValidateResponse(uint32_t message_type,
|
|
const std::vector<ODK_Field>& extra_fields,
|
|
const F& odk_parse_func, const G& kdo_prepare_func) {
|
|
uint32_t message_size = 0;
|
|
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
|
uint16_t api_major_version = ODK_MAJOR_VERSION;
|
|
uint32_t nonce = 0xdeadbeef;
|
|
uint32_t session_id = 0xcafebabe;
|
|
std::vector<ODK_Field> total_fields = {
|
|
{ODK_UINT32, &message_type, "message_type"},
|
|
{ODK_UINT32, &message_size, "message_size"},
|
|
{ODK_UINT16, &api_minor_version, "api_minor_version"},
|
|
{ODK_UINT16, &api_major_version, "api_major_version"},
|
|
{ODK_UINT32, &nonce, "nonce"},
|
|
{ODK_UINT32, &session_id, "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* zero = new uint8_t[message_size]();
|
|
size_t bytes_read = 0, bytes_written = 0;
|
|
|
|
T t = {};
|
|
t.api_minor_version = api_minor_version;
|
|
t.api_major_version = api_major_version;
|
|
t.nonce = nonce;
|
|
t.session_id = session_id;
|
|
|
|
// 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
|
|
ODK_NonceValues nonce_values{ODK_MINOR_VERSION, api_major_version, nonce,
|
|
session_id};
|
|
EXPECT_EQ(OEMCrypto_SUCCESS,
|
|
odk_parse_func(buf, bytes_written, &nonce_values));
|
|
|
|
// serialize odk output to oemcrypto_core_message
|
|
std::string oemcrypto_core_message;
|
|
EXPECT_TRUE(kdo_prepare_func(t, &oemcrypto_core_message));
|
|
EXPECT_EQ(bytes_written, message_size);
|
|
|
|
EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, oemcrypto_core_message.data(),
|
|
message_size, total_fields));
|
|
delete[] buf;
|
|
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], "x[0]"}, {ODK_UINT32, &x[1], "x[1]"},
|
|
{ODK_UINT32, &x[2], "x[2]"}, {ODK_UINT64, &y[0], "y[0]"},
|
|
{ODK_UINT64, &y[1], "y[1]"}, {ODK_UINT64, &y[2], "y[2]"},
|
|
{ODK_SUBSTRING, &s, "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_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, bytes_read, fields));
|
|
}
|
|
|
|
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));
|
|
fields[i].value = malloc(ODK_AllocSize(fields[i].type));
|
|
fields[i].name = "stress";
|
|
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_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, total_size, fields));
|
|
|
|
// 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,
|
|
ODK_NonceValues* nonce_values) {
|
|
return ODK_PrepareCoreLicenseRequest(buf, SIZE_MAX, size, nonce_values);
|
|
};
|
|
auto kdo_parse_func = CoreLicenseRequestFromMessage;
|
|
ValidateRequest<ODK_LicenseRequest>(ODK_License_Request_Type, empty,
|
|
odk_prepare_func, kdo_parse_func);
|
|
}
|
|
|
|
TEST(OdkTest, RenewalRequest) {
|
|
constexpr uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
|
|
uint64_t playback_time = 0xCAFE00000000;
|
|
const uint64_t playback_start = system_time_seconds - playback_time;
|
|
const std::vector<ODK_Field> extra_fields = {
|
|
{ODK_UINT64, &playback_time, "playback_time"},
|
|
};
|
|
ODK_ClockValues clock_values = {0};
|
|
clock_values.time_of_first_decrypt = playback_start;
|
|
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
|
|
ODK_NonceValues* nonce_values) {
|
|
return ODK_PrepareCoreRenewalRequest(buf, SIZE_MAX, size, nonce_values,
|
|
&clock_values, system_time_seconds);
|
|
};
|
|
auto kdo_parse_func = [&](const std::string& oemcrypto_core_message,
|
|
ODK_RenewalRequest* core_renewal_request) {
|
|
bool ok = CoreRenewalRequestFromMessage(oemcrypto_core_message,
|
|
core_renewal_request);
|
|
if (ok) {
|
|
playback_time = core_renewal_request->playback_time_seconds;
|
|
}
|
|
return ok;
|
|
};
|
|
ValidateRequest<ODK_RenewalRequest>(ODK_Renewal_Request_Type, extra_fields,
|
|
odk_prepare_func, kdo_parse_func);
|
|
}
|
|
|
|
TEST(OdkTest, ProvisionRequest) {
|
|
uint32_t device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
|
|
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
|
|
memset(device_id, 0xff, device_id_length);
|
|
std::vector<ODK_Field> extra_fields = {
|
|
{ODK_UINT32, &device_id_length, "device_id_length"},
|
|
{ODK_DEVICEID, device_id, "device_id"},
|
|
};
|
|
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
|
|
const ODK_NonceValues* nonce_values) {
|
|
return ODK_PrepareCoreProvisioningRequest(buf, SIZE_MAX, size, nonce_values,
|
|
device_id, device_id_length);
|
|
};
|
|
auto kdo_parse_func =
|
|
[&](const std::string& oemcrypto_core_message,
|
|
ODK_ProvisioningRequest* core_provisioning_request) {
|
|
bool ok = CoreProvisioningRequestFromMessage(oemcrypto_core_message,
|
|
core_provisioning_request);
|
|
if (ok) {
|
|
const std::string& device_id_str =
|
|
core_provisioning_request->device_id;
|
|
device_id_length = device_id_str.size();
|
|
memcpy(device_id, device_id_str.data(), device_id_length);
|
|
}
|
|
return ok;
|
|
};
|
|
ValidateRequest<ODK_ProvisioningRequest>(ODK_Provisioning_Request_Type,
|
|
extra_fields, odk_prepare_func,
|
|
kdo_parse_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 = OEMCrypto_EntitlementLicense,
|
|
.nonce_required = true,
|
|
.timer_limits =
|
|
{
|
|
.soft_enforce_rental_duration = true,
|
|
.soft_enforce_playback_duration = false,
|
|
.earliest_playback_start_seconds = 10,
|
|
.rental_duration_seconds = 11,
|
|
.total_playback_duration_seconds = 12,
|
|
.initial_renewal_duration_seconds = 13,
|
|
},
|
|
.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},
|
|
},
|
|
},
|
|
};
|
|
|
|
const uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
|
|
uint8_t request_hash_read[ODK_SHA256_HASH_SIZE];
|
|
memcpy(request_hash_read, request_hash, sizeof(request_hash));
|
|
std::vector<ODK_Field> extra_fields = {
|
|
{ODK_SUBSTRING, &parsed_license.enc_mac_keys_iv, ".enc_mac_keys_iv"},
|
|
{ODK_SUBSTRING, &parsed_license.enc_mac_keys, ".enc_mac_keys"},
|
|
{ODK_SUBSTRING, &parsed_license.pst, ".pst"},
|
|
{ODK_SUBSTRING, &parsed_license.srm_restriction_data,
|
|
".srm_restriction_data"},
|
|
{ODK_UINT32, &parsed_license.license_type, ".license_type"},
|
|
{ODK_UINT32, &parsed_license.nonce_required, ".nonce_required"},
|
|
{ODK_UINT32, &parsed_license.timer_limits.soft_enforce_rental_duration,
|
|
".soft_enforce_rental_duration"},
|
|
{ODK_UINT32, &parsed_license.timer_limits.soft_enforce_playback_duration,
|
|
".soft_enforce_playback_duration"},
|
|
{ODK_UINT64, &parsed_license.timer_limits.earliest_playback_start_seconds,
|
|
".earliest_playback_start_seconds"},
|
|
{ODK_UINT64, &parsed_license.timer_limits.rental_duration_seconds,
|
|
".rental_duration_seconds"},
|
|
{ODK_UINT64, &parsed_license.timer_limits.total_playback_duration_seconds,
|
|
".total_playback_duration_seconds"},
|
|
{ODK_UINT64,
|
|
&parsed_license.timer_limits.initial_renewal_duration_seconds,
|
|
".initial_renewal_duration_seconds"},
|
|
{ODK_UINT32, &parsed_license.key_array_length, ".key_array_length"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[0].key_id, ".key_id"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data_iv, ".key_data_iv"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data, ".key_data"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control_iv,
|
|
".key_control_iv"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control, ".key_control"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[1].key_id, ".key_id"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data_iv, ".key_data_iv"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data, ".key_data"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control_iv,
|
|
".key_control_iv"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control, ".key_control"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[2].key_id, ".key_id"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data_iv, ".key_data_iv"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data, ".key_data"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control_iv,
|
|
".key_control_iv"},
|
|
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control, ".key_control"},
|
|
{ODK_HASH, request_hash_read, ".request_hash"},
|
|
};
|
|
const std::string request_hash_string(
|
|
reinterpret_cast<const char*>(request_hash), sizeof(request_hash));
|
|
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
|
|
ODK_NonceValues* nonce_values) {
|
|
ODK_TimerLimits timer_limits;
|
|
ODK_ClockValues clock_values;
|
|
constexpr bool initial_license_load = true;
|
|
constexpr bool usage_entry_present = true;
|
|
return ODK_ParseLicense(buf, size + 128, size, initial_license_load,
|
|
usage_entry_present, request_hash, &timer_limits,
|
|
&clock_values, nonce_values, &parsed_license);
|
|
};
|
|
auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request,
|
|
std::string* oemcrypto_core_message) {
|
|
return CreateCoreLicenseResponse(parsed_license, core_request,
|
|
request_hash_string,
|
|
oemcrypto_core_message);
|
|
};
|
|
ValidateResponse<ODK_LicenseRequest>(ODK_License_Response_Type, extra_fields,
|
|
odk_parse_func, kdo_prepare_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;
|
|
constexpr uint64_t renewal_duration = 130;
|
|
uint64_t var_renewal_duration = renewal_duration;
|
|
std::vector<ODK_Field> extra_fields = {
|
|
{ODK_UINT64, &message_playback_clock, "message_playback_clock"},
|
|
{ODK_UINT64, &var_renewal_duration, "renewal_duration"},
|
|
};
|
|
|
|
ODK_TimerLimits timer_limits = {
|
|
.soft_enforce_rental_duration = false,
|
|
.soft_enforce_playback_duration = false,
|
|
.earliest_playback_start_seconds = 0,
|
|
.rental_duration_seconds = 1000,
|
|
.total_playback_duration_seconds = 2000,
|
|
.initial_renewal_duration_seconds = 30,
|
|
};
|
|
|
|
ODK_ClockValues clock_values = {
|
|
.time_of_license_signed = system_time - playback_clock - 42,
|
|
.time_of_first_decrypt = system_time - playback_clock,
|
|
.time_of_last_decrypt = 0,
|
|
.time_of_renewal_request = message_playback_clock,
|
|
.time_when_timer_expires = system_time + playback_timer,
|
|
.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED,
|
|
.status = kUnused,
|
|
};
|
|
|
|
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
|
|
ODK_NonceValues* nonce_values) {
|
|
OEMCryptoResult err =
|
|
ODK_ParseRenewal(buf, size, size, nonce_values, system_time,
|
|
&timer_limits, &clock_values, &playback_timer);
|
|
|
|
EXPECT_EQ(ODK_SET_TIMER, err);
|
|
EXPECT_EQ(renewal_duration, 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;
|
|
};
|
|
auto kdo_prepare_func = [&](ODK_RenewalRequest& core_request,
|
|
std::string* oemcrypto_core_message) {
|
|
core_request.playback_time_seconds = message_playback_clock;
|
|
return CreateCoreRenewalResponse(core_request, renewal_duration,
|
|
oemcrypto_core_message);
|
|
};
|
|
ValidateResponse<ODK_RenewalRequest>(ODK_Renewal_Response_Type, extra_fields,
|
|
odk_parse_func, kdo_prepare_func);
|
|
}
|
|
|
|
TEST(OdkTest, ProvisionResponse) {
|
|
uint32_t device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
|
|
uint8_t device_id[ODK_DEVICE_ID_LEN_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, "device_id_length"},
|
|
{ODK_DEVICEID, device_id, "device_id"},
|
|
{ODK_UINT32, &parsed_response.key_type, "key_type"},
|
|
{ODK_SUBSTRING, &parsed_response.enc_private_key, "enc_private_key"},
|
|
{ODK_SUBSTRING, &parsed_response.enc_private_key_iv,
|
|
"enc_private_key_iv"},
|
|
{ODK_SUBSTRING, &parsed_response.encrypted_message_key,
|
|
"encrypted_message_key"},
|
|
};
|
|
|
|
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
|
|
ODK_NonceValues* nonce_values) {
|
|
// restore device id because it is not part of parsed_response
|
|
device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
|
|
memset(device_id, 0xff, device_id_length);
|
|
OEMCryptoResult err =
|
|
ODK_ParseProvisioning(buf, size + 16, size, nonce_values, device_id,
|
|
device_id_length, &parsed_response);
|
|
return err;
|
|
};
|
|
auto kdo_prepare_func = [&](ODK_ProvisioningRequest& core_request,
|
|
std::string* oemcrypto_core_message) {
|
|
core_request.device_id.assign(reinterpret_cast<char*>(device_id),
|
|
device_id_length);
|
|
return CreateCoreProvisioningResponse(parsed_response, core_request,
|
|
oemcrypto_core_message);
|
|
};
|
|
ValidateResponse<ODK_ProvisioningRequest>(ODK_Provisioning_Response_Type,
|
|
extra_fields, odk_parse_func,
|
|
kdo_prepare_func);
|
|
}
|
|
|
|
TEST(OdkSizeTest, LicenseRequest) {
|
|
uint8_t* message = nullptr;
|
|
size_t message_length = 0;
|
|
size_t core_message_length = 0;
|
|
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
|
uint16_t api_major_version = 0;
|
|
uint32_t nonce = 0;
|
|
uint32_t session_id = 0;
|
|
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
|
session_id};
|
|
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
|
ODK_PrepareCoreLicenseRequest(message, message_length,
|
|
&core_message_length, &nonce_values));
|
|
// 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;
|
|
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
|
uint16_t api_major_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;
|
|
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
|
session_id};
|
|
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
|
ODK_PrepareCoreRenewalRequest(message, message_length,
|
|
&core_message_length, &nonce_values,
|
|
&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;
|
|
uint16_t api_minor_version = ODK_MINOR_VERSION;
|
|
uint16_t api_major_version = 0;
|
|
uint32_t nonce = 0;
|
|
uint32_t session_id = 0;
|
|
uint32_t device_id_length = 0;
|
|
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
|
session_id};
|
|
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
|
ODK_PrepareCoreProvisioningRequest(
|
|
message, message_length, &core_message_length, &nonce_values,
|
|
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);
|
|
}
|
|
|
|
} // namespace
|