#include #include #include #include #include #include #include #include "nugget/app/protoapi/control.pb.h" #include "nugget/app/protoapi/header.pb.h" #include "nugget/app/protoapi/testing_api.pb.h" #include "src/util.h" #ifdef ANDROID #define FLAGS_nos_test_dump_protos false #else #include DEFINE_bool(nos_test_dump_protos, false, "Dump binary protobufs to a file."); #endif // ANDROID using nugget::app::protoapi::AesCbcEncryptTest; using nugget::app::protoapi::AesCbcEncryptTestResult; using nugget::app::protoapi::APImessageID; using nugget::app::protoapi::DcryptError; using nugget::app::protoapi::KeySize; using nugget::app::protoapi::Notice; using nugget::app::protoapi::NoticeCode; using nugget::app::protoapi::OneofTestParametersCase; using nugget::app::protoapi::OneofTestResultsCase; using nugget::app::protoapi::TrngTest; using nugget::app::protoapi::TrngTestResult; using std::cout; using std::vector; using std::unique_ptr; using test_harness::TestHarness; #define ASSERT_NO_TH_ERROR(code) \ ASSERT_EQ(code, test_harness::error_codes::NO_ERROR) \ << code << " is " << test_harness::error_codes_name(code) #define ASSERT_MSG_TYPE(msg, type_) \ do{if(type_ != APImessageID::NOTICE && msg.type == APImessageID::NOTICE){ \ Notice received; \ received.ParseFromArray(reinterpret_cast(msg.data), msg.data_len); \ ASSERT_EQ(msg.type, type_) \ << msg.type << " is " << APImessageID_Name((APImessageID) msg.type) \ << "\n" << received.DebugString(); \ }else{ \ ASSERT_EQ(msg.type, type_) \ << msg.type << " is " << APImessageID_Name((APImessageID) msg.type); \ }}while(0) #define ASSERT_SUBTYPE(msg, type_) \ EXPECT_GT(msg.data_len, 2); \ uint16_t subtype = (msg.data[0] << 8) | msg.data[1]; \ EXPECT_EQ(subtype, type_) namespace { using test_harness::BYTE_TIME; class NuggetOsTest: public testing::Test { protected: static void SetUpTestCase(); static void TearDownTestCase(); public: static unique_ptr harness; static std::random_device random_number_generator; }; unique_ptr NuggetOsTest::harness; std::random_device NuggetOsTest::random_number_generator; void NuggetOsTest::SetUpTestCase() { harness = TestHarness::MakeUnique(); #ifndef CONFIG_NO_UART if (!harness->UsingSpi()) { EXPECT_TRUE(harness->SwitchFromConsoleToProtoApi()); EXPECT_TRUE(harness->ttyState()); } #endif // CONFIG_NO_UART } void NuggetOsTest::TearDownTestCase() { #ifndef CONFIG_NO_UART if (!harness->UsingSpi()) { harness->ReadUntil(test_harness::BYTE_TIME * 1024); EXPECT_TRUE(harness->SwitchFromProtoApiToConsole(NULL)); } #endif // CONFIG_NO_UART harness = unique_ptr(); } TEST_F(NuggetOsTest, NoticePing) { Notice ping_msg; ping_msg.set_notice_code(NoticeCode::PING); Notice pong_msg; ASSERT_NO_TH_ERROR(harness->SendProto(APImessageID::NOTICE, ping_msg)); if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) { cout << ping_msg.DebugString(); } test_harness::raw_message receive_msg; ASSERT_NO_TH_ERROR(harness->GetData(&receive_msg, 4096 * BYTE_TIME)); ASSERT_MSG_TYPE(receive_msg, APImessageID::NOTICE); pong_msg.set_notice_code(NoticeCode::PING); ASSERT_TRUE(pong_msg.ParseFromArray( reinterpret_cast(receive_msg.data), receive_msg.data_len)); if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) { cout << pong_msg.DebugString() << std::endl; } EXPECT_EQ(pong_msg.notice_code(), NoticeCode::PONG); ASSERT_NO_TH_ERROR(harness->SendProto(APImessageID::NOTICE, ping_msg)); if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) { cout << ping_msg.DebugString(); } ASSERT_NO_TH_ERROR(harness->GetData(&receive_msg, 4096 * BYTE_TIME)); ASSERT_MSG_TYPE(receive_msg, APImessageID::NOTICE); pong_msg.set_notice_code(NoticeCode::PING); ASSERT_TRUE(pong_msg.ParseFromArray( reinterpret_cast(receive_msg.data), receive_msg.data_len)); if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) { cout << pong_msg.DebugString() << std::endl; } EXPECT_EQ(pong_msg.notice_code(), NoticeCode::PONG); ASSERT_NO_TH_ERROR(harness->SendProto(APImessageID::NOTICE, ping_msg)); if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) { cout << ping_msg.DebugString(); } ASSERT_NO_TH_ERROR(harness->GetData(&receive_msg, 4096 * BYTE_TIME)); ASSERT_MSG_TYPE(receive_msg, APImessageID::NOTICE); pong_msg.set_notice_code(NoticeCode::PING); ASSERT_TRUE(pong_msg.ParseFromArray( reinterpret_cast(receive_msg.data), receive_msg.data_len)); if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) { cout << pong_msg.DebugString() << std::endl; } EXPECT_EQ(pong_msg.notice_code(), NoticeCode::PONG); } TEST_F(NuggetOsTest, InvalidMessageType) { const char content[] = "This is a test message."; test_harness::raw_message msg; msg.type = 0; std::copy(content, content + sizeof(content), msg.data); msg.data_len = sizeof(content); ASSERT_NO_TH_ERROR(harness->SendData(msg)); ASSERT_NO_TH_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME)); ASSERT_MSG_TYPE(msg, APImessageID::NOTICE); Notice notice_msg; ASSERT_TRUE(notice_msg.ParseFromArray(reinterpret_cast(msg.data), msg.data_len)); if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) { cout << notice_msg.DebugString() << std::endl; } EXPECT_EQ(notice_msg.notice_code(), NoticeCode::UNRECOGNIZED_MESSAGE); } TEST_F(NuggetOsTest, Sequence) { test_harness::raw_message msg; msg.type = APImessageID::SEND_SEQUENCE; msg.data_len = 256; for (size_t x = 0; x < msg.data_len; ++x) { msg.data[x] = x; } ASSERT_NO_TH_ERROR(harness->SendData(msg)); ASSERT_NO_TH_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME)); ASSERT_MSG_TYPE(msg, APImessageID::SEND_SEQUENCE); for (size_t x = 0; x < msg.data_len; ++x) { ASSERT_EQ(msg.data[x], x) << "Inconsistency at index " << x; } } TEST_F(NuggetOsTest, Echo) { test_harness::raw_message msg; msg.type = APImessageID::ECHO_THIS; // Leave some room for bytes which need escaping msg.data_len = sizeof(msg.data) - 64; for (size_t x = 0; x < msg.data_len; ++x) { msg.data[x] = random_number_generator(); } ASSERT_NO_TH_ERROR(harness->SendData(msg)); test_harness::raw_message receive_msg; ASSERT_NO_TH_ERROR(harness->GetData(&receive_msg, 4096 * BYTE_TIME)); ASSERT_MSG_TYPE(msg, APImessageID::ECHO_THIS); ASSERT_EQ(receive_msg.data_len, msg.data_len); for (size_t x = 0; x < msg.data_len; ++x) { ASSERT_EQ(msg.data[x], receive_msg.data[x]) << "Inconsistency at index " << x; } } TEST_F(NuggetOsTest, AesCbc) { const size_t number_of_blocks = 3; for (auto key_size : {KeySize::s128b, KeySize::s192b, KeySize::s256b}) { if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) { cout << "Testing with a key size of: " << std::dec << (key_size * 8) << std::endl; } AesCbcEncryptTest request; request.set_key_size(key_size); request.set_number_of_blocks(number_of_blocks); vector key_data(key_size / sizeof(int)); for (auto &part : key_data) { part = random_number_generator(); } request.set_key(key_data.data(), key_data.size() * sizeof(int)); if (FLAGS_nos_test_dump_protos) { std::ofstream outfile; outfile.open("AesCbcEncryptTest_" + std::to_string(key_size * 8) + ".proto.bin", std::ios_base::binary); outfile << request.SerializeAsString(); outfile.close(); } ASSERT_NO_TH_ERROR(harness->SendOneofProto( APImessageID::TESTING_API_CALL, OneofTestParametersCase::kAesCbcEncryptTest, request)); test_harness::raw_message msg; ASSERT_NO_TH_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME)); ASSERT_MSG_TYPE(msg, APImessageID::TESTING_API_RESPONSE); ASSERT_SUBTYPE(msg, OneofTestResultsCase::kAesCbcEncryptTestResult); AesCbcEncryptTestResult result; ASSERT_TRUE(result.ParseFromArray(reinterpret_cast(msg.data + 2), msg.data_len - 2)); EXPECT_EQ(result.result_code(), DcryptError::DE_NO_ERROR) << result.result_code() << " is " << DcryptError_Name(result.result_code()); ASSERT_EQ(result.cipher_text().size(), number_of_blocks * AES_BLOCK_SIZE) << "\n" << result.DebugString(); uint32_t in[4] = {0, 0, 0, 0}; uint8_t sw_out[AES_BLOCK_SIZE]; uint8_t iv[AES_BLOCK_SIZE]; memset(&iv, 0, sizeof(iv)); AES_KEY aes_key; AES_set_encrypt_key(reinterpret_cast(key_data.data()), key_size * 8, &aes_key); for (size_t x = 0; x < number_of_blocks; ++x) { AES_cbc_encrypt(reinterpret_cast(in), reinterpret_cast(sw_out), AES_BLOCK_SIZE, &aes_key, reinterpret_cast(iv), true); for (size_t y = 0; y < AES_BLOCK_SIZE; ++y) { size_t index = x * AES_BLOCK_SIZE + y; ASSERT_EQ(result.cipher_text()[index] & 0x00ff, sw_out[y] & 0x00ff) << "Inconsistency at index " << index; } } ASSERT_EQ(result.initialization_vector().size(), (size_t) AES_BLOCK_SIZE) << "\n" << result.DebugString(); for (size_t x = 0; x < AES_BLOCK_SIZE; ++x) { ASSERT_EQ(result.initialization_vector()[x] & 0x00ff, iv[x] & 0x00ff) << "Inconsistency at index " << x; } } } TEST_F(NuggetOsTest, Trng) { // Have a bin for every possible byte value. std::vector counts(256, 0); // Use most of the available space while leaving room for the transport // header, escape sequences, etc. const size_t request_size = 475; const size_t repeats = 10; TrngTest request; request.set_number_of_bytes(request_size); int verbosity = harness->getVerbosity(); for (size_t x = 0; x < repeats; ++x) { ASSERT_NO_TH_ERROR(harness->SendOneofProto( APImessageID::TESTING_API_CALL, OneofTestParametersCase::kTrngTest, request)); test_harness::raw_message msg; ASSERT_NO_TH_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME)); ASSERT_MSG_TYPE(msg, APImessageID::TESTING_API_RESPONSE); ASSERT_SUBTYPE(msg, OneofTestResultsCase::kTrngTestResult); TrngTestResult result; ASSERT_TRUE(result.ParseFromArray(reinterpret_cast(msg.data + 2), msg.data_len - 2)); ASSERT_EQ(result.random_bytes().size(), request_size); for (const auto rand_byte : result.random_bytes()) { ++counts[0x00ff & rand_byte]; } // Print the first exchange only for debugging. if (x == 0) { harness->setVerbosity(harness->getVerbosity() - 1); } } harness->setVerbosity(verbosity); double kl_divergence = 0; double ratio = (double) counts.size() / (repeats * request_size); for (const auto count : counts) { ASSERT_NE(count, 0u); kl_divergence += count * log2(count * ratio); } kl_divergence *= ratio; if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) { cout << "K.L. Divergence: " << kl_divergence << "\n"; cout.flush(); } ASSERT_LT(kl_divergence, 15.0); } } // namespace