Home | History | Annotate | Download | only in src
      1 
      2 #include <gtest/gtest.h>
      3 #include <openssl/aes.h>
      4 
      5 #include <algorithm>
      6 #include <fstream>
      7 #include <iostream>
      8 #include <memory>
      9 #include <random>
     10 
     11 #include "nugget/app/protoapi/control.pb.h"
     12 #include "nugget/app/protoapi/header.pb.h"
     13 #include "nugget/app/protoapi/testing_api.pb.h"
     14 #include "src/util.h"
     15 
     16 #ifdef ANDROID
     17 #define FLAGS_nos_test_dump_protos false
     18 #else
     19 #include <gflags/gflags.h>
     20 
     21 DEFINE_bool(nos_test_dump_protos, false, "Dump binary protobufs to a file.");
     22 #endif  // ANDROID
     23 
     24 using nugget::app::protoapi::AesCbcEncryptTest;
     25 using nugget::app::protoapi::AesCbcEncryptTestResult;
     26 using nugget::app::protoapi::APImessageID;
     27 using nugget::app::protoapi::DcryptError;
     28 using nugget::app::protoapi::KeySize;
     29 using nugget::app::protoapi::Notice;
     30 using nugget::app::protoapi::NoticeCode;
     31 using nugget::app::protoapi::OneofTestParametersCase;
     32 using nugget::app::protoapi::OneofTestResultsCase;
     33 using nugget::app::protoapi::TrngTest;
     34 using nugget::app::protoapi::TrngTestResult;
     35 using std::cout;
     36 using std::vector;
     37 using std::unique_ptr;
     38 using test_harness::TestHarness;
     39 
     40 #define ASSERT_NO_TH_ERROR(code) \
     41   ASSERT_EQ(code, test_harness::error_codes::NO_ERROR) \
     42       << code << " is " << test_harness::error_codes_name(code)
     43 
     44 #define ASSERT_MSG_TYPE(msg, type_) \
     45 do{if(type_ != APImessageID::NOTICE && msg.type == APImessageID::NOTICE){ \
     46   Notice received; \
     47   received.ParseFromArray(reinterpret_cast<char *>(msg.data), msg.data_len); \
     48   ASSERT_EQ(msg.type, type_) \
     49       << msg.type << " is " << APImessageID_Name((APImessageID) msg.type) \
     50       << "\n" << received.DebugString(); \
     51 }else{ \
     52   ASSERT_EQ(msg.type, type_) \
     53       << msg.type << " is " << APImessageID_Name((APImessageID) msg.type); \
     54 }}while(0)
     55 
     56 #define ASSERT_SUBTYPE(msg, type_) \
     57   EXPECT_GT(msg.data_len, 2); \
     58   uint16_t subtype = (msg.data[0] << 8) | msg.data[1]; \
     59   EXPECT_EQ(subtype, type_)
     60 
     61 namespace {
     62 
     63 using test_harness::BYTE_TIME;
     64 
     65 class NuggetOsTest: public testing::Test {
     66  protected:
     67   static void SetUpTestCase();
     68   static void TearDownTestCase();
     69 
     70  public:
     71   static unique_ptr<TestHarness> harness;
     72   static std::random_device random_number_generator;
     73 };
     74 
     75 unique_ptr<TestHarness> NuggetOsTest::harness;
     76 std::random_device NuggetOsTest::random_number_generator;
     77 
     78 void NuggetOsTest::SetUpTestCase() {
     79   harness = TestHarness::MakeUnique();
     80 
     81 #ifndef CONFIG_NO_UART
     82   if (!harness->UsingSpi()) {
     83     EXPECT_TRUE(harness->SwitchFromConsoleToProtoApi());
     84     EXPECT_TRUE(harness->ttyState());
     85   }
     86 #endif  // CONFIG_NO_UART
     87 }
     88 
     89 void NuggetOsTest::TearDownTestCase() {
     90 #ifndef CONFIG_NO_UART
     91   if (!harness->UsingSpi()) {
     92     harness->ReadUntil(test_harness::BYTE_TIME * 1024);
     93     EXPECT_TRUE(harness->SwitchFromProtoApiToConsole(NULL));
     94   }
     95 #endif  // CONFIG_NO_UART
     96   harness = unique_ptr<TestHarness>();
     97 }
     98 
     99 TEST_F(NuggetOsTest, NoticePing) {
    100   Notice ping_msg;
    101   ping_msg.set_notice_code(NoticeCode::PING);
    102   Notice pong_msg;
    103 
    104   ASSERT_NO_TH_ERROR(harness->SendProto(APImessageID::NOTICE, ping_msg));
    105   if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) {
    106     cout << ping_msg.DebugString();
    107   }
    108   test_harness::raw_message receive_msg;
    109   ASSERT_NO_TH_ERROR(harness->GetData(&receive_msg, 4096 * BYTE_TIME));
    110   ASSERT_MSG_TYPE(receive_msg, APImessageID::NOTICE);
    111   pong_msg.set_notice_code(NoticeCode::PING);
    112   ASSERT_TRUE(pong_msg.ParseFromArray(
    113       reinterpret_cast<char *>(receive_msg.data), receive_msg.data_len));
    114   if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) {
    115     cout << pong_msg.DebugString() << std::endl;
    116   }
    117   EXPECT_EQ(pong_msg.notice_code(), NoticeCode::PONG);
    118 
    119   ASSERT_NO_TH_ERROR(harness->SendProto(APImessageID::NOTICE, ping_msg));
    120   if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) {
    121     cout << ping_msg.DebugString();
    122   }
    123   ASSERT_NO_TH_ERROR(harness->GetData(&receive_msg, 4096 * BYTE_TIME));
    124   ASSERT_MSG_TYPE(receive_msg, APImessageID::NOTICE);
    125   pong_msg.set_notice_code(NoticeCode::PING);
    126   ASSERT_TRUE(pong_msg.ParseFromArray(
    127       reinterpret_cast<char *>(receive_msg.data), receive_msg.data_len));
    128   if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) {
    129     cout << pong_msg.DebugString() << std::endl;
    130   }
    131   EXPECT_EQ(pong_msg.notice_code(), NoticeCode::PONG);
    132 
    133   ASSERT_NO_TH_ERROR(harness->SendProto(APImessageID::NOTICE, ping_msg));
    134   if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) {
    135     cout << ping_msg.DebugString();
    136   }
    137   ASSERT_NO_TH_ERROR(harness->GetData(&receive_msg, 4096 * BYTE_TIME));
    138   ASSERT_MSG_TYPE(receive_msg, APImessageID::NOTICE);
    139   pong_msg.set_notice_code(NoticeCode::PING);
    140   ASSERT_TRUE(pong_msg.ParseFromArray(
    141       reinterpret_cast<char *>(receive_msg.data), receive_msg.data_len));
    142   if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) {
    143     cout << pong_msg.DebugString() << std::endl;
    144   }
    145   EXPECT_EQ(pong_msg.notice_code(), NoticeCode::PONG);
    146 }
    147 
    148 TEST_F(NuggetOsTest, InvalidMessageType) {
    149   const char content[] = "This is a test message.";
    150 
    151   test_harness::raw_message msg;
    152   msg.type = 0;
    153   std::copy(content, content + sizeof(content), msg.data);
    154   msg.data_len = sizeof(content);
    155 
    156   ASSERT_NO_TH_ERROR(harness->SendData(msg));
    157   ASSERT_NO_TH_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME));
    158   ASSERT_MSG_TYPE(msg, APImessageID::NOTICE);
    159 
    160   Notice notice_msg;
    161   ASSERT_TRUE(notice_msg.ParseFromArray(reinterpret_cast<char *>(msg.data),
    162                                         msg.data_len));
    163   if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) {
    164     cout << notice_msg.DebugString() << std::endl;
    165   }
    166   EXPECT_EQ(notice_msg.notice_code(), NoticeCode::UNRECOGNIZED_MESSAGE);
    167 }
    168 
    169 TEST_F(NuggetOsTest, Sequence) {
    170   test_harness::raw_message msg;
    171   msg.type = APImessageID::SEND_SEQUENCE;
    172   msg.data_len = 256;
    173   for (size_t x = 0; x < msg.data_len; ++x) {
    174     msg.data[x] = x;
    175   }
    176 
    177   ASSERT_NO_TH_ERROR(harness->SendData(msg));
    178   ASSERT_NO_TH_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME));
    179   ASSERT_MSG_TYPE(msg, APImessageID::SEND_SEQUENCE);
    180   for (size_t x = 0; x < msg.data_len; ++x) {
    181     ASSERT_EQ(msg.data[x], x) << "Inconsistency at index " << x;
    182   }
    183 }
    184 
    185 TEST_F(NuggetOsTest, Echo) {
    186   test_harness::raw_message msg;
    187   msg.type = APImessageID::ECHO_THIS;
    188   // Leave some room for bytes which need escaping
    189   msg.data_len = sizeof(msg.data) - 64;
    190   for (size_t x = 0; x < msg.data_len; ++x) {
    191     msg.data[x] = random_number_generator();
    192   }
    193 
    194   ASSERT_NO_TH_ERROR(harness->SendData(msg));
    195 
    196   test_harness::raw_message receive_msg;
    197   ASSERT_NO_TH_ERROR(harness->GetData(&receive_msg, 4096 * BYTE_TIME));
    198   ASSERT_MSG_TYPE(msg, APImessageID::ECHO_THIS);
    199   ASSERT_EQ(receive_msg.data_len, msg.data_len);
    200 
    201   for (size_t x = 0; x < msg.data_len; ++x) {
    202     ASSERT_EQ(msg.data[x], receive_msg.data[x])
    203         << "Inconsistency at index " << x;
    204   }
    205 }
    206 
    207 TEST_F(NuggetOsTest, AesCbc) {
    208   const size_t number_of_blocks = 3;
    209 
    210   for (auto key_size : {KeySize::s128b, KeySize::s192b, KeySize::s256b}) {
    211     if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) {
    212       cout << "Testing with a key size of: " << std::dec << (key_size * 8)
    213            << std::endl;
    214     }
    215     AesCbcEncryptTest request;
    216     request.set_key_size(key_size);
    217     request.set_number_of_blocks(number_of_blocks);
    218 
    219     vector<int> key_data(key_size / sizeof(int));
    220     for (auto &part : key_data) {
    221       part = random_number_generator();
    222     }
    223     request.set_key(key_data.data(), key_data.size() * sizeof(int));
    224 
    225 
    226     if (FLAGS_nos_test_dump_protos) {
    227       std::ofstream outfile;
    228       outfile.open("AesCbcEncryptTest_" + std::to_string(key_size * 8) +
    229                    ".proto.bin", std::ios_base::binary);
    230       outfile << request.SerializeAsString();
    231       outfile.close();
    232     }
    233 
    234     ASSERT_NO_TH_ERROR(harness->SendOneofProto(
    235         APImessageID::TESTING_API_CALL,
    236         OneofTestParametersCase::kAesCbcEncryptTest,
    237         request));
    238 
    239     test_harness::raw_message msg;
    240     ASSERT_NO_TH_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME));
    241     ASSERT_MSG_TYPE(msg, APImessageID::TESTING_API_RESPONSE);
    242     ASSERT_SUBTYPE(msg, OneofTestResultsCase::kAesCbcEncryptTestResult);
    243 
    244     AesCbcEncryptTestResult result;
    245     ASSERT_TRUE(result.ParseFromArray(reinterpret_cast<char *>(msg.data + 2),
    246                                       msg.data_len - 2));
    247     EXPECT_EQ(result.result_code(), DcryptError::DE_NO_ERROR)
    248         << result.result_code() << " is "
    249         << DcryptError_Name(result.result_code());
    250     ASSERT_EQ(result.cipher_text().size(), number_of_blocks * AES_BLOCK_SIZE)
    251         << "\n" << result.DebugString();
    252 
    253     uint32_t in[4] = {0, 0, 0, 0};
    254     uint8_t sw_out[AES_BLOCK_SIZE];
    255     uint8_t iv[AES_BLOCK_SIZE];
    256     memset(&iv, 0, sizeof(iv));
    257     AES_KEY aes_key;
    258     AES_set_encrypt_key(reinterpret_cast<uint8_t *>(key_data.data()),
    259                         key_size * 8, &aes_key);
    260     for (size_t x = 0; x < number_of_blocks; ++x) {
    261       AES_cbc_encrypt(reinterpret_cast<uint8_t *>(in),
    262                       reinterpret_cast<uint8_t *>(sw_out), AES_BLOCK_SIZE,
    263                       &aes_key, reinterpret_cast<uint8_t *>(iv), true);
    264       for (size_t y = 0; y < AES_BLOCK_SIZE; ++y) {
    265         size_t index = x * AES_BLOCK_SIZE + y;
    266         ASSERT_EQ(result.cipher_text()[index] & 0x00ff,
    267                   sw_out[y] & 0x00ff) << "Inconsistency at index " << index;
    268       }
    269     }
    270 
    271     ASSERT_EQ(result.initialization_vector().size(), (size_t) AES_BLOCK_SIZE)
    272         << "\n" << result.DebugString();
    273     for (size_t x = 0; x < AES_BLOCK_SIZE; ++x) {
    274       ASSERT_EQ(result.initialization_vector()[x] & 0x00ff, iv[x] & 0x00ff)
    275                 << "Inconsistency at index " << x;
    276     }
    277   }
    278 }
    279 
    280 TEST_F(NuggetOsTest, Trng) {
    281   // Have a bin for every possible byte value.
    282   std::vector<size_t> counts(256, 0);
    283 
    284   // Use most of the available space while leaving room for the transport
    285   // header, escape sequences, etc.
    286   const size_t request_size = 475;
    287   const size_t repeats = 10;
    288 
    289   TrngTest request;
    290   request.set_number_of_bytes(request_size);
    291 
    292   int verbosity = harness->getVerbosity();
    293   for (size_t x = 0; x < repeats; ++x) {
    294     ASSERT_NO_TH_ERROR(harness->SendOneofProto(
    295         APImessageID::TESTING_API_CALL,
    296         OneofTestParametersCase::kTrngTest,
    297         request));
    298     test_harness::raw_message msg;
    299     ASSERT_NO_TH_ERROR(harness->GetData(&msg, 4096 * BYTE_TIME));
    300     ASSERT_MSG_TYPE(msg, APImessageID::TESTING_API_RESPONSE);
    301     ASSERT_SUBTYPE(msg, OneofTestResultsCase::kTrngTestResult);
    302 
    303     TrngTestResult result;
    304     ASSERT_TRUE(result.ParseFromArray(reinterpret_cast<char *>(msg.data + 2),
    305                                       msg.data_len - 2));
    306     ASSERT_EQ(result.random_bytes().size(), request_size);
    307     for (const auto rand_byte : result.random_bytes()) {
    308       ++counts[0x00ff & rand_byte];
    309     }
    310 
    311     // Print the first exchange only for debugging.
    312     if (x == 0) {
    313       harness->setVerbosity(harness->getVerbosity() - 1);
    314     }
    315   }
    316   harness->setVerbosity(verbosity);
    317 
    318   double kl_divergence = 0;
    319   double ratio = (double) counts.size() / (repeats * request_size);
    320   for (const auto count : counts) {
    321     ASSERT_NE(count, 0u);
    322     kl_divergence += count * log2(count * ratio);
    323   }
    324   kl_divergence *= ratio;
    325   if (harness->getVerbosity() >= TestHarness::VerbosityLevels::INFO) {
    326     cout << "K.L. Divergence: " << kl_divergence << "\n";
    327     cout.flush();
    328   }
    329   ASSERT_LT(kl_divergence, 15.0);
    330 }
    331 
    332 }  // namespace
    333