blob: f6e316118cac2aabf0d8e45281efd45d686c4762 [file] [log] [blame]
#include <memory>
#include "gtest/gtest.h"
#include "avb_tools.h"
#include "nugget_tools.h"
#include "nugget/app/avb/avb.pb.h"
#include "Avb.client.h"
#include "src/test-data/test-keys/reset_key_data.h"
#include <avb.h>
#include <application.h>
#include <nos/AppClient.h>
#include <nos/NuggetClientInterface.h>
#include "util.h"
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
using std::cout;
using std::string;
using std::unique_ptr;
using namespace nugget::app::avb;
using namespace avb_tools;
namespace {
class AvbTest: public testing::Test {
protected:
static unique_ptr<nos::NuggetClientInterface> client;
static unique_ptr<test_harness::TestHarness> uart_printer;
static void SetUpTestCase();
static void TearDownTestCase();
virtual void SetUp(void);
int ProductionResetTest(uint32_t selector, uint64_t nonce,
const uint8_t *device_data, size_t data_len,
const uint8_t *signature, size_t signature_len);
int SignChallenge(const struct ResetMessage *message,
uint8_t *signature, size_t *siglen);
int Load(uint8_t slot, uint64_t *version);
int Store(uint8_t slot, uint64_t version);
int SetDeviceLock(uint8_t locked);
int SetBootLock(uint8_t locked);
int SetOwnerLock(uint8_t locked, const uint8_t *metadata, size_t size);
int GetOwnerKey(uint32_t offset, uint8_t *metadata, size_t *size);
int SetCarrierLock(uint8_t locked, const uint8_t *metadata, size_t size);
public:
const uint64_t LAST_NONCE = 0x4141414141414140ULL;
const uint64_t VERSION = 1;
const uint64_t NONCE = 0x4141414141414141ULL;
const uint8_t DEVICE_DATA[AVB_DEVICE_DATA_SIZE] = {
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
};
const uint8_t SIGNATURE[AVB_SIGNATURE_SIZE] = {
0x68, 0x86, 0x9a, 0x16, 0xca, 0x62, 0xea, 0xa9,
0x9b, 0xa0, 0x51, 0x03, 0xa6, 0x00, 0x3f, 0xe8,
0xf1, 0x43, 0xe6, 0xb7, 0xde, 0x76, 0xfe, 0x21,
0x65, 0x87, 0x78, 0xe5, 0x1d, 0x11, 0x6a, 0xe1,
0x7b, 0xc6, 0x2e, 0xe2, 0x96, 0x25, 0x48, 0xa7,
0x09, 0x43, 0x2c, 0xfd, 0x28, 0xa9, 0x66, 0x8a,
0x09, 0xd5, 0x83, 0x3b, 0xde, 0x18, 0x5d, 0xef,
0x50, 0x12, 0x8a, 0x8d, 0xfb, 0x2d, 0x46, 0x20,
0x69, 0x55, 0x4e, 0x86, 0x63, 0xf6, 0x10, 0xe3,
0x59, 0x3f, 0x55, 0x72, 0x18, 0xcb, 0x60, 0x80,
0x0d, 0x2e, 0x2f, 0xfc, 0xc2, 0xbf, 0xda, 0x3f,
0x4f, 0x2b, 0x6b, 0xf1, 0x5d, 0x28, 0x6b, 0x2b,
0x9b, 0x92, 0xf3, 0x4e, 0xf2, 0xb6, 0x23, 0x8e,
0x50, 0x64, 0xf6, 0xee, 0xc7, 0x78, 0x6a, 0xe0,
0xed, 0xce, 0x2c, 0x1f, 0x0a, 0x47, 0x43, 0x5c,
0xe4, 0x69, 0xc5, 0xc1, 0xf9, 0x52, 0x8c, 0xed,
0xfd, 0x71, 0x8f, 0x9a, 0xde, 0x62, 0xfc, 0x21,
0x07, 0xf9, 0x5f, 0xe1, 0x1e, 0xdc, 0x65, 0x95,
0x15, 0xc8, 0xe7, 0xf2, 0xce, 0xa9, 0xd0, 0x55,
0xf1, 0x18, 0x89, 0xae, 0xe8, 0x47, 0xd8, 0x8a,
0x1f, 0x68, 0xa8, 0x6f, 0x5e, 0x5c, 0xda, 0x3d,
0x98, 0xeb, 0x82, 0xf8, 0x1f, 0x7a, 0x43, 0x6d,
0x3a, 0x7c, 0x36, 0x76, 0x4f, 0x55, 0xa4, 0x55,
0x5f, 0x52, 0x47, 0xa5, 0x71, 0x17, 0x7b, 0x73,
0xaa, 0x5c, 0x85, 0x94, 0xb6, 0xe2, 0x37, 0x1f,
0x22, 0x29, 0x46, 0x59, 0x20, 0x1f, 0x49, 0x36,
0x50, 0xa9, 0x60, 0x5d, 0xeb, 0x99, 0x3f, 0x92,
0x31, 0xa0, 0x1d, 0xad, 0xdb, 0xde, 0x40, 0xf6,
0xaf, 0x9c, 0x36, 0xe4, 0x0c, 0xf4, 0xcc, 0xaf,
0x9f, 0x8b, 0xf9, 0xe6, 0x12, 0x53, 0x4e, 0x58,
0xeb, 0x9a, 0x57, 0x08, 0x89, 0xa5, 0x4f, 0x7c,
0xb9, 0x78, 0x07, 0x02, 0x17, 0x2c, 0xce, 0xb8,
};
};
unique_ptr<nos::NuggetClientInterface> AvbTest::client;
unique_ptr<test_harness::TestHarness> AvbTest::uart_printer;
void AvbTest::SetUpTestCase() {
uart_printer = test_harness::TestHarness::MakeUnique();
client = nugget_tools::MakeNuggetClient();
client->Open();
EXPECT_TRUE(client->IsOpen()) << "Unable to connect";
}
void AvbTest::TearDownTestCase() {
client->Close();
client = unique_ptr<nos::NuggetClientInterface>();
uart_printer = nullptr;
}
void AvbTest::SetUp(void)
{
bool bootloader;
bool production;
uint8_t locks[4];
int code;
avb_tools::BootloaderDone(client.get()); // We don't need BL for setup.
// Perform a challenge/response. If this fails, either
// the reset path is broken or the image is probably not
// TEST_IMAGE=1.
// Note: the reset tests are not safe on -UTEST_IMAGE unless
// the storage can be reflashed.
ResetProduction(client.get());
code = Reset(client.get(), ResetRequest::LOCKS, NULL, 0);
ASSERT_NO_ERROR(code, "");
GetState(client.get(), &bootloader, &production, locks);
EXPECT_EQ(bootloader, false);
EXPECT_EQ(production, false);
EXPECT_EQ(locks[BOOT], 0x00);
EXPECT_EQ(locks[CARRIER], 0x00);
EXPECT_EQ(locks[DEVICE], 0x00);
EXPECT_EQ(locks[OWNER], 0x00);
}
RSA *GetResetKey()
{
BIO *bio = BIO_new_mem_buf((void*)test_data::kResetKeyPem,
test_data::kResetKeyPemSize - 1);
RSA *rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, 0, NULL);
BIO_free(bio);
return rsa;
}
int AvbTest::ProductionResetTest(uint32_t selector, uint64_t nonce,
const uint8_t *device_data, size_t data_len,
const uint8_t *signature, size_t signature_len)
{
ProductionResetTestRequest request;
request.set_selector(selector);
request.set_nonce(nonce);
if (signature && signature_len) {
request.set_signature(signature, signature_len);
}
if (device_data && data_len) {
request.set_device_data(device_data, data_len);
}
Avb service(*client);
return service.ProductionResetTest(request, nullptr);
}
int AvbTest::SignChallenge(const struct ResetMessage *message,
uint8_t *signature, size_t *maxsig)
{
size_t siglen = *maxsig;
RSA *key = GetResetKey();
if (!key)
return -1;
EVP_PKEY *pkey = EVP_PKEY_new();
if (!pkey)
return -1;
if (!EVP_PKEY_set1_RSA(pkey, key))
return -1;
EVP_MD_CTX md_ctx;
EVP_MD_CTX_init(&md_ctx);
EVP_PKEY_CTX *pkey_ctx;
if (!EVP_DigestSignInit(&md_ctx, &pkey_ctx, EVP_sha256(), NULL, pkey))
return -1;
if (!EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING))
return -1;
if (!EVP_DigestSignUpdate(&md_ctx, (uint8_t *)message, sizeof(*message)))
return -1;
EVP_DigestSignFinal(&md_ctx, NULL, &siglen);
if (siglen > *maxsig) {
std::cerr << "Signature length too long: " << siglen << " > "
<< *maxsig << std::endl;
return -2;
}
*maxsig = siglen;
int code = EVP_DigestSignFinal(&md_ctx, signature, &siglen);
if (!code) {
std::cerr << "OpenSSL error: " << ERR_get_error() << ": "
<< ERR_reason_error_string(ERR_get_error()) << std::endl;
return -3;
}
EVP_MD_CTX_cleanup(&md_ctx);
EVP_PKEY_free(pkey);
RSA_free(key);
// Feed it in
return 0;
}
int AvbTest::Load(uint8_t slot, uint64_t *version)
{
LoadRequest request;
LoadResponse response;
int code;
request.set_slot(slot);
Avb service(*client);
code = service.Load(request, &response);
if (code == APP_SUCCESS) {
*version = response.version();
}
return code;
}
int AvbTest::Store(uint8_t slot, uint64_t version)
{
StoreRequest request;
request.set_slot(slot);
request.set_version(version);
Avb service(*client);
return service.Store(request, nullptr);
}
int AvbTest::SetDeviceLock(uint8_t locked)
{
SetDeviceLockRequest request;
request.set_locked(locked);
Avb service(*client);
return service.SetDeviceLock(request, nullptr);
}
int AvbTest::SetBootLock(uint8_t locked)
{
SetBootLockRequest request;
request.set_locked(locked);
Avb service(*client);
return service.SetBootLock(request, nullptr);
}
int AvbTest::SetOwnerLock(uint8_t locked, const uint8_t *metadata, size_t size)
{
SetOwnerLockRequest request;
request.set_locked(locked);
if (metadata != NULL && size > 0) {
request.set_key(metadata, size);
}
Avb service(*client);
return service.SetOwnerLock(request, nullptr);
}
int AvbTest::GetOwnerKey(uint32_t offset, uint8_t *metadata, size_t *size)
{
GetOwnerKeyRequest request;
GetOwnerKeyResponse response;
size_t i;
int code;
request.set_offset(offset);
if (size != NULL)
request.set_size(*size);
Avb service(*client);
code = service.GetOwnerKey(request, &response);
if (code == APP_SUCCESS) {
auto chunk = response.chunk();
if (metadata != NULL) {
for (i = 0; i < chunk.size(); i++)
metadata[i] = chunk[i];
}
if (size != NULL)
*size = chunk.size();
}
return code;
}
int AvbTest::SetCarrierLock(uint8_t locked, const uint8_t *metadata, size_t size)
{
CarrierLockRequest request;
request.set_locked(locked);
if (metadata != NULL && size > 0) {
request.set_device_data(metadata, size);
}
Avb service(*client);
return service.CarrierLock(request, nullptr);
}
// Tests
TEST_F(AvbTest, CarrierLockTest)
{
uint8_t carrier_data[AVB_DEVICE_DATA_SIZE];
uint8_t locks[4];
int code;
// Test we can set and unset the carrier lock in non production mode
memset(carrier_data, 0, sizeof(carrier_data));
code = SetCarrierLock(0x12, carrier_data, sizeof(carrier_data));
ASSERT_NO_ERROR(code, "");
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[CARRIER], 0x12);
code = SetCarrierLock(0, NULL, 0);
ASSERT_NO_ERROR(code, "");
// Set production mode
avb_tools::SetBootloader(client.get());
code = SetProduction(client.get(), true, NULL, 0);
ASSERT_NO_ERROR(code, "");
avb_tools::BootloaderDone(client.get());
// Test we cannot set or unset the carrier lock in production mode
code = SetCarrierLock(0x12, carrier_data, sizeof(carrier_data));
ASSERT_EQ(code, APP_ERROR_AVB_AUTHORIZATION);
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[CARRIER], 0x00);
code = SetCarrierLock(0, NULL, 0);
ASSERT_EQ(code, APP_ERROR_AVB_AUTHORIZATION);
}
TEST_F(AvbTest, CarrierUnlockTest)
{
CarrierLockTestRequest request;
CarrierLockTestResponse response;
CarrierUnlock *token;
token = new CarrierUnlock();
token->set_nonce(NONCE);
token->set_version(VERSION);
token->set_signature(SIGNATURE, sizeof(SIGNATURE));
request.set_last_nonce(LAST_NONCE);
request.set_version(VERSION);
request.set_device_data(DEVICE_DATA, sizeof(DEVICE_DATA));
request.set_allocated_token(token);
Avb service(*client);
ASSERT_NO_ERROR(service.CarrierLockTest(request, &response), "");
// The nonce is covered by the signature, so changing it should trip the
// signature verification
token->set_nonce(NONCE + 1);
ASSERT_EQ(service.CarrierLockTest(request, &response), APP_ERROR_AVB_AUTHORIZATION);
}
TEST_F(AvbTest, DeviceLockTest)
{
bool bootloader;
bool production;
uint8_t locks[4];
int code;
// Test cannot set the lock
avb_tools::SetBootloader(client.get());
code = SetProduction(client.get(), true, NULL, 0);
ASSERT_NO_ERROR(code, "");
code = SetDeviceLock(0x12);
ASSERT_EQ(code, APP_ERROR_AVB_HLOS);
// Test can set lock
ResetProduction(client.get());
avb_tools::SetBootloader(client.get());
code = SetDeviceLock(0x34);
ASSERT_NO_ERROR(code, "");
GetState(client.get(), &bootloader, &production, locks);
ASSERT_TRUE(bootloader);
ASSERT_FALSE(production);
ASSERT_EQ(locks[DEVICE], 0x34);
// Test cannot set while set
code = SetDeviceLock(0x56);
ASSERT_EQ(code, APP_ERROR_AVB_DENIED);
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[DEVICE], 0x34);
// Test can unset
code = SetDeviceLock(0x00);
ASSERT_NO_ERROR(code, "");
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[DEVICE], 0x00);
}
TEST_F(AvbTest, SetDeviceLockIsIdempotent) {
ASSERT_NO_ERROR(SetDeviceLock(0x65), "");
ASSERT_NO_ERROR(SetDeviceLock(0x65), "");
}
TEST_F(AvbTest, BootLockTest)
{
uint8_t locks[4];
int code;
// Test production logic.
code = SetProduction(client.get(), true, NULL, 0);
ASSERT_NO_ERROR(code, "");
// Test cannot set lock
code = SetBootLock(0x12);
ASSERT_EQ(code, APP_ERROR_AVB_BOOTLOADER);
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[BOOT], 0x00);
// Show the bootloader setting and unsetting.
avb_tools::SetBootloader(client.get());
code = SetBootLock(0x12);
ASSERT_NO_ERROR(code, "");
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[BOOT], 0x12);
code = SetBootLock(0x0);
ASSERT_NO_ERROR(code, "");
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[BOOT], 0x00);
// Test cannot unset lock while carrier set
ResetProduction(client.get());
code = Reset(client.get(), ResetRequest::LOCKS, NULL, 0);
ASSERT_NO_ERROR(code, "");
code = SetCarrierLock(0x34, DEVICE_DATA, sizeof(DEVICE_DATA));
ASSERT_NO_ERROR(code, "");
code = SetProduction(client.get(), true, NULL, 0);
ASSERT_NO_ERROR(code, "");
// Can lock when carrier lock is set.
avb_tools::SetBootloader(client.get());
code = SetBootLock(0x56);
ASSERT_NO_ERROR(code, "");
// Cannot unlock.
code = SetBootLock(0x0);
ASSERT_EQ(code, APP_ERROR_AVB_DENIED);
// Or change the value.
code = SetBootLock(0x42);
ASSERT_EQ(code, APP_ERROR_AVB_DENIED);
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[CARRIER], 0x34);
ASSERT_EQ(locks[BOOT], 0x56);
// Clear the locks to show device lock enforcement.
ResetProduction(client.get());
code = Reset(client.get(), ResetRequest::LOCKS, NULL, 0);
ASSERT_NO_ERROR(code, "");
code = SetProduction(client.get(), true, NULL, 0);
ASSERT_NO_ERROR(code, "");
avb_tools::SetBootloader(client.get());
// Need to be in the HLOS.
code = SetDeviceLock(0x78);
ASSERT_EQ(code, APP_ERROR_AVB_HLOS);
avb_tools::BootloaderDone(client.get());
code = SetDeviceLock(0x78);
ASSERT_NO_ERROR(code, "");
// We can move to a locked state when
// device lock is true.
avb_tools::SetBootloader(client.get());
code = SetBootLock(0x9A);
ASSERT_NO_ERROR(code, "");
// But we can't move back.
code = SetBootLock(0x0);
ASSERT_EQ(code, APP_ERROR_AVB_DENIED);
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[DEVICE], 0x78);
ASSERT_EQ(locks[BOOT], 0x9A);
}
TEST_F(AvbTest, SetBootLockIsIdempotent) {
ASSERT_NO_ERROR(SetBootLock(0x12), "");
ASSERT_NO_ERROR(SetBootLock(0x12), "");
}
TEST_F(AvbTest, SetBootLockAfterWipingUserData) {
// This is a sequence of commands that the bootloader will issue
ASSERT_NO_ERROR(SetProduction(client.get(), true, NULL, 0), "");
avb_tools::SetBootloader(client.get());
ASSERT_TRUE(nugget_tools::WipeUserData(client.get()));
ASSERT_NO_ERROR(SetBootLock(0xdc), "");
}
TEST_F(AvbTest, OwnerLockTest)
{
uint8_t owner_key[AVB_METADATA_MAX_SIZE];
uint8_t chunk[AVB_CHUNK_MAX_SIZE];
uint8_t locks[4];
int code;
size_t i;
for (i = 0; i < AVB_METADATA_MAX_SIZE; i += 2) {
owner_key[i + 0] = (i >> 8) & 0xFF;
owner_key[i + 1] = (i >> 8) & 0xFF;
}
// This should pass when BOOT lock is not set
code = SetOwnerLock(0x65, owner_key, sizeof(owner_key));
ASSERT_NO_ERROR(code, "");
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[OWNER], 0x65);
for (i = 0; i < AVB_METADATA_MAX_SIZE; i += AVB_CHUNK_MAX_SIZE) {
size_t size = sizeof(chunk);
size_t j;
code = GetOwnerKey(i, chunk, &size);
ASSERT_NO_ERROR(code, "");
ASSERT_EQ(size, sizeof(chunk));
for (j = 0; j < size; j++) {
ASSERT_EQ(chunk[j], owner_key[i + j]);
}
}
// Test setting the lock while set fails
code = SetOwnerLock(0x87, owner_key, sizeof(owner_key));
ASSERT_EQ(code, APP_ERROR_AVB_DENIED);
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[OWNER], 0x65);
// Clear it
code = SetOwnerLock(0x00, owner_key, sizeof(owner_key));
ASSERT_NO_ERROR(code, "");
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[OWNER], 0x00);
// Set the boot lock
avb_tools::SetBootloader(client.get());
code = SetBootLock(0x43);
ASSERT_NO_ERROR(code, "");
GetState(client.get(), NULL, NULL, locks);
ASSERT_EQ(locks[BOOT], 0x43);
// Test setting the lock while BOOT is locked fails
code = SetOwnerLock(0x21, owner_key, sizeof(owner_key));
ASSERT_EQ(code, APP_ERROR_AVB_DENIED);
}
TEST_F(AvbTest, ProductionMode)
{
bool production;
uint8_t locks[4];
int code;
// Check we're not in production mode
GetState(client.get(), NULL, &production, NULL);
ASSERT_FALSE(production);
// Set some lock values to make sure production doesn't affect them
avb_tools::SetBootloader(client.get());
code = SetOwnerLock(0x11, NULL, 0);
ASSERT_NO_ERROR(code, "");
code = SetBootLock(0x22);
ASSERT_NO_ERROR(code, "");
code = SetCarrierLock(0x33, DEVICE_DATA, sizeof(DEVICE_DATA));
ASSERT_NO_ERROR(code, "");
code = SetDeviceLock(0x44);
ASSERT_NO_ERROR(code, "");
// Set production mode with a DUT hash
code = SetProduction(client.get(), true, NULL, 0);
ASSERT_NO_ERROR(code, "");
GetState(client.get(), NULL, &production, locks);
ASSERT_TRUE(production);
ASSERT_EQ(locks[OWNER], 0x11);
ASSERT_EQ(locks[BOOT], 0x22);
ASSERT_EQ(locks[CARRIER], 0x33);
ASSERT_EQ(locks[DEVICE], 0x44);
// Test production cannot be turned off.
code = SetProduction(client.get(), false, NULL, 0);
ASSERT_EQ(code, APP_ERROR_AVB_AUTHORIZATION);
avb_tools::BootloaderDone(client.get());
code = SetProduction(client.get(), false, NULL, 0);
ASSERT_EQ(code, APP_ERROR_AVB_AUTHORIZATION);
}
TEST_F(AvbTest, Rollback)
{
uint64_t value = ~0ULL;
int code, i;
// Test we cannot change values in normal mode
code = SetProduction(client.get(), true, NULL, 0);
ASSERT_NO_ERROR(code, "");
for (i = 0; i < 8; i++) {
code = Store(i, 0xFF00000011223344 + i);
ASSERT_EQ(code, APP_ERROR_AVB_BOOTLOADER);
code = Load(i, &value);
ASSERT_NO_ERROR(code, "");
ASSERT_EQ(value, 0x00ULL);
}
// Test we can change values in bootloader mode
avb_tools::SetBootloader(client.get());
for (i = 0; i < 8; i++) {
code = Store(i, 0xFF00000011223344 + i);
ASSERT_NO_ERROR(code, "");
code = Load(i, &value);
ASSERT_NO_ERROR(code, "");
ASSERT_EQ(value, 0xFF00000011223344 + i);
code = Store(i, 0x8800000011223344 - i);
ASSERT_NO_ERROR(code, "");
code = Load(i, &value);
ASSERT_NO_ERROR(code, "");
ASSERT_EQ(value, 0x8800000011223344 - i);
}
}
TEST_F(AvbTest, Reset)
{
bool bootloader;
bool production;
uint8_t locks[4];
int code;
// Set some locks and production mode*/
avb_tools::SetBootloader(client.get());
code = SetBootLock(0x12);
ASSERT_NO_ERROR(code, "");
code = SetDeviceLock(0x34);
ASSERT_NO_ERROR(code, "");
code = SetProduction(client.get(), true, NULL, 0);
ASSERT_NO_ERROR(code, "");
avb_tools::BootloaderDone(client.get());
GetState(client.get(), &bootloader, &production, locks);
ASSERT_FALSE(bootloader);
ASSERT_TRUE(production);
ASSERT_EQ(locks[BOOT], 0x12);
ASSERT_EQ(locks[DEVICE], 0x34);
// Try reset, should fail
code = Reset(client.get(), ResetRequest::LOCKS, NULL, 0);
ASSERT_EQ(code, APP_ERROR_AVB_DENIED);
GetState(client.get(), &bootloader, &production, locks);
ASSERT_FALSE(bootloader);
ASSERT_TRUE(production);
ASSERT_EQ(locks[BOOT], 0x12);
ASSERT_EQ(locks[DEVICE], 0x34);
// Disable production, try reset, should pass
ResetProduction(client.get());
code = Reset(client.get(), ResetRequest::LOCKS, NULL, 0);
ASSERT_NO_ERROR(code, "");
GetState(client.get(), &bootloader, &production, locks);
ASSERT_FALSE(bootloader);
ASSERT_FALSE(production);
ASSERT_EQ(locks[BOOT], 0x00);
ASSERT_EQ(locks[DEVICE], 0x00);
}
TEST_F(AvbTest, GetResetChallengeTest)
{
int code;
uint32_t selector;
uint64_t nonce;
uint8_t data[32];
uint8_t empty[32];
size_t len = sizeof(data);
memset(data, 0, sizeof(data));
memset(empty, 0, sizeof(empty));
code = GetResetChallenge(client.get(), &selector, &nonce, data, &len);
ASSERT_LE(sizeof(data), len);
ASSERT_NO_ERROR(code, "");
EXPECT_NE(0ULL, nonce);
EXPECT_LE(selector, (uint32_t)ResetToken::CURRENT);
EXPECT_EQ(32UL, len);
// We didn't set a device id.
EXPECT_EQ(0, memcmp(data, empty, sizeof(empty)));
}
// TODO(drewry) move to new test suite since this is unsafe on
// non-TEST_IMAGE builds.
TEST_F(AvbTest, ResetProductionValid)
{
static const uint8_t kDeviceHash[] = {
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8
};
struct ResetMessage message;
int code;
uint32_t selector;
size_t len = sizeof(message.data);
uint8_t signature[2048/8 + 1];
size_t siglen = sizeof(signature);
// Lock in a fixed device hash
code = SetProduction(client.get(), true, kDeviceHash, sizeof(kDeviceHash));
EXPECT_EQ(0, code);
memset(message.data, 0, sizeof(message.data));
code = GetResetChallenge(client.get(), &selector, &message.nonce, message.data, &len);
ASSERT_NO_ERROR(code, "");
// Expect, not assert, just in case something goes weird, we may still
// exit cleanly.
EXPECT_EQ(0, memcmp(message.data, kDeviceHash, sizeof(kDeviceHash)));
ASSERT_EQ(0, SignChallenge(&message, signature, &siglen));
// Try a bad challenge, wasting the nonce.
uint8_t orig = signature[0];
signature[0] += 1;
code = Reset(client.get(), ResetRequest::PRODUCTION, signature, siglen);
// TODO: expect the LINENO error
EXPECT_NE(0, code);
signature[0] = orig;
// Re-lock since TEST_IMAGE will unlock on failure.
code = SetProduction(client.get(), true, kDeviceHash, sizeof(kDeviceHash));
EXPECT_EQ(0, code);
// Now use a good one, but without getting a new nonce.
code = Reset(client.get(), ResetRequest::PRODUCTION, signature, siglen);
EXPECT_EQ(code, APP_ERROR_AVB_DENIED);
// Now get the nonce and use a good signature.
code = GetResetChallenge(client.get(), &selector, &message.nonce, message.data, &len);
ASSERT_NO_ERROR(code, "");
ASSERT_EQ(0, SignChallenge(&message, signature, &siglen));
code = Reset(client.get(), ResetRequest::PRODUCTION, signature, siglen);
ASSERT_NO_ERROR(code, "");
}
TEST_F(AvbTest, ProductionResetTestValid)
{
static const uint8_t kDeviceHash[] = {
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8
};
struct ResetMessage message;
int code;
uint32_t selector = ResetToken::CURRENT;
size_t len = sizeof(message.data);
uint8_t signature[2048/8 + 1];
size_t siglen = sizeof(signature);
memcpy(message.data, kDeviceHash, sizeof(message.data));
message.nonce = 123456;
ASSERT_EQ(0, SignChallenge(&message, signature, &siglen));
// Try a bad challenge, wasting the nonce.
uint8_t orig = signature[0];
signature[0] += 1;
code = ProductionResetTest(selector, message.nonce, message.data, len,
signature, siglen);
EXPECT_NE(0, code);
signature[0] = orig;
// Now use a good signature.
code = ProductionResetTest(selector, message.nonce, message.data, len,
signature, siglen);
ASSERT_NO_ERROR(code, "");
// Note, testing nonce expiration is handled in the Reset(PRODUCTION)
// test. This just checks a second signature over an app sourced nonce.
code = GetResetChallenge(client.get(), &selector, &message.nonce, message.data, &len);
ASSERT_NO_ERROR(code, "");
ASSERT_EQ(0, SignChallenge(&message, signature, &siglen));
code = Reset(client.get(), ResetRequest::PRODUCTION, signature, siglen);
ASSERT_NO_ERROR(code, "");
// Now test a null signature as we will for the real keys
message.nonce = 0;
memset(message.data, 0, sizeof(message.data));
code = ProductionResetTest(selector, message.nonce, message.data, len,
test_data::kResetSignatures[selector],
test_data::kResetSignatureLengths[selector]);
ASSERT_NO_ERROR(code, "");
}
TEST_F(AvbTest, WipeUserDataDoesNotLockDeviceLock) {
ASSERT_NO_ERROR(SetDeviceLock(0x00), "");
ASSERT_TRUE(nugget_tools::WipeUserData(client.get()));
uint8_t locks[4];
GetState(client.get(), nullptr, nullptr, locks);
EXPECT_EQ(locks[DEVICE], 0x00);
}
TEST_F(AvbTest, ResetKeyNullTokenSignatureTest)
{
struct ResetMessage message;
int code;
uint32_t selector;
uint64_t nonce;
uint8_t device_data[AVB_DEVICE_DATA_SIZE];
size_t len = sizeof(device_data);
// Get the selector
code = GetResetChallenge(client.get(), &selector, &nonce,
device_data, &len);
ASSERT_NO_ERROR(code, "");
ASSERT_LT(selector, test_data::kResetSignatureCount);
memset(&message, 0, sizeof(message));
// Now use a good one, but without getting a new nonce.
cout << "Testing key: " << selector << "\n";
code = ProductionResetTest(selector, message.nonce, message.data, 32,
test_data::kResetSignatures[selector],
test_data::kResetSignatureLengths[selector]);
ASSERT_NO_ERROR(code, "");
}
} // namespace