#include "avb_tools.h" #include "gtest/gtest.h" #include "nugget/app/avb/avb.pb.h" #include #include "Avb.client.h" #include #include #include #include #include #include #ifdef ANDROID #include #include "nos/CitadeldProxyClient.h" #else #include "gflags/gflags.h" #endif // ANDROID using namespace nugget::app::avb; namespace avb_tools { void SetBootloader(nos::NuggetClientInterface *client) { // Force AVB to believe that the AP is in the BIOS. ::nos::AppClient app(*client, APP_ID_AVB_TEST); /* We have to have a buffer, because it's called by reference. */ std::vector buffer; // No params, no args needed. This is all that the fake "AVB_TEST" app does. uint32_t retval = app.Call(0, buffer, &buffer); EXPECT_EQ(retval, APP_SUCCESS); } void BootloaderDone(nos::NuggetClientInterface *client) { BootloaderDoneRequest request; Avb service(*client); ASSERT_NO_ERROR(service.BootloaderDone(request, nullptr), ""); } void GetState(nos::NuggetClientInterface *client, bool *bootloader, bool *production, uint8_t *locks) { GetStateRequest request; GetStateResponse response; Avb service(*client); ASSERT_NO_ERROR(service.GetState(request, &response), ""); EXPECT_EQ(response.number_of_locks(), 4U); if (bootloader != NULL) *bootloader = response.bootloader(); if (production != NULL) *production = response.production(); auto response_locks = response.locks(); if (locks != NULL) { for (size_t i = 0; i < response_locks.size(); i++) locks[i] = response_locks[i]; } } int Reset(nos::NuggetClientInterface *client, ResetRequest_ResetKind kind, const uint8_t *sig, size_t size) { ResetRequest request; request.set_kind(kind); request.mutable_token()->set_selector(ResetToken::CURRENT); if (sig && size) { request.mutable_token()->set_signature(sig, size); } else { uint8_t empty[AVB_SIGNATURE_SIZE]; memset(empty, 0, sizeof(empty)); request.mutable_token()->set_signature(empty, sizeof(empty)); } Avb service(*client); return service.Reset(request, nullptr); } int GetResetChallenge(nos::NuggetClientInterface *client, uint32_t *selector, uint64_t *nonce, uint8_t *device_data, size_t *len) { GetResetChallengeRequest request; GetResetChallengeResponse response; Avb service(*client); uint32_t ret = service.GetResetChallenge(request, &response); if (ret != APP_SUCCESS) { return ret; } *selector = response.selector(); *nonce = response.nonce(); // Only copy what there is space for. *len = (*len < response.device_data().size() ? *len : response.device_data().size()); memcpy(device_data, response.device_data().data(), *len); // Let the caller assert if the requested size was too large. *len = response.device_data().size(); return ret; } int SetProduction(nos::NuggetClientInterface *client, bool production, const uint8_t *data, size_t size) { SetProductionRequest request; request.set_production(production); if (size != 0 && data != NULL) { request.set_device_data(data, size); } // Substitute an empty hash uint8_t empty[AVB_DEVICE_DATA_SIZE]; memset(empty, 0, sizeof(empty)); if (production && data == NULL) { request.set_device_data(empty, sizeof(empty)); } Avb service(*client); return service.SetProduction(request, nullptr); } void ResetProduction(nos::NuggetClientInterface *client) { struct ResetMessage message; int code; uint32_t selector; size_t len = sizeof(message.data); uint8_t signature[AVB_SIGNATURE_SIZE]; size_t siglen = sizeof(signature); memset(signature, 0, siglen); // We need the nonce to be set before we get fallthrough. memset(message.data, 0, sizeof(message.data)); code = GetResetChallenge(client, &selector, &message.nonce, message.data, &len); ASSERT_NO_ERROR(code, ""); // No signature is needed for TEST_IMAGE. //EXPECT_EQ(0, SignChallenge(&message, signature, &siglen)); Reset(client, ResetRequest::PRODUCTION, signature, siglen); bool bootloader; bool production; uint8_t locks[4]; GetState(client, &bootloader, &production, locks); ASSERT_EQ(production, false); } } // namespace nugget_tools