diff --git a/TESTS/host_tests/trng_reset.py b/TESTS/host_tests/trng_reset.py new file mode 100644 index 0000000000..b6b4adfef2 --- /dev/null +++ b/TESTS/host_tests/trng_reset.py @@ -0,0 +1,133 @@ +""" +Copyright (c) 2018 ARM Limited +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. +""" + +""" +This script is the host script for trng test sequence, it send the +step signaling sequence and receive and transmit data to the device after +reset if necesarry (default lading and storing while reseting the device +is NVstore, in case NVstore isn't enabled we'll use current infrastructure, +for more details see main.cpp file) +""" + +import time +from mbed_host_tests import BaseHostTest +from mbed_host_tests.host_tests_runner.host_test_default import DefaultTestSelector + +DEFAULT_CYCLE_PERIOD = 1.0 +MSG_VALUE_DUMMY = '0' +MSG_TRNG_READY = 'ready' +MSG_TRNG_BUFFER = 'buffer' +MSG_TRNG_FINISH = 'finish' +MSG_TRNG_TEST_STEP1 = 'check_step1' +MSG_TRNG_TEST_STEP2 = 'check_step2' +MSG_KEY_SYNC = '__sync' +MSG_KEY_TEST_SUITE_ENDED = 'Test suite ended' + +class TRNGResetTest(BaseHostTest): + """Test for the TRNG API. + """ + + def __init__(self): + super(TRNGResetTest, self).__init__() + self.reset = False + self.finish = False + self.suite_ended = False + self.buffer = 0 + cycle_s = self.get_config_item('program_cycle_s') + self.program_cycle_s = cycle_s if cycle_s is not None else DEFAULT_CYCLE_PERIOD + self.test_steps_sequence = self.test_steps() + # Advance the coroutine to it's first yield statement. + self.test_steps_sequence.send(None) + + #define callback functions for msg handling + def setup(self): + self.register_callback(MSG_TRNG_READY, self.cb_device_ready) + self.register_callback(MSG_TRNG_BUFFER, self.cb_trng_buffer) + self.register_callback(MSG_TRNG_FINISH, self.cb_device_finish) + self.register_callback(MSG_KEY_TEST_SUITE_ENDED, self.cb_device_test_suit_ended) + + #receive sent data from device before reset + def cb_trng_buffer(self, key, value, timestamp): + """Acknowledge device rebooted correctly and feed the test execution + """ + self.buffer = value + + def cb_device_ready(self, key, value, timestamp): + """Acknowledge device rebooted correctly and feed the test execution + """ + self.reset = True + + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.notify_complete(False) + + def cb_device_finish(self, key, value, timestamp): + """Acknowledge device finished a test step correctly and feed the test execution + """ + self.finish = True + + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.notify_complete(False) + + def cb_device_test_suit_ended(self, key, value, timestamp): + """Acknowledge device finished a test step correctly and feed the test execution + """ + self.suite_ended = True + + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.notify_complete(False) + + #define test steps and actions + def test_steps(self): + """Test step 1 + """ + wait_for_communication = yield + + self.reset = False + self.send_kv(MSG_TRNG_TEST_STEP1, MSG_VALUE_DUMMY) + time.sleep(self.program_cycle_s) + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) + + wait_for_communication = yield + + if self.reset == False: + raise RuntimeError('Phase 1: Platform did not reset as expected.') + + """Test step 2 (After reset) + """ + self.finish = False + self.send_kv(MSG_TRNG_TEST_STEP2, self.buffer) + time.sleep(self.program_cycle_s) + + wait_for_communication = yield + + if self.finish == False: + raise RuntimeError('Test failed.') + + wait_for_communication = yield + + if self.suite_ended == False: + raise RuntimeError('Test failed.') + + # The sequence is correct -- test passed. + yield True \ No newline at end of file diff --git a/TESTS/mbed_hal/trng/base64b/base64b.cpp b/TESTS/mbed_hal/trng/base64b/base64b.cpp new file mode 100644 index 0000000000..e9a8cf35fc --- /dev/null +++ b/TESTS/mbed_hal/trng/base64b/base64b.cpp @@ -0,0 +1,185 @@ +#include "base64b.h" + +using namespace std; + +static char IntToBase64Char(uint8_t intVal) +{ + const char* base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + return base64Digits[intVal & 0x3F]; +} + +#define BASE_64_PAD 0xFF +static base64_result_e Base64CharToInt(char base64, uint8_t* intVal) +{ + if (NULL == intVal) + { + return BASE64_INVALID_PARAMETER; + } + + if ((base64 >= 'A') && (base64 <= 'Z')) + *intVal = base64 - 'A' ; + else if((base64 >= 'a')&&(base64 <= 'z')) + *intVal = base64 - 'a' + 26; + else if ((base64 >= '0')&&(base64 <= '9')) + *intVal = base64 - '0' + 52; + else if (base64 == '+') + *intVal = 62; + else if (base64 == '/') + *intVal = 63; + else if (base64 == '=') + *intVal = BASE_64_PAD; + else + { + return BASE64_ERROR; + } + + return BASE64_SUCCESS; +} + +base64_result_e esfs_DecodeNBase64(const char* string, + uint32_t stringMaxSize, + void* buffer, + uint32_t bufferSize, + uint32_t* lengthWritten, + uint32_t* charsProcessed) +{ + base64_result_e result = BASE64_SUCCESS; + uint32_t bitOffset =0; + uint8_t* writePtr = (uint8_t*)buffer; + uint8_t* bufferEnd = (uint8_t*)buffer + bufferSize; + uint8_t tempVal = 0; + uint32_t currPos = 0; + uint32_t localBytesWritten = 0; + uint32_t localCharsProcessed = 0; + bool isEndOfString = false; + + if ((NULL == string) || (NULL == buffer) || (bufferSize == 0)) + { + return BASE64_INVALID_PARAMETER; + } + + *writePtr = 0; + while (( string[currPos] != 0 ) && + ( currPos < stringMaxSize ) && + ( writePtr < bufferEnd ) && + ( !isEndOfString )) + { + uint8_t val; + + if (string[currPos] == 0 || currPos >= stringMaxSize) + break; + + result = Base64CharToInt(string[currPos++], &val); + if (result != BASE64_SUCCESS) + break; + + if (val != BASE_64_PAD) + { + if(bitOffset <= 2) + { + tempVal |= val << (2 - bitOffset); + if (bitOffset == 2) + { + *writePtr++ = tempVal; + tempVal = 0; + } + } + else + { + *writePtr++ = (uint8_t)(tempVal | (val >> (bitOffset - 2))); + tempVal = (uint8_t)(val << (10 - bitOffset)); + } + } + else // found BASE_64_PAD + { + // At most two pad characters may occur at the end of the encoded stream + if (bitOffset == 2) + isEndOfString = true; // The last padding byte has been processed. + else if (bitOffset != 4) + return BASE64_ERROR; // Incorrect padding + } + + bitOffset = (bitOffset + 6) & 0x7; + if (bitOffset == 0) + { + localBytesWritten = (uint32_t)(writePtr - (uint8_t*)buffer); + localCharsProcessed = currPos; + } + } + if (charsProcessed == NULL) + localBytesWritten = (uint32_t)(writePtr - (uint8_t*)buffer); + else + *charsProcessed = localCharsProcessed; + if (lengthWritten != NULL) + *lengthWritten = localBytesWritten; + else if (bufferSize != localBytesWritten) + return BASE64_BUFFER_TOO_SMALL; + + // Check if additional bytes should have been processed but buffer isn't sufficient. + if (( result == BASE64_SUCCESS ) && + ( !isEndOfString ) && + ( string[currPos] != '=' ) && + ( string[currPos] != 0 ) && + ( currPos < stringMaxSize) ) + return BASE64_BUFFER_TOO_SMALL; + + if (result != BASE64_SUCCESS) + return result; + + return BASE64_SUCCESS; +} + +base64_result_e esfs_EncodeBase64(const void* buffer, uint32_t bufferSize, char* string, uint32_t stringSize) +{ + uint32_t bitOffset = 0; + + const uint8_t* readPtr = (const uint8_t*)buffer; + const uint8_t* bufferEnd = (const uint8_t*)buffer + bufferSize; + + char* writePtr = string; + char* stringEnd = string + stringSize - 1; + + if ((NULL == string) || (NULL == buffer) || (stringSize == 0)) + return BASE64_INVALID_PARAMETER; + + stringSize--; + while(readPtr < bufferEnd && writePtr < stringEnd) + { + uint8_t tempVal = 0; + switch (bitOffset) + { + case 0: + *writePtr++ = IntToBase64Char(*readPtr >> 2); // take upper 6 bits + break; + case 6: + tempVal = *readPtr++ << 4; + if (readPtr < bufferEnd) + tempVal |= *readPtr >> 4; + *writePtr++ = IntToBase64Char(tempVal); + break; + case 4: + tempVal = *readPtr++ << 2; + if (readPtr < bufferEnd) + tempVal |= *readPtr >> 6; + *writePtr++ = IntToBase64Char(tempVal); + break; + case 2: + *writePtr++ = IntToBase64Char(*readPtr++); + break; + default: + return BASE64_ERROR; // we should never reach this code. + } + bitOffset = (bitOffset + 6) & 0x7; + } + while (bitOffset > 0 && writePtr < stringEnd) + { + *writePtr++ = '='; + bitOffset = (bitOffset + 6) & 0x7; + } + *writePtr = 0; + + if ((readPtr < bufferEnd) || (bitOffset != 0)) + return (BASE64_BUFFER_TOO_SMALL); + + return(BASE64_SUCCESS); +} diff --git a/TESTS/mbed_hal/trng/base64b/base64b.h b/TESTS/mbed_hal/trng/base64b/base64b.h new file mode 100644 index 0000000000..2a35d05983 --- /dev/null +++ b/TESTS/mbed_hal/trng/base64b/base64b.h @@ -0,0 +1,16 @@ +#include +#include +#include + +typedef enum { + BASE64_SUCCESS = 0, + BASE64_INVALID_PARAMETER = 1, + BASE64_BUFFER_TOO_SMALL = 2, + BASE64_ERROR = 3, +}base64_result_e; + +base64_result_e esfs_EncodeBase64(const void* buffer, uint32_t bufferSize, char* string, uint32_t stringSize); +base64_result_e esfs_DecodeNBase64(const char* string, uint32_t stringMaxSize, void* buffer, uint32_t bufferSize, + uint32_t* lengthWritten, uint32_t* charsProcessed); + + diff --git a/TESTS/mbed_hal/trng/lzflib/lzf.h b/TESTS/mbed_hal/trng/lzflib/lzf.h new file mode 100644 index 0000000000..1f848a09b8 --- /dev/null +++ b/TESTS/mbed_hal/trng/lzflib/lzf.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2000-2008 Marc Alexander Lehmann + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#ifndef LZF_H +#define LZF_H + +/*********************************************************************** +** +** lzf -- an extremely fast/free compression/decompression-method +** http://liblzf.plan9.de/ +** +** This algorithm is believed to be patent-free. +** +***********************************************************************/ + +#define LZF_VERSION 0x0105 /* 1.5, API version */ + +/* + * Compress in_len bytes stored at the memory block starting at + * in_data and write the result to out_data, up to a maximum length + * of out_len bytes. + * + * If the output buffer is not large enough or any error occurs return 0, + * otherwise return the number of bytes used, which might be considerably + * more than in_len (but less than 104% of the original size), so it + * makes sense to always use out_len == in_len - 1), to ensure _some_ + * compression, and store the data uncompressed otherwise (with a flag, of + * course. + * + * lzf_compress might use different algorithms on different systems and + * even different runs, thus might result in different compressed strings + * depending on the phase of the moon or similar factors. However, all + * these strings are architecture-independent and will result in the + * original data when decompressed using lzf_decompress. + * + * The buffers must not be overlapping. + * + * If the option LZF_STATE_ARG is enabled, an extra argument must be + * supplied which is not reflected in this header file. Refer to lzfP.h + * and lzf_c.c. + * + */ +unsigned int +lzf_compress (const void *const in_data, unsigned int in_len, + void *out_data, unsigned int out_len, + unsigned char **htab); + +/* + * Decompress data compressed with some version of the lzf_compress + * function and stored at location in_data and length in_len. The result + * will be stored at out_data up to a maximum of out_len characters. + * + * If the output buffer is not large enough to hold the decompressed + * data, a 0 is returned and errno is set to E2BIG. Otherwise the number + * of decompressed bytes (i.e. the original length of the data) is + * returned. + * + * If an error in the compressed data is detected, a zero is returned and + * errno is set to EINVAL. + * + * This function is very fast, about as fast as a copying loop. + */ +unsigned int +lzf_decompress (const void *const in_data, unsigned int in_len, + void *out_data, unsigned int out_len); + +#endif + diff --git a/TESTS/mbed_hal/trng/lzflib/lzfP.h b/TESTS/mbed_hal/trng/lzflib/lzfP.h new file mode 100644 index 0000000000..7244bc1547 --- /dev/null +++ b/TESTS/mbed_hal/trng/lzflib/lzfP.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2000-2007 Marc Alexander Lehmann + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#ifndef LZFP_h +#define LZFP_h + +#define STANDALONE 1 /* at the moment, this is ok. */ + +#ifndef STANDALONE +# include "lzf.h" +#endif + +/* + * Size of hashtable is (1 << HLOG) * sizeof (char *) + * decompression is independent of the hash table size + * the difference between 15 and 14 is very small + * for small blocks (and 14 is usually a bit faster). + * For a low-memory/faster configuration, use HLOG == 13; + * For best compression, use 15 or 16 (or more, up to 22). + */ +#ifndef HLOG +# define HLOG 14 +#endif + +/* + * Sacrifice very little compression quality in favour of compression speed. + * This gives almost the same compression as the default code, and is + * (very roughly) 15% faster. This is the preferred mode of operation. + */ +#ifndef VERY_FAST +# define VERY_FAST 1 +#endif + +/* + * Sacrifice some more compression quality in favour of compression speed. + * (roughly 1-2% worse compression for large blocks and + * 9-10% for small, redundant, blocks and >>20% better speed in both cases) + * In short: when in need for speed, enable this for binary data, + * possibly disable this for text data. + */ +#ifndef ULTRA_FAST +# define ULTRA_FAST 0 +#endif + +/* + * Unconditionally aligning does not cost very much, so do it if unsure + */ +#ifndef STRICT_ALIGN +# define STRICT_ALIGN !(defined(__i386) || defined (__amd64)) +#endif + +/* + * You may choose to pre-set the hash table (might be faster on some + * modern cpus and large (>>64k) blocks, and also makes compression + * deterministic/repeatable when the configuration otherwise is the same). + */ +#ifndef INIT_HTAB +# define INIT_HTAB 0 +#endif + +/* + * Avoid assigning values to errno variable? for some embedding purposes + * (linux kernel for example), this is necessary. NOTE: this breaks + * the documentation in lzf.h. Avoiding errno has no speed impact. + */ +#ifndef AVOID_ERRNO +# define AVOID_ERRNO 0 +#endif + +/* + * Whether to pass the LZF_STATE variable as argument, or allocate it + * on the stack. For small-stack environments, define this to 1. + * NOTE: this breaks the prototype in lzf.h. + */ +#ifndef LZF_STATE_ARG +# define LZF_STATE_ARG 1 +#endif + +/* + * Whether to add extra checks for input validity in lzf_decompress + * and return EINVAL if the input stream has been corrupted. This + * only shields against overflowing the input buffer and will not + * detect most corrupted streams. + * This check is not normally noticeable on modern hardware + * (<1% slowdown), but might slow down older cpus considerably. + */ +#ifndef CHECK_INPUT +# define CHECK_INPUT 1 +#endif + +/* + * Whether to store pointers or offsets inside the hash table. On + * 64 bit architetcures, pointers take up twice as much space, + * and might also be slower. Default is to autodetect. + */ +/*#define LZF_USER_OFFSETS autodetect */ + +/*****************************************************************************/ +/* nothing should be changed below */ + +#ifdef __cplusplus +# include +# include +using namespace std; +#else +# include +# include +#endif + +#ifndef LZF_USE_OFFSETS +# if defined (WIN32) +# define LZF_USE_OFFSETS defined(_M_X64) +# else +# if __cplusplus > 199711L +# include +# else +# include +# endif +# define LZF_USE_OFFSETS (UINTPTR_MAX > 0xffffffffU) +# endif +#endif + +typedef unsigned char u8; + +#if LZF_USE_OFFSETS +# define LZF_HSLOT_BIAS ((const u8 *)in_data) + typedef unsigned int LZF_HSLOT; +#else +# define LZF_HSLOT_BIAS 0 + typedef const u8 *LZF_HSLOT; +#endif + +typedef LZF_HSLOT LZF_STATE[1 << (HLOG)]; + +#if !STRICT_ALIGN +/* for unaligned accesses we need a 16 bit datatype. */ +# if USHRT_MAX == 65535 + typedef unsigned short u16; +# elif UINT_MAX == 65535 + typedef unsigned int u16; +# else +# undef STRICT_ALIGN +# define STRICT_ALIGN 1 +# endif +#endif + +#if ULTRA_FAST +# undef VERY_FAST +#endif + +#endif + diff --git a/TESTS/mbed_hal/trng/lzflib/lzf_c.c b/TESTS/mbed_hal/trng/lzflib/lzf_c.c new file mode 100644 index 0000000000..6831d43b16 --- /dev/null +++ b/TESTS/mbed_hal/trng/lzflib/lzf_c.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2000-2010 Marc Alexander Lehmann + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#include "lzfP.h" + +#define HSIZE (1 << (HLOG)) + +/* + * don't play with this unless you benchmark! + * the data format is not dependent on the hash function. + * the hash function might seem strange, just believe me, + * it works ;) + */ +#ifndef FRST +# define FRST(p) (((p[0]) << 8) | p[1]) +# define NEXT(v,p) (((v) << 8) | p[2]) +# if ULTRA_FAST +# define IDX(h) ((( h >> (3*8 - HLOG)) - h ) & (HSIZE - 1)) +# elif VERY_FAST +# define IDX(h) ((( h >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) +# else +# define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) +# endif +#endif +/* + * IDX works because it is very similar to a multiplicative hash, e.g. + * ((h * 57321 >> (3*8 - HLOG)) & (HSIZE - 1)) + * the latter is also quite fast on newer CPUs, and compresses similarly. + * + * the next one is also quite good, albeit slow ;) + * (int)(cos(h & 0xffffff) * 1e6) + */ + +#if 0 +/* original lzv-like hash function, much worse and thus slower */ +# define FRST(p) (p[0] << 5) ^ p[1] +# define NEXT(v,p) ((v) << 5) ^ p[2] +# define IDX(h) ((h) & (HSIZE - 1)) +#endif + +#define MAX_LIT (1 << 5) +#define MAX_OFF (1 << 13) +#define MAX_REF ((1 << 8) + (1 << 3)) + +#if __GNUC__ >= 3 +# define expect(expr,value) __builtin_expect ((expr),(value)) +# define inline inline +#else +# define expect(expr,value) (expr) +# define inline static +#endif + +#define expect_false(expr) expect ((expr) != 0, 0) +#define expect_true(expr) expect ((expr) != 0, 1) + +/* + * compressed format + * + * 000LLLLL ; literal, L+1=1..33 octets + * LLLooooo oooooooo ; backref L+1=1..7 octets, o+1=1..4096 offset + * 111ooooo LLLLLLLL oooooooo ; backref L+8 octets, o+1=1..4096 offset + * + */ + +unsigned int +lzf_compress (const void *const in_data, unsigned int in_len, + void *out_data, unsigned int out_len +#if LZF_STATE_ARG + , LZF_STATE htab +#endif + ) +{ +#if !LZF_STATE_ARG + LZF_STATE htab; +#endif + const u8 *ip = (const u8 *)in_data; + u8 *op = (u8 *)out_data; + const u8 *in_end = ip + in_len; + u8 *out_end = op + out_len; + const u8 *ref; + + /* off requires a type wide enough to hold a general pointer difference. + * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only + * works for differences within a single object). We also assume that no + * no bit pattern traps. Since the only platform that is both non-POSIX + * and fails to support both assumptions is windows 64 bit, we make a + * special workaround for it. + */ +#if defined (WIN32) && defined (_M_X64) + unsigned _int64 off; /* workaround for missing POSIX compliance */ +#else + unsigned long off; +#endif + unsigned int hval; + int lit; + + if (!in_len || !out_len) + return 0; + +#if INIT_HTAB + memset (htab, 0, sizeof (htab)); +#endif + + lit = 0; op++; /* start run */ + + hval = FRST (ip); + while (ip < in_end - 2) + { + LZF_HSLOT *hslot; + + hval = NEXT (hval, ip); + hslot = htab + IDX (hval); + ref = *hslot + LZF_HSLOT_BIAS; *hslot = ip - LZF_HSLOT_BIAS; + + if (1 +#if INIT_HTAB + && ref < ip /* the next test will actually take care of this, but this is faster */ +#endif + && (off = (unsigned long)(uintptr_t)(ip - ref - 1)) < MAX_OFF + && ref > (u8 *)in_data + && ref[2] == ip[2] +#if STRICT_ALIGN + && ((ref[1] << 8) | ref[0]) == ((ip[1] << 8) | ip[0]) +#else + && *(u16 *)ref == *(u16 *)ip +#endif + ) + { + /* match found at *ref++ */ + unsigned int len = 2; + unsigned int maxlen = (unsigned int)((uintptr_t)in_end - (uintptr_t)ip) - len; + maxlen = maxlen > MAX_REF ? MAX_REF : maxlen; + + if (expect_false (op + 3 + 1 >= out_end)) /* first a faster conservative test */ + if (op - !lit + 3 + 1 >= out_end) /* second the exact but rare test */ + return 0; + + op [- lit - 1] = lit - 1; /* stop run */ + op -= !lit; /* undo run if length is zero */ + + for (;;) + { + if (expect_true (maxlen > 16)) + { + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + len++; if (ref [len] != ip [len]) break; + } + + do + len++; + while (len < maxlen && ref[len] == ip[len]); + + break; + } + + len -= 2; /* len is now #octets - 1 */ + ip++; + + if (len < 7) + { + *op++ = (u8)((off >> 8) + (len << 5)); + } + else + { + *op++ = (u8)((off >> 8) + ( 7 << 5)); + *op++ = len - 7; + } + + *op++ = (u8)off; + + lit = 0; op++; /* start run */ + + ip += len + 1; + + if (expect_false (ip >= in_end - 2)) + break; + +#if ULTRA_FAST || VERY_FAST + --ip; +# if VERY_FAST && !ULTRA_FAST + --ip; +# endif + hval = FRST (ip); + + hval = NEXT (hval, ip); + htab[IDX (hval)] = ip - LZF_HSLOT_BIAS; + ip++; + +# if VERY_FAST && !ULTRA_FAST + hval = NEXT (hval, ip); + htab[IDX (hval)] = ip - LZF_HSLOT_BIAS; + ip++; +# endif +#else + ip -= len + 1; + + do + { + hval = NEXT (hval, ip); + htab[IDX (hval)] = ip - LZF_HSLOT_BIAS; + ip++; + } + while (len--); +#endif + } + else + { + /* one more literal byte we must copy */ + if (expect_false (op >= out_end)) + return 0; + + lit++; *op++ = *ip++; + + if (expect_false (lit == MAX_LIT)) + { + op [- lit - 1] = lit - 1; /* stop run */ + lit = 0; op++; /* start run */ + } + } + } + + if (op + 3 > out_end) /* at most 3 bytes can be missing here */ + return 0; + + while (ip < in_end) + { + lit++; *op++ = *ip++; + + if (expect_false (lit == MAX_LIT)) + { + op [- lit - 1] = lit - 1; /* stop run */ + lit = 0; op++; /* start run */ + } + } + + op [- lit - 1] = lit - 1; /* end run */ + op -= !lit; /* undo run if length is zero */ + + return (unsigned int)((uintptr_t)op - (uintptr_t)out_data); +} + diff --git a/TESTS/mbed_hal/trng/main.cpp b/TESTS/mbed_hal/trng/main.cpp new file mode 100644 index 0000000000..c63a244b80 --- /dev/null +++ b/TESTS/mbed_hal/trng/main.cpp @@ -0,0 +1,276 @@ +/* +* 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 + +/*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; +}