mirror of https://github.com/ARMmbed/mbed-os.git
277 lines
9.8 KiB
C++
277 lines
9.8 KiB
C++
|
/*
|
||
|
* Copyright (c) 2018 ARM Limited. All rights reserved.
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||
|
* not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* The test is based on the assumption that trng will generate random data, random so
|
||
|
* there will not be any similar patterns in it, that kind of data will be impossible to
|
||
|
* compress, if compression will acuur the test will result in failure.
|
||
|
*
|
||
|
* The test is composed out of three parts:
|
||
|
* the first, generate a trng buffer and try to compress it, at the end of first part
|
||
|
* we will reset the device.
|
||
|
* In second part we will generate a trng buffer with a different buffer size and try to
|
||
|
* compress it.
|
||
|
* In the third part we will again generate a trng buffer to see that the same trng output
|
||
|
* is not generated as the stored trng buffer from part one (before reseting), the new trng data will
|
||
|
* be concatenated to the trng data from the first part and then try to compress it
|
||
|
* together, if there are similar patterns the compression will succeed.
|
||
|
*
|
||
|
* We need to store and load the first part data before and after reset, the mechanism
|
||
|
* we chose is NVstore, mainly because its simplicity and the fact it is not platform
|
||
|
* dependent, in case a specific board does not support NVstore we will use the
|
||
|
* mbed greentea platform for sending and receving the data from the device to the
|
||
|
* host running the test and back, the problem with this mechanism is that it doesn't handle
|
||
|
* well certain characters, especially non ASCII ones, so we used the base64 algorithm
|
||
|
* to ensure all characters will be transmitted correctly.
|
||
|
*/
|
||
|
|
||
|
#include "greentea-client/test_env.h"
|
||
|
#include "unity/unity.h"
|
||
|
#include "utest/utest.h"
|
||
|
#include "hal/trng_api.h"
|
||
|
#include "base64b.h"
|
||
|
#include "nvstore.h"
|
||
|
#include <stdio.h>
|
||
|
|
||
|
/*Include LZF Compressor librart */
|
||
|
extern "C" {
|
||
|
#include "lzf.h"
|
||
|
}
|
||
|
|
||
|
#if !DEVICE_TRNG
|
||
|
#error [NOT_SUPPORTED] TRNG API not supported for this target
|
||
|
#endif
|
||
|
|
||
|
#define MSG_VALUE_DUMMY "0"
|
||
|
#define MSG_VALUE_LEN 64
|
||
|
#define MSG_KEY_LEN 32
|
||
|
|
||
|
#define BUFFER_LEN (MSG_VALUE_LEN/2) //size of first step data, and half of the second step data
|
||
|
|
||
|
#define MSG_TRNG_READY "ready"
|
||
|
#define MSG_TRNG_BUFFER "buffer"
|
||
|
|
||
|
#define MSG_TRNG_TEST_STEP1 "check_step1"
|
||
|
#define MSG_TRNG_TEST_STEP2 "check_step2"
|
||
|
#define MSG_TRNG_TEST_SUITE_ENDED "Test_suite_ended"
|
||
|
|
||
|
#define NVKEY 1 //NVstore key for storing and loading data
|
||
|
|
||
|
/*there are some issues with nvstore and greentea reset, so for now nvstore is disabled,
|
||
|
*When solved delete current define and replace all NVSTORE_RESET with NVSTORE_ENABLED*/
|
||
|
#define NVSTORE_RESET (NVSTORE_ENABLED & 0)
|
||
|
|
||
|
using namespace utest::v1;
|
||
|
|
||
|
static int fill_buffer_trng(uint8_t *buffer, trng_t *trng_obj, size_t trng_len)
|
||
|
{
|
||
|
size_t temp_size = 0, output_length = 0;
|
||
|
int trng_res = 0;
|
||
|
uint8_t *temp_in_buf = buffer;
|
||
|
|
||
|
trng_init(trng_obj);
|
||
|
memset(buffer, 0, BUFFER_LEN);
|
||
|
|
||
|
while (true)
|
||
|
{
|
||
|
trng_res = trng_get_bytes(trng_obj, temp_in_buf, trng_len, &output_length);
|
||
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, trng_res, "trng_get_bytes error!");
|
||
|
temp_size += output_length;
|
||
|
temp_in_buf += output_length;
|
||
|
trng_len -= output_length;
|
||
|
if (temp_size >= trng_len)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
temp_in_buf = NULL;
|
||
|
trng_free(trng_obj);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void compress_and_compare(char *key, char *value)
|
||
|
{
|
||
|
trng_t trng_obj;
|
||
|
uint8_t out_comp_buf[BUFFER_LEN * 2] = {0}, buffer[BUFFER_LEN] = {0};
|
||
|
uint8_t input_buf[BUFFER_LEN * 2] = {0}, temp_buff[BUFFER_LEN * 2] = {0};
|
||
|
size_t out_comp_buf_len = 0;
|
||
|
unsigned int comp_res = 0, result = 0;
|
||
|
unsigned char htab[32][32] = {0};
|
||
|
|
||
|
#if NVSTORE_RESET
|
||
|
NVStore &nvstore = NVStore::get_instance();
|
||
|
#endif
|
||
|
|
||
|
/*At the begining of step 2 load trng buffer from step 1*/
|
||
|
if (strcmp(key, MSG_TRNG_TEST_STEP2) == 0)
|
||
|
{
|
||
|
#if NVSTORE_RESET
|
||
|
uint16_t actual = 0;
|
||
|
result = nvstore.get(NVKEY, sizeof(buffer), buffer, actual);
|
||
|
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
|
||
|
#else
|
||
|
/*Using base64 to decode data sent from host*/
|
||
|
uint32_t lengthWritten = 0;
|
||
|
uint32_t charsProcessed = 0;
|
||
|
result = esfs_DecodeNBase64((const char*)value, MSG_VALUE_LEN, buffer, BUFFER_LEN, &lengthWritten, &charsProcessed);
|
||
|
TEST_ASSERT_EQUAL(0, result);
|
||
|
#endif
|
||
|
memcpy(input_buf, buffer, BUFFER_LEN);
|
||
|
}
|
||
|
|
||
|
/*Fill buffer with trng values*/
|
||
|
result = fill_buffer_trng(buffer, &trng_obj, sizeof(buffer));
|
||
|
TEST_ASSERT_EQUAL(0, result);
|
||
|
|
||
|
/*lzf_compress will try to compress the random data, if it succeeded it means the data is not really random*/
|
||
|
if (strcmp(key, MSG_TRNG_TEST_STEP1) == 0)
|
||
|
{
|
||
|
printf("\n******TRNG_TEST_STEP1*****\n");
|
||
|
out_comp_buf_len = BUFFER_LEN + (BUFFER_LEN / 4);
|
||
|
comp_res = lzf_compress((const void *)buffer,
|
||
|
(unsigned int)sizeof(buffer),
|
||
|
(void *)out_comp_buf,
|
||
|
out_comp_buf_len,
|
||
|
(unsigned char **)htab);
|
||
|
if (comp_res >= BUFFER_LEN)
|
||
|
{
|
||
|
printf("trng_get_bytes for buffer size %d was successful", sizeof(buffer));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("trng_get_bytes for buffer size %d was unsuccessful", sizeof(buffer));
|
||
|
TEST_ASSERT(false);
|
||
|
}
|
||
|
printf("\n******FINISHED_TRNG_TEST_STEP1*****\n\n");
|
||
|
}
|
||
|
else if (strcmp(key, MSG_TRNG_TEST_STEP2) == 0)
|
||
|
{
|
||
|
printf("\n******TRNG_TEST_STEP2*****\n");
|
||
|
result = fill_buffer_trng(temp_buff, &trng_obj, sizeof(temp_buff));
|
||
|
TEST_ASSERT_EQUAL(0, result);
|
||
|
|
||
|
out_comp_buf_len = 2 * BUFFER_LEN + (BUFFER_LEN / 2);
|
||
|
comp_res = lzf_compress((const void *)temp_buff,
|
||
|
(unsigned int)sizeof(temp_buff),
|
||
|
(void *)out_comp_buf,
|
||
|
out_comp_buf_len,
|
||
|
(unsigned char **)htab);
|
||
|
|
||
|
if (comp_res >= BUFFER_LEN)
|
||
|
{
|
||
|
printf("trng_get_bytes for buffer size %d was successful", sizeof(temp_buff));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("trng_get_bytes for buffer size %d was unsuccessful", sizeof(temp_buff));
|
||
|
TEST_ASSERT(false);
|
||
|
}
|
||
|
printf("\n******FINISHED_TRNG_TEST_STEP2*****\n\n");
|
||
|
|
||
|
printf("******TRNG_TEST_STEP3*****\n");
|
||
|
|
||
|
memcpy(input_buf + BUFFER_LEN, buffer, BUFFER_LEN);
|
||
|
comp_res = lzf_compress((const void *)input_buf,
|
||
|
(unsigned int)sizeof(input_buf),
|
||
|
(void *)out_comp_buf,
|
||
|
out_comp_buf_len,
|
||
|
(unsigned char **)htab);
|
||
|
|
||
|
if (comp_res >= BUFFER_LEN)
|
||
|
{
|
||
|
printf("compression for concatenated buffer after reset was successful");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("compression for concatenated buffer after reset was unsuccessful");
|
||
|
TEST_ASSERT(false);
|
||
|
}
|
||
|
printf("\n******FINISHED_TRNG_TEST_STEP3*****\n\n");
|
||
|
}
|
||
|
|
||
|
/*At the end of step 1 store trng buffer and reset the device*/
|
||
|
if (strcmp(key, MSG_TRNG_TEST_STEP1) == 0)
|
||
|
{
|
||
|
int result = 0;
|
||
|
#if NVSTORE_RESET
|
||
|
result = nvstore.set(NVKEY, sizeof(buffer), buffer);
|
||
|
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
|
||
|
#else
|
||
|
/*Using base64 to encode data sending from host*/
|
||
|
result = esfs_EncodeBase64(buffer, BUFFER_LEN, (char*)out_comp_buf, sizeof(out_comp_buf));
|
||
|
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
|
||
|
greentea_send_kv(MSG_TRNG_BUFFER, (const char *)out_comp_buf);
|
||
|
#endif
|
||
|
system_reset();
|
||
|
TEST_ASSERT_MESSAGE(false, "system_reset() did not reset the device as expected.");
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*This method call first and second steps, it directs by the key received from the host*/
|
||
|
void trng_test()
|
||
|
{
|
||
|
greentea_send_kv(MSG_TRNG_READY, MSG_VALUE_DUMMY);
|
||
|
|
||
|
static char key[MSG_KEY_LEN + 1] = { };
|
||
|
static char value[MSG_VALUE_LEN + 1] = { };
|
||
|
memset(key, 0, MSG_KEY_LEN + 1);
|
||
|
memset(value, 0, MSG_VALUE_LEN + 1);
|
||
|
|
||
|
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||
|
|
||
|
if (strcmp(key, MSG_TRNG_TEST_STEP1) == 0)
|
||
|
{
|
||
|
/*create trng data buffer and try to compress it, store it for later checks*/
|
||
|
compress_and_compare(key, value);
|
||
|
return trng_test();
|
||
|
}
|
||
|
|
||
|
if (strcmp(key, MSG_TRNG_TEST_STEP2) == 0)
|
||
|
{
|
||
|
/*create another trng data buffer and concatenate it to the stored trng data buffer
|
||
|
try to compress them both*/
|
||
|
compress_and_compare(key, value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason) {
|
||
|
greentea_case_failure_abort_handler(source, reason);
|
||
|
return STATUS_CONTINUE;
|
||
|
}
|
||
|
|
||
|
Case cases[] = {
|
||
|
Case("TRNG: trng_test", trng_test, greentea_failure_handler),
|
||
|
};
|
||
|
|
||
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||
|
{
|
||
|
GREENTEA_SETUP(100, "trng_reset");
|
||
|
return greentea_test_setup_handler(number_of_cases);
|
||
|
}
|
||
|
|
||
|
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||
|
|
||
|
int main()
|
||
|
{
|
||
|
bool ret = !Harness::run(specification);
|
||
|
greentea_send_kv(MSG_TRNG_TEST_SUITE_ENDED, MSG_VALUE_DUMMY);
|
||
|
|
||
|
return ret;
|
||
|
}
|