diff options
author | Bill Richardson <wfrichar@google.com> | 2018-05-31 21:06:43 -0700 |
---|---|---|
committer | Bill Richardson <wfrichar@google.com> | 2018-05-31 21:15:42 -0700 |
commit | f6f1a8a7d61ebe6814a8a2fafdf9526569e5242b (patch) | |
tree | 2d4d0d8c9df79ca96bc450c07f986b3212333931 | |
parent | 65451da67d41659e1ccc76dd0a8d5ee5d53644a0 (diff) | |
download | system-test-harness-f6f1a8a7d61ebe6814a8a2fafdf9526569e5242b.tar.gz |
Sync with goog/upstream-master
Sync, not merge, because we reverted the previous merge and now
git/gerrit are not being helpful.
Bug: b/63388215
Test: It Works For Me(tm)
Change-Id: Icaa46c0e44b9799bfa102e183d1091838a9a061c
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | scripts/README | 14 | ||||
-rwxr-xr-x | scripts/release-tests.sh | 70 | ||||
-rw-r--r-- | src/aes-cmac-tests.cc | 3 | ||||
-rw-r--r-- | src/avb_tests.cc | 79 | ||||
-rw-r--r-- | src/blob.h | 11 | ||||
-rw-r--r-- | src/cavptests.cc | 7 | ||||
-rw-r--r-- | src/gtest_with_gflags_main.cc | 67 | ||||
-rw-r--r-- | src/keymaster-import-key-tests.cc | 39 | ||||
-rw-r--r-- | src/keymaster-import-wrapped-key-tests.cc | 12 | ||||
-rw-r--r-- | src/keymaster-provision-tests.cc | 22 | ||||
-rw-r--r-- | src/nugget_core_tests.cc | 100 | ||||
-rw-r--r-- | src/runtests.cc | 2 | ||||
-rw-r--r-- | src/stress_test.cc | 2 | ||||
-rw-r--r-- | src/util.cc | 147 | ||||
-rw-r--r-- | src/util.h | 8 | ||||
-rw-r--r-- | src/weaver_tests.cc | 37 | ||||
-rw-r--r-- | tools/Android.bp | 2 | ||||
-rw-r--r-- | tools/BUILD | 3 | ||||
-rw-r--r-- | tools/avb_tools.cc | 22 | ||||
-rw-r--r-- | tools/avb_tools.h | 2 | ||||
-rw-r--r-- | tools/keymaster_tools.cc | 46 | ||||
-rw-r--r-- | tools/keymaster_tools.h | 19 | ||||
-rw-r--r-- | tools/nugget_tools.cc | 209 | ||||
-rw-r--r-- | tools/nugget_tools.h | 10 |
25 files changed, 762 insertions, 178 deletions
@@ -14,7 +14,12 @@ citdadel chip. ## Quickstart -The command to run the tests is: +The command to run the tests from a host machine is: > bazel run runtests +On Android run: +> mmma -j`nproc` external/nos +Make sure verity is disabled and the system partion is remounted then run: +> adb sync +> adb citadel_integration_tests diff --git a/scripts/README b/scripts/README new file mode 100644 index 0000000..4ab9078 --- /dev/null +++ b/scripts/README @@ -0,0 +1,14 @@ +Steps for running release tests. + +* build a release ec.bin image +* relax file system restrictions + adb root && adb disable-verity && adb reboot + && adb wait-for-device root && adb remount +* update an Android device: + adb push ec.bin /data/local/tmp/ec.bin \ + && adb exec-out '/vendor/bin/hw/citadel_updater -v --rw --ro \ + /data/local/tmp/ec.bin' \ + && adb exec-out '/vendor/bin/hw/citadel_updater \ + --enable_ro --enable_rw --reboot ""' \ + && adb shell rm /data/local/tmp/ec.bin +* run release-tests.sh diff --git a/scripts/release-tests.sh b/scripts/release-tests.sh new file mode 100755 index 0000000..b14ab75 --- /dev/null +++ b/scripts/release-tests.sh @@ -0,0 +1,70 @@ +#!/bin/bash +set -e + +function integration_tests() { + m -j citadel_integration_tests || return 1 + adb shell stop || return 1 + adb sync || return 1 + adb shell start || return 1 + adb exec-out \ + '/vendor/bin/hw/citadel_integration_tests --release-tests' || return 1 +} + +# TODO: add AVB / Weaver / Keymaster VTS / CTS tests with filters here. + +function oem_lock_vts_tests() { + atest VtsHalOemLockV1_0TargetTest || return 1 +} + +function keymaster_cts_tests() { + return 0 +} + +function keymaster_vts_tests() { + return 0 +} + +function weaver_cts_tests() { + # These CTS tests make a lot of use of Weaver by enrolling and changing + # credentials. Add omre if you come across them. + atest com.android.cts.devicepolicy.ManagedProfileTest\#testLockNowWithKeyEviction || return 1 + atest com.android.cts.devicepolicy.DeviceAdminHostSideTestApi24 || return 1 +} + +function weaver_vts_tests() { + atest VtsHalWeaverV1_0TargetTest || return 1 +} + +function auth_secret_vts_tests() { + atest VtsHalAuthSecretV1_0TargetTest || return 1 +} + +function pay_cts_tests() { + : + # TODO(ngm): uncomment once these are working. + # runtest --path \ + # cts/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java || return 1 +} + +# TODO: add any other tests + +if [ -z "${TARGET_PRODUCT:-}" ]; then + echo "You need to run the Android setup stuff first" + exit 1 +fi + +for t in integration_tests \ + oem_lock_vts_tests \ + keymaster_cts_tests \ + keymaster_vts_tests \ + weaver_cts_tests \ + weaver_vts_tests \ + auth_secret_vts_tests \ + pay_cts_tests; do + if eval "${t}"; then + echo "PASS: ${t}" + else + echo "FAIL: ${t}" + exit 1 + fi +done diff --git a/src/aes-cmac-tests.cc b/src/aes-cmac-tests.cc index 8d87c33..ac66d29 100644 --- a/src/aes-cmac-tests.cc +++ b/src/aes-cmac-tests.cc @@ -65,7 +65,6 @@ void DcryptoTest::SetUpTestCase() { } void DcryptoTest::TearDownTestCase() { - harness->ReadUntil(test_harness::BYTE_TIME * 1024); harness = unique_ptr<test_harness::TestHarness>(); } @@ -74,7 +73,6 @@ void DcryptoTest::TearDownTestCase() { TEST_F(DcryptoTest, AesCmacRfc4493Test) { const int verbosity = harness->getVerbosity(); harness->setVerbosity(verbosity - 1); - harness->ReadUntil(test_harness::BYTE_TIME * 1024); for (size_t i = 0; i < ARRAYSIZE(RFC4493_AES_CMAC_DATA); i++) { const cmac_data *test_case = &RFC4493_AES_CMAC_DATA[i]; @@ -107,7 +105,6 @@ TEST_F(DcryptoTest, AesCmacRfc4493Test) { } } - harness->ReadUntil(test_harness::BYTE_TIME * 1024); harness->setVerbosity(verbosity); } diff --git a/src/avb_tests.cc b/src/avb_tests.cc index 3cc22f5..32ff5ef 100644 --- a/src/avb_tests.cc +++ b/src/avb_tests.cc @@ -9,6 +9,7 @@ #include <application.h> #include <nos/AppClient.h> #include <nos/NuggetClientInterface.h> +#include "util.h" #include <openssl/bio.h> #include <openssl/evp.h> @@ -26,15 +27,13 @@ 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); - void SetBootloader(void); - void BootloaderDone(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); @@ -96,8 +95,11 @@ class AvbTest: public testing::Test { }; 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"; @@ -106,6 +108,8 @@ void AvbTest::SetUpTestCase() { void AvbTest::TearDownTestCase() { client->Close(); client = unique_ptr<nos::NuggetClientInterface>(); + + uart_printer = nullptr; } void AvbTest::SetUp(void) @@ -115,7 +119,7 @@ void AvbTest::SetUp(void) uint8_t locks[4]; int code; - BootloaderDone(); // We don't need BL for setup. + 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. @@ -135,28 +139,6 @@ void AvbTest::SetUp(void) EXPECT_EQ(locks[OWNER], 0x00); } -void AvbTest::SetBootloader(void) -{ - // 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<uint8_t> 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 AvbTest::BootloaderDone(void) -{ - BootloaderDoneRequest request; - - Avb service(*client); - ASSERT_NO_ERROR(service.BootloaderDone(request, nullptr), ""); -} - static const uint8_t kResetKeyPem[] = "-----BEGIN RSA PRIVATE KEY-----\n\ MIIEpAIBAAKCAQEAo0IoAa5cK7XyAj7u1jFStsfEcxkgAZVF9VWKzH1bofKxLioA\n\ @@ -375,10 +357,10 @@ TEST_F(AvbTest, CarrierLockTest) ASSERT_NO_ERROR(code, ""); // Set production mode - SetBootloader(); + avb_tools::SetBootloader(client.get()); code = SetProduction(client.get(), true, NULL, 0); ASSERT_NO_ERROR(code, ""); - BootloaderDone(); + 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)); @@ -424,7 +406,7 @@ TEST_F(AvbTest, DeviceLockTest) int code; // Test cannot set the lock - SetBootloader(); + avb_tools::SetBootloader(client.get()); code = SetProduction(client.get(), true, NULL, 0); ASSERT_NO_ERROR(code, ""); @@ -433,6 +415,7 @@ TEST_F(AvbTest, DeviceLockTest) // Test can set lock ResetProduction(client.get()); + avb_tools::SetBootloader(client.get()); code = SetDeviceLock(0x34); ASSERT_NO_ERROR(code, ""); @@ -457,6 +440,11 @@ TEST_F(AvbTest, DeviceLockTest) 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]; @@ -473,7 +461,7 @@ TEST_F(AvbTest, BootLockTest) ASSERT_EQ(locks[BOOT], 0x00); // Show the bootloader setting and unsetting. - SetBootloader(); + avb_tools::SetBootloader(client.get()); code = SetBootLock(0x12); ASSERT_NO_ERROR(code, ""); @@ -498,6 +486,7 @@ TEST_F(AvbTest, BootLockTest) ASSERT_NO_ERROR(code, ""); // Can lock when carrier lock is set. + avb_tools::SetBootloader(client.get()); code = SetBootLock(0x56); ASSERT_NO_ERROR(code, ""); @@ -519,18 +508,19 @@ TEST_F(AvbTest, BootLockTest) 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); - BootloaderDone(); + avb_tools::BootloaderDone(client.get()); code = SetDeviceLock(0x78); ASSERT_NO_ERROR(code, ""); // We can move to a locked state when // device lock is true. - SetBootloader(); + avb_tools::SetBootloader(client.get()); code = SetBootLock(0x9A); ASSERT_NO_ERROR(code, ""); @@ -543,6 +533,19 @@ TEST_F(AvbTest, BootLockTest) 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]; @@ -590,7 +593,7 @@ TEST_F(AvbTest, OwnerLockTest) ASSERT_EQ(locks[OWNER], 0x00); // Set the boot lock - SetBootloader(); + avb_tools::SetBootloader(client.get()); code = SetBootLock(0x43); ASSERT_NO_ERROR(code, ""); @@ -613,7 +616,7 @@ TEST_F(AvbTest, ProductionMode) ASSERT_FALSE(production); // Set some lock values to make sure production doesn't affect them - SetBootloader(); + avb_tools::SetBootloader(client.get()); code = SetOwnerLock(0x11, NULL, 0); ASSERT_NO_ERROR(code, ""); @@ -640,7 +643,7 @@ TEST_F(AvbTest, ProductionMode) // Test production cannot be turned off. code = SetProduction(client.get(), false, NULL, 0); ASSERT_EQ(code, APP_ERROR_AVB_AUTHORIZATION); - BootloaderDone(); + avb_tools::BootloaderDone(client.get()); code = SetProduction(client.get(), false, NULL, 0); ASSERT_EQ(code, APP_ERROR_AVB_AUTHORIZATION); } @@ -663,7 +666,7 @@ TEST_F(AvbTest, Rollback) } // Test we can change values in bootloader mode - SetBootloader(); + avb_tools::SetBootloader(client.get()); for (i = 0; i < 8; i++) { code = Store(i, 0xFF00000011223344 + i); ASSERT_NO_ERROR(code, ""); @@ -689,7 +692,7 @@ TEST_F(AvbTest, Reset) int code; // Set some locks and production mode*/ - SetBootloader(); + avb_tools::SetBootloader(client.get()); code = SetBootLock(0x12); ASSERT_NO_ERROR(code, ""); @@ -699,7 +702,7 @@ TEST_F(AvbTest, Reset) code = SetProduction(client.get(), true, NULL, 0); ASSERT_NO_ERROR(code, ""); - BootloaderDone(); + avb_tools::BootloaderDone(client.get()); GetState(client.get(), &bootloader, &production, locks); ASSERT_FALSE(bootloader); @@ -44,9 +44,8 @@ struct blob_ec { } __attribute__((packed)); struct blob_sym { - /* TODO: max HMAC key size? */ uint32_t key_bits; - uint8_t bytes[2048 >> 3]; + uint8_t bytes[512 >> 3]; } __attribute__((packed)); enum blob_alg { @@ -62,12 +61,12 @@ enum blob_alg { struct km_blob { struct { - uint32_t id; - /* TODO: salt etc. */ - } header __attribute__((packed)); - struct { uint32_t magic; uint32_t version; + uint32_t id; + uint32_t iv[4]; + } h __attribute__((packed)); + struct { /* TODO: is sw_enforced expected to be managed by h/w? */ struct blob_enforcements sw_enforced; struct blob_enforcements tee_enforced; diff --git a/src/cavptests.cc b/src/cavptests.cc index 2fd55eb..fc9c9b2 100644 --- a/src/cavptests.cc +++ b/src/cavptests.cc @@ -58,8 +58,7 @@ class NuggetOsTest: public testing::Test { unique_ptr<test_harness::TestHarness> NuggetOsTest::harness; void NuggetOsTest::SetUpTestCase() { - harness = unique_ptr<test_harness::TestHarness>( - new test_harness::TestHarness()); + harness = TestHarness::MakeUnique(); if (!harness->UsingSpi()) { EXPECT_TRUE(harness->SwitchFromConsoleToProtoApi()); @@ -109,10 +108,10 @@ TEST_F(NuggetOsTest, AesGcm) { ASSERT_NO_ERROR(harness->SendOneofProto( APImessageID::TESTING_API_CALL, OneofTestParametersCase::kAesGcmEncryptTest, - request)); + request), ""); test_harness::raw_message msg; - ASSERT_NO_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME)); + ASSERT_NO_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME), ""); ASSERT_MSG_TYPE(msg, APImessageID::TESTING_API_RESPONSE); ASSERT_SUBTYPE(msg, OneofTestResultsCase::kAesGcmEncryptTestResult); diff --git a/src/gtest_with_gflags_main.cc b/src/gtest_with_gflags_main.cc index 0209549..3bf7958 100644 --- a/src/gtest_with_gflags_main.cc +++ b/src/gtest_with_gflags_main.cc @@ -8,22 +8,64 @@ #ifdef ANDROID #define FLAGS_list_slow_tests false #define FLAGS_disable_slow_tests false +// TODO: how does FLAGS_release_tests feature here? +#define FLAGS_release_tests true #else #include <gflags/gflags.h> - DEFINE_bool(list_slow_tests, false, "List tests included in the set of slow tests."); DEFINE_bool(disable_slow_tests, false, "Enables a filter to disable a set of slow tests."); +DEFINE_bool(release_tests, false, "Disables tests that would fail for firmware images built with TEST_IMAGE=0"); #endif // ANDROID +static void generate_disabled_test_list( + const std::vector<std::string>& tests, + std::stringstream *ss) { + for (const auto& test : tests) { + if (ss->tellp() == 0) { + *ss << "-"; + } else { + *ss << ":"; + } + *ss << test; + } +} + int main(int argc, char** argv) { const std::vector<std::string> slow_tests{ "AvbTest.*", "ImportKeyTest.RSASuccess", + "NuggetCoreTest.EnterDeepSleep", "NuggetCoreTest.HardRebootTest", - "WeaverTest.WriteHardRebootRead", + "WeaverTest.ReadAttemptCounterPersistsDeepSleep", + "WeaverTest.ReadAttemptCounterPersistsHardReboot", + "WeaverTest.ReadThrottleAfterDeepSleep", "WeaverTest.ReadThrottleAfterHardReboot", "WeaverTest.ReadThrottleAfterSleep", - "WeaverTest.ReadAttemptCounterPersistsHardReboot", + "WeaverTest.WriteDeepSleepRead", + "WeaverTest.WriteHardRebootRead", + }; + + const std::vector<std::string> disabled_for_release_tests{ + "DcryptoTest.AesCmacRfc4493Test", + "KeymasterProvisionTest.ProvisionDeviceIdsSuccess", + "KeymasterProvisionTest.ReProvisionDeviceIdsSuccess", + "KeymasterProvisionTest.ProductionModeProvisionFails", + "KeymasterProvisionTest.InvalidDeviceIdFails", + "KeymasterProvisionTest.MaxDeviceIdSuccess", + "KeymasterProvisionTest.NoMeidSuccess", + "NuggetCoreTest.GetUartPassthruInBootloader", + "NuggetCoreTest.EnableUartPassthruInBootloader", + "NuggetCoreTest.DisableUartPassthruInBootloader", + "NuggetOsTest.NoticePing", + "NuggetOsTest.InvalidMessageType", + "NuggetOsTest.Sequence", + "NuggetOsTest.Echo", + "NuggetOsTest.AesCbc", + "NuggetOsTest.Trng", + "WeaverTest.ProductionResetWipesUserData", + "AvbTest.*", + "ImportKeyTest.*", + "ImportWrappedKeyTest.ImportSuccess", }; testing::InitGoogleMock(&argc, argv); @@ -40,18 +82,15 @@ int main(int argc, char** argv) { exit(0); } + std::stringstream ss; if (FLAGS_disable_slow_tests) { - std::stringstream ss; - bool first = true; - for (const auto& test : slow_tests) { - if (first) { - first = false; - ss << "-"; - } else { - ss << ":"; - } - ss << test; - } + generate_disabled_test_list(slow_tests, &ss); + } + if (FLAGS_release_tests) { + generate_disabled_test_list(disabled_for_release_tests, &ss); + } + + if (FLAGS_disable_slow_tests || FLAGS_release_tests) { ::testing::GTEST_FLAG(filter) = ss.str(); } diff --git a/src/keymaster-import-key-tests.cc b/src/keymaster-import-key-tests.cc index f26c454..0392de7 100644 --- a/src/keymaster-import-key-tests.cc +++ b/src/keymaster-import-key-tests.cc @@ -1,9 +1,12 @@ #include "gtest/gtest.h" +#include "avb_tools.h" +#include "keymaster_tools.h" #include "nugget_tools.h" #include "nugget/app/keymaster/keymaster.pb.h" #include "nugget/app/keymaster/keymaster_defs.pb.h" #include "nugget/app/keymaster/keymaster_types.pb.h" #include "Keymaster.client.h" +#include "util.h" #include "src/blob.h" #include "src/macros.h" @@ -30,6 +33,7 @@ class ImportKeyTest: public testing::Test { protected: static unique_ptr<nos::NuggetClientInterface> client; static unique_ptr<Keymaster> service; + static unique_ptr<test_harness::TestHarness> uart_printer; static void SetUpTestCase(); static void TearDownTestCase(); @@ -77,18 +81,26 @@ class ImportKeyTest: public testing::Test { unique_ptr<nos::NuggetClientInterface> ImportKeyTest::client; unique_ptr<Keymaster> ImportKeyTest::service; +unique_ptr<test_harness::TestHarness> ImportKeyTest::uart_printer; void ImportKeyTest::SetUpTestCase() { + uart_printer = test_harness::TestHarness::MakeUnique(); + client = nugget_tools::MakeNuggetClient(); client->Open(); EXPECT_TRUE(client->IsOpen()) << "Unable to connect"; service.reset(new Keymaster(*client)); + + // Do setup that is normally done by the bootloader. + keymaster_tools::SetRootOfTrust(client.get()); } void ImportKeyTest::TearDownTestCase() { client->Close(); client = unique_ptr<nos::NuggetClientInterface>(); + + uart_printer = nullptr; } // TODO: refactor into import key tests. @@ -291,9 +303,9 @@ TEST_F(ImportKeyTest, ECMisMatchedCurveIdTagFails) { param = params->add_params(); param->set_tag(Tag::EC_CURVE); - param->set_integer((uint32_t)EcCurve::P_224); + param->set_integer((uint32_t)EcCurve::P_256); - request.mutable_ec()->set_curve_id((uint32_t)EcCurve::P_256); + request.mutable_ec()->set_curve_id(((uint32_t)EcCurve::P_256) + 1); ASSERT_NO_ERROR(service->ImportKey(request, &response), ""); EXPECT_EQ((ErrorCode)response.error_code(), ErrorCode::INVALID_ARGUMENT); @@ -310,20 +322,18 @@ TEST_F(ImportKeyTest, ECMisMatchedKeySizeTagCurveTagFails) { param = params->add_params(); param->set_tag(Tag::EC_CURVE); - param->set_integer((uint32_t)EcCurve::P_224); + param->set_integer((uint32_t)EcCurve::P_256); param = params->add_params(); param->set_tag(Tag::KEY_SIZE); - param->set_integer((uint32_t)256); /* Should be 224 */ + param->set_integer((uint32_t)384); /* Should be 256 */ - request.mutable_ec()->set_curve_id((uint32_t)EcCurve::P_224); + request.mutable_ec()->set_curve_id((uint32_t)EcCurve::P_256); ASSERT_NO_ERROR(service->ImportKey(request, &response), ""); EXPECT_EQ((ErrorCode)response.error_code(), ErrorCode::INVALID_ARGUMENT); } -// TODO: tests for P224. - TEST_F(ImportKeyTest, ECMisMatchedP256KeySizeFails) { ImportKeyRequest request; ImportKeyResponse response; @@ -338,9 +348,9 @@ TEST_F(ImportKeyTest, ECMisMatchedP256KeySizeFails) { param->set_integer((uint32_t)EcCurve::P_256); request.mutable_ec()->set_curve_id((uint32_t)EcCurve::P_256); - request.mutable_ec()->set_d(string((224 >> 3) - 1, '\0')); - request.mutable_ec()->set_x(string((224 >> 3), '\0')); - request.mutable_ec()->set_y(string((224 >> 3), '\0')); + request.mutable_ec()->set_d(string((256 >> 3) - 1, '\0')); + request.mutable_ec()->set_x(string((256 >> 3), '\0')); + request.mutable_ec()->set_y(string((256 >> 3), '\0')); ASSERT_NO_ERROR(service->ImportKey(request, &response), ""); EXPECT_EQ((ErrorCode)response.error_code(), ErrorCode::INVALID_ARGUMENT); @@ -361,9 +371,9 @@ TEST_F(ImportKeyTest, ECP256BadKeyFails) { param->set_integer((uint32_t)EcCurve::P_256); request.mutable_ec()->set_curve_id((uint32_t)EcCurve::P_256); - request.mutable_ec()->set_d(string((224 >> 3), '\0')); - request.mutable_ec()->set_x(string((224 >> 3), '\0')); - request.mutable_ec()->set_y(string((224 >> 3), '\0')); + request.mutable_ec()->set_d(string((256 >> 3), '\0')); + request.mutable_ec()->set_x(string((256 >> 3), '\0')); + request.mutable_ec()->set_y(string((256 >> 3), '\0')); ASSERT_NO_ERROR(service->ImportKey(request, &response), ""); EXPECT_EQ((ErrorCode)response.error_code(), ErrorCode::INVALID_ARGUMENT); @@ -417,9 +427,6 @@ TEST_F (ImportKeyTest, ImportECP256KeySuccess) { EXPECT_EQ((ErrorCode)response.error_code(), ErrorCode::OK); } -// TODO: tests for P384, P521. - - // TODO: add tests for symmetric key import. } // namespace diff --git a/src/keymaster-import-wrapped-key-tests.cc b/src/keymaster-import-wrapped-key-tests.cc index ecd7884..832a5c8 100644 --- a/src/keymaster-import-wrapped-key-tests.cc +++ b/src/keymaster-import-wrapped-key-tests.cc @@ -1,9 +1,12 @@ #include "gtest/gtest.h" +#include "avb_tools.h" +#include "keymaster_tools.h" #include "nugget_tools.h" #include "nugget/app/keymaster/keymaster.pb.h" #include "nugget/app/keymaster/keymaster_defs.pb.h" #include "nugget/app/keymaster/keymaster_types.pb.h" #include "Keymaster.client.h" +#include "util.h" #include "src/blob.h" #include "src/macros.h" @@ -27,6 +30,7 @@ class ImportWrappedKeyTest: public testing::Test { protected: static unique_ptr<nos::NuggetClientInterface> client; static unique_ptr<Keymaster> service; + static unique_ptr<test_harness::TestHarness> uart_printer; static void SetUpTestCase(); static void TearDownTestCase(); @@ -34,18 +38,26 @@ class ImportWrappedKeyTest: public testing::Test { unique_ptr<nos::NuggetClientInterface> ImportWrappedKeyTest::client; unique_ptr<Keymaster> ImportWrappedKeyTest::service; +unique_ptr<test_harness::TestHarness> ImportWrappedKeyTest::uart_printer; void ImportWrappedKeyTest::SetUpTestCase() { + uart_printer = test_harness::TestHarness::MakeUnique(); + client = nugget_tools::MakeNuggetClient(); client->Open(); EXPECT_TRUE(client->IsOpen()) << "Unable to connect"; service.reset(new Keymaster(*client)); + + // Do setup that is normally done by the bootloader. + keymaster_tools::SetRootOfTrust(client.get()); } void ImportWrappedKeyTest::TearDownTestCase() { client->Close(); client = unique_ptr<nos::NuggetClientInterface>(); + + uart_printer = nullptr; } /* Wrapped key DER just for reference; fields below have been pulled diff --git a/src/keymaster-provision-tests.cc b/src/keymaster-provision-tests.cc index 6432e1a..2edabad 100644 --- a/src/keymaster-provision-tests.cc +++ b/src/keymaster-provision-tests.cc @@ -10,6 +10,7 @@ #include <keymaster.h> #include <nos/AppClient.h> #include <nos/NuggetClientInterface.h> +#include "util.h" #include <openssl/bio.h> #include <openssl/evp.h> @@ -27,6 +28,7 @@ namespace { class KeymasterProvisionTest: public testing::Test { protected: static unique_ptr<nos::NuggetClientInterface> client; + static unique_ptr<test_harness::TestHarness> uart_printer; static void SetUpTestCase(); static void TearDownTestCase(); @@ -37,8 +39,11 @@ class KeymasterProvisionTest: public testing::Test { }; unique_ptr<nos::NuggetClientInterface> KeymasterProvisionTest::client; +unique_ptr<test_harness::TestHarness> KeymasterProvisionTest::uart_printer; void KeymasterProvisionTest::SetUpTestCase() { + uart_printer = test_harness::TestHarness::MakeUnique(); + client = nugget_tools::MakeNuggetClient(); client->Open(); EXPECT_TRUE(client->IsOpen()) << "Unable to connect"; @@ -47,6 +52,8 @@ void KeymasterProvisionTest::SetUpTestCase() { void KeymasterProvisionTest::TearDownTestCase() { client->Close(); client = unique_ptr<nos::NuggetClientInterface>(); + + uart_printer = nullptr; } void KeymasterProvisionTest::SetUp(void) { @@ -145,4 +152,19 @@ TEST_F(KeymasterProvisionTest, MaxDeviceIdSuccess) { ASSERT_EQ((ErrorCode)response.error_code(), ErrorCode::OK); } +// Regression test for b/77830050#comment6 +TEST_F(KeymasterProvisionTest, NoMeidSuccess) { + + ProvisionDeviceIdsRequest request; + ProvisionDeviceIdsResponse response; + + PopulateDefaultRequest(&request); + request.clear_meid(); + + Keymaster service(*client); + + ASSERT_NO_ERROR(service.ProvisionDeviceIds(request, &response), ""); + ASSERT_EQ((ErrorCode)response.error_code(), ErrorCode::OK); +} + } // namespace diff --git a/src/nugget_core_tests.cc b/src/nugget_core_tests.cc index 9f07f01..5fc06e0 100644 --- a/src/nugget_core_tests.cc +++ b/src/nugget_core_tests.cc @@ -6,6 +6,7 @@ #include <chrono> #include <memory> +#include "avb_tools.h" #include "nugget_tools.h" #include "util.h" @@ -22,16 +23,20 @@ class NuggetCoreTest: public testing::Test { static void TearDownTestCase(); static unique_ptr<nos::NuggetClientInterface> client; + static unique_ptr<test_harness::TestHarness> uart_printer; static vector<uint8_t> input_buffer; static vector<uint8_t> output_buffer; }; unique_ptr<nos::NuggetClientInterface> NuggetCoreTest::client; +unique_ptr<test_harness::TestHarness> NuggetCoreTest::uart_printer; vector<uint8_t> NuggetCoreTest::input_buffer; vector<uint8_t> NuggetCoreTest::output_buffer; void NuggetCoreTest::SetUpTestCase() { + uart_printer = test_harness::TestHarness::MakeUnique(); + client = nugget_tools::MakeNuggetClient(); client->Open(); input_buffer.reserve(0x4000); @@ -42,6 +47,8 @@ void NuggetCoreTest::SetUpTestCase() { void NuggetCoreTest::TearDownTestCase() { client->Close(); client = unique_ptr<nos::NuggetClientInterface>(); + + uart_printer = nullptr; } TEST_F(NuggetCoreTest, GetVersionStringTest) { @@ -67,12 +74,12 @@ TEST_F(NuggetCoreTest, GetDeviceIdTest) { } } -TEST_F(NuggetCoreTest, SoftRebootTest) { - ASSERT_TRUE(nugget_tools::RebootNugget(client.get(), NUGGET_REBOOT_SOFT)); +TEST_F(NuggetCoreTest, EnterDeepSleep) { + ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), nullptr)); } TEST_F(NuggetCoreTest, HardRebootTest) { - ASSERT_TRUE(nugget_tools::RebootNugget(client.get(), NUGGET_REBOOT_HARD)); + ASSERT_TRUE(nugget_tools::RebootNugget(client.get())); } TEST_F(NuggetCoreTest, WipeUserData) { @@ -98,4 +105,91 @@ TEST_F(NuggetCoreTest, GetLowPowerStats) { ASSERT_GT(stats.time_spent_awake, 0UL); } +TEST_F(NuggetCoreTest, GetUartPassthruInBootloader) { + std::vector<uint8_t> send; + std::vector<uint8_t> get; + + get.reserve(1); + ASSERT_NO_ERROR(NuggetCoreTest::client->CallApp( + APP_ID_NUGGET, NUGGET_PARAM_AP_UART_PASSTHRU, + send, &get), "Get UART passthru value"); + /* Should be either on or off. If USB is active, disconnect SuzyQable */ + ASSERT_TRUE(get[0] == NUGGET_AP_UART_OFF || + get[0] == NUGGET_AP_UART_ENABLED); +} + +TEST_F(NuggetCoreTest, GetUartPassthruInHLOS) { + std::vector<uint8_t> send; + std::vector<uint8_t> get; + + avb_tools::BootloaderDone(client.get()); + + get.reserve(1); + ASSERT_NO_ERROR(NuggetCoreTest::client->CallApp( + APP_ID_NUGGET, NUGGET_PARAM_AP_UART_PASSTHRU, + send, &get), "Get UART passthru value"); + /* Should be either on or off. If USB is active, disconnect SuzyQable */ + ASSERT_TRUE(get[0] == NUGGET_AP_UART_OFF || + get[0] == NUGGET_AP_UART_ENABLED); +} + + +TEST_F(NuggetCoreTest, EnableUartPassthruInBootloader) { + std::vector<uint8_t> send; + std::vector<uint8_t> get; + + avb_tools::SetBootloader(client.get()); + + send.push_back(NUGGET_AP_UART_ENABLED); + get.reserve(1); + ASSERT_NO_ERROR(NuggetCoreTest::client->CallApp( + APP_ID_NUGGET, NUGGET_PARAM_AP_UART_PASSTHRU, + send, &get), "Enable UART passthru"); + + ASSERT_EQ(get[0], NUGGET_AP_UART_ENABLED); +} + +TEST_F(NuggetCoreTest, DisableUartPassthruInBootloader) { + std::vector<uint8_t> send; + std::vector<uint8_t> get; + + avb_tools::SetBootloader(client.get()); + + send.push_back(NUGGET_AP_UART_OFF); + get.reserve(1); + ASSERT_NO_ERROR(NuggetCoreTest::client->CallApp( + APP_ID_NUGGET, NUGGET_PARAM_AP_UART_PASSTHRU, + send, &get), "Disable UART passthru"); + + ASSERT_EQ(get[0], NUGGET_AP_UART_OFF); +} + +TEST_F(NuggetCoreTest, EnableUartPassthruInHLOSFails) { + std::vector<uint8_t> send; + std::vector<uint8_t> get; + + avb_tools::BootloaderDone(client.get()); + + send.push_back(NUGGET_AP_UART_ENABLED); + get.reserve(1); + /* This should fail */ + ASSERT_NE(APP_SUCCESS, NuggetCoreTest::client->CallApp( + APP_ID_NUGGET, NUGGET_PARAM_AP_UART_PASSTHRU, + send, &get)); +} + +TEST_F(NuggetCoreTest, DisableUartPassthruInHLOSFails) { + std::vector<uint8_t> send; + std::vector<uint8_t> get; + + avb_tools::BootloaderDone(client.get()); + + send.push_back(NUGGET_AP_UART_OFF); + get.reserve(1); + /* This should fail */ + ASSERT_NE(APP_SUCCESS, NuggetCoreTest::client->CallApp( + APP_ID_NUGGET, NUGGET_PARAM_AP_UART_PASSTHRU, + send, &get)); +} + } // namespace diff --git a/src/runtests.cc b/src/runtests.cc index c86730e..0ce167a 100644 --- a/src/runtests.cc +++ b/src/runtests.cc @@ -76,7 +76,7 @@ unique_ptr<TestHarness> NuggetOsTest::harness; std::random_device NuggetOsTest::random_number_generator; void NuggetOsTest::SetUpTestCase() { - harness = unique_ptr<TestHarness>(new TestHarness()); + harness = TestHarness::MakeUnique(); #ifndef CONFIG_NO_UART if (!harness->UsingSpi()) { diff --git a/src/stress_test.cc b/src/stress_test.cc index 10f4ada..794a53e 100644 --- a/src/stress_test.cc +++ b/src/stress_test.cc @@ -25,7 +25,7 @@ unique_ptr<TestHarness> harness = unique_ptr<TestHarness>(new TestHarness()); void cleanup() { std::cout << "Performing Reboot!\n"; - harness->RebootNugget(NUGGET_REBOOT_HARD); + harness->RebootNugget(); std::cout << "Done!\n"; } diff --git a/src/util.cc b/src/util.cc index 49f5394..eac022e 100644 --- a/src/util.cc +++ b/src/util.cc @@ -21,10 +21,13 @@ #ifdef ANDROID #define FLAGS_util_use_ahdlc false +#define FLAGS_util_print_uart false #else #include "gflags/gflags.h" DEFINE_bool(util_use_ahdlc, false, "Use aHDLC over UART instead of SPI."); +DEFINE_bool(util_print_uart, false, "Print the output of citadel UART."); +DEFINE_string(util_verbosity, "ERROR", "One of SILENT, CRITICAL, ERROR, WARNING, or INFO."); #endif // ANDROID using nugget::app::protoapi::APImessageID; @@ -37,24 +40,55 @@ using std::chrono::high_resolution_clock; using std::chrono::microseconds; namespace test_harness { +namespace { -string find_uart(int verbosity){ +int GetVerbosityFromFlag() { +#ifdef ANDROID + return TestHarness::ERROR; +#else + std::string upper_case_flag; + upper_case_flag.reserve(FLAGS_util_verbosity.size()); + std::transform(FLAGS_util_verbosity.begin(), FLAGS_util_verbosity.end(), + std::back_inserter(upper_case_flag), toupper); + + if (upper_case_flag == "SILENT") + return TestHarness::SILENT; + if (upper_case_flag == "CRITICAL") + return TestHarness::CRITICAL; + if (upper_case_flag == "WARNING") + return TestHarness::WARNING; + if (upper_case_flag == "INFO") + return TestHarness::INFO; + + // Default to ERROR. + return TestHarness::ERROR; +#endif // ANDROID +} + +#ifndef ANDROID +string find_uart(int verbosity) { constexpr char dir_path[] = "/dev/"; auto dir = opendir(dir_path); if (!dir) { return ""; } + string manual_serial_no = nugget_tools::GetCitadelUSBSerialNo(); + const char prefix[] = "ttyUltraTarget_"; + string return_value = ""; - const char prefix[] = "ttyUltraTarget"; - const size_t prefix_length = sizeof(prefix) / sizeof(prefix[0]) - 1; - while (auto listing = readdir(dir)) { - // The following is always true so it is not checked: - // sizeof(listing->d_name) >= sizeof(prefix) - if (std::equal(prefix, prefix + prefix_length, listing->d_name)) { - return_value = string(dir_path) + listing->d_name; - break; + if (manual_serial_no.empty()) { + const size_t prefix_length = sizeof(prefix) / sizeof(prefix[0]) - 1; + while (auto listing = readdir(dir)) { + // The following is always true so it is not checked: + // sizeof(listing->d_name) >= sizeof(prefix) + if (std::equal(prefix, prefix + prefix_length, listing->d_name)) { + return_value = string(dir_path) + listing->d_name; + break; + } } + } else { + return_value = string(dir_path) + prefix + manual_serial_no; } if (verbosity >= TestHarness::VerbosityLevels::INFO) { @@ -68,8 +102,15 @@ string find_uart(int verbosity){ closedir(dir); return return_value; } +#endif // ANDROID + +} // namespace -TestHarness::TestHarness() : verbosity(ERROR), +std::unique_ptr<TestHarness> TestHarness::MakeUnique() { + return std::unique_ptr<TestHarness>(new TestHarness()); +} + +TestHarness::TestHarness() : verbosity(GetVerbosityFromFlag()), output_buffer(PROTO_BUFFER_MAX_LEN, 0), input_buffer(PROTO_BUFFER_MAX_LEN, 0), tty_fd(-1) { #ifdef CONFIG_NO_UART @@ -91,8 +132,14 @@ TestHarness::~TestHarness() { if (verbosity >= INFO) { std::cout << "CLOSING TEST HARNESS" << std::endl; } - if (FLAGS_util_use_ahdlc) { - close(tty_fd); + if (ttyState()) { + auto temp = tty_fd; + tty_fd = -1; + close(temp); + } + if (print_uart_worker) { + print_uart_worker->join(); + print_uart_worker = nullptr; } #endif // CONFIG_NO_UART @@ -122,8 +169,8 @@ void TestHarness::flushConsole() { #endif // CONFIG_NO_UART } -bool TestHarness::RebootNugget(uint8_t type) { - return nugget_tools::RebootNugget(client.get(), type); +bool TestHarness::RebootNugget() { + return nugget_tools::RebootNugget(client.get()); } void print_bin(std::ostream &out, uint8_t c) { @@ -425,6 +472,23 @@ void TestHarness::Init(const char* path) { std::cout << "init() finish\n"; std::cout.flush(); } + + if (FLAGS_util_print_uart) { + print_uart_worker = std::unique_ptr<std::thread>(new std::thread( + [](TestHarness* harness){ + if (harness->getVerbosity() >= INFO) { + std::cout << "Citadel UART printing enabled!\n"; + std::cout.flush(); + } + while(harness->ttyState()) { + harness->PrintUntilClosed(); + } + if (harness->getVerbosity() >= INFO) { + std::cout << "Citadel UART printing disabled!\n"; + std::cout.flush(); + } + }, this)); + } } bool TestHarness::UsingSpi() const { @@ -548,6 +612,10 @@ void TestHarness::BlockingWrite(const char* data, size_t len) { } string TestHarness::ReadLineUntilBlock() { + if (!ttyState()) { + return ""; + } + string line = ""; line.reserve(128); char read_value = ' '; @@ -589,6 +657,10 @@ string TestHarness::ReadUntil(microseconds end) { std::this_thread::sleep_for(end); return ""; #else + if (!ttyState()) { + return ""; + } + char read_value = ' '; bool first = true; std::stringstream ss; @@ -629,6 +701,53 @@ string TestHarness::ReadUntil(microseconds end) { #endif // CONFIG_NO_UART } +void TestHarness::PrintUntilClosed() { +#ifdef CONFIG_NO_UART +#else + if (!ttyState()) { + return; + } + + char read_value = ' '; + bool first = true; + std::stringstream ss("UART: "); + + while (ttyState()) { + errno = 0; + while (read(tty_fd, &read_value, 1) > 0) { + first = false; + if (read_value == '\r') + continue; + if (read_value == '\n') { + ss << "\n"; + std::cout.flush(); + std::cout << ss.str(); + std::cout.flush(); + ss.str(""); + ss << "UART: "; + } else { + print_bin(ss, read_value); + } + } + if (verbosity >= CRITICAL && errno != 0 && errno != EAGAIN) { + if (errno != EBADF) { + perror("ERROR read()"); + } + break; + } + + /* Wait for at least one bit time before checking read() again. */ + std::this_thread::sleep_for(BIT_TIME); + } + if (!first) { + ss << "\n"; + std::cout.flush(); + std::cout << ss.str(); + std::cout.flush(); + } +#endif // CONFIG_NO_UART +} + void FatalError(const string& msg) { std::cerr << "FATAL ERROR: " << msg << std::endl; @@ -4,6 +4,7 @@ #include <chrono> #include <iostream> #include <memory> +#include <thread> #include <vector> #include <google/protobuf/message.h> @@ -62,6 +63,8 @@ class TestHarness { INFO = 40, }; + static std::unique_ptr<TestHarness> MakeUnique(); + TestHarness(); /** * @param path The device path to the tty (e.g. "/dev/tty1"). */ @@ -80,8 +83,9 @@ class TestHarness { void flushConsole(); /** Reads from tty until the specified duration has passed. */ string ReadUntil(std::chrono::microseconds end); + void PrintUntilClosed(); - bool RebootNugget(uint8_t type); + bool RebootNugget(); int SendData(const raw_message& msg); int SendOneofProto(uint16_t type, uint16_t subtype, @@ -129,6 +133,8 @@ class TestHarness { unique_ptr<nos::NuggetClientInterface> client; int SendSpi(const raw_message& msg); int GetSpi(raw_message* msg, std::chrono::microseconds timeout); + + std::unique_ptr<std::thread> print_uart_worker; }; void FatalError(const string& msg); diff --git a/src/weaver_tests.cc b/src/weaver_tests.cc index b08ff05..539b050 100644 --- a/src/weaver_tests.cc +++ b/src/weaver_tests.cc @@ -6,6 +6,7 @@ #include "avb_tools.h" #include "nugget_tools.h" #include "nugget/app/weaver/weaver.pb.h" +#include "util.h" #include "Weaver.client.h" #define __STAMP_STR1__(a) #a @@ -27,6 +28,7 @@ class WeaverTest: public testing::Test { static uint32_t slot; static unique_ptr<nos::NuggetClientInterface> client; + static unique_ptr<test_harness::TestHarness> uart_printer; static void SetUpTestCase(); static void TearDownTestCase(); @@ -63,8 +65,11 @@ std::random_device WeaverTest::random_number_generator; uint32_t WeaverTest::slot = WeaverTest::random_number_generator() & SLOT_MASK; unique_ptr<nos::NuggetClientInterface> WeaverTest::client; +unique_ptr<test_harness::TestHarness> WeaverTest::uart_printer; void WeaverTest::SetUpTestCase() { + uart_printer = test_harness::TestHarness::MakeUnique(); + client = nugget_tools::MakeNuggetClient(); client->Open(); EXPECT_TRUE(client->IsOpen()) << "Unable to connect"; @@ -73,6 +78,8 @@ void WeaverTest::SetUpTestCase() { void WeaverTest::TearDownTestCase() { client->Close(); client = unique_ptr<nos::NuggetClientInterface>(); + + uart_printer = nullptr; } void WeaverTest::testWrite(const string& msg, uint32_t slot, const uint8_t *key, @@ -219,15 +226,15 @@ TEST_F(WeaverTest, WriteToMultipleSlotsInDifferentRecordsDecreasingOrder) { testRead(__STAMP__, 5, TEST_KEY, TEST_VALUE); } -TEST_F(WeaverTest, WriteSoftRebootRead) { +TEST_F(WeaverTest, WriteDeepSleepRead) { testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); - ASSERT_TRUE(nugget_tools::RebootNugget(client.get(), NUGGET_REBOOT_SOFT)); + ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), 0)); testRead(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); } TEST_F(WeaverTest, WriteHardRebootRead) { testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); - ASSERT_TRUE(nugget_tools::RebootNugget(client.get(), NUGGET_REBOOT_HARD)); + ASSERT_TRUE(nugget_tools::RebootNugget(client.get())); testRead(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); } @@ -236,32 +243,33 @@ TEST_F(WeaverTest, ReadThrottle) { testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30); } -TEST_F(WeaverTest, ReadThrottleAfterSoftReboot) { +TEST_F(WeaverTest, ReadThrottleAfterDeepSleep) { activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30); - ASSERT_TRUE(nugget_tools::RebootNugget(client.get(), NUGGET_REBOOT_SOFT)); + ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), 0)); testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30); } TEST_F(WeaverTest, ReadThrottleAfterHardReboot) { activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30); - ASSERT_TRUE(nugget_tools::RebootNugget(client.get(), NUGGET_REBOOT_HARD)); + ASSERT_TRUE(nugget_tools::RebootNugget(client.get())); testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30); } TEST_F(WeaverTest, ReadThrottleAfterSleep) { + uint32_t waited = 0; activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30); - const uint32_t waited = nugget_tools::WaitForSleep(); + ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), &waited)); testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30 - waited); } -TEST_F(WeaverTest, ReadAttemptCounterPersistsSoftReboot) { +TEST_F(WeaverTest, ReadAttemptCounterPersistsDeepSleep) { testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); - ASSERT_TRUE(nugget_tools::RebootNugget(client.get(), NUGGET_REBOOT_SOFT)); + ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), 0)); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 30); @@ -273,7 +281,7 @@ TEST_F(WeaverTest, ReadAttemptCounterPersistsHardReboot) { testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); - ASSERT_TRUE(nugget_tools::RebootNugget(client.get(), NUGGET_REBOOT_HARD)); + ASSERT_TRUE(nugget_tools::RebootNugget(client.get())); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); @@ -320,4 +328,13 @@ TEST_F(WeaverTest, ProductionResetWipesUserData) { testRead(__STAMP__, WeaverTest::slot, TEST_KEY, ZERO_VALUE); } +// Regression tests +TEST_F(WeaverTest, WipeUserDataWriteSlot0ReadSlot1) { + testWrite(__STAMP__, 0, TEST_KEY, TEST_VALUE); + testWrite(__STAMP__, 1, TEST_KEY, TEST_VALUE); + ASSERT_TRUE(nugget_tools::WipeUserData(client.get())); + testWrite(__STAMP__, 0, TEST_KEY, TEST_VALUE); + testRead(__STAMP__, 1, TEST_KEY, ZERO_VALUE); +} + } // namespace diff --git a/tools/Android.bp b/tools/Android.bp index fdebb3c..fd4c72f 100644 --- a/tools/Android.bp +++ b/tools/Android.bp @@ -18,6 +18,7 @@ cc_library { name: "nugget_tools", srcs: [ "avb_tools.cc", + "keymaster_tools.cc", "nugget_tools.cc", ], header_libs: [ @@ -33,6 +34,7 @@ cc_library { "libnosprotos", "libprotobuf-cpp-full", "nos_app_avb", + "nos_app_keymaster", ], defaults: ["nos_cc_defaults"], export_include_dirs: ["."], diff --git a/tools/BUILD b/tools/BUILD index eb73348..c814589 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -2,10 +2,12 @@ cc_library( name = "nugget_tools", srcs = [ "avb_tools.cc", + "keymaster_tools.cc", "nugget_tools.cc", ], hdrs = [ "avb_tools.h", + "keymaster_tools.h", "nugget_tools.h", ], visibility = ["//visibility:public"], @@ -14,6 +16,7 @@ cc_library( "@gtest//:gtest", "@nugget_host_generic_libnos//:libnos", "@nugget_host_generic_nugget_proto//:avb_client_proto", + "@nugget_host_generic_nugget_proto//:keymaster_client_proto", "@nugget_host_linux_citadel_libnos_datagram//:libnos_datagram", ], ) diff --git a/tools/avb_tools.cc b/tools/avb_tools.cc index aed881e..5443867 100644 --- a/tools/avb_tools.cc +++ b/tools/avb_tools.cc @@ -24,6 +24,28 @@ 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<uint8_t> 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; diff --git a/tools/avb_tools.h b/tools/avb_tools.h index f27a867..89b4664 100644 --- a/tools/avb_tools.h +++ b/tools/avb_tools.h @@ -29,6 +29,8 @@ struct ResetMessage { uint8_t data[AVB_DEVICE_DATA_SIZE]; }; +void SetBootloader(nos::NuggetClientInterface *client); +void BootloaderDone(nos::NuggetClientInterface *client); void GetState(nos::NuggetClientInterface *client, bool *bootloader, bool *production, uint8_t *locks); int Reset(nos::NuggetClientInterface *client, ResetRequest_ResetKind kind, diff --git a/tools/keymaster_tools.cc b/tools/keymaster_tools.cc new file mode 100644 index 0000000..68697c9 --- /dev/null +++ b/tools/keymaster_tools.cc @@ -0,0 +1,46 @@ +#include "keymaster_tools.h" +#include "avb_tools.h" + +#include "gtest/gtest.h" +#include "nugget/app/keymaster/keymaster.pb.h" + +#include <app_nugget.h> +#include "Keymaster.client.h" +#include <keymaster.h> +#include <nos/NuggetClient.h> + +#include <chrono> +#include <iostream> +#include <thread> +#include <vector> + +#ifdef ANDROID +#include <android-base/endian.h> +#include "nos/CitadeldProxyClient.h" +#else +#include "gflags/gflags.h" +#endif // ANDROID + +using std::string; +using namespace nugget::app::keymaster; +using namespace avb_tools; + +namespace keymaster_tools { + +void SetRootOfTrust(nos::NuggetClientInterface *client) +{ + + // Do keymaster setup that is normally executed by the bootloader. + avb_tools::SetBootloader(client); + + SetRootOfTrustRequest request; + SetRootOfTrustResponse response; + Keymaster service(*client); + request.set_digest(string(32, '\0')); + ASSERT_NO_ERROR(service.SetRootOfTrust(request, &response), ""); + EXPECT_EQ((ErrorCode)response.error_code(), ErrorCode::OK); + + avb_tools::BootloaderDone(client); +} + +} // namespace keymaster_tools diff --git a/tools/keymaster_tools.h b/tools/keymaster_tools.h new file mode 100644 index 0000000..2b659cd --- /dev/null +++ b/tools/keymaster_tools.h @@ -0,0 +1,19 @@ +#ifndef KEYMASTER_TOOLS_H +#define KEYMASTER_TOOLS_H + +#include <app_nugget.h> +#include <application.h> +#include <keymaster.h> +#include <nos/debug.h> +#include <nos/NuggetClientInterface.h> + +#include <memory> +#include <string> + +namespace keymaster_tools { + +void SetRootOfTrust(nos::NuggetClientInterface *client); + +} // namespace keymaster_tools + +#endif // KEYMASTER_TOOLS_H diff --git a/tools/nugget_tools.cc b/tools/nugget_tools.cc index ace0249..3a692fc 100644 --- a/tools/nugget_tools.cc +++ b/tools/nugget_tools.cc @@ -4,6 +4,8 @@ #include <nos/NuggetClient.h> #include <chrono> +#include <cinttypes> +#include <cstring> #include <iostream> #include <thread> #include <vector> @@ -25,19 +27,25 @@ using std::chrono::duration; using std::chrono::duration_cast; using std::chrono::high_resolution_clock; using std::chrono::microseconds; +using std::string; namespace nugget_tools { -namespace { - -void WaitForHardReboot() { - // POST (which takes ~50ms) runs on a hard-reboot, plus an - // additional ~30ms for RO+RW verification. - std::this_thread::sleep_for(std::chrono::milliseconds(80)); +std::string GetCitadelUSBSerialNo() { +#ifdef ANDROID + return ""; +#else + if (FLAGS_nos_core_serial.empty()) { + const char *env_default = secure_getenv("CITADEL_DEVICE"); + if (env_default && *env_default) { + FLAGS_nos_core_serial.assign(env_default); + std::cerr << "Using CITADEL_DEVICE=" << FLAGS_nos_core_serial << "\n"; + } + } + return FLAGS_nos_core_serial; +#endif } -} // namesapce - std::unique_ptr<nos::NuggetClientInterface> MakeNuggetClient() { #ifdef ANDROID std::unique_ptr<nos::NuggetClientInterface> client = @@ -49,15 +57,8 @@ std::unique_ptr<nos::NuggetClientInterface> MakeNuggetClient() { } return client; #else - if (FLAGS_nos_core_serial.empty()) { - const char *env_default = secure_getenv("CITADEL_DEVICE"); - if (env_default && *env_default) { - FLAGS_nos_core_serial.assign(env_default); - std::cerr << "Using CITADEL_DEVICE=" << FLAGS_nos_core_serial << "\n"; - } - } return std::unique_ptr<nos::NuggetClientInterface>( - new nos::NuggetClient(FLAGS_nos_core_serial)); + new nos::NuggetClient(GetCitadelUSBSerialNo())); #endif } @@ -78,81 +79,163 @@ bool CyclesSinceBoot(nos::NuggetClientInterface *client, uint32_t *cycles) { return true; } -bool RebootNugget(nos::NuggetClientInterface *client, uint8_t type) { - // Capture the time here to allow for some tolerance on the reported time. - auto start = high_resolution_clock::now(); +static void ShowStats(const char *msg, + const struct nugget_app_low_power_stats& stats) { + printf("%s\n", msg); + printf(" hard_reset_count %" PRIu64 "\n", stats.hard_reset_count); + printf(" time_since_hard_reset %" PRIu64 "\n", + stats.time_since_hard_reset); + printf(" wake_count %" PRIu64 "\n", stats.wake_count); + printf(" time_at_last_wake %" PRIu64 "\n", stats.time_at_last_wake); + printf(" time_spent_awake %" PRIu64 "\n", stats.time_spent_awake); + printf(" deep_sleep_count %" PRIu64 "\n", stats.deep_sleep_count); + printf(" time_at_last_deep_sleep %" PRIu64 "\n", + stats.time_at_last_deep_sleep); + printf(" time_spent_in_deep_sleep %" PRIu64 "\n", + stats.time_spent_in_deep_sleep); +} + +bool RebootNugget(nos::NuggetClientInterface *client) { + struct nugget_app_low_power_stats stats0; + struct nugget_app_low_power_stats stats1; + std::vector<uint8_t> buffer; - // See what time Nugget OS has now - uint32_t pre_reboot; - if (!CyclesSinceBoot(client, &pre_reboot)) { + // Grab stats before sleeping + buffer.reserve(sizeof(struct nugget_app_low_power_stats)); + if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS, + buffer, &buffer) != app_status::APP_SUCCESS) { + LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n"; return false; } + memcpy(&stats0, buffer.data(), sizeof(stats0)); - // Tell it to reboot: 0 = soft reboot, 1 = hard reboot - std::vector<uint8_t> input_buffer(1, type); - if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_REBOOT, input_buffer, + // Capture the time here to allow for some tolerance on the reported time. + auto start = high_resolution_clock::now(); + + // Tell Nugget OS to reboot + std::vector<uint8_t> ignored; + if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_REBOOT, ignored, nullptr) != app_status::APP_SUCCESS) { LOG(ERROR) << "CallApp(..., NUGGET_PARAM_REBOOT, ...) failed!\n"; return false; } - if (!type) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } else { - WaitForHardReboot(); - } - - // See what time Nugget OS has after rebooting. - uint32_t post_reboot; - if (!CyclesSinceBoot(client, &post_reboot)) { + // Grab stats after sleeping + buffer.empty(); + buffer.reserve(sizeof(struct nugget_app_low_power_stats)); + if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS, + buffer, &buffer) != app_status::APP_SUCCESS) { + LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n"; return false; } + memcpy(&stats1, buffer.data(), sizeof(stats1)); - // Hard reboots reset the clock to zero, but soft reboots should keep counting - if (!type) { - // Make sure time advanced - if (post_reboot <= pre_reboot) { - LOG(ERROR) << "pre_reboot time (" << pre_reboot << ") should be less than " - << "post_reboot time (" << post_reboot << ")\n"; - return false; - } - // Change this to elapsed time, not absolute time - post_reboot -= pre_reboot; - } - - // Verify that the Nugget OS counter shows a reasonable value. - // Use the elapsed time +5% for the threshold. - auto threshold_microseconds = + // Figure a max elapsed time that Nugget OS should see (our time + 5%). + auto max_usecs = duration_cast<microseconds>(high_resolution_clock::now() - start) * 105 / 100; - if (std::chrono::microseconds(post_reboot) > threshold_microseconds ) { - LOG(ERROR) << "Counter is " << post_reboot - << " but is expected to be less than " - << threshold_microseconds.count() * 1.05 << "!\n"; - return false; + + // Verify that Citadel rebooted + if (stats1.hard_reset_count == stats0.hard_reset_count + 1 && + stats1.time_at_last_wake == 0 && + stats1.deep_sleep_count == 0 && + std::chrono::microseconds(stats1.time_since_hard_reset) < max_usecs) { + return true; } - // Looks okay - return true; + LOG(ERROR) << "Citadel didn't reboot within " + << max_usecs.count() << " microseconds\n"; + ShowStats("stats before waiting", stats0); + ShowStats("stats after waiting", stats1); + + return false; } -uint32_t WaitForSleep() { - constexpr uint32_t wait_seconds = 5; +bool WaitForSleep(nos::NuggetClientInterface *client, uint32_t *seconds_waited) { + struct nugget_app_low_power_stats stats0; + struct nugget_app_low_power_stats stats1; + std::vector<uint8_t> buffer; + + buffer.reserve(sizeof(struct nugget_app_low_power_stats)); + // Grab stats before sleeping + if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS, + buffer, &buffer) != app_status::APP_SUCCESS) { + LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n"; + return false; + } + memcpy(&stats0, buffer.data(), sizeof(stats0)); + + // Wait for Citadel to fall asleep + constexpr uint32_t wait_seconds = 4; std::this_thread::sleep_for(std::chrono::seconds(wait_seconds)); - // TODO: Can we check it has gone to sleep? - return wait_seconds; + + // Grab stats after sleeping + buffer.empty(); + buffer.reserve(sizeof(struct nugget_app_low_power_stats)); + if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS, + buffer, &buffer) != app_status::APP_SUCCESS) { + LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n"; + return false; + } + memcpy(&stats1, buffer.data(), sizeof(stats1)); + + // Verify that Citadel went to sleep but didn't reboot + if (stats1.hard_reset_count == stats0.hard_reset_count && + stats1.deep_sleep_count == stats0.deep_sleep_count + 1 && + stats1.wake_count == stats0.wake_count + 1 && + stats1.time_spent_in_deep_sleep > stats0.time_spent_in_deep_sleep) { + // Yep, looks good + if (seconds_waited) { + *seconds_waited = wait_seconds; + } + return true; + } + + LOG(ERROR) << "Citadel didn't sleep\n"; + ShowStats("stats before waiting", stats0); + ShowStats("stats after waiting", stats1); + + return false; } bool WipeUserData(nos::NuggetClientInterface *client) { + struct nugget_app_low_power_stats stats0; + struct nugget_app_low_power_stats stats1; + std::vector<uint8_t> buffer; + + // Grab stats before sleeping + buffer.reserve(sizeof(struct nugget_app_low_power_stats)); + if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS, + buffer, &buffer) != app_status::APP_SUCCESS) { + LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n"; + return false; + } + memcpy(&stats0, buffer.data(), sizeof(stats0)); + // Request wipe of user data which should hard reboot - std::vector<uint8_t> buffer(4); + buffer.resize(4); *reinterpret_cast<uint32_t *>(buffer.data()) = htole32(ERASE_CONFIRMATION); if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT, buffer, nullptr) != app_status::APP_SUCCESS) { return false; } - WaitForHardReboot(); - return true; + + // Grab stats after sleeping + buffer.empty(); + buffer.reserve(sizeof(struct nugget_app_low_power_stats)); + if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS, + buffer, &buffer) != app_status::APP_SUCCESS) { + LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n"; + return false; + } + memcpy(&stats1, buffer.data(), sizeof(stats1)); + + // Verify that Citadel didn't reset + const bool ret = stats1.hard_reset_count == stats0.hard_reset_count; + if (!ret) { + LOG(ERROR) << "Citadel reset while wiping user data\n"; + } + return ret; } } // namespace nugget_tools diff --git a/tools/nugget_tools.h b/tools/nugget_tools.h index 4ab0c40..1db4c0c 100644 --- a/tools/nugget_tools.h +++ b/tools/nugget_tools.h @@ -18,12 +18,16 @@ namespace nugget_tools { +std::string GetCitadelUSBSerialNo(); + std::unique_ptr<nos::NuggetClientInterface> MakeNuggetClient(); -bool RebootNugget(nos::NuggetClientInterface *client, uint8_t type); +// Always does a hard reboot. Use WaitForSleep() if you just want deep sleep. +bool RebootNugget(nos::NuggetClientInterface *client); -// Returns an underestimate of the number of seconds waited. -uint32_t WaitForSleep(); +// Returns true if Citadel entered deep sleep +// Passes back an underestimate of the number of seconds waited if so. +bool WaitForSleep(nos::NuggetClientInterface *client, uint32_t *seconds_waited); bool WipeUserData(nos::NuggetClientInterface *client); |