#include "common/debug.h"
#include <errno.h>
-int get_random_bytes(char *buf, int len)
+// use getentropy() if available. it uses the same source of randomness
+// as /dev/urandom without the filesystem overhead
+#ifdef HAVE_GETENTROPY
+
+#include <unistd.h>
+
+CryptoRandom::CryptoRandom() : fd(0) {}
+CryptoRandom::~CryptoRandom() = default;
+
+void CryptoRandom::get_bytes(char *buf, int len)
{
- int fd = TEMP_FAILURE_RETRY(::open("/dev/urandom", O_RDONLY));
- if (fd < 0)
- return -errno;
- int ret = safe_read_exact(fd, buf, len);
- VOID_TEMP_FAILURE_RETRY(::close(fd));
- return ret;
+ auto ret = TEMP_FAILURE_RETRY(::getentropy(buf, len));
+ if (ret < 0) {
+ throw std::system_error(errno, std::system_category());
+ }
}
-static int get_random_bytes(int len, bufferlist& bl)
+#else // !HAVE_GETENTROPY
+
+// open /dev/urandom once on construction and reuse the fd for all reads
+CryptoRandom::CryptoRandom()
+ : fd(TEMP_FAILURE_RETRY(::open("/dev/urandom", O_RDONLY)))
{
- char buf[len];
- get_random_bytes(buf, len);
- bl.append(buf, len);
- return 0;
+ if (fd < 0) {
+ throw std::system_error(errno, std::system_category());
+ }
}
-uint64_t get_random(uint64_t min_val, uint64_t max_val)
+CryptoRandom::~CryptoRandom()
{
- uint64_t r;
- get_random_bytes((char *)&r, sizeof(r));
- r = min_val + r % (max_val - min_val + 1);
- return r;
+ VOID_TEMP_FAILURE_RETRY(::close(fd));
}
+void CryptoRandom::get_bytes(char *buf, int len)
+{
+ auto ret = safe_read_exact(fd, buf, len);
+ if (ret < 0) {
+ throw std::system_error(-ret, std::system_category());
+ }
+}
+
+#endif
+
// ---------------------------------------------------
int get_type() const override {
return CEPH_CRYPTO_NONE;
}
- int create(bufferptr& secret) override {
+ int create(CryptoRandom *random, bufferptr& secret) override {
return 0;
}
int validate_secret(const bufferptr& secret) override {
int get_type() const override {
return CEPH_CRYPTO_AES;
}
- int create(bufferptr& secret) override;
+ int create(CryptoRandom *random, bufferptr& secret) override;
int validate_secret(const bufferptr& secret) override;
CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error) override;
};
// ------------------------------------------------------------
-int CryptoAES::create(bufferptr& secret)
+int CryptoAES::create(CryptoRandom *random, bufferptr& secret)
{
- bufferlist bl;
- int r = get_random_bytes(AES_KEY_LEN, bl);
- if (r < 0)
- return r;
- secret = buffer::ptr(bl.c_str(), bl.length());
+ bufferptr buf(AES_KEY_LEN);
+ random->get_bytes(buf.c_str(), buf.length());
+ secret = std::move(buf);
return 0;
}
return -EOPNOTSUPP;
}
bufferptr s;
- int r = ch->create(s);
+ int r = ch->create(nullptr, s); // fixme
delete ch;
if (r < 0)
return r;
class CryptoKeyContext;
namespace ceph { class Formatter; }
+/*
+ * Random byte stream generator suitable for cryptographic use
+ */
+class CryptoRandom {
+ const int fd;
+ public:
+ CryptoRandom(); // throws on failure
+ ~CryptoRandom();
+
+ /// copy up to 256 random bytes into the given buffer. throws on failure
+ void get_bytes(char *buf, int len);
+};
/*
* some per-key context that is specific to a particular crypto backend
public:
virtual ~CryptoHandler() {}
virtual int get_type() const = 0;
- virtual int create(bufferptr& secret) = 0;
+ virtual int create(CryptoRandom *random, bufferptr& secret) = 0;
virtual int validate_secret(const bufferptr& secret) = 0;
virtual CryptoKeyHandler *get_key_handler(const bufferptr& secret,
string& error) = 0;
static CryptoHandler *create(int type);
};
-extern int get_random_bytes(char *buf, int len);
-extern uint64_t get_random(uint64_t min_val, uint64_t max_val);
#endif
}
TEST(AES, Loop) {
- int err;
+ CryptoRandom random;
- char secret_s[16];
- err = get_random_bytes(secret_s, sizeof(secret_s));
- ASSERT_EQ(0, err);
- bufferptr secret(secret_s, sizeof(secret_s));
+ bufferptr secret(16);
+ random.get_bytes(secret.c_str(), secret.length());
- char orig_plaintext_s[1024];
- err = get_random_bytes(orig_plaintext_s, sizeof(orig_plaintext_s));
- ASSERT_EQ(0, err);
+ bufferptr orig_plaintext(1024);
+ random.get_bytes(orig_plaintext.c_str(), orig_plaintext.length());
bufferlist plaintext;
- plaintext.append(orig_plaintext_s, sizeof(orig_plaintext_s));
+ plaintext.append(orig_plaintext.c_str(), orig_plaintext.length());
for (int i=0; i<10000; i++) {
bufferlist cipher;
}
}
- char plaintext_s[sizeof(orig_plaintext_s)];
- plaintext.copy(0, sizeof(plaintext_s), &plaintext_s[0]);
- err = memcmp(plaintext_s, orig_plaintext_s, sizeof(orig_plaintext_s));
- ASSERT_EQ(0, err);
+ bufferlist orig;
+ orig.append(orig_plaintext);
+ ASSERT_EQ(orig, plaintext);
}
TEST(AES, LoopKey) {
+ CryptoRandom random;
bufferptr k(16);
- get_random_bytes(k.c_str(), k.length());
+ random.get_bytes(k.c_str(), k.length());
CryptoKey key(CEPH_CRYPTO_AES, ceph_clock_now(), k);
bufferlist data;
bufferptr r(128);
- get_random_bytes(r.c_str(), r.length());
+ random.get_bytes(r.c_str(), r.length());
data.append(r);
utime_t start = ceph_clock_now();