Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: FXAS21002 FXOS8700Q
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
Generated on Tue Jul 12 2022 20:21:01 by
