summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2018-04-05 13:24:22 +0200
committerantirez <antirez@gmail.com>2018-04-05 13:24:22 +0200
commitb2868c7b9cfed0e517547768d2bb9178d409daef (patch)
treed5498c84cc0c8666ad666d54ab86963ad4f648fe
parentc75582889a49ed15524a05f855d2a6327451c8c6 (diff)
downloadredis-b2868c7b9cfed0e517547768d2bb9178d409daef.tar.gz
Modules API: RM_GetRandomBytes() / GetRandomHexChars().
-rw-r--r--src/module.c21
-rw-r--r--src/redismodule.h5
-rw-r--r--src/server.h3
-rw-r--r--src/util.c102
4 files changed, 71 insertions, 60 deletions
diff --git a/src/module.c b/src/module.c
index 1aa398849..7cf1a24bd 100644
--- a/src/module.c
+++ b/src/module.c
@@ -4184,6 +4184,25 @@ int RM_GetTimerInfo(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remain
}
/* --------------------------------------------------------------------------
+ * Modules utility APIs
+ * -------------------------------------------------------------------------- */
+
+/* Return random bytes using SHA1 in counter mode with a /dev/urandom
+ * initialized seed. This function is fast so can be used to generate
+ * many bytes without any effect on the operating system entropy pool.
+ * Currently this function is not thread safe. */
+void RM_GetRandomBytes(unsigned char *dst, size_t len) {
+ getRandomBytes(dst,len);
+}
+
+/* Like RedisModule_GetRandomBytes() but instead of setting the string to
+ * random bytes the string is set to random characters in the in the
+ * hex charset [0-9a-f]. */
+void RM_GetRandomHexChars(char *dst, size_t len) {
+ getRandomHexChars(dst,len);
+}
+
+/* --------------------------------------------------------------------------
* Modules API internals
* -------------------------------------------------------------------------- */
@@ -4575,4 +4594,6 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(GetTimerInfo);
REGISTER_API(GetMyClusterID);
REGISTER_API(GetClusterSize);
+ REGISTER_API(GetRandomBytes);
+ REGISTER_API(GetRandomHexChars);
}
diff --git a/src/redismodule.h b/src/redismodule.h
index d232c0fa0..f663006ab 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -278,6 +278,9 @@ int REDISMODULE_API_FUNC(RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModule
int REDISMODULE_API_FUNC(RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data);
const char *REDISMODULE_API_FUNC(RedisModule_GetMyClusterID)(void);
size_t REDISMODULE_API_FUNC(RedisModule_GetClusterSize)(void);
+void REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t len);
+void REDISMODULE_API_FUNC(RedisModule_GetRandomHexChars)(char *dst, size_t len);
+
/* Experimental APIs */
#ifdef REDISMODULE_EXPERIMENTAL_API
@@ -411,6 +414,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(GetTimerInfo);
REDISMODULE_GET_API(GetMyClusterID);
REDISMODULE_GET_API(GetClusterSize);
+ REDISMODULE_GET_API(GetRandomBytes);
+ REDISMODULE_GET_API(GetRandomHexChars);
#ifdef REDISMODULE_EXPERIMENTAL_API
REDISMODULE_GET_API(GetThreadSafeContext);
diff --git a/src/server.h b/src/server.h
index 682129d0b..09c99bdd4 100644
--- a/src/server.h
+++ b/src/server.h
@@ -1366,7 +1366,8 @@ void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid)
/* Utils */
long long ustime(void);
long long mstime(void);
-void getRandomHexChars(char *p, unsigned int len);
+void getRandomHexChars(char *p, size_t len);
+void getRandomBytes(unsigned char *p, size_t len);
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
void exitFromChild(int retcode);
size_t redisPopcount(void *s, long count);
diff --git a/src/util.c b/src/util.c
index 36cbc43d3..db5b018d5 100644
--- a/src/util.c
+++ b/src/util.c
@@ -536,14 +536,12 @@ int ld2string(char *buf, size_t len, long double value, int humanfriendly) {
return l;
}
-/* Generate the Redis "Run ID", a SHA1-sized random number that identifies a
- * given execution of Redis, so that if you are talking with an instance
- * having run_id == A, and you reconnect and it has run_id == B, you can be
- * sure that it is either a different instance or it was restarted. */
-void getRandomHexChars(char *p, unsigned int len) {
- char *charset = "0123456789abcdef";
- unsigned int j;
-
+/* Get random bytes, attempts to get an initial seed from /dev/urandom and
+ * the uses a one way hash function in counter mode to generate a random
+ * stream. However if /dev/urandom is not available, a weaker seed is used.
+ *
+ * This function is not thread safe, since the state is global. */
+void getRandomBytes(unsigned char *p, size_t len) {
/* Global state. */
static int seed_initialized = 0;
static unsigned char seed[20]; /* The SHA1 seed, from /dev/urandom. */
@@ -555,64 +553,50 @@ void getRandomHexChars(char *p, unsigned int len) {
* function we just need non-colliding strings, there are no
* cryptographic security needs. */
FILE *fp = fopen("/dev/urandom","r");
- if (fp && fread(seed,sizeof(seed),1,fp) == 1)
+ if (fp == NULL || fread(seed,sizeof(seed),1,fp) != 1) {
+ /* Revert to a weaker seed, and in this case reseed again
+ * at every call.*/
+ for (unsigned int j = 0; j < sizeof(seed); j++) {
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+ pid_t pid = getpid();
+ seed[j] = tv.tv_sec ^ tv.tv_usec ^ pid ^ (long)fp;
+ }
+ } else {
seed_initialized = 1;
+ }
if (fp) fclose(fp);
}
- if (seed_initialized) {
- while(len) {
- unsigned char digest[20];
- SHA1_CTX ctx;
- unsigned int copylen = len > 20 ? 20 : len;
-
- SHA1Init(&ctx);
- SHA1Update(&ctx, seed, sizeof(seed));
- SHA1Update(&ctx, (unsigned char*)&counter,sizeof(counter));
- SHA1Final(digest, &ctx);
- counter++;
-
- memcpy(p,digest,copylen);
- /* Convert to hex digits. */
- for (j = 0; j < copylen; j++) p[j] = charset[p[j] & 0x0F];
- len -= copylen;
- p += copylen;
- }
- } else {
- /* If we can't read from /dev/urandom, do some reasonable effort
- * in order to create some entropy, since this function is used to
- * generate run_id and cluster instance IDs */
- char *x = p;
- unsigned int l = len;
- struct timeval tv;
- pid_t pid = getpid();
-
- /* Use time and PID to fill the initial array. */
- gettimeofday(&tv,NULL);
- if (l >= sizeof(tv.tv_usec)) {
- memcpy(x,&tv.tv_usec,sizeof(tv.tv_usec));
- l -= sizeof(tv.tv_usec);
- x += sizeof(tv.tv_usec);
- }
- if (l >= sizeof(tv.tv_sec)) {
- memcpy(x,&tv.tv_sec,sizeof(tv.tv_sec));
- l -= sizeof(tv.tv_sec);
- x += sizeof(tv.tv_sec);
- }
- if (l >= sizeof(pid)) {
- memcpy(x,&pid,sizeof(pid));
- l -= sizeof(pid);
- x += sizeof(pid);
- }
- /* Finally xor it with rand() output, that was already seeded with
- * time() at startup, and convert to hex digits. */
- for (j = 0; j < len; j++) {
- p[j] ^= rand();
- p[j] = charset[p[j] & 0x0F];
- }
+ while(len) {
+ unsigned char digest[20];
+ SHA1_CTX ctx;
+ unsigned int copylen = len > 20 ? 20 : len;
+
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, seed, sizeof(seed));
+ SHA1Update(&ctx, (unsigned char*)&counter,sizeof(counter));
+ SHA1Final(digest, &ctx);
+ counter++;
+
+ memcpy(p,digest,copylen);
+ len -= copylen;
+ p += copylen;
}
}
+/* Generate the Redis "Run ID", a SHA1-sized random number that identifies a
+ * given execution of Redis, so that if you are talking with an instance
+ * having run_id == A, and you reconnect and it has run_id == B, you can be
+ * sure that it is either a different instance or it was restarted. */
+void getRandomHexChars(char *p, size_t len) {
+ char *charset = "0123456789abcdef";
+ size_t j;
+
+ getRandomBytes((unsigned char*)p,len);
+ for (j = 0; j < len; j++) p[j] = charset[p[j] & 0x0F];
+}
+
/* Given the filename, return the absolute path as an SDS string, or NULL
* if it fails for some reason. Note that "filename" may be an absolute path
* already, this will be detected and handled correctly.