Modules API: RM_GetRandomBytes() / GetRandomHexChars().

This commit is contained in:
antirez 2018-04-05 13:24:22 +02:00
parent c75582889a
commit b2868c7b9c
4 changed files with 69 additions and 58 deletions

View File

@ -4183,6 +4183,25 @@ int RM_GetTimerInfo(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remain
return REDISMODULE_OK; return REDISMODULE_OK;
} }
/* --------------------------------------------------------------------------
* 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 * Modules API internals
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
@ -4575,4 +4594,6 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(GetTimerInfo); REGISTER_API(GetTimerInfo);
REGISTER_API(GetMyClusterID); REGISTER_API(GetMyClusterID);
REGISTER_API(GetClusterSize); REGISTER_API(GetClusterSize);
REGISTER_API(GetRandomBytes);
REGISTER_API(GetRandomHexChars);
} }

View File

@ -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); int REDISMODULE_API_FUNC(RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data);
const char *REDISMODULE_API_FUNC(RedisModule_GetMyClusterID)(void); const char *REDISMODULE_API_FUNC(RedisModule_GetMyClusterID)(void);
size_t REDISMODULE_API_FUNC(RedisModule_GetClusterSize)(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 */ /* Experimental APIs */
#ifdef REDISMODULE_EXPERIMENTAL_API #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(GetTimerInfo);
REDISMODULE_GET_API(GetMyClusterID); REDISMODULE_GET_API(GetMyClusterID);
REDISMODULE_GET_API(GetClusterSize); REDISMODULE_GET_API(GetClusterSize);
REDISMODULE_GET_API(GetRandomBytes);
REDISMODULE_GET_API(GetRandomHexChars);
#ifdef REDISMODULE_EXPERIMENTAL_API #ifdef REDISMODULE_EXPERIMENTAL_API
REDISMODULE_GET_API(GetThreadSafeContext); REDISMODULE_GET_API(GetThreadSafeContext);

View File

@ -1366,7 +1366,8 @@ void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid)
/* Utils */ /* Utils */
long long ustime(void); long long ustime(void);
long long mstime(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); uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
void exitFromChild(int retcode); void exitFromChild(int retcode);
size_t redisPopcount(void *s, long count); size_t redisPopcount(void *s, long count);

View File

@ -536,14 +536,12 @@ int ld2string(char *buf, size_t len, long double value, int humanfriendly) {
return l; return l;
} }
/* Generate the Redis "Run ID", a SHA1-sized random number that identifies a /* Get random bytes, attempts to get an initial seed from /dev/urandom and
* given execution of Redis, so that if you are talking with an instance * the uses a one way hash function in counter mode to generate a random
* having run_id == A, and you reconnect and it has run_id == B, you can be * stream. However if /dev/urandom is not available, a weaker seed is used.
* sure that it is either a different instance or it was restarted. */ *
void getRandomHexChars(char *p, unsigned int len) { * This function is not thread safe, since the state is global. */
char *charset = "0123456789abcdef"; void getRandomBytes(unsigned char *p, size_t len) {
unsigned int j;
/* Global state. */ /* Global state. */
static int seed_initialized = 0; static int seed_initialized = 0;
static unsigned char seed[20]; /* The SHA1 seed, from /dev/urandom. */ static unsigned char seed[20]; /* The SHA1 seed, from /dev/urandom. */
@ -555,12 +553,21 @@ void getRandomHexChars(char *p, unsigned int len) {
* function we just need non-colliding strings, there are no * function we just need non-colliding strings, there are no
* cryptographic security needs. */ * cryptographic security needs. */
FILE *fp = fopen("/dev/urandom","r"); 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; seed_initialized = 1;
}
if (fp) fclose(fp); if (fp) fclose(fp);
} }
if (seed_initialized) {
while(len) { while(len) {
unsigned char digest[20]; unsigned char digest[20];
SHA1_CTX ctx; SHA1_CTX ctx;
@ -573,44 +580,21 @@ void getRandomHexChars(char *p, unsigned int len) {
counter++; counter++;
memcpy(p,digest,copylen); memcpy(p,digest,copylen);
/* Convert to hex digits. */
for (j = 0; j < copylen; j++) p[j] = charset[p[j] & 0x0F];
len -= copylen; len -= copylen;
p += 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. */ /* Generate the Redis "Run ID", a SHA1-sized random number that identifies a
gettimeofday(&tv,NULL); * given execution of Redis, so that if you are talking with an instance
if (l >= sizeof(tv.tv_usec)) { * having run_id == A, and you reconnect and it has run_id == B, you can be
memcpy(x,&tv.tv_usec,sizeof(tv.tv_usec)); * sure that it is either a different instance or it was restarted. */
l -= sizeof(tv.tv_usec); void getRandomHexChars(char *p, size_t len) {
x += sizeof(tv.tv_usec); char *charset = "0123456789abcdef";
} size_t j;
if (l >= sizeof(tv.tv_sec)) {
memcpy(x,&tv.tv_sec,sizeof(tv.tv_sec)); getRandomBytes((unsigned char*)p,len);
l -= sizeof(tv.tv_sec); for (j = 0; j < len; j++) p[j] = charset[p[j] & 0x0F];
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];
}
}
} }
/* Given the filename, return the absolute path as an SDS string, or NULL /* Given the filename, return the absolute path as an SDS string, or NULL