Introduced PRNG based on Hash_DRBG (NIST SP 800-90A)
This pseudo-random number generator solves problem with platform-independent generation of randomized nonces. Current implementation uses weak entropy, especially when kernel.randomize_va_space = 0. But it can be improved by adding new sources to xmpp_rand_reseed(). New internal API introduced: xmpp_rand_new xmpp_rand_free xmpp_rand xmpp_rand_bytes xmpp_rand_nonce
This commit is contained in:
@@ -23,10 +23,10 @@ libstrophe_la_SOURCES = src/auth.c src/conn.c src/ctx.c \
|
||||
src/event.c src/handler.c src/hash.c \
|
||||
src/jid.c src/md5.c src/sasl.c src/scram.c src/sha1.c \
|
||||
src/snprintf.c src/sock.c src/stanza.c src/thread.c \
|
||||
src/tls_openssl.c src/util.c \
|
||||
src/tls_openssl.c src/util.c src/rand.c \
|
||||
src/common.h src/hash.h src/md5.h src/ostypes.h src/parser.h \
|
||||
src/sasl.h src/scram.h src/sha1.h src/sock.h src/thread.h src/tls.h \
|
||||
src/util.h
|
||||
src/util.h src/rand.h
|
||||
|
||||
if PARSER_EXPAT
|
||||
libstrophe_la_SOURCES += src/parser_expat.c
|
||||
|
||||
27
src/auth.c
27
src/auth.c
@@ -16,12 +16,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "strophe.h"
|
||||
#include "common.h"
|
||||
#include "sasl.h"
|
||||
#include "sha1.h"
|
||||
#include "rand.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strcasecmp stricmp
|
||||
@@ -501,46 +501,25 @@ err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *_get_nonce(xmpp_ctx_t *ctx)
|
||||
{
|
||||
unsigned char buffer[sizeof(clock_t) + sizeof(time_t)] = {0};
|
||||
clock_t ticks = clock();
|
||||
time_t t;
|
||||
|
||||
if (ticks != (clock_t)-1) {
|
||||
*(clock_t *)buffer = ticks;
|
||||
}
|
||||
t = time((time_t *)(buffer + sizeof(clock_t)));
|
||||
if (t == (time_t)-1) {
|
||||
*(time_t *)(buffer + sizeof(clock_t)) = (time_t)rand();
|
||||
}
|
||||
|
||||
return base64_encode(ctx, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
static char *_make_scram_sha1_init_msg(xmpp_conn_t * const conn)
|
||||
{
|
||||
size_t message_len;
|
||||
char *node;
|
||||
char *message;
|
||||
char *nonce;
|
||||
char nonce[32];
|
||||
|
||||
node = xmpp_jid_node(conn->ctx, conn->jid);
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nonce = _get_nonce(conn->ctx);
|
||||
if (!nonce) {
|
||||
return NULL;
|
||||
}
|
||||
xmpp_rand_nonce(conn->ctx, nonce, sizeof(nonce));
|
||||
message_len = strlen(node) + strlen(nonce) + 8 + 1;
|
||||
message = xmpp_alloc(conn->ctx, message_len);
|
||||
if (message) {
|
||||
xmpp_snprintf(message, message_len, "n,,n=%s,r=%s", node, nonce);
|
||||
xmpp_free(conn->ctx, node);
|
||||
}
|
||||
xmpp_free(conn->ctx, nonce);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "hash.h"
|
||||
#include "util.h"
|
||||
#include "parser.h"
|
||||
#include "rand.h"
|
||||
|
||||
/** run-time context **/
|
||||
|
||||
@@ -45,6 +46,7 @@ struct _xmpp_ctx_t {
|
||||
const xmpp_mem_t *mem;
|
||||
const xmpp_log_t *log;
|
||||
|
||||
xmpp_rand_t *rand;
|
||||
xmpp_loop_status_t loop_status;
|
||||
xmpp_connlist_t *connlist;
|
||||
};
|
||||
|
||||
@@ -404,6 +404,11 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
|
||||
|
||||
ctx->connlist = NULL;
|
||||
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
|
||||
ctx->rand = xmpp_rand_new(ctx);
|
||||
if (ctx->rand == NULL) {
|
||||
xmpp_free(ctx, ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ctx;
|
||||
@@ -418,6 +423,7 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
|
||||
void xmpp_ctx_free(xmpp_ctx_t * const ctx)
|
||||
{
|
||||
/* mem and log are owned by their suppliers */
|
||||
xmpp_rand_free(ctx, ctx->rand);
|
||||
xmpp_free(ctx, ctx); /* pull the hole in after us */
|
||||
}
|
||||
|
||||
|
||||
306
src/rand.c
Normal file
306
src/rand.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/* rand.c
|
||||
* strophe XMPP client library -- pseudo-random number generator
|
||||
*
|
||||
* Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||
*
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This software is distributed under license and may not be copied,
|
||||
* modified or distributed except as expressly authorized under the
|
||||
* terms of the license contained in the file LICENSE.txt in this
|
||||
* distribution.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Pseudo-random number generator.
|
||||
*
|
||||
* Implemented Hash_DRBG mechanism according to NIST SP 800-90A.
|
||||
* Hash function is SHA1.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "ostypes.h"
|
||||
#include "sha1.h"
|
||||
|
||||
#define outlen SHA1_DIGEST_SIZE
|
||||
#define seedlen (440 / 8)
|
||||
#define reseed_interval 0x7fffffff
|
||||
|
||||
/* maximum number of bytes that can be generated per call */
|
||||
#define GENERATE_MAX (outlen * 10)
|
||||
#define ENTROPY_MAX 128
|
||||
#define NONCE_MAX 8
|
||||
|
||||
#define RESEED_NEEDED (-1)
|
||||
|
||||
struct Hash_DRBG_CTX_struc {
|
||||
uint8_t V[seedlen];
|
||||
uint8_t C[seedlen];
|
||||
uint32_t reseed_counter;
|
||||
};
|
||||
typedef struct Hash_DRBG_CTX_struc Hash_DRBG_CTX;
|
||||
|
||||
struct _xmpp_rand_t {
|
||||
int inited;
|
||||
unsigned reseed_count;
|
||||
Hash_DRBG_CTX ctx;
|
||||
};
|
||||
|
||||
/* returns smallest number mupliple of y that not less than x */
|
||||
#define round_up(x, y) (((x) + (y) - 1) / (y) * (y))
|
||||
/* returns smallest integer number that not less than x/y */
|
||||
#define div_round_up(x, y) (((x) + (y) - 1) / (y))
|
||||
|
||||
/* adds two arrays as numbers in big-endian representation and stores
|
||||
* result in the first one.
|
||||
*/
|
||||
static void arr_add(uint8_t *arr1, size_t arr1_len,
|
||||
uint8_t *arr2, size_t arr2_len)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t acc;
|
||||
uint32_t carry = 0;
|
||||
|
||||
assert(arr1_len >= arr2_len);
|
||||
|
||||
for (i = 1; (i <= arr2_len) || (carry != 0 && i <= arr1_len); ++i) {
|
||||
acc = (uint32_t)arr1[arr1_len - i] + carry;
|
||||
if (i <= arr2_len)
|
||||
acc += (uint32_t)arr2[arr2_len - i];
|
||||
carry = acc >> 8;
|
||||
arr1[arr1_len - i] = (uint8_t)(acc & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
/* stores 32-bit number in big-endian representation */
|
||||
static void store_be32(uint32_t val, uint8_t be[4])
|
||||
{
|
||||
be[0] = (uint8_t)((val >> 24) & 0xff);
|
||||
be[1] = (uint8_t)((val >> 16) & 0xff);
|
||||
be[2] = (uint8_t)((val >> 8) & 0xff);
|
||||
be[3] = (uint8_t)(val & 0xff);
|
||||
}
|
||||
|
||||
static void Hash_df(uint8_t *input_string, size_t input_string_len,
|
||||
uint8_t *output_string, size_t no_of_bytes_to_return)
|
||||
{
|
||||
uint8_t counter;
|
||||
uint8_t temp[round_up(seedlen, outlen)];
|
||||
uint8_t conj[ENTROPY_MAX + NONCE_MAX + seedlen + 6];
|
||||
size_t len;
|
||||
size_t i;
|
||||
size_t offset;
|
||||
|
||||
assert(no_of_bytes_to_return <= sizeof(temp));
|
||||
assert(input_string_len + 5 <= sizeof(conj));
|
||||
|
||||
len = div_round_up(no_of_bytes_to_return, outlen);
|
||||
for (i = 1; i <= len; ++i) {
|
||||
offset = (i - 1) * outlen;
|
||||
counter = (uint8_t)i;
|
||||
conj[0] = counter;
|
||||
store_be32((uint32_t)no_of_bytes_to_return * 8, conj + 1);
|
||||
memcpy(conj + 5, input_string, input_string_len);
|
||||
SHA1(conj, input_string_len + 5, temp + offset);
|
||||
}
|
||||
|
||||
memcpy(output_string, temp, no_of_bytes_to_return);
|
||||
}
|
||||
|
||||
/* assume personalization_string is zero length string */
|
||||
static void Hash_DRBG_Instantiate(Hash_DRBG_CTX *ctx,
|
||||
uint8_t *entropy_input,
|
||||
size_t entropy_input_len,
|
||||
uint8_t *nonce, size_t nonce_len)
|
||||
{
|
||||
uint8_t seed_material[ENTROPY_MAX + NONCE_MAX];
|
||||
uint8_t seed0[seedlen + 1];
|
||||
uint8_t *seed = seed0 + 1;
|
||||
|
||||
assert(entropy_input_len <= ENTROPY_MAX);
|
||||
assert(nonce_len <= NONCE_MAX);
|
||||
|
||||
memcpy(seed_material, entropy_input, entropy_input_len);
|
||||
memcpy(seed_material + entropy_input_len, nonce, nonce_len);
|
||||
Hash_df(seed_material, entropy_input_len + nonce_len, seed, seedlen);
|
||||
seed0[0] = 0;
|
||||
|
||||
memcpy(ctx->V, seed, seedlen);
|
||||
Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
|
||||
ctx->reseed_counter = 1;
|
||||
}
|
||||
|
||||
/* assume additional_input is zero length string */
|
||||
static void Hash_DRBG_Reseed(Hash_DRBG_CTX *ctx,
|
||||
uint8_t *entropy_input,
|
||||
size_t entropy_input_len)
|
||||
{
|
||||
uint8_t seed_material[1 + seedlen + ENTROPY_MAX];
|
||||
uint8_t seed0[seedlen + 1];
|
||||
uint8_t *seed = seed0 + 1;
|
||||
|
||||
assert(entropy_input_len <= ENTROPY_MAX);
|
||||
|
||||
seed_material[0] = 1;
|
||||
memcpy(seed_material + 1, ctx->V, seedlen);
|
||||
memcpy(seed_material + 1 + seedlen, entropy_input, entropy_input_len);
|
||||
Hash_df(seed_material, entropy_input_len + seedlen + 1, seed, seedlen);
|
||||
seed0[0] = 0;
|
||||
|
||||
memcpy(ctx->V, seed, seedlen);
|
||||
Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
|
||||
ctx->reseed_counter = 1;
|
||||
}
|
||||
|
||||
static void Hashgen(uint8_t *V, uint8_t *output,
|
||||
size_t requested_number_of_bytes)
|
||||
{
|
||||
uint8_t data[seedlen];
|
||||
uint8_t W[GENERATE_MAX];
|
||||
uint8_t i1 = 1;
|
||||
size_t m;
|
||||
size_t i;
|
||||
size_t offset;
|
||||
|
||||
assert(requested_number_of_bytes <= sizeof(W));
|
||||
|
||||
m = div_round_up(requested_number_of_bytes, outlen);
|
||||
memcpy(data, V, seedlen);
|
||||
for (i = 1; i <= m; ++i) {
|
||||
offset = (i - 1) * outlen;
|
||||
SHA1(data, seedlen, W + offset);
|
||||
/* increase data by 1 */
|
||||
arr_add(data, sizeof(data), &i1, 1);
|
||||
}
|
||||
|
||||
memcpy(output, W, requested_number_of_bytes);
|
||||
}
|
||||
|
||||
/* assume additional_input is zero length string */
|
||||
static int Hash_DRBG_Generate(Hash_DRBG_CTX *ctx, uint8_t *output,
|
||||
size_t requested_number_of_bytes)
|
||||
{
|
||||
uint8_t H[outlen];
|
||||
uint8_t V3[seedlen + 1];
|
||||
uint8_t reseed_counter[4];
|
||||
|
||||
if (ctx->reseed_counter > reseed_interval || ctx->reseed_counter == 0)
|
||||
return RESEED_NEEDED;
|
||||
|
||||
Hashgen(ctx->V, output, requested_number_of_bytes);
|
||||
|
||||
V3[0] = 3;
|
||||
memcpy(V3 + 1, ctx->V, seedlen);
|
||||
SHA1(V3, sizeof(V3), H);
|
||||
arr_add(ctx->V, sizeof(ctx->V), ctx->C, sizeof(ctx->C));
|
||||
arr_add(ctx->V, sizeof(ctx->V), H, sizeof(H));
|
||||
store_be32(ctx->reseed_counter, reseed_counter);
|
||||
arr_add(ctx->V, sizeof(ctx->V), reseed_counter, sizeof(reseed_counter));
|
||||
|
||||
++ctx->reseed_counter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ENTROPY_ACCUMULATE(ptr, last, type, arg) \
|
||||
do { \
|
||||
type __arg = (type)(arg); \
|
||||
if ((char*)ptr + sizeof(__arg) < (char*)last && \
|
||||
__arg != (type)-1) \
|
||||
{ \
|
||||
*(type*)ptr = __arg; \
|
||||
ptr = (void*)((char*)ptr + sizeof(__arg)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void xmpp_rand_reseed(xmpp_ctx_t *ctx)
|
||||
{
|
||||
uint8_t entropy[ENTROPY_MAX];
|
||||
uint8_t *ptr = entropy;
|
||||
const uint8_t *last = entropy + sizeof(entropy);
|
||||
size_t len;
|
||||
xmpp_rand_t *rand = ctx->rand;
|
||||
|
||||
/* entropy:
|
||||
* 1. time(2)
|
||||
* 2. clock(3) if != -1
|
||||
* 3. xmpp_ctx_t address to make unique seed within one process
|
||||
* 4. counter to make unique seed within one context
|
||||
* 5. local ports of every connection in list (getsockname)
|
||||
* 6. other non-constant info that can be retieved from socket
|
||||
*
|
||||
* rand(3) can't be used as it isn't thread-safe.
|
||||
* XXX 5 and 6 not implemented yet.
|
||||
*/
|
||||
|
||||
ENTROPY_ACCUMULATE(ptr, last, time_t, time(NULL));
|
||||
ENTROPY_ACCUMULATE(ptr, last, clock_t, clock());
|
||||
ENTROPY_ACCUMULATE(ptr, last, void *, ctx);
|
||||
ENTROPY_ACCUMULATE(ptr, last, unsigned, ++rand->reseed_count);
|
||||
len = ptr - entropy;
|
||||
|
||||
if (rand->inited) {
|
||||
Hash_DRBG_Reseed(&rand->ctx, entropy, len);
|
||||
} else {
|
||||
Hash_DRBG_Instantiate(&rand->ctx, entropy, len, NULL, 0);
|
||||
rand->inited = 1;
|
||||
}
|
||||
}
|
||||
|
||||
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_rand_t *out = xmpp_alloc(ctx, sizeof(*out));
|
||||
if (out != NULL) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
|
||||
{
|
||||
xmpp_free(ctx, rand);
|
||||
}
|
||||
|
||||
void xmpp_rand_bytes(xmpp_ctx_t *ctx, uint8_t *output, size_t len)
|
||||
{
|
||||
int rc;
|
||||
xmpp_rand_t *rand = ctx->rand;
|
||||
|
||||
rc = Hash_DRBG_Generate(&rand->ctx, output, len);
|
||||
if (rc == RESEED_NEEDED) {
|
||||
xmpp_rand_reseed(ctx);
|
||||
rc = Hash_DRBG_Generate(&rand->ctx, output, len);
|
||||
assert(rc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int xmpp_rand(xmpp_ctx_t *ctx)
|
||||
{
|
||||
int result;
|
||||
|
||||
xmpp_rand_bytes(ctx, (uint8_t *)&result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
void xmpp_rand_nonce(xmpp_ctx_t *ctx, char *output, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
size_t rand_len = len / 2;
|
||||
uint8_t rand_buf[rand_len];
|
||||
|
||||
/* current implementation returns printable HEX representation of
|
||||
* a random buffer, however base64 encoding can be used instead;
|
||||
* the only problem is that base64_encode() allocates memory and
|
||||
* as result can fail.
|
||||
*/
|
||||
|
||||
xmpp_rand_bytes(ctx, rand_buf, rand_len);
|
||||
for (i = 0; i < rand_len; ++i) {
|
||||
xmpp_snprintf(output + i * 2, len, "%02x", (unsigned char)rand_buf[i]);
|
||||
len -= 2;
|
||||
}
|
||||
}
|
||||
43
src/rand.h
Normal file
43
src/rand.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* rand.h
|
||||
* strophe XMPP client library -- pseudo-random number generator
|
||||
*
|
||||
* Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||
*
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This software is distributed under license and may not be copied,
|
||||
* modified or distributed except as expressly authorized under the
|
||||
* terms of the license contained in the file LICENSE.txt in this
|
||||
* distribution.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Pseudo-random number generator.
|
||||
*/
|
||||
|
||||
#ifndef __LIBSTROPHE_RAND_H__
|
||||
#define __LIBSTROPHE_RAND_H__
|
||||
|
||||
#include "strophe.h"
|
||||
#include "ostypes.h"
|
||||
|
||||
typedef struct _xmpp_rand_t xmpp_rand_t;
|
||||
|
||||
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx);
|
||||
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand);
|
||||
|
||||
/** Analogue of rand(3). */
|
||||
int xmpp_rand(xmpp_ctx_t *ctx);
|
||||
|
||||
/** Generates random bytes. */
|
||||
void xmpp_rand_bytes(xmpp_ctx_t *ctx, uint8_t *output, size_t len);
|
||||
|
||||
/** Generates a nonce that is printable randomized string.
|
||||
*
|
||||
* @param len Number of bytes reserved for the output string, including
|
||||
* end of line '\0'.
|
||||
*/
|
||||
void xmpp_rand_nonce(xmpp_ctx_t *ctx, char *output, size_t len);
|
||||
|
||||
#endif /* __LIBSTROPHE_RAND_H__ */
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "md5.h"
|
||||
#include "sha1.h"
|
||||
#include "scram.h"
|
||||
#include "rand.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define strtok_r strtok_s
|
||||
@@ -218,6 +219,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
||||
struct MD5Context MD5;
|
||||
unsigned char digest[16], HA1[16], HA2[16];
|
||||
char hex[32];
|
||||
char cnonce[13];
|
||||
|
||||
/* our digest response is
|
||||
Hex( KD( HEX(MD5(A1)),
|
||||
@@ -250,8 +252,8 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
||||
|
||||
/* add our response fields */
|
||||
hash_add(table, "username", xmpp_strdup(ctx, node));
|
||||
/* TODO: generate a random cnonce */
|
||||
hash_add(table, "cnonce", xmpp_strdup(ctx, "00DEADBEEF00"));
|
||||
xmpp_rand_nonce(ctx, cnonce, sizeof(cnonce));
|
||||
hash_add(table, "cnonce", xmpp_strdup(ctx, cnonce));
|
||||
hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
|
||||
hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
|
||||
value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
|
||||
|
||||
Reference in New Issue
Block a user