#include <unistd.h>
-CryptoRandom::CryptoRandom() : fd(0) {}
-CryptoRandom::~CryptoRandom() = default;
+static bool getentropy_works()
+{
+ char buf;
+ auto ret = TEMP_FAILURE_RETRY(::getentropy(&buf, sizeof(buf)));
+ if (ret == 0) {
+ return true;
+ } else if (errno == ENOSYS || errno == EPERM) {
+ return false;
+ } else {
+ throw std::system_error(errno, std::system_category());
+ }
+}
+
+CryptoRandom::CryptoRandom() : fd(getentropy_works() ? -1 : open_urandom())
+{}
+
+CryptoRandom::~CryptoRandom()
+{
+ if (fd >= 0) {
+ VOID_TEMP_FAILURE_RETRY(::close(fd));
+ }
+}
void CryptoRandom::get_bytes(char *buf, int len)
{
- auto ret = TEMP_FAILURE_RETRY(::getentropy(buf, len));
+ ssize_t ret = 0;
+ if (unlikely(fd >= 0)) {
+ ret = safe_read_exact(fd, buf, len);
+ } else {
+ // getentropy() reads up to 256 bytes
+ assert(len <= 256);
+ ret = TEMP_FAILURE_RETRY(::getentropy(buf, len));
+ }
if (ret < 0) {
throw std::system_error(errno, std::system_category());
}
// open /dev/urandom once on construction and reuse the fd for all reads
CryptoRandom::CryptoRandom()
- : fd(TEMP_FAILURE_RETRY(::open("/dev/urandom", O_CLOEXEC|O_RDONLY)))
+ : fd{open_urandom()}
{
if (fd < 0) {
throw std::system_error(errno, std::system_category());
#endif
+int CryptoRandom::open_urandom()
+{
+ int fd = TEMP_FAILURE_RETRY(::open("/dev/urandom", O_CLOEXEC|O_RDONLY));
+ if (fd < 0) {
+ throw std::system_error(errno, std::system_category());
+ }
+ return fd;
+}
// ---------------------------------------------------
// fallback implementation of the bufferlist-free
// let's pad the data
std::uint8_t pad_len = out_tmp.length() - in.length();
ceph::bufferptr pad_buf{pad_len};
+ // FIPS zeroization audit 20191115: this memset is not intended to
+ // wipe out a secret after use.
memset(pad_buf.c_str(), pad_len, pad_len);
// form contiguous buffer for block cipher. The ctor copies shallowly.
std::array<unsigned char, AES_BLOCK_LEN> last_block;
memcpy(last_block.data(), in.buf + in.length - tail_len, tail_len);
+ // FIPS zeroization audit 20191115: this memset is not intended to
+ // wipe out a secret after use.
memset(last_block.data() + tail_len, pad_len, pad_len);
// need a local copy because AES_cbc_encrypt takes `iv` as non-const.