diff --git a/src/machine/machine_nrf.go b/src/machine/machine_nrf.go index 9457007115..4da6645352 100644 --- a/src/machine/machine_nrf.go +++ b/src/machine/machine_nrf.go @@ -318,9 +318,9 @@ func (i2c *I2C) signalStop() error { var rngStarted = false -// GetRNG returns 32 bits of non-deterministic random data based on internal thermal noise. +// getRNG returns 32 bits of non-deterministic random data based on internal thermal noise. // According to Nordic's documentation, the random output is suitable for cryptographic purposes. -func GetRNG() (ret uint32, err error) { +func getRNG() (ret uint32, err error) { // There's no apparent way to check the status of the RNG peripheral's task, so simply start it // to avoid deadlocking while waiting for output. if !rngStarted { diff --git a/src/machine/machine_nrf_bare.go b/src/machine/machine_nrf_bare.go new file mode 100644 index 0000000000..b94886ed91 --- /dev/null +++ b/src/machine/machine_nrf_bare.go @@ -0,0 +1,9 @@ +//go:build nrf && !softdevice + +package machine + +// GetRNG returns 32 bits of non-deterministic random data based on internal thermal noise. +// According to Nordic's documentation, the random output is suitable for cryptographic purposes. +func GetRNG() (ret uint32, err error) { + return getRNG() +} diff --git a/src/machine/machine_nrf_sd.go b/src/machine/machine_nrf_sd.go new file mode 100644 index 0000000000..b816e62ee0 --- /dev/null +++ b/src/machine/machine_nrf_sd.go @@ -0,0 +1,59 @@ +//go:build nrf && softdevice + +package machine + +import ( + "device/arm" + "device/nrf" + + "errors" +) + +// avoid a heap allocation in GetRNG. +var ( + softdeviceEnabled uint8 + bytesAvailable uint8 + buf [4]uint8 + + errNoSoftDeviceSupport = errors.New("rng: softdevice not supported on this device") + errNotEnoughRandomData = errors.New("rng: not enough random data available") +) + +// GetRNG returns 32 bits of non-deterministic random data based on internal thermal noise. +// According to Nordic's documentation, the random output is suitable for cryptographic purposes. +func GetRNG() (ret uint32, err error) { + // First check whether the SoftDevice is enabled. + // sd_rand_application_bytes_available_get cannot be called when the SoftDevice is not enabled. + arm.SVCall1(0x12, &softdeviceEnabled) // sd_softdevice_is_enabled + + if softdeviceEnabled == 0 { + return getRNG() + } + + // call into the SoftDevice to get random data bytes available + switch nrf.Device { + case "nrf51": + // sd_rand_application_bytes_available_get: SOC_SVC_BASE_NOT_AVAILABLE + 4 + arm.SVCall1(0x2B+4, &bytesAvailable) + case "nrf52", "nrf52840", "nrf52833": + // sd_rand_application_bytes_available_get: SOC_SVC_BASE_NOT_AVAILABLE + 4 + arm.SVCall1(0x2C+4, &bytesAvailable) + default: + return 0, errNoSoftDeviceSupport + } + + if bytesAvailable < 4 { + return 0, errNotEnoughRandomData + } + + switch nrf.Device { + case "nrf51": + // sd_rand_application_vector_get: SOC_SVC_BASE_NOT_AVAILABLE + 5 + arm.SVCall2(0x2B+5, &buf, 4) + case "nrf52", "nrf52840", "nrf52833": + // sd_rand_application_vector_get: SOC_SVC_BASE_NOT_AVAILABLE + 5 + arm.SVCall2(0x2C+5, &buf, 4) + } + + return uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24, nil +}