-
Notifications
You must be signed in to change notification settings - Fork 3k
Add TRNG for NRF52832 #6116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add TRNG for NRF52832 #6116
Conversation
ARM Internal Ref: IOTSSL-2100 |
Can you please resolve the conflicts? |
@marcuschangarm This PR may require rebase. |
@marcuschangarm I can confirm that this works on my NRF52_DK after rebasing it with current master (to get the SWO support) and resolving the conflict on targets.json. |
*@} | ||
**/ | ||
|
||
#ifndef SUPPRESS_INLINE_IMPLEMENTATION |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason for this macro? Where are the functions defined if it's set?
{ | ||
*((volatile uint32_t *)((uint8_t *)NRF_RNG + rng_event)) = NRF_RNG_EVENT_CLEAR; | ||
#if __CORTEX_M == 0x04 | ||
volatile uint32_t dummy = *((volatile uint32_t *)((uint8_t *)NRF_RNG + rng_event)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the significance of this dummy read?
void trng_free(trng_t *obj) | ||
{ | ||
(void) obj; | ||
|
||
nrf_drv_rng_uninit(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why has this been removed even in case SD is enabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no good way (at least in this version of the SDK) to keep track of SoftDevice state. Imagine a case where trng_init
is called before the SoftDevice is enabled and trng_free
is called after, then we would have an inconsistent pairing of init/uninit calls.
In this version the TRNG is kept running after the first call. The power consumption for that is orders of magnitude lower than the SoftDevice in sleep mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcuschangarm Ok, but with the present code, issuing multiple trng_init / trng_free
calls leads to failure, which is not consistent with the TRNG API. What about calling nrf_drg_rng_uninit
if only if trng_init == false
-- i.e. if the TRNG has been initialized when SoftDevice was enabled -- and resetting it to trng_init = true
? This way, multiple trng_init / trng_free
calls would be fine, as long as it doesn't happen that the SoftDevice gets disabled in the middle of a trng_init / trng_free
pair.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issuing multiple trng_init / trng_free calls leads to failure
I don't understand, can you please elaborate? trng_init
doesn't do anything after the first successful call and trng_free
is empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcuschangarm There was a misunderstanding on my side, I agree that multiple trng_init
calls with SoftDevice enabled are fine, with all but the first one being no-ops.
Regarding the case you described -- SoftDevice being enabled after trng_init
was called: Can we use trng_init == false
(and perhaps give it a more descriptive name, pointing out that it's about TRNG initialization in the presence of SoftDevice) instead of NRF_HAL_SD_IS_ENABLED()
in trng_get_bytes
to avoid using SoftDevice functionality without being sure that it is properly initialized?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use trng_init == false (and perhaps give it a more descriptive name, pointing out that it's about TRNG initialization in the presence of SoftDevice) instead of NRF_HAL_SD_IS_ENABLED() in trng_get_bytes to avoid using SoftDevice functionality without being sure that it is properly initialized?
The SoftDevice does its own TRNG initialization. So we might end up in a situation where we use the low-level TRNG API and steal random numbers from the SoftDevice.
How about moving nrf_drv_rng_init
to trng_get_bytes
instead?
Edit: I went ahead and moved the initialization call inside get bytes. This is symmetric to the non-driver, raw register get-bytes code path.
int trng_get_bytes(trng_t *obj, uint8_t *output, size_t length, size_t *output_length)
{
(void) obj;
/* Use SDK RNG driver if SoftDevice is enabled. */
if (NRF_HAL_SD_IS_ENABLED() ) {
static bool nordic_driver_init = true;
if (nordic_driver_init) {
nordic_driver_init = false;
nrf_drv_rng_init(NULL);
}
#include "nrf_sdm.h" | ||
#include "nrf_soc.h" | ||
#else | ||
#include "app_fifo.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where can I find this file? It doesn't seem to be contained in the code base.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcuschangarm Is it intended that this file is missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see what you are saying. SOFTDEVICE_PRESENT
must always be defined in the current setup. Removing it will break the code in many places. The ability to remove the SoftDevice will be introduced in the SDK upgrade we are working on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcuschangarm Ok, thanks for clarifying this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcuschangarm I would prefer not to have large parts of this PR depending on a configuration which is known to be broken, though. Wouldn't it make more sense to error out in a single place if SOFTDEVICE_PRESENT
is not defined, and adding support for ! SOFTDEVICE_PRESENT
in a single go in your SDK upgrade?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, all files in the Nordic SDK have this built in bug! 😒
We are completely re-implementing most of the mbed HAL for the new SDK and reorganizing the directory structure so that we can both change SoftDevices on the fly and even compile without them. This PR is just to get the Cloud client going until we have the new SDK in place.
uint8_t softdevice_is_enabled; | ||
result = sd_softdevice_is_enabled(&softdevice_is_enabled); | ||
|
||
if (softdevice_is_enabled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to be safer to only take this branch if sd_softdevice_is_enabled
has been successful and softdevice_is_enabled
is set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note this is 3rd party SDK thus should be sent upstream if needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requested clarification on some parts of the code.
@hanno-arm Thank you for the review! Everything in the Also, this is a temporary implementation until the new SDK upgrade arrives. |
@yogpan01 @0xc0170 @TeroJaasko Rebased! |
Use SoftDevice API to get random numbers when present and active, otherwise read random numbers directly from TRNG peripheral.
/morph build |
Build : SUCCESSBuild number : 1212 Triggering tests/morph test |
@marcuschangarm the latest version still works with Cloud client. |
Exporter Build : SUCCESSBuild number : 883 |
The review should be only for file ( |
/** Deinitialize the TRNG peripheral | ||
* | ||
* @param obj The TRNG object | ||
*/ | ||
void trng_free(trng_t *obj) | ||
{ | ||
(void) obj; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcuschangarm Apologies if this is repeating something we already touched yesterday, but the following is still not clear to me: Why aren't we initializing the TRNG based on the SoftDevice state at the time of the trng_init
call, saving this initialization state somewhere within the trng_t
struct or in a global variable, and handling trng_get_bytes
and trng_init
based on it (in particular, not checking whether SoftDevice has been enabled in the meantime). This way, we could correctly setup the SoftDevice / low-level TRNG drivers in trng_init
via nrf_drv_rng_init(NULL)
resp. nrf_rng_task_trigger(NRF_RNG_TASK_START);
, and free them via nrf_drv_rng_uninit
resp. nrf_rng_task_trigger(NRF_RNG_TASK_STOP);
in trng_free
. Is this unacceptable because we cannot guarantee that SoftDevice is enabled before trng_init
is called, and we definitely want to use SoftDevice is possible?
The SoftDevice does its own TRNG initialization. So we might end up in a situation where we use the low-level TRNG API and steal random numbers from the SoftDevice.
I didn't yet understand how that could happen in the above approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The user application can enable and disable the SoftDevice at any time. Usually this would be done to save power. On the other hand, the TRNG might be called from different parts of the application at times that are not in lock step with the SoftDevice. So the code must be able to handle init and free calls that are not in sync with the SoftDevice state.
The SoftDevice does its own TRNG initialization. So we might end up in a situation where we use the low-level TRNG API and steal random numbers from the SoftDevice.
I believe the SoftDevice is copying the TRNG byte to a buffer on each interrupt. If we read that byte using the low level API before the SoftDevice gets to read it, weird things might happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the SoftDevice is copying the TRNG byte to a buffer on each interrupt. If we read that byte using the low level API before the SoftDevice gets to read it, weird things might happen.
Ah, ok, so this might happen even if the SoftDevice's TRNG driver API is not used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so this might happen even if the SoftDevice's TRNG driver API is not used?
Sorry, I don't understand the question.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood you as saying that when SoftDevice is active, the TRNG byte is automatically read by the SoftDevice driver code when ready, even if there is no code explicitly requesting random data through nrf_drv_rng_rand
- is that right? So if trng_init
would be called before SoftDevice is initialized, then my suggestion for trng_get_bytes
would lead to random data being read from the low-level driver that has potentially already been automatically processed by the SoftDevice driver code, even though trng_api.c
didn't request that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when SoftDevice is active, the TRNG byte is automatically read by the SoftDevice driver code when ready, even if there is no code explicitly requesting random data through nrf_drv_rng_rand - is that right?
Yes.
So if trng_init would be called before SoftDevice is initialized, then my suggestion for trng_get_bytes would lead to random data being read from the low-level driver that has potentially already been automatically processed by the SoftDevice driver code, even though trng_api.c didn't request that.
Yes, that is my concern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcuschangarm Thanks for confirming, then I think your solution is the best one. 👍
Test : FAILUREBuild number : 1013 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like that this PR introduces code under ! SOFTDEVICE_PRESENT
which is known to be broken.
Leaving that side, I am ok with how trng_api.c
multiplexes between the NRF driver implementation in case SoftDevice is present + active, and a hand-crafted direct driver implementation.
As mentioned in the comments, though, I would prefer to do initialization and cleanup of the native driver resp. the SoftDevice driver in trng_init
and trng_free
, respectively, which is what these functions are intended for.
Given the importance of the PR and the fact that it looks fine from the perspective of functionality and security, the above concerns are no blockers, and I'm ok approving after @yanesca has given his opinion.
@hanno-arm I completely understand where you are coming from! It's an imperfect solution to a bad situation. I would also be uncomfortable adding files that I can't test because the SOFTDEVICE_PRESENT flag isn't working. For reference, this is how the new implementation in SDK 14.2 looks like: https://github.com/ARMmbed/mbed-os/blob/feature-nrf528xx/targets/TARGET_NORDIC/TARGET_NRF5x/trng_api.c#L49-L79 |
/morph test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the same reservations as @hanno-arm, and although I am approving it on account of the deadline, I would like to emphasise that this approval is for the part of the code that does not use the SDK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving, reinforcing @yanesca's remarks.
/morph build |
Build : SUCCESSBuild number : 1223 Triggering tests/morph test |
Exporter Build : SUCCESSBuild number : 892 |
Test : SUCCESSBuild number : 1024 |
Waiting for the second Test: SUCCESS to come in. Was pointed out that I jumped the gun in issuing the |
we had aborted first |
Use SoftDevice API to get random numbers when present and active,
otherwise read random numbers directly from TRNG peripheral.