Mayank Gupta / Mbed OS pelion-example-frdm

Dependencies:   FXAS21002 FXOS8700Q

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pal_plat_drbg_noise.c Source File

pal_plat_drbg_noise.c

00001 /*******************************************************************************
00002 * Copyright 2016-2019 ARM Ltd.
00003 *
00004 * Licensed under the Apache License, Version 2.0 (the "License");
00005 * you may not use this file except in compliance with the License.
00006 * You may obtain a copy of the License at
00007 *
00008 *     http://www.apache.org/licenses/LICENSE-2.0
00009 *
00010 * Unless required by applicable law or agreed to in writing, software
00011 * distributed under the License is distributed on an "AS IS" BASIS,
00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013 * See the License for the specific language governing permissions and
00014 * limitations under the License.
00015 *******************************************************************************/
00016 
00017 #include "pal.h"
00018 #include "pal_plat_drbg.h"
00019 
00020 /**
00021  * Although this file is really only relevant when a TRNG is available, it is not all wrapped in #if PAL_USE_HW_TRNG.
00022  * This is because pal_plat_drbg_sotp.c uses these functions in both cases. If there is no TRNG, no noise will actually
00023  * be read but the functions will still work (without noise).
00024  * If pal_plat_drbg_w_entropy_sources.c is used and not pal_plat_drbg_sotp.c, and PAL_USE_HW_TRNG is 0, then this file
00025  * is completely irrelevant and no part of this code should be linked into the image.
00026  */
00027 
00028 
00029 #define TRACE_GROUP "PAL"
00030 
00031 #define PAL_NOISE_WAIT_FOR_WRITERS_DELAY_MILLI_SEC 1
00032 #define PAL_NOISE_BITS_TO_BYTES(x) (x / CHAR_BIT)
00033 
00034 typedef struct palNoise
00035 {
00036     int32_t buffer[PAL_NOISE_BUFFER_LEN];
00037     volatile uint32_t bitCountAllocated;
00038     volatile uint32_t bitCountActual;
00039     volatile uint32_t numWriters;
00040     volatile bool isReading;
00041 } palNoise_t;
00042 
00043 PAL_PRIVATE palNoise_t g_noise;
00044 PAL_PRIVATE bool g_palNoiseInitialized = false;
00045 
00046 // XXX: these are not part of ANY public API, yet the test code accesses them.
00047 palStatus_t pal_plat_noiseWriteBuffer(int32_t* buffer, uint16_t lenBits, uint16_t* bitsWritten); // forward declaration
00048 palStatus_t pal_plat_noiseRead(int32_t buffer[PAL_NOISE_BUFFER_LEN], bool partial, uint16_t* bitsRead); // forward declaration
00049 
00050 PAL_PRIVATE palThreadID_t g_trngThreadID = NULLPTR;
00051 
00052 
00053 extern palStatus_t pal_plat_CtrDRBGGenerateWithAdditional(palCtrDrbgCtxHandle_t ctx, unsigned char* out, size_t len, unsigned char* additional, size_t additionalLen);
00054 
00055 palStatus_t pal_plat_noiseInit()
00056 {
00057     palStatus_t status = PAL_SUCCESS;
00058     if (!g_palNoiseInitialized)
00059     {
00060         memset(g_noise.buffer, 0, PAL_NOISE_SIZE_BYTES);
00061         g_noise.bitCountActual = g_noise.bitCountAllocated = 0;
00062         g_noise.numWriters = 0;
00063         g_noise.isReading = false;
00064         g_trngThreadID = NULLPTR;
00065         g_palNoiseInitialized = true;
00066 
00067         return status;
00068     } else {
00069         return PAL_SUCCESS;
00070     }
00071 }
00072 
00073 palStatus_t pal_plat_noiseDestroy(void)
00074 {
00075     palStatus_t status = PAL_SUCCESS;
00076     if (!g_palNoiseInitialized)
00077     {
00078         return PAL_ERR_NOT_INITIALIZED ;
00079     }
00080 
00081     if (NULLPTR != g_trngThreadID)
00082     {
00083         status = pal_osThreadTerminate(&g_trngThreadID);
00084         if (PAL_SUCCESS != status)
00085         {
00086             PAL_LOG_ERR("pal_plat_noiseDestroy: failed to terminate trng noise thread ");
00087         }
00088         else
00089         {
00090             g_trngThreadID = NULLPTR;
00091         }
00092     }
00093 
00094     g_palNoiseInitialized = false;
00095     return status;
00096 }
00097 
00098 #if PAL_USE_HW_TRNG
00099 PAL_PRIVATE void pal_trngNoiseThreadFunc(void const* arg)
00100 {
00101     uint8_t buf[PAL_NOISE_SIZE_BYTES] PAL_PTR_ADDR_ALIGN_UINT8_TO_UINT32 = { 0 };
00102     size_t trngBytesRead = 0;
00103     uint16_t noiseBitsWritten = 0;
00104     palStatus_t status;
00105     while (true)
00106     {
00107         status = pal_plat_osRandomBuffer(buf, PAL_NOISE_SIZE_BYTES, &trngBytesRead);
00108         if ((0 < trngBytesRead) && ((PAL_SUCCESS == status) || (PAL_ERR_RTOS_TRNG_PARTIAL_DATA  == status)))
00109         {
00110             noiseBitsWritten = 0;
00111             (void)pal_plat_noiseWriteBuffer((int32_t*)buf, (trngBytesRead * CHAR_BIT), &noiseBitsWritten);            
00112             /*
00113              * Discard the return status. The buffer will fill up pretty quickly and we will start
00114              * getting PAL_ERR_RTOS_NOISE_BUFFER_FULL error every time. No added value in logging
00115              * this as it is not a true error. Beside we would like to keep the stack of the noise
00116              * thread as small as possible and our logs use a lot of stack.
00117              */  
00118         }
00119         pal_osDelay(PAL_NOISE_TRNG_THREAD_DELAY_MILLI_SEC);
00120     }
00121 }
00122 
00123 palStatus_t pal_plat_noiseCreateThread(void)
00124 {
00125     if (!g_trngThreadID)
00126     {
00127         return pal_osThreadCreateWithAlloc(pal_trngNoiseThreadFunc, NULL, PAL_osPriorityReservedTRNG, PAL_NOISE_TRNG_THREAD_STACK_SIZE, NULL, &g_trngThreadID);
00128     } 
00129     else
00130     {
00131         return PAL_SUCCESS;
00132     }
00133 }
00134 
00135 #endif
00136 
00137 // this function generates drbg with the possibility of adding noise as additional input to the drbg function.
00138 palStatus_t pal_plat_generateDrbgWithNoiseAttempt(palCtrDrbgCtxHandle_t drbgContext, uint8_t* outBuffer, bool partial, size_t numBytesToGenerate)
00139 {
00140     uint16_t bitsRead = 0;
00141     int32_t buffer[PAL_NOISE_BUFFER_LEN] = { 0 };
00142     palStatus_t status = pal_plat_noiseRead(buffer, partial, &bitsRead);
00143     if (PAL_SUCCESS == status)
00144     {
00145         status = pal_plat_CtrDRBGGenerateWithAdditional(drbgContext, (unsigned char*)outBuffer, numBytesToGenerate, (unsigned char*)buffer, (size_t)PAL_NOISE_BITS_TO_BYTES(bitsRead));
00146     }
00147     else
00148     {
00149         status = pal_CtrDRBGGenerate(drbgContext, (unsigned char*)outBuffer, numBytesToGenerate);
00150     }
00151     return status;
00152 }
00153 
00154 /*! Write a value (either all or specific bits) to the global noise buffer
00155 *
00156 * @param[in] data The value containing the bits to be written.
00157 * @param[in] startBit The index of the first bit to be written, valid values are 0-31.
00158 * @param[in] lenBits The number of bits that should be written (startBit+lenBits must be less than 32).
00159 * @param[out] bitsWritten The number of bits that were actually written.
00160 *
00161 * \return PAL_SUCCESS(0) in case of success and a negative value indicating a specific error code in case of failure.
00162 */
00163 palStatus_t pal_plat_noiseWriteValue(const int32_t* data, uint8_t startBit, uint8_t lenBits, uint8_t* bitsWritten)
00164 {
00165     PAL_VALIDATE_ARGUMENTS((NULL == data) || (PAL_INT32_BITS - 1 < startBit) || (PAL_INT32_BITS < lenBits + startBit) || (NULL == bitsWritten));
00166 
00167     palStatus_t status = PAL_SUCCESS;
00168     uint16_t incrementedBitCount;
00169     uint8_t currentIndex, occupiedBitsInCurrentIndex, availableBitsInCurrentIndex;
00170     uint32_t mask, value;
00171 
00172     *bitsWritten = 0;
00173     if (PAL_NOISE_SIZE_BITS == g_noise.bitCountActual)
00174     {
00175         return PAL_ERR_RTOS_NOISE_BUFFER_FULL ;
00176     }
00177 
00178     pal_osAtomicIncrement((int32_t*)(&g_noise.numWriters), 1); // increment number of writers
00179     if (g_noise.isReading) // if we're in read mode then discard & exit
00180     {
00181         status = PAL_ERR_RTOS_NOISE_BUFFER_IS_READING ;
00182         goto finish;
00183     }
00184 
00185     incrementedBitCount = (uint16_t)pal_osAtomicIncrement((int32_t*)(&g_noise.bitCountAllocated), lenBits); // reserve space in the array
00186     if (PAL_NOISE_SIZE_BITS < incrementedBitCount) // we want to write more bits than are available in the (entire) buffer
00187     {
00188         lenBits -= incrementedBitCount - PAL_NOISE_SIZE_BITS; // max number of bits that are avialable for writing
00189         if ((int8_t)lenBits <= 0) // we don't have any available bits for writing
00190         {
00191             status = PAL_ERR_RTOS_NOISE_BUFFER_FULL ;
00192             goto finish;
00193         }
00194         incrementedBitCount = PAL_NOISE_SIZE_BITS;
00195     }
00196 
00197     currentIndex = (incrementedBitCount - lenBits) / PAL_INT32_BITS; // the current index in the array
00198     occupiedBitsInCurrentIndex = (incrementedBitCount - lenBits) % PAL_INT32_BITS; // how many bits are already occupied (with either 0 or 1) in the current index
00199     availableBitsInCurrentIndex = PAL_INT32_BITS - occupiedBitsInCurrentIndex; // how many bits are available in the current index
00200 
00201     if (lenBits > availableBitsInCurrentIndex) // we want to write more bits than are available in the current index so we need to split the bits
00202     {
00203         mask = ((((int32_t)1) << availableBitsInCurrentIndex) - 1) << startBit; // mask to isolate the wanted bits
00204         value = *data & mask;
00205         if (((int8_t)(startBit - occupiedBitsInCurrentIndex)) > 0)
00206         {
00207             value = value >> (startBit - occupiedBitsInCurrentIndex);
00208         }
00209         else if (((int8_t)(startBit - occupiedBitsInCurrentIndex)) < 0)
00210         {
00211             value = value << (occupiedBitsInCurrentIndex - startBit);
00212         }
00213         pal_osAtomicIncrement(&g_noise.buffer[currentIndex], value); // write the 1st part of the splitted bits to the current index of the noise buffer
00214         *bitsWritten = availableBitsInCurrentIndex;
00215         lenBits -= availableBitsInCurrentIndex; // how many bits remain to be written
00216         startBit += availableBitsInCurrentIndex;
00217         mask = ((((int32_t)1) << lenBits) - 1) << startBit; // mask for the remaining bits that have not been written yet
00218         value = *data & mask;
00219         value = value >> startBit; // since we're writting to the next index we start at bit 0
00220         pal_osAtomicIncrement(&g_noise.buffer[currentIndex + 1], value); // write the 2nd part of the splitted bits to the next index of the noise buffer
00221         *bitsWritten += lenBits;
00222     }
00223     else // we have enough available bits for the current index (no need to split the bits)
00224     {
00225         mask = ((((int64_t)1) << lenBits) - 1) << startBit; // int64_t in case we want all the 32 bits
00226         value = *data & mask;
00227         if (((int8_t)(startBit - occupiedBitsInCurrentIndex)) > 0)
00228         {
00229             value = value >> (startBit - occupiedBitsInCurrentIndex);
00230         }
00231         else if (((int8_t)(startBit - occupiedBitsInCurrentIndex)) < 0)
00232         {
00233             value = value << (occupiedBitsInCurrentIndex - startBit);
00234         }
00235         pal_osAtomicIncrement(&g_noise.buffer[currentIndex], value); // write the bits to the current index of the noise buffer
00236         *bitsWritten = lenBits;
00237     }
00238     pal_osAtomicIncrement((int32_t*)(&g_noise.bitCountActual) , *bitsWritten); // increment how many bits were actually written    
00239 finish:
00240     pal_osAtomicIncrement((int32_t*)(&g_noise.numWriters), -1); // decrement number of writers
00241     return status;
00242 }
00243 
00244 /*! Write values to the global noise buffer
00245 *
00246 * @param[in] buffer The buffer which contains the values to be written.
00247 * @param[in] lenBits The number of bits that should be written.
00248 * @param[out] bitsWritten The number of bits that were actually written.
00249 *
00250 * \return PAL_SUCCESS(0) in case of success and a negative value indicating a specific error code in case of failure.
00251 */
00252 palStatus_t pal_plat_noiseWriteBuffer(int32_t* buffer, uint16_t lenBits, uint16_t* bitsWritten)
00253 {
00254     PAL_VALIDATE_ARGUMENTS((NULL == buffer) || (PAL_NOISE_SIZE_BITS < lenBits) || (NULL == bitsWritten));
00255 
00256     palStatus_t status;
00257     uint8_t idx, bitsToWrite;
00258     uint16_t totalBitsWritten;
00259 
00260     idx = 0;
00261     totalBitsWritten = 0;
00262     do
00263     {
00264         bitsToWrite = (lenBits > PAL_INT32_BITS) ? PAL_INT32_BITS : lenBits; // we can write a max number of 32 bits at a time
00265         status = pal_plat_noiseWriteValue(&buffer[idx], 0, bitsToWrite, (uint8_t*)bitsWritten);
00266         lenBits -= bitsToWrite;
00267         idx++;
00268         totalBitsWritten += *bitsWritten;
00269     } while ((PAL_SUCCESS == status) && (bitsToWrite == *bitsWritten) && lenBits); // exit if there was an error, or the noise buffer has no more space, or all bits were written
00270 
00271     *bitsWritten = totalBitsWritten;
00272     if (0 < totalBitsWritten)
00273     {
00274         status = PAL_SUCCESS;
00275     }
00276     return status;
00277 }
00278 
00279 /*! Read values from the global noise buffer
00280 *
00281 * @param[out] buffer The output buffer which will contain the noise data collected.
00282 * @param[in] partial When true read what was collected so far, otherwise read only if the noise buffer is full.
00283 * @param[out] bitsRead he number of bits that were actually read.
00284 *
00285 * \return PAL_SUCCESS(0) in case of success and a negative value indicating a specific error code in case of failure.
00286 */
00287 palStatus_t pal_plat_noiseRead(int32_t buffer[PAL_NOISE_BUFFER_LEN], bool partial, uint16_t* bitsRead)
00288 {
00289     PAL_VALIDATE_ARGUMENTS((NULL == buffer) || (NULL == bitsRead));
00290 
00291     static uint32_t numOfNoiseReaders = 0; // allow only one reader at a time (no concurrent reads)
00292     palStatus_t status = PAL_SUCCESS;
00293     uint8_t numBytesToRead, numReadersLocal;
00294     uint16_t bitCountActual = (uint16_t)g_noise.bitCountActual;
00295     numReadersLocal = (uint8_t)pal_osAtomicIncrement((int32_t*)(&numOfNoiseReaders), 1); // increment number of readers
00296     *bitsRead = 0;
00297     if (1 != numReadersLocal) // single reader
00298     {
00299         PAL_LOG_DBG("noise cannot read by multiple readers\n");
00300         status = PAL_ERR_RTOS_NOISE_BUFFER_EMPTY ;
00301         goto finish;
00302     }
00303     
00304     if ((CHAR_BIT > bitCountActual) || (!partial && (PAL_NOISE_SIZE_BITS != bitCountActual))) // exit if less than 1 byte was written or if we want a full read and not all bits were written
00305     {
00306         status = (CHAR_BIT > bitCountActual) ? PAL_ERR_RTOS_NOISE_BUFFER_EMPTY  : PAL_ERR_RTOS_NOISE_BUFFER_NOT_FULL ;
00307         goto finish;
00308     }
00309 
00310     g_noise.isReading = true; // set mode to reading so that no more writes will be allowed
00311     while (g_noise.numWriters) // wait for currently executing writers to finish (relevant only for partial read)
00312     {
00313         pal_osDelay(PAL_NOISE_WAIT_FOR_WRITERS_DELAY_MILLI_SEC);
00314     }
00315     bitCountActual = (uint16_t)g_noise.bitCountActual; // this may occur if we waited for the writers to finish writing, meaning we might have a few more bits (relevant only for partial read)
00316     numBytesToRead = (uint8_t)PAL_NOISE_BITS_TO_BYTES(bitCountActual);    
00317     memcpy((void*)buffer, (void*)g_noise.buffer, numBytesToRead); // copy noise buffer to output buffer
00318     *bitsRead = (numBytesToRead * CHAR_BIT); // set out param of how many bits were actually read
00319     memset((void*)g_noise.buffer, 0, PAL_NOISE_SIZE_BYTES); // reset the noise buffer
00320     g_noise.bitCountActual = g_noise.bitCountAllocated = 0; // reset counters
00321     g_noise.isReading = false; // exit read mode so that writers will be able to continue writting
00322     PAL_LOG_DBG("noise read %" PRIu8 " bits\n", *bitsRead);
00323 finish:
00324     pal_osAtomicIncrement((int32_t*)(&numOfNoiseReaders), -1); // decrement number of readers
00325     return status;
00326 }
00327