blob: b3304eb7c39b60fa6643583228aab9dd0dec0bbf [file] [log] [blame]
/*
*
* Copyright (c) 2019 Google LLC.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* Provides implementations for the Weave entropy sourcing functions
* on the Nordic nRF52 platforms.
*/
//
// !!!TEMPORARY CODE!!!
//
// The following code is a temporary implementation of the OpenWeave Entropy APIs
// that has been specially designed to work around the lack of thread-safety in the
// Nordic port of OpenThread.
//
// In Nordic's OpenThread platform implementation, the code assumes it has exclusive
// access to the underlying entropy source. In a multi-threaded environment such as
// an OpenWeave application this precludes code running in other threads from directly
// sourcing entropy.
//
// To work around this, the code here acquires the OpenThread stack lock before interacting
// with the entropy source, effectively blocking all OpenThread activity until OpenWeave
// has acquired its entropy. Because the entropy is being fed into a DRBG, which then feeds
// the application, this should happen extremely rarely.
//
// Ultimately this code will be revised to use a new thread-safe entropy sourcing API
// provided by Nordic.
//
#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
#include <Weave/Support/crypto/WeaveRNG.h>
#if SOFTDEVICE_PRESENT
#include <nrf_soc.h>
#else
#include <openthread/platform/random.h>
#endif
using namespace ::nl;
using namespace ::nl::Weave;
#if !WEAVE_CONFIG_RNG_IMPLEMENTATION_NESTDRBG
#error "Nest DRBG implementation must be enabled on nRF5 platforms"
#endif // !WEAVE_CONFIG_RNG_IMPLEMENTATION_NESTDRBG
namespace nl {
namespace Weave {
namespace DeviceLayer {
namespace Internal {
/**
* Retrieve entropy from the underlying RNG source.
*
* This function is called by the Nest DRBG to acquire entropy.
*/
int GetEntropy_nRF5(uint8_t * buf, size_t count)
{
int res;
VerifyOrDie(count <= UINT16_MAX);
// If OpenThread is active, acquire the stack lock to prevent the
// OpenThread task from interacting with the entropy source.
#if WEAVE_DEVICE_CONFIG_ENABLE_THREAD
if (ThreadStackManagerImpl::IsInitialized())
{
ThreadStackMgr().LockThreadStack();
}
#endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD
#if SOFTDEVICE_PRESENT
// Loop until we get the requested number of random bytes...
while (count > 0)
{
// Determine how many byte of entropy are currently available in
// the SoftDevice.
uint8_t avail;
sd_rand_application_bytes_available_get(&avail);
// Only collect as many as we need.
if (avail > count)
{
avail = count;
}
// Attempt to read the random bytes from the SoftDevice. Note that
// it could be that something in the SoftDevice as consumed the available
// entropy in which case NRF_ERROR_SOC_RAND_NOT_ENOUGH_VALUES will be returned.
uint32_t err = sd_rand_application_vector_get(buf, avail);
if (err == NRF_SUCCESS)
{
buf += avail;
count -= avail;
}
else if (err != NRF_ERROR_SOC_RAND_NOT_ENOUGH_VALUES)
{
res = 0;
break;
}
}
#else // SOFTDEVICE_PRESENT
// In the absence of the SoftDevice, call the OpenThread platform API for
// retrieving entropy.
otError otErr = otPlatRandomGetTrue(buf, (uint16_t)count);
res = (otErr == OT_ERROR_NONE);
#endif // SOFTDEVICE_PRESENT
#if WEAVE_DEVICE_CONFIG_ENABLE_THREAD
if (ThreadStackManagerImpl::IsInitialized())
{
ThreadStackMgr().UnlockThreadStack();
}
#endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD
return res;
}
WEAVE_ERROR InitEntropy()
{
WEAVE_ERROR err;
// Initialize the Nest DRBG.
err = Platform::Security::InitSecureRandomDataSource(GetEntropy_nRF5, 64, NULL, 0);
SuccessOrExit(err);
// Seed the standard rand() pseudo-random generator with data from the secure random source.
{
unsigned int seed;
err = Platform::Security::GetSecureRandomData((uint8_t *)&seed, sizeof(seed));
SuccessOrExit(err);
srand(seed);
}
exit:
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(Crypto, "InitEntropy() failed: 0x%08" PRIX32, err);
}
return err;
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace Weave
} // namespace nl