Problem: nrf_crypto_sign() fails with NRF_ERROR_INTERNAL. Observed in SDK 12.3.0 on NRF51422. This probably applies to SDK 14.0 when using micro-ecc backend, as the root cause of the bug is still there (see below).
Cause: nrf_crypto_sign() uses ecc_p256_sign() from ecc.c, which in turn calls uECC_sign() from micro-ecc library. However, uECC_sign() requires a working random number generator. NRF SDK does NOT supply the RNG to micro-ecc during initialization. uECC_sign() is thus unable to generate signature, and throws an error which is propagated up the call chain.
Solution: Tell micro-ecc to use a RNG. In nrf_crypto_init() (nrf_crypto.c), change
ecc_init(false);
to
ecc_init(true);
This should remove the cause of error.
Alternative solution: In ecc_p256_sign() in ecc.c replace call to uECC_sign() with a call to uECC_sign_deterministic(), which does not use RNG. I did not test this.
Further observations: In ecc.c, a function for supplying RNG output to micro-ecc is defined as follows:
static int ecc_rng(uint8_t *dest, unsigned size)
{
nrf_drv_rng_block_rand(dest, (uint32_t) size);
return 1;
}
This implementation is very naive. Consider the following:
nrf_crypto_init();
uint8_t available;
APP_ERROR_CHECK(nrf_drv_rng_bytes_available(&available));
_PRINTF("%d random bytes available\n", available);
uint8_t rnd[32];
for (int i=0; i<4; i++) {
nrf_drv_rng_rand(rnd, 32);
_print_hex("RAW ", rnd, 32);
}
Result:
0 random bytes available
RAW (32 bytes): 12000000120000001BC701000C5000200C29002012000000120000001C500020
RAW (32 bytes): 12000000120000001BC701000C5000200C29002012000000120000001C500020
RAW (32 bytes): 12000000120000001BC701000C5000200C29002012000000120000001C500020
RAW (32 bytes): 12000000120000001BC701000C5000200C29002012000000120000001C500020
Ouch.
Also, the code in ecc.c fails to call nrf_drv_rng_init(), but that works because the softdevice does the initialization anyway.
My solution is to simply block until more random data becomes available:
static bool rng_initialized = false;
void crypto_get_random_block(uint8_t *buf, const uint8_t len)
{
if ( ! rng_initialized ) {
APP_ERROR_CHECK(nrf_drv_rng_init(NULL));
rng_initialized = true;
}
uint8_t available = 0;
uint8_t offset = 0;
int needed = len;
_DEBUG("RNG request for %d bytes\n", len);
while (1) {
APP_ERROR_CHECK(nrf_drv_rng_bytes_available(&available));
if ( available > 0 ) {
uint8_t used = ( needed >= available ) ? available : needed;
APP_ERROR_CHECK(nrf_drv_rng_block_rand(buf + offset, used));
//_DEBUG("len=%d offset=%d used=%d available=%d needed=%d\n", len, offset, used, available, needed);
offset = offset + used;
needed = needed - used;
if ( needed == 0 )
break;
if ( needed < 0 )
_ERROR("should not happen!\n");
} else {
osDelay(1); // wait (***)
}
}
}
The line marked (***) is for Keil RTX and should be replaced by the wait call of your choice.
Test code:
for (int i=0; i<4; i++) {
crypto_get_random_block(rnd, 32);
_print_hex("PROC", rnd, 32);
}
Result:
@5015ms crypto.c:34 DEBUG RNG request for 32 bytes
PROC (32 bytes): FAB97C56056922A5194FFA17A74F5FFE40182C05C50F34FD267485F052C04E33
@5095ms crypto.c:34 DEBUG RNG request for 32 bytes
PROC (32 bytes): E3918A1C576DCC353218090611ED0153437001E49DE9D8059A0D13300BA60327
@5127ms crypto.c:34 DEBUG RNG request for 32 bytes
PROC (32 bytes): F830C7840F9EC967492C318625682C3F9C02E332037AD43EEF4EF772ADF0D6FC
@5160ms crypto.c:34 DEBUG RNG request for 32 bytes
PROC (32 bytes): 389BEF03336EAC71715C4863A9E6D94C1A4FE92F5FF2E85187F088C593131BC8
Much better, albeit also much slower.