aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@google.com>2018-05-31 21:06:43 -0700
committerBill Richardson <wfrichar@google.com>2018-05-31 21:15:42 -0700
commitf6f1a8a7d61ebe6814a8a2fafdf9526569e5242b (patch)
tree2d4d0d8c9df79ca96bc450c07f986b3212333931
parent65451da67d41659e1ccc76dd0a8d5ee5d53644a0 (diff)
downloadsystem-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.md7
-rw-r--r--scripts/README14
-rwxr-xr-xscripts/release-tests.sh70
-rw-r--r--src/aes-cmac-tests.cc3
-rw-r--r--src/avb_tests.cc79
-rw-r--r--src/blob.h11
-rw-r--r--src/cavptests.cc7
-rw-r--r--src/gtest_with_gflags_main.cc67
-rw-r--r--src/keymaster-import-key-tests.cc39
-rw-r--r--src/keymaster-import-wrapped-key-tests.cc12
-rw-r--r--src/keymaster-provision-tests.cc22
-rw-r--r--src/nugget_core_tests.cc100
-rw-r--r--src/runtests.cc2
-rw-r--r--src/stress_test.cc2
-rw-r--r--src/util.cc147
-rw-r--r--src/util.h8
-rw-r--r--src/weaver_tests.cc37
-rw-r--r--tools/Android.bp2
-rw-r--r--tools/BUILD3
-rw-r--r--tools/avb_tools.cc22
-rw-r--r--tools/avb_tools.h2
-rw-r--r--tools/keymaster_tools.cc46
-rw-r--r--tools/keymaster_tools.h19
-rw-r--r--tools/nugget_tools.cc209
-rw-r--r--tools/nugget_tools.h10
25 files changed, 762 insertions, 178 deletions
diff --git a/README.md b/README.md
index a2e627f..18efe89 100644
--- a/README.md
+++ b/README.md
@@ -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);
diff --git a/src/blob.h b/src/blob.h
index 65be8a8..4a88b45 100644
--- a/src/blob.h
+++ b/src/blob.h
@@ -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;
diff --git a/src/util.h b/src/util.h
index 34e4ef1..daf3a40 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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);